diff --git a/CMakeLists.txt b/CMakeLists.txt index 3708d3590..0441197fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,6 +174,19 @@ IF(JPEG_FOUND) ELSE() MESSAGE(WARNING "libjpeg library NOT available. Who cares?") SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_JPEG) + SET(JPEG_LIBRARIES) +ENDIF() + +SET(FTE_DEP_DBUS true CACHE BOOL "Link against libdbus.") +IF(FTE_DEP_DBUS) + FIND_PACKAGE(DBus1) +ENDIF() +IF(DBUS1_FOUND) + INCLUDE_DIRECTORIES( ${DBus1_INCLUDE_DIRS} ) + SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};HAVE_DBUS) + SET(FTE_LIBS ${FTE_LIBS} ${DBus1_LIBRARIES}) +ELSE() + MESSAGE(WARNING "libdbus-1 library NOT available. Who cares?") ENDIF() SET(FTE_DEP_PNG true CACHE BOOL "Link against libpng.") @@ -187,6 +200,7 @@ IF(PNG_FOUND) ELSE() MESSAGE(WARNING "libpng library NOT available. Good luck with screenshots.") SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_PNG) + SET(PNG_LIBRARIES) ENDIF() SET(FTE_DEP_FREETYPE true CACHE BOOL "Link against libfreetype.") @@ -221,7 +235,7 @@ ELSE() ENDIF() SET(FTE_DEP_VORBISFILE true CACHE BOOL "Link against libvorbisfile.") -IF(FTE_DEP_VROBISFILE) +IF(FTE_DEP_VORBISFILE) FIND_LIBRARY(VORBISFILE_LIBRARY NAMES vorbisfile) ENDIF() IF(NOT VORBISFILE_LIBRARY) @@ -230,18 +244,30 @@ IF(NOT VORBISFILE_LIBRARY) SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_OGG) ENDIF() +IF(CMAKE_BUILD_TYPE MATCHES "Debug") + SET(FTE_WERROR true CACHE BOOL "Warnings as errors.") +ELSE() + SET(FTE_WERROR false CACHE BOOL "Warnings as errors.") +ENDIF() +IF(FTE_WERROR) + SET(FTE_WERROR_ARG "-Werror") +ELSE() + SET(FTE_WERROR_ARG "") +ENDIF() + IF(CMAKE_C_COMPILER_ID MATCHES "Clang") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-pointer-sign") IF(CMAKE_BUILD_TYPE MATCHES "Debug") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wno-pointer-sign -Wno-unknown-pragmas -Wno-format-zero-length -Wno-strict-aliasing -Wno-error=cpp") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall ${FTE_WERROR_ARG} -Wno-pointer-sign -Wno-unknown-pragmas -Wno-format-zero-length -Wno-strict-aliasing -Wno-error=cpp") ELSE() - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 ${FTE_WERROR_ARG}") ENDIF() endif() IF(CMAKE_C_COMPILER_ID MATCHES "GNU") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-prototypes") # SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wold-style-definition") #k&r c is weird and can't cope with 64bit types. + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-parameter-type") #k&r c is weird and can't cope with 64bit types. SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wold-style-declaration") # SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wpointer-arith") #void* stuff SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wvla") #msvc doesn't support vla @@ -254,9 +280,9 @@ IF(CMAKE_C_COMPILER_ID MATCHES "GNU") #might as well do this, public builds use the regular Makefile. set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native") IF(CMAKE_BUILD_TYPE MATCHES "Debug") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wno-pointer-sign -Wno-unknown-pragmas -Wno-format-zero-length -Wno-strict-aliasing -Wno-error=cpp") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall ${FTE_WERROR_} -Wno-pointer-sign -Wno-unknown-pragmas -Wno-format-zero-length -Wno-strict-aliasing -Wno-error=cpp") ELSE() - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 ${FTE_WERROR_}") ENDIF() IF (NOT FTE_USE_SDL) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,--warn-common") @@ -266,8 +292,8 @@ ENDIF() IF(CMAKE_BUILD_TYPE MATCHES "Debug") IF(NOT ${WIN32}) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu89") ENDIF() - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu89") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_DEBUG") ENDIF() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FILE_OFFSET_BITS=64") @@ -289,6 +315,32 @@ FUNCTION(EMBED_PLUGIN_META PLUGNAME PLUGTITLE PLUGDESC) VERBATIM) ENDFUNCTION() +SET(FTE_DEP_GNUTLS true CACHE BOOL "Link against gnutls") +IF(FTE_DEP_GNUTLS) + FIND_PACKAGE(GnuTLS) + IF(NOT GNUTLS_FOUND) + MESSAGE(WARNING "gnutls library NOT available. HTTPS/DTLS will not be available.") + SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_GNUTLS) + ELSE() + IF(WIN32) + SET(GNUTLS_STATIC true CACHE BOOL "Link gnutls statically.") #usually as an .so though. :/ + ELSE() + SET(GNUTLS_STATIC false CACHE BOOL "Link gnutls statically.") #usually as an .so though. :/ + ENDIF() + IF(GNUTLS_STATIC) + SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};GNUTLS_STATIC) + SET(FTE_LIBS ${FTE_LIBS} ${GNUTLS_LIBRARY}) + SET(FTESV_LIBS ${FTESV_LIBS} ${GNUTLS_LIBRARY}) + ENDIF() + ENDIF() +ENDIF() +IF(WIN32) + SET(FTE_DEP_WINSSPI true CACHE BOOL "Link against winsspi(schannel)") + IF(NOT FTE_DEP_WINSSPI) + SET(FTE_DEFINES ${FTE_DEFINES};NO_WINSSPI) + ENDIF() +ENDIF() + IF(${ANDROID}) # FIND_PACKAGE(Freetype REQUIRED) @@ -315,6 +367,7 @@ ELSEIF(WIN32 AND NOT FTE_USE_SDL) engine/client/winquake.rc engine/common/sys_win_threads.c engine/common/net_ssl_winsspi.c + engine/common/net_ssl_gnutls.c engine/common/fs_win32.c engine/client/cd_win.c engine/client/in_win.c @@ -345,25 +398,13 @@ ELSEIF(WIN32 AND NOT FTE_USE_SDL) engine/client/winquake.rc engine/common/sys_win_threads.c engine/common/net_ssl_winsspi.c + engine/common/net_ssl_gnutls.c engine/common/fs_win32.c engine/server/sv_sys_win.c ) ELSEIF(UNIX AND NOT FTE_USE_SDL) #linux(ish) #openbsd will have issues with snd_linux.c - FIND_PACKAGE(GnuTLS) - IF(NOT GNUTLS_FOUND) - MESSAGE(WARNING "gnutls library NOT available. HTTPS/DTLS will not be available.") - SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_GNUTLS) - ELSE() - SET(GNUTLS_STATIC false CACHE BOOL "Link gnutls statically.") #usually as an .so though. :/ - IF(GNUTLS_STATIC) - SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};GNUTLS_STATIC) - SET(FTE_LIBS ${FTE_LIBS} ${GNUTLS_LIBRARY}) - SET(FTESV_LIBS ${FTESV_LIBS} ${GNUTLS_LIBRARY}) - ENDIF() - ENDIF() - #linux-only packages FIND_PACKAGE(ALSA) IF(ALSA_FOUND) @@ -382,6 +423,10 @@ ELSEIF(UNIX AND NOT FTE_USE_SDL) #linux(ish) SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_X11_RANDR) MESSAGE(WARNING "Xrandr library NOT available.") ENDIF() + IF (NOT X11_Xscreensaver_FOUND) + SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_X11_XSS) + MESSAGE(WARNING "Xss library NOT available.") + ENDIF() ELSE() MESSAGE(WARNING "x11 library NOT available.") SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_X11) @@ -450,7 +495,7 @@ ELSEIF(UNIX AND NOT FTE_USE_SDL) #linux(ish) ENDIF() ENDIF() - SET(FTESV_DEFINES MULTITHREAD) + SET(FTESV_DEFINES ${FTESV_DEFINES};MULTITHREAD) SET(FTESV_ARCH_FILES ${FTESV_ARCH_FILES} engine/server/sv_sys_unix.c engine/common/sys_linux_threads.c @@ -481,7 +526,7 @@ ELSEIF(1) #SDL engine/gl/gl_vidsdl.c ) - SET(FTESV_DEFINES FTE_SDL) + SET(FTESV_DEFINES ${FTESV_DEFINES};MULTITHREAD) SET(FTESV_LIBS ${FTESV_LIBS} ${SYS_LIBS} ${CMAKE_DL_LIBS} ${SDL2_LIBRARIES}) IF(WIN32) @@ -505,6 +550,7 @@ ELSEIF(1) #SDL engine/common/sys_linux_threads.c engine/server/sv_sys_unix.c ) + SET(FTESV_LIBS ${FTESV_LIBS} pthread) ENDIF() ELSE() # engine/common/sys_linux_threads.c @@ -862,7 +908,7 @@ SET(FTE_Q3_FILES ) #For annoying compressed gltf2 files. -SET(FTE_DEP_DRACO false CACHE BOOL "Link against libdraco.") +SET(FTE_DEP_DRACO false CACHE BOOL "Link against libdraco (apache2).") IF(FTE_DEP_DRACO) FIND_LIBRARY( DRACO_LIBRARY @@ -1052,8 +1098,8 @@ ELSE() imgtool.c iqm/iqm.h ) - SET_TARGET_PROPERTIES(iqmtool PROPERTIES COMPILE_DEFINITIONS "IQMTOOL;${DRACO_CFLAGS};${FTE_REVISON}") - TARGET_LINK_LIBRARIES(iqmtool ${CMAKE_DL_LIBS} ${DRACO_LIBRARY}) + SET_TARGET_PROPERTIES(iqmtool PROPERTIES COMPILE_DEFINITIONS "IQMTOOL;${DRACO_CFLAGS};${FTE_LIB_DEFINES};${FTE_REVISON}") + TARGET_LINK_LIBRARIES(iqmtool ${CMAKE_DL_LIBS} ${DRACO_LIBRARY} ${JPEG_LIBRARIES} ${PNG_LIBRARIES}) SET(INSTALLTARGS ${INSTALLTARGS} iqmtool) ENDIF() @@ -1151,6 +1197,7 @@ ELSE() engine/qclib/qcc_pr_lex.c engine/qclib/qccmain.c engine/qclib/qcd_main.c + engine/qclib/decomp.c engine/qclib/packager.c ) SET_TARGET_PROPERTIES(fteqcc PROPERTIES COMPILE_DEFINITIONS "${FTE_LIB_DEFINES};${FTE_REVISON}") @@ -1260,6 +1307,8 @@ SET(FTE_PLUG_OPENSSL false CACHE BOOL "Compile OpenSSL.") IF(FTE_PLUG_OPENSSL) #the openssl license is incompatible with the GPL, so while we have code to use it distributing the binaries built with it is not a (legal) option. #note that openssl 3.0.0 upwards are apache-2 licensed, which IS gpl-3 compatible (though not gpl-2). debian has not caught up with that yet, however. + #Crosscompile linux->win64: sudo ln -s ${pwd}/engine/libs-x86_64-w64-mingw32/openssl-openssl-3.0.1/ /usr/x86_64-w64-mingw32/OpenSSL + SET(OPENSSL_USE_STATIC_LIBS true CACHE BOOL "Link openssl statically.") #usually as an .so though. :/) FIND_PACKAGE(OpenSSL) IF(OPENSSL_VERSION_MAJOR LESS 3) SET(FTE_PRIVATE_USE_ONLY false CACHE BOOL "Ignore license violations.") @@ -1275,11 +1324,15 @@ IF(FTE_PLUG_OPENSSL) MESSAGE(WARNING "Using openssl. Resulting plugin must be licensed as GPLv3.") ENDIF() SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES}) + if (WIN32) + SET(OPENSSL_LIBRARIES ${OPENSSL_LIBRARIES} ws2_32) + ENDIF() ADD_LIBRARY(plug_openssl MODULE plugins/plugin.c plugins/net_ssl_openssl.c ) + TARGET_INCLUDE_DIRECTORIES(plug_openssl PRIVATE ${OPENSSL_INCLUDE_DIR}) SET_TARGET_PROPERTIES(plug_openssl PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN;${FTE_LIB_DEFINES}") TARGET_LINK_LIBRARIES(plug_openssl ${SYS_LIBS} ${OPENSSL_LIBRARIES}) @@ -1291,7 +1344,7 @@ ENDIF() #IF(FTE_PLUG_GNUTLS) # FIND_PACKAGE(GnuTLS) # IF(NOT GNUTLS_FOUND) -# MESSAGE(WARNING "openssl library NOT available. you'll have to use some other library.") +# MESSAGE(WARNING "gnutls library NOT available. you'll have to use some other library.") # ELSE() # SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES}) # @@ -1628,7 +1681,8 @@ IF(FTE_MENU_SYS) DEPENDS fteqcc WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/quakec/menusys/" COMMAND fteqcc -srcfile "menu.src" -o "${CMAKE_CURRENT_BINARY_DIR}/menu.dat" -DREVISION="${SVNREVISION}" -DDATE="${FTE_DATE}" -DBRANCH="${FTE_BRANCH}" - BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/menu.dat" "${CMAKE_CURRENT_BINARY_DIR}/menu.lno" + COMMAND /bin/echo -e "{\\n package fte_menusys\\n ver \"${SVNREVISION}\"\\n category Plugins\\n title \"Replacement Menus\"\\n gamedir \"id1\"\\n desc \"Modern menus to replace the ancient quake ones\"\\n}" | zip -j -q -9 -fz- "${CMAKE_CURRENT_BINARY_DIR}/menusys.pk3" - "${CMAKE_CURRENT_BINARY_DIR}/menu.dat" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/menu.dat" "${CMAKE_CURRENT_BINARY_DIR}/menu.lno" "${CMAKE_CURRENT_BINARY_DIR}/menusys.pk3" SOURCES quakec/menusys/menu.src quakec/menusys/fteextensions.qc @@ -1665,12 +1719,8 @@ IF(FTE_MENU_SYS) quakec/menusys/menu/quit.qc ) - ADD_CUSTOM_COMMAND( - TARGET menusys POST_BUILD - COMMAND /bin/echo -e "{\\n package fte_menusys\\n ver \"${SVNREVISION}\"\\n category Plugins\\n title \"Replacement Menus\"\\n gamedir \"id1\"\\n desc \"Modern menus to replace the ancient quake ones\"\\n}" | zip -q -9 -fz- menusys.pk3 - menu.dat - VERBATIM) INSTALL(FILES - menusys.pk3 + ${CMAKE_CURRENT_BINARY_DIR}/menusys.pk3 DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/games/quake/id1/") ENDIF() @@ -1681,7 +1731,8 @@ IF(FTE_CSADDON) DEPENDS fteqcc WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/quakec/csaddon/src/" COMMAND fteqcc -srcfile "csaddon.src" -o "${CMAKE_CURRENT_BINARY_DIR}/csaddon.dat" - BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/csaddon.dat" "${CMAKE_CURRENT_BINARY_DIR}/csaddon.lno" + COMMAND /bin/echo -e "{\\n package fte_csaddon\\n ver \"${SVNREVISION}\"\\n category Plugins\\n title \"${PLUGTITLE}\"\\n gamedir \"id1\"\\n desc \"${PLUGDESC}\"\\n}" | zip -j -q -9 -fz- "${CMAKE_CURRENT_BINARY_DIR}/csaddon.pk3" - "${CMAKE_CURRENT_BINARY_DIR}/csaddon.dat" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/csaddon.dat" "${CMAKE_CURRENT_BINARY_DIR}/csaddon.lno" "${CMAKE_CURRENT_BINARY_DIR}/csaddon.pk3" SOURCES quakec/csaddon/src/csaddon.src @@ -1704,11 +1755,7 @@ IF(FTE_CSADDON) quakec/csaddon/src/csaddon.qc ) - ADD_CUSTOM_COMMAND( - TARGET csaddon POST_BUILD - COMMAND /bin/echo -e "{\\n package fte_csaddon\\n ver \"${SVNREVISION}\"\\n category Plugins\\n title \"${PLUGTITLE}\"\\n gamedir \"id1\"\\n desc \"${PLUGDESC}\"\\n}" | zip -q -9 -fz- csaddon.pk3 - csaddon.dat - VERBATIM) INSTALL(FILES - csaddon.pk3 + ${CMAKE_CURRENT_BINARY_DIR}/csaddon.pk3 DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/games/quake/id1/") ENDIF() diff --git a/engine/Makefile b/engine/Makefile index 5375e404b..5e358fd64 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -137,14 +137,10 @@ endif ifneq (,$(findstring DLINK_QUAKE3,$(FTE_CONFIG_EXTRA))) LINK_QUAKE3=1 endif +ifeq ($(findstring web,$(FTE_TARGET)),) #the web target uses javascript/browser loaders for common audio+image files instead of needing to embed our own, which keeps sizes down slightly. ifeq (,$(findstring DNO_VORBISFILE,$(FTE_CONFIG_EXTRA))) USE_VORBISFILE=1 endif -ifneq (,$(findstring DLINK_FREETYPE,$(FTE_CONFIG_EXTRA))) - LINK_FREETYPE=1 - LINK_ZLIB=1 - LINK_PNG=1 -endif ifneq (,$(findstring DLINK_JPEG,$(FTE_CONFIG_EXTRA))) LINK_JPEG=1 endif @@ -152,6 +148,12 @@ ifneq (,$(findstring DLINK_PNG,$(FTE_CONFIG_EXTRA))) LINK_ZLIB=1 LINK_PNG=1 endif +endif +ifneq (,$(findstring DLINK_FREETYPE,$(FTE_CONFIG_EXTRA))) + LINK_FREETYPE=1 + LINK_ZLIB=1 + LINK_PNG=1 +endif ifneq (,$(findstring -Os,$(FTE_CONFIG_EXTRA))) CPUOPTIMIZATIONS+=-Os BRANDFLAGS:=$(filter-out -O%,$(BRANDFLAGS)) @@ -164,6 +166,12 @@ ifneq (,$(findstring DLINK_INTERNAL_BULLET,$(FTE_CONFIG_EXTRA))) #bullet plugin will be built into the exe itself INTERNAL_BULLET=1 endif +ifneq (,$(findstring DLINK_EZHUD,$(FTE_CONFIG_EXTRA))) + LINK_EZHUD=1 +endif +ifneq (,$(findstring DLINK_OPENSSL,$(FTE_CONFIG_EXTRA))) + LINK_OPENSSL=1 +endif ifeq ($(BITS),64) CC:=$(CC) -m64 @@ -249,8 +257,9 @@ DO_CMAKE=cmake -DCMAKE_C_COMPILER="$(firstword $(CC))" -DCMAKE_C_FLAGS="$(wordli ifeq ($(DROID_ARCH),) #armeabi armeabi-v7a arm64-v8a x86 x86_64 mips mips64 - DROID_ARCH=armeabi-v7a - DROID_ARCH+=x86 + DROID_ARCH+=armeabi-v7a #old 32bit android. yucky. + DROID_ARCH+=arm64-v8a #modern android devices are 64bit-only. urgh. + DROID_ARCH+=x86 #mostly for testing... #DROID_ARCH+=x86_64 #starting with DROID_API_LEVEL 21 endif ifeq ($(FTE_TARGET),droid) @@ -632,8 +641,6 @@ ifeq (win,$(findstring cyg,$(FTE_TARGET))$(findstring win,$(FTE_TARGET))) # OGGVORBISLDFLAGS=$(MINGW_LIBS_DIR)/libvorbisfile.a $(MINGW_LIBS_DIR)/libvorbis.a $(MINGW_LIBS_DIR)/libogg.a endif -OGGVORBISLDFLAGS ?= -lvorbisfile -lvorbis -logg - #BASELDFLAGS=-lm -lz XLDFLAGS=-L$(ARCHLIBS) $(IMAGELDFLAGS) @@ -987,8 +994,8 @@ ifeq (1,$(LINK_QUAKE3)) l_struct.o endif -COMMONLIBFLAGS= -COMMONLDDEPS= +COMMONLIBFLAGS?= +COMMONLDDEPS?= CLIENTLIBFLAGS=$(COMMONLIBFLAGS) $(LIBOPUS_STATIC) $(LIBSPEEX_STATIC) $(OGGVORBISFILE_STATIC) SERVERLIBFLAGS=$(COMMONLIBFLAGS) CLIENTLDDEPS=$(COMMONLDDEPS) $(LIBOPUS_LDFLAGS) $(LIBSPEEX_LDFLAGS) $(OGGVORBISLDFLAGS) @@ -997,14 +1004,17 @@ ifeq (1,$(USE_OPUS)) LIBOPUS_STATIC=-DOPUS_STATIC LIBOPUS_LDFLAGS=-lopus ALL_CFLAGS+=-I/usr/include/opus + MAKELIBS+=libs-$(ARCH)/libopus.a endif ifeq (1,$(USE_SPEEX)) LIBSPEEX_STATIC=-DSPEEX_STATIC LIBSPEEX_LDFLAGS=-lspeex -lspeexdsp + MAKELIBS+=libs-$(ARCH)/libspeex.a libs-$(ARCH)/libspeexdsp.a endif ifeq (1,$(USE_VORBISFILE)) OGGVORBISFILE_STATIC=-DLIBVORBISFILE_STATIC + OGGVORBISLDFLAGS ?= -lvorbisfile -lvorbis -logg else OGGVORBISLDFLAGS= OGGVORBISFILE_STATIC= @@ -1062,6 +1072,34 @@ ifeq (1,$(strip $(INTERNAL_BULLET))) LDCC=$(CXX) MAKELIBS+=libs-$(ARCH)/libBulletDynamics.a endif +ifeq (1,$(LINK_EZHUD)) + VPATH := $(VPATH) : $(BASE_DIR)/../plugins/ezhud + ALL_CFLAGS+=-DSTATIC_EZHUD #let the plugins code know it needs to add the appropriate entry. + CLIENT_OBJS += \ + ezquakeisms.o \ + hud.o \ + hud_common.o \ + hud_editor.o +endif +ifeq (1,$(LINK_OPENSSL)) + ifeq (1,$(shell $(PKGCONFIG) --version-atleast 3 openssl && echo 1)) #must exist... and if its not openssl3 then its not gpl3-compatible so refuse to use it. + VPATH := $(VPATH) : $(BASE_DIR)/../plugins + ALL_CFLAGS+=-DSTATIC_OPENSSL #let the plugins code know it needs to add the appropriate entry. + COMMON_OBJS += \ + net_ssl_openssl.o + ALL_DFLAGS+=$(shell $(PKGCONFIG) openssl --cflags --silence-errors) + COMMONLDDEPS+=$(shell $(PKGCONFIG) openssl --libs --silence-errors) + endif +endif + +ifeq (1,$(shell $(PKGCONFIG) --exists gnutls && echo 1)) + ALL_CFLAGS+=$(shell $(PKGCONFIG) gnutls --cflags --silence-errors) + #ALL_CFLAGS+=-DGNUTLS_STATIC + #COMMONLDDEPS+=$(shell $(PKGCONFIG) gnutls --static --libs --silence-errors) #we soft-link, so we don't need the -lgnutls stuff. +else + ALL_CFLAGS+=-DNO_GNUTLS +endif + #the defaults for sdl come first #CC_MACHINE:=$(shell $(CC) -dumpmachine) @@ -1761,13 +1799,14 @@ ifeq ($(FTE_TARGET),web) #GLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) cd_null.o #GL_LDFLAGS=$(GLLDFLAGS) GLB_DIR=gl_web + GLCL_DIR=glcl_web GL_EXE_NAME=../ftewebgl.js + GLCL_EXE_NAME=../ftewebglcl.js GL_LDFLAGS=$(GLLDFLAGS) $(IMAGELDFLAGS) - GL_CFLAGS=$(GLCFLAGS) + GL_CFLAGS=$(GLCFLAGS) $(CLIENTLIBFLAGS) IMAGELDFLAGS= - CLIENTLDDEPS= SERVERLDDEPS= #generate deps properly @@ -2074,17 +2113,24 @@ qccgui-scintilla: scintilla$(BITS)_static @LTO= $(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME="../fteqccgui$(BITS)" OUT_DIR="$(RELEASE_DIR)/$(QCC_DIR)scin" SOBJS="qccgui.o qccguistuff.o packager.o decomp.o fteqcc.o" WCFLAGS="$(WCFLAGS) -DSCISTATIC" LDFLAGS="$(LDFLAGS) $(RELEASE_DIR)/scintilla$(BITS).a -static -luuid -lole32 -limm32 -lstdc++ -loleaut32 -lcomdlg32 -lcomctl32 -lshlwapi -mwindows" ifdef windir -debugdir: - @-mkdir -p "$(subst /,\, $(OUT_DIR))" -reldir: +outdir: +ifneq "$(OUT_DIR)" "" @-mkdir -p "$(subst /,\, $(OUT_DIR))" +endif +debugdir: outdir + @-mkdir -p "$(subst /,\, $(RELEASE_DIR))" +reldir: outdir + @-mkdir -p "$(subst /,\, $(DEBUG_DIR))" else -reldir: +outdir: +ifneq "$(OUT_DIR)" "" + @-mkdir -p "$(OUT_DIR)" +endif +debugdir: outdir +reldir: outdir @-mkdir -p "$(RELEASE_DIR)" - @-mkdir -p "$(OUT_DIR)" -debugdir: +debugdir: outdir @-mkdir -p "$(DEBUG_DIR)" - @-mkdir -p "$(OUT_DIR)" endif plugins-dbg: @@ -2172,15 +2218,23 @@ else endif web-rel: - @PATH="$(EMSCRIPTENPATH)" $(MAKE) gl-rel FTE_TARGET=web CC="$(EMCC)" - cp $(BASE_DIR)/web/fteshell.html $(RELEASE_DIR)/ftewebgl.html + @PATH="$(EMSCRIPTENPATH)" $(MAKE) makelibs FTE_TARGET=web CC="$(EMCC)" && PATH="$(EMSCRIPTENPATH)" $(MAKE) gl-rel FTE_TARGET=web CC="$(EMCC)" + @cp $(BASE_DIR)/web/fteshell.html $(RELEASE_DIR)/ftewebgl.html @gzip -kf $(RELEASE_DIR)/ftewebgl.html @gzip -kf $(RELEASE_DIR)/ftewebgl.js @gzip -kf $(RELEASE_DIR)/ftewebgl.wasm +webcl-rel: + @PATH="$(EMSCRIPTENPATH)" $(MAKE) makelibs FTE_TARGET=web CC="$(EMCC)" && PATH="$(EMSCRIPTENPATH)" $(MAKE) glcl-rel FTE_TARGET=web CC="$(EMCC)" + @cp $(BASE_DIR)/web/fteshell.html $(RELEASE_DIR)/ftewebglcl.html + @sed -i 's/ftewebgl.js/ftewebglcl.js/g' release/ftewebglcl.html #swap out the .js filename for the client-only one. + @gzip -kf $(RELEASE_DIR)/ftewebglcl.html + @gzip -kf $(RELEASE_DIR)/ftewebglcl.js + @gzip -kf $(RELEASE_DIR)/ftewebglcl.wasm + web-dbg: @PATH="$(EMSCRIPTENPATH)" $(MAKE) gl-dbg FTE_TARGET=web CC="$(EMCC)" - cp $(BASE_DIR)/web/fteshell.html $(DEBUG_DIR)/ftewebgl.html + @cp $(BASE_DIR)/web/fteshell.html $(DEBUG_DIR)/ftewebgl.html @gzip -kf $(DEBUG_DIR)/ftewebgl.html @gzip -kf $(DEBUG_DIR)/ftewebgl.js @gzip -kf $(DEBUG_DIR)/ftewebgl.wasm @@ -2288,23 +2342,26 @@ AR?=$(ARCH)-ar CONFIGARGS+= -host=$(ARCH) --enable-shared=no CC="$(CC)" CONFIGARGS:= $(CONFIGARGS) #--disable-silent-rules +OPUSCONFIGARGS=$(CONFIGARGS) TOOLOVERRIDES+=CFLAGS="$$CFLAGS -Os" +TOOLCONGIGUREOVERRIDES=$(TOOLOVERRIDES) +TOOLMAKEOVERRIDES=$(TOOLOVERRIDES) +ifeq (web,$(FTE_TARGET)) + TOOLCONFIGUREOVERRIDES=emconfigure + TOOLMAKEOVERRIDES=emmake + OPUSCONFIGARGS=--disable-rtcd --disable-hardening --enable-stack-protector=no --enable-shared=no + CONFIGARGS=--enable-shared=no +endif libs-$(ARCH)/libjpeg.a: test -f jpegsrc.v$(JPEGVER).tar.gz || wget http://www.ijg.org/files/jpegsrc.v$(JPEGVER).tar.gz - -test -f libs-$(ARCH)/libjpeg.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../jpegsrc.v$(JPEGVER).tar.gz && cd jpeg-$(JPEGVER) && $(TOOLOVERRIDES) ./configure $(CONFIGARGS) && $(TOOLOVERRIDES) $(MAKE) && cp .libs/libjpeg.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libjpeg.a && cp jconfig.h jerror.h jmorecfg.h jpeglib.h jversion.h ../ ) + test -f libs-$(ARCH)/libjpeg.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../jpegsrc.v$(JPEGVER).tar.gz && cd jpeg-$(JPEGVER) && $(CONFIGUREOVERRIDES) ./configure $(CONFIGARGS) && $(TOOLOVERRIDES) $(MAKE) && cp .libs/libjpeg.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libjpeg.a && cp jconfig.h jerror.h jmorecfg.h jpeglib.h jversion.h ../ ) -ifeq ($(FTE_TARGET),web) libs-$(ARCH)/libz.a libs-$(ARCH)/libz.pc: test -f zlib-$(ZLIBVER).tar.gz || wget http://zlib.net/fossils/zlib-$(ZLIBVER).tar.gz - -test -f libs-$(ARCH)/libz.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../zlib-$(ZLIBVER).tar.gz && cd zlib-$(ZLIBVER) && emconfigure ./configure --static && emmake $(MAKE) libz.a CC="$(CC) $(W32_CFLAGS) -fPIC" && cp libz.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libz.a && cp zlib.h zconf.h zutil.h zlib.pc ../ ) -else -libs-$(ARCH)/libz.a libs-$(ARCH)/libz.pc: - test -f zlib-$(ZLIBVER).tar.gz || wget http://zlib.net/fossils/zlib-$(ZLIBVER).tar.gz - -test -f libs-$(ARCH)/libz.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../zlib-$(ZLIBVER).tar.gz && cd zlib-$(ZLIBVER) && $(TOOLOVERRIDES) ./configure --static && $(TOOLOVERRIDES) $(MAKE) libz.a CC="$(CC) $(W32_CFLAGS) -fPIC" && cp libz.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libz.a && cp zlib.h zconf.h zutil.h zlib.pc ../ ) -endif + test -f libs-$(ARCH)/libz.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../zlib-$(ZLIBVER).tar.gz && cd zlib-$(ZLIBVER) && $(TOOLCONFIGUREOVERRIDES) ./configure --static && $(TOOLMAKEOVERRIDES) $(MAKE) libz.a CC="$(CC) $(W32_CFLAGS) -fPIC" && cp libz.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libz.a && cp zlib.h zconf.h zutil.h zlib.pc ../ ) libs-$(ARCH)/libz9.a: libs-$(ARCH)/libz.a (cd libs-$(ARCH)/zlib-$(ZLIBVER) && \ $(CC) -o contrib/infback9/infback9.o -c contrib/infback9/infback9.c -I. && \ @@ -2315,35 +2372,35 @@ libs-$(ARCH)/libz9.a: libs-$(ARCH)/libz.a libs-$(ARCH)/libpng.a libs-$(ARCH)/libpng.pc: libs-$(ARCH)/libz.a libs-$(ARCH)/libz.pc test -f libpng-$(PNGVER).tar.gz || wget http://prdownloads.sourceforge.net/libpng/libpng-$(PNGVER).tar.gz?download -O libpng-$(PNGVER).tar.gz - -test -f libs-$(ARCH)/libpng.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../libpng-$(PNGVER).tar.gz && cd libpng-$(PNGVER) && $(TOOLOVERRIDES) ./configure CPPFLAGS=-I$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/ LDFLAGS=-L$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/ $(CONFIGARGS) --enable-static && $(TOOLOVERRIDES) $(MAKE) && cp .libs/libpng16.a ../libpng.a && cp libpng.pc png*.h ../ ) + test -f libs-$(ARCH)/libpng.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../libpng-$(PNGVER).tar.gz && cd libpng-$(PNGVER) && $(TOOLOVERRIDES) ./configure CPPFLAGS=-I$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/ LDFLAGS=-L$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/ $(CONFIGARGS) --enable-static && $(TOOLOVERRIDES) $(MAKE) && cp .libs/libpng16.a ../libpng.a && cp libpng.pc png*.h ../ ) libs-$(ARCH)/libogg.a: test -f libogg-$(OGGVER).tar.gz || wget http://downloads.xiph.org/releases/ogg/libogg-$(OGGVER).tar.gz - -test -f libs-$(ARCH)/libogg.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../libogg-$(OGGVER).tar.gz && cd libogg-$(OGGVER) && $(TOOLOVERRIDES) ./configure $(CONFIGARGS) && $(TOOLOVERRIDES) $(MAKE) && cp src/.libs/libogg.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libogg.a && mkdir ../ogg && cp include/ogg/*.h ../ogg) + test -f libs-$(ARCH)/libogg.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../libogg-$(OGGVER).tar.gz && cd libogg-$(OGGVER) && $(TOOLOVERRIDES) ./configure $(CONFIGARGS) && $(TOOLOVERRIDES) $(MAKE) && cp src/.libs/libogg.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libogg.a && mkdir ../ogg && cp include/ogg/*.h ../ogg) libs-$(ARCH)/libvorbis.a: libs-$(ARCH)/libogg.a test -f libvorbis-$(VORBISVER).tar.gz || wget http://downloads.xiph.org/releases/vorbis/libvorbis-$(VORBISVER).tar.gz - -test -f libs-$(ARCH)/libvorbisfile.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../libvorbis-$(VORBISVER).tar.gz && cd libvorbis-$(VORBISVER) && $(TOOLOVERRIDES) ./configure PKG_CONFIG= $(CONFIGARGS) --disable-oggtest --with-ogg-libraries=.. --with-ogg-includes=$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/libogg-$(OGGVER)/include && $(TOOLOVERRIDES) $(MAKE) && cp lib/.libs/libvorbis.a ../ && cp lib/.libs/libvorbisfile.a ../ && mkdir ../vorbis && cp include/vorbis/*.h ../vorbis) + test -f libs-$(ARCH)/libvorbisfile.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../libvorbis-$(VORBISVER).tar.gz && cd libvorbis-$(VORBISVER) && $(TOOLOVERRIDES) ./configure PKG_CONFIG= $(CONFIGARGS) --disable-oggtest --with-ogg-libraries=.. --with-ogg-includes=$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/libogg-$(OGGVER)/include && $(TOOLOVERRIDES) $(MAKE) && cp lib/.libs/libvorbis.a ../ && cp lib/.libs/libvorbisfile.a ../ && mkdir ../vorbis && cp include/vorbis/*.h ../vorbis) libs-$(ARCH)/libopus.a: test -f opus-$(OPUSVER).tar.gz || wget https://archive.mozilla.org/pub/opus/opus-$(OPUSVER).tar.gz - -test -f libs-$(ARCH)/libopus.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../opus-$(OPUSVER).tar.gz && cd opus-$(OPUSVER) && CFLAGS="-D_FORTIFY_SOURCE=0 $(CFLAGS) -Os" $(TOOLOVERRIDES) ./configure $(CONFIGARGS) && $(TOOLOVERRIDES) $(MAKE) && cp .libs/libopus.a ../ && cp include/opus*.h ../) + test -f libs-$(ARCH)/libopus.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../opus-$(OPUSVER).tar.gz && cd opus-$(OPUSVER) && CFLAGS="-D_FORTIFY_SOURCE=0 $(CFLAGS) -Os" $(TOOLCONFIGUREOVERRIDES) ./configure $(OPUSCONFIGARGS) --disable-extra-programs && $(TOOLMAKEOVERRIDES) $(MAKE) && cp .libs/libopus.a ../ && cp include/opus*.h ../) libs-$(ARCH)/libspeex.a: test -f speex-$(SPEEXVER).tar.gz || wget http://downloads.us.xiph.org/releases/speex/speex-$(SPEEXVER).tar.gz - -test -f libs-$(ARCH)/libspeex.a || (mkdir -p libs-$(ARCH)/speex && cd libs-$(ARCH) && tar -xvzf ../speex-$(SPEEXVER).tar.gz && cd speex-$(SPEEXVER) && CFLAGS="$(CFLAGS) -Os" $(TOOLOVERRIDES) ./configure $(CONFIGARGS) && $(TOOLOVERRIDES) $(MAKE) && cp libspeex/.libs/libspeex.a ../ && cp -r include/speex/*.h ../speex/) + test -f libs-$(ARCH)/libspeex.a || (mkdir -p libs-$(ARCH)/speex && cd libs-$(ARCH) && tar -xvzf ../speex-$(SPEEXVER).tar.gz && cd speex-$(SPEEXVER) && CFLAGS="$(CFLAGS) -Os" $(TOOLCONFIGUREOVERRIDES) ./configure $(CONFIGARGS) --disable-binaries && $(TOOLMAKEOVERRIDES) $(MAKE) && cp libspeex/.libs/libspeex.a ../ && cp -r include/speex/*.h ../speex/) libs-$(ARCH)/libspeexdsp.a: test -f speexdsp-$(SPEEXDSPVER).tar.gz || wget http://downloads.xiph.org/releases/speex/speexdsp-$(SPEEXDSPVER).tar.gz - -test -f libs-$(ARCH)/libspeexdsp.a || (mkdir -p libs-$(ARCH)/speex && cd libs-$(ARCH) && tar -xvzf ../speexdsp-$(SPEEXDSPVER).tar.gz && cd speexdsp-$(SPEEXDSPVER) && CFLAGS="$(CFLAGS) -Os" $(TOOLOVERRIDES) ./configure $(CONFIGARGS) && $(TOOLOVERRIDES) $(MAKE) && cp libspeexdsp/.libs/libspeexdsp.a ../ && cp -r include/speex/*.h ../speex/) + test -f libs-$(ARCH)/libspeexdsp.a || (mkdir -p libs-$(ARCH)/speex && cd libs-$(ARCH) && tar -xvzf ../speexdsp-$(SPEEXDSPVER).tar.gz && cd speexdsp-$(SPEEXDSPVER) && CFLAGS="$(CFLAGS) -Os" $(TOOLCONFIGUREOVERRIDES) ./configure $(CONFIGARGS) && $(TOOLMAKEOVERRIDES) $(MAKE) && cp libspeexdsp/.libs/libspeexdsp.a ../ && cp -r include/speex/*.h ../speex/) libs-$(ARCH)/libfreetype.a libs-$(ARCH)/ft2build.h: libs-$(ARCH)/libpng.a libs-$(ARCH)/libpng.pc test -f freetype-$(FREETYPEVER).tar.gz || wget https://download-mirror.savannah.gnu.org/releases/freetype/freetype-$(FREETYPEVER).tar.gz - -test -f libs-$(ARCH)/libfreetype.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../freetype-$(FREETYPEVER).tar.gz && cd freetype-$(FREETYPEVER) && PKG_CONFIG_LIBDIR=$(NATIVE_ABSBASE_DIR)/libs-$(ARCH) CFLAGS="$(CFLAGS) -Os" $(TOOLOVERRIDES) ./configure CPPFLAGS=-I$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/ LDFLAGS=-L$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/ $(CONFIGARGS) --with-zlib=yes --with-png=yes --with-bzip2=no --with-harfbuzz=no && $(TOOLOVERRIDES) $(MAKE) && cp objs/.libs/libfreetype.a ../ && cp -r include/* ../) + test -f libs-$(ARCH)/libfreetype.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../freetype-$(FREETYPEVER).tar.gz && cd freetype-$(FREETYPEVER) && PKG_CONFIG_LIBDIR=$(NATIVE_ABSBASE_DIR)/libs-$(ARCH) CFLAGS="$(CFLAGS) -Os" $(TOOLOVERRIDES) ./configure CPPFLAGS=-I$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/ LDFLAGS=-L$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/ $(CONFIGARGS) --with-zlib=yes --with-png=yes --with-bzip2=no --with-harfbuzz=no && $(TOOLOVERRIDES) $(MAKE) && cp objs/.libs/libfreetype.a ../ && cp -r include/* ../) libs-$(ARCH)/libBulletDynamics.a: test -f bullet3-$(BULLETVER).tar.gz || wget https://github.com/bulletphysics/bullet3/archive/$(BULLETVER).tar.gz -O bullet3-$(BULLETVER).tar.gz - -test -f libs-$(ARCH)/libBulletDynamics.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../bullet3-$(BULLETVER).tar.gz && cd bullet3-$(BULLETVER) && CFLAGS="$(CFLAGS) -Os" $(TOOLOVERRIDES) $(DO_CMAKE) . && $(TOOLOVERRIDES) $(MAKE) LinearMath BulletDynamics BulletCollision && cp src/LinearMath/libLinearMath.a src/BulletDynamics/libBulletDynamics.a src/BulletCollision/libBulletCollision.a src/btBulletCollisionCommon.h src/btBulletDynamicsCommon.h ..) + test -f libs-$(ARCH)/libBulletDynamics.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../bullet3-$(BULLETVER).tar.gz && cd bullet3-$(BULLETVER) && CFLAGS="$(CFLAGS) -Os" $(TOOLOVERRIDES) $(DO_CMAKE) . && $(TOOLOVERRIDES) $(MAKE) LinearMath BulletDynamics BulletCollision && cp src/LinearMath/libLinearMath.a src/BulletDynamics/libBulletDynamics.a src/BulletCollision/libBulletCollision.a src/btBulletCollisionCommon.h src/btBulletDynamicsCommon.h ..) libs-$(ARCH)/vulkan/vulkan.h: test -f vulkan-sdk-$(VULKANVER).tar.gz || wget https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/vulkan-sdk-$(VULKANVER).tar.gz @@ -2380,13 +2437,13 @@ $(RELEASE_DIR)/imgtool$(BITS)$(EXEPOSTFIX): $(IMGTOOL_OBJECTS) imgtool-rel: $(RELEASE_DIR)/imgtool$(BITS)$(EXEPOSTFIX) imgtool: imgtool-rel -MASTER_OBJECTS=server/sv_sys_unix.c common/sys_linux_threads.c common/net_ssl_gnutls.c server/sv_master.c common/net_wins.c common/net_ice.c common/cvar.c common/cmd.c common/sha1.c http/httpclient.c common/log.c common/fs.c common/fs_stdio.c common/common.c common/translate.c common/zone.c qclib/hash.c +MASTER_OBJECTS=server/sv_sys_unix.c common/sys_linux_threads.c common/net_ssl_gnutls.c server/sv_master.c common/net_wins.c common/net_ice.c common/cvar.c common/cmd.c common/sha1.c common/sha2.c http/httpclient.c common/log.c common/fs.c common/fs_stdio.c common/common.c common/translate.c common/zone.c qclib/hash.c $(RELEASE_DIR)/ftemaster$(BITS)$(EXEPOSTFIX): $(MASTER_OBJECTS) $(CC) -o $@ $(MASTER_OBJECTS) -flto=jobserver -fvisibility=hidden -Icommon -Iclient -Iqclib -Igl -Iserver -DMASTERONLY -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp -lm -ldl -lz $(RELEASE_CFLAGS) $(RELEASE_LDFLAGS) $(DEBUG_DIR)/ftemaster$(BITS)$(EXEPOSTFIX): $(MASTER_OBJECTS) $(CC) -o $@ $(MASTER_OBJECTS) -Icommon -Iclient -Iqclib -Igl -Iserver -DMASTERONLY -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp -lm -ldl -lz $(DEBUG_CFLAGS) $(DEBUG_LDFLAGS) -master-rel: $(RELEASE_DIR)/ftemaster$(BITS)$(EXEPOSTFIX) -master-dbg: $(DEBUG_DIR)/ftemaster$(BITS)$(EXEPOSTFIX) +master-rel: reldir $(RELEASE_DIR)/ftemaster$(BITS)$(EXEPOSTFIX) +master-dbg: dbgdir $(DEBUG_DIR)/ftemaster$(BITS)$(EXEPOSTFIX) master: master-rel QTV_OBJECTS= \ diff --git a/engine/client/cl_cam.c b/engine/client/cl_cam.c index e6e489458..d2a13c898 100644 --- a/engine/client/cl_cam.c +++ b/engine/client/cl_cam.c @@ -68,7 +68,7 @@ void Cam_AutoTrack_Update(const char *mode) autotrack_statsrule = NULL; if (!*mode || !Q_strcasecmp(mode, "auto")) { - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) { autotrackmode = TM_STATS; autotrack_statsrule = Z_StrDup(""); //default @@ -501,7 +501,7 @@ static int CL_AutoTrack_Choose(int seat) best = cl.autotrack_killer; if (autotrackmode == TM_MODHINTS && seat == 0 && cl.autotrack_hint >= 0) best = cl.autotrack_hint; - if (autotrackmode == TM_STATS && (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV)) + if (autotrackmode == TM_STATS && cls.demoplayback == DPB_MVD) best = CL_FindHighTrack(seat, autotrack_statsrule); if (autotrackmode == TM_HIGHTRACK || best == -1) best = CL_FindHighTrack(seat, "%f"); @@ -578,7 +578,7 @@ void Cam_Lock(playerview_t *pv, int playernum) Skin_FlushPlayers(); - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) { memcpy(&pv->stats, cl.players[playernum].stats, sizeof(pv->stats)); // pv->cam_state = CAM_; @@ -1001,13 +1001,13 @@ void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd) if (cls.state != ca_active) return; - if (!pv->spectator && (cls.demoplayback != DPB_MVD && cls.demoplayback != DPB_EZTV)) // only in spectator mode + if (!pv->spectator && cls.demoplayback != DPB_MVD) // only in spectator mode return; - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) { - int nb; - nb = (cmd->sidemove<0)?4:0; + int nb = 0; + nb |= (cmd->sidemove<0)?4:0; nb |= (cmd->sidemove>0)?8:0; nb |= (cmd->forwardmove<0)?16:0; nb |= (cmd->forwardmove>0)?32:0; @@ -1015,18 +1015,21 @@ void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd) nb |= (cmd->upmove>0)?128:0; if (Cam_TrackNum(pv) >= 0) { - if (nb & (nb ^ pv->cam_oldbuttons) & 4) - Cvar_SetValue(&cl_demospeed, max(cl_demospeed.value - 0.1, 0)); - if (nb & (nb ^ pv->cam_oldbuttons) & 8) - Cvar_SetValue(&cl_demospeed, min(cl_demospeed.value + 0.1, 10)); - if (nb & (nb ^ pv->cam_oldbuttons) & (4|8)) - Con_Printf("playback speed: %g%%\n", cl_demospeed.value*100); - if (nb & (nb ^ pv->cam_oldbuttons) & 16) - Cbuf_AddText("demo_jump +10", RESTRICT_LOCAL); - if (nb & (nb ^ pv->cam_oldbuttons) & 32) - Cbuf_AddText("demo_jump -10", RESTRICT_LOCAL); - if (nb & (nb ^ pv->cam_oldbuttons) & (4|8)) - Con_Printf("playback speed: %g%%\n", cl_demospeed.value*100); + if (*cls.lastdemoname) + { //changing rates or jumping doesn't make sense with mvds. + if (nb & (nb ^ pv->cam_oldbuttons) & 4) + Cvar_SetValue(&cl_demospeed, max(cl_demospeed.value - 0.1, 0)); + if (nb & (nb ^ pv->cam_oldbuttons) & 8) + Cvar_SetValue(&cl_demospeed, min(cl_demospeed.value + 0.1, 10)); + if (nb & (nb ^ pv->cam_oldbuttons) & (4|8)) + Con_Printf("playback speed: %g%%\n", cl_demospeed.value*100); + if (nb & (nb ^ pv->cam_oldbuttons) & 16) + Cbuf_AddText("demo_jump +10", RESTRICT_LOCAL); + if (nb & (nb ^ pv->cam_oldbuttons) & 32) + Cbuf_AddText("demo_jump -10", RESTRICT_LOCAL); + if (nb & (nb ^ pv->cam_oldbuttons) & (4|8)) + Con_Printf("playback speed: %g%%\n", cl_demospeed.value*100); + } if (nb & (nb ^ pv->cam_oldbuttons) & 64) Cvar_SetValue(&cl_splitscreen, max(cl_splitscreen.ival - 1, 0)); if (nb & (nb ^ pv->cam_oldbuttons) & 128) diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index d64ca4dc7..93e1dda6f 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -78,6 +78,7 @@ void CL_StopPlayback (void) cls.demoplayback = DPB_NONE; cls.demoseeking = false; //just in case cls.demotrack = -1; + cls.demoeztv_ext = 0; if (cls.timedemo) CL_FinishTimeDemo (); @@ -215,7 +216,7 @@ int demo_preparsedemo(unsigned char *buffer, int bytes) int ofs; unsigned int length; #define dem_mask 7 - if (cls.demoplayback != DPB_MVD && cls.demoplayback != DPB_EZTV) + if (cls.demoplayback != DPB_MVD) return bytes; //no need if its not an mvd (this simplifies it a little) while (bytes>2) @@ -407,6 +408,12 @@ void CL_DemoJump_f(void) return; } + if (!*cls.lastdemoname) + { + Con_Printf("unable to seak in qtv streams.\n"); + return; //can't seek live streams... + } + if (*s == '+' || *s == '-') { if (colon) @@ -465,6 +472,12 @@ void CL_DemoNudge_f(void) return; } + if (!*cls.lastdemoname) + { + Con_Printf("unable to seak in qtv streams.\n"); + return; //can't seek live streams... + } + if (!move) move = 1; @@ -519,6 +532,7 @@ qboolean CL_GetDemoMessage (void) q1usercmd_t q1cmd; int demopos = 0; int msglength; + static float throttle; if (endofdemo) { @@ -667,7 +681,7 @@ readnext: } // read the time from the packet - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) { if (demtime < 0) { @@ -681,7 +695,9 @@ readnext: if (readdemobytes(&demopos, &msecsadded, sizeof(msecsadded)) != sizeof(msecsadded)) { - Con_DPrintf("Not enough buffered\n"); + Con_ThrottlePrintf(&throttle, 1, "Not enough buffered\n"); + olddemotime = demtime+1; + demotime = olddemotime; nextdemotime = demotime; return 0; @@ -696,7 +712,7 @@ readnext: { if (readdemobytes(&demopos, &demotime, sizeof(demotime)) != sizeof(demotime)) { - Con_DPrintf("Not enough buffered\n"); + Con_ThrottlePrintf(&throttle, 1, "Not enough buffered\n"); olddemotime = demtime; //if we ran out of buffered demo, delay the demo parsing a little return 0; } @@ -763,15 +779,10 @@ readnext: else demtime = demotime; // we're warping - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) { if ((msecsadded || cls.netchan.incoming_sequence < 2) && olddemotime != demotime) { - if (!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)) - { - cls.netchan.incoming_sequence++; - cls.netchan.incoming_acknowledged++; - } cls.netchan.frame_latency = 0; cls.netchan.last_received = realtime; // just to happy timeout check } @@ -783,7 +794,7 @@ readnext: // get the msg type if (readdemobytes (&demopos, &c, sizeof(c)) != sizeof(c)) { - Con_DPrintf("Not enough buffered\n"); + Con_ThrottlePrintf(&throttle, 1, "Not enough buffered\n"); olddemotime = demtime+1; return 0; } @@ -805,7 +816,7 @@ readnext: r = readdemobytes (&demopos, &q1cmd, sizeof(q1cmd)); if (r != sizeof(q1cmd)) { - Con_DPrintf("Not enough buffered\n"); + Con_ThrottlePrintf(&throttle, 1, "Not enough buffered\n"); olddemotime = demtime+1; CL_StopPlayback (); return 0; @@ -843,7 +854,7 @@ readit: // get the next message if (readdemobytes (&demopos, &msglength, 4) != 4) { - Con_DPrintf("Not enough buffered\n"); + Con_ThrottlePrintf(&throttle, 1, "Not enough buffered\n"); olddemotime = demtime+1; return 0; } @@ -857,20 +868,27 @@ readit: } if (readdemobytes (&demopos, net_message.data, msglength) != msglength) { - Con_DPrintf("Not enough buffered\n"); + Con_ThrottlePrintf(&throttle, 1, "Not enough buffered\n"); olddemotime = demtime+1; return 0; } NET_UpdateRates(cls.sockets, true, msglength); //keep any rate calcs sane net_message.cursize = msglength; - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) { int seat; cl.defaultnetsplit = 0; switch(cls_lasttype) { case dem_multiple: + if (!cls_lastto && (cls.ezprotocolextensions1 & EZPEXT1_HIDDEN_MESSAGES)) + { //an 'mvdsv hidden message' packet. + MSG_BeginReading(&net_message, cls.netchan.netprim); + CLEZ_ParseHiddenDemoMessage(); + olddemotime = demotime; + goto readnext; + } for (seat = 0; seat < cl.splitclients; seat++) { tracknum = cl.playerview[seat].cam_spec_track; @@ -939,7 +957,7 @@ readit: NET_UpdateRates(cls.sockets, false, demopos); //keep any rate calcs sane - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) cls.netchan.incoming_acknowledged = cls.netchan.incoming_sequence; goto readnext; @@ -975,8 +993,21 @@ readit: } demo_flushbytes(demopos); - olddemotime = demotime; + if (cls.demoplayback == DPB_MVD) + { + if ((msecsadded || cls.netchan.incoming_sequence < 2) && olddemotime != demotime) + { + if (!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)) + { + cls.netchan.incoming_sequence++; + cls.netchan.incoming_acknowledged++; + } + cls.netchan.frame_latency = 0; + cls.netchan.last_received = realtime; // just to happy timeout check + } + } + olddemotime = demotime; net_from.type = NA_INVALID; return 1; } @@ -1009,6 +1040,7 @@ void CL_Stop_f (void) } else #endif + if (cls.demorecording == DPB_QUAKEWORLD) { SZ_Clear (&net_message); MSG_WriteLong (&net_message, -1); // -1 sequence means out of band @@ -1139,12 +1171,16 @@ void CL_RecordMap_f (void) CL_Disconnect_f(); SV_SpawnServer (mapname, NULL, false, false, 0); + if (!sv.state) + return; #ifdef MVD_RECORDING - COM_DefaultExtension(demoname, ".mvd", sizeof(demoname)); -#else - COM_DefaultExtension(demoname, ".dem", sizeof(demoname)); + if (svs.allocated_client_slots > 1) + COM_DefaultExtension(demoname, ".mvd", sizeof(demoname)); + else #endif + COM_DefaultExtension(demoname, ".qwd", sizeof(demoname)); + COM_FileExtension(demoname, demoext, sizeof(demoext)); #if defined(AVAIL_GZDEC) && !defined(CLIENTONLY) @@ -1166,6 +1202,19 @@ void CL_RecordMap_f (void) else #endif { +#ifdef NQPROT + if (!strcmp(demoext, "dem")) + cls.demorecording = DPB_NETQUAKE; + else +#endif + if (!strcmp(demoext, "qwd")) + cls.demorecording = DPB_QUAKEWORLD; + else + { + CL_Disconnect_f(); + return; + } + cls.demooutfile = FS_OpenVFS (demoname, "wb", FS_GAME); if (!cls.demooutfile) { @@ -1176,16 +1225,10 @@ void CL_RecordMap_f (void) if (!Q_strcasecmp(".gz", COM_GetFileExtension(demoname, NULL))) cls.demooutfile = FS_GZ_WriteFilter(cls.demooutfile, true, true); #endif - #ifdef NQPROT - if (!strcmp(demoext, "dem")) - { - cls.demorecording = DPB_NETQUAKE; + if (cls.demorecording == DPB_NETQUAKE) VFS_PUTS(cls.demooutfile, "-1\n"); - } - else #endif - cls.demorecording = DPB_QUAKEWORLD; CL_WriteSetDemoMessage(); } } @@ -1384,11 +1427,11 @@ void CLNQ_WriteServerData(sizebuf_t *buf) //for demo recording MSG_WriteByte (buf, cl.deathmatch?GAME_DEATHMATCH:GAME_COOP); MSG_WriteString (buf, cl.levelname); - for (i = 1; *cl.model_name[i] && i < MAX_PRECACHE_MODELS; i++) + for (i = 1; cl.model_name[i] && i < MAX_PRECACHE_MODELS; i++) MSG_WriteString (buf, cl.model_name[i]); MSG_WriteByte (buf, 0); - for (i = 1; *cl.sound_name[i] && i < MAX_PRECACHE_SOUNDS ; i++) + for (i = 1; cl.sound_name[i] && i < MAX_PRECACHE_SOUNDS ; i++) MSG_WriteString (buf, cl.sound_name[i]); MSG_WriteByte (buf, 0); } @@ -2284,7 +2327,7 @@ void CL_PlayDemo_f (void) } //dl is provided so that we can receive files via chunked/gziped http downloads and on systems that don't provide sockets etc. its tracked so we can cancel the download if the client aborts playback early. -void CL_PlayDemoStream(vfsfile_t *file, char *filename, qboolean issyspath, int demotype, float bufferdelay) +void CL_PlayDemoStream(vfsfile_t *file, char *filename, qboolean issyspath, int demotype, float bufferdelay, unsigned int eztv_ext) { int protocol = CP_UNKNOWN; @@ -2295,7 +2338,6 @@ void CL_PlayDemoStream(vfsfile_t *file, char *filename, qboolean issyspath, int switch(demotype) { - case DPB_EZTV: case DPB_MVD: case DPB_QUAKEWORLD: protocol = CP_QUAKEWORLD; @@ -2345,13 +2387,14 @@ void CL_PlayDemoStream(vfsfile_t *file, char *filename, qboolean issyspath, int Con_Printf ("Playing demo from %s.\n", filename); } - cls.findtrack = (demotype == DPB_MVD || demotype == DPB_EZTV); + cls.findtrack = (demotype == DPB_MVD); cls.demoplayback = demotype; + cls.demoeztv_ext = eztv_ext; cls.protocol = protocol; cls.state = ca_demostart; net_message.packing = SZ_RAWBYTES; - Netchan_Setup (NS_CLIENT, &cls.netchan, &net_from, 0); + Netchan_Setup (NCF_CLIENT, &cls.netchan, &net_from, 0, 0); demtime = -bufferdelay; cls.demostarttime = 0; @@ -2391,20 +2434,20 @@ void CL_PlayDemoFile(vfsfile_t *f, char *demoname, qboolean issyspath) if (!Q_strcasecmp(demoname + strlen(demoname) - 3, "dm2") || !Q_strcasecmp(demoname + strlen(demoname) - 6, "dm2.gz")) { - CL_PlayDemoStream(f, demoname, issyspath, DPB_QUAKE2, 0); + CL_PlayDemoStream(f, demoname, issyspath, DPB_QUAKE2, 0, 0); return; } #endif if (!Q_strcasecmp(demoname + strlen(demoname) - 3, "mvd") || !Q_strcasecmp(demoname + strlen(demoname) - 6, "mvd.gz")) { - CL_PlayDemoStream(f, demoname, issyspath, DPB_MVD, 0); + CL_PlayDemoStream(f, demoname, issyspath, DPB_MVD, 0, 0); return; } if (!Q_strcasecmp(demoname + strlen(demoname) - 3, "qwd") || !Q_strcasecmp(demoname + strlen(demoname) - 6, "qwd.gz")) { - CL_PlayDemoStream(f, demoname, issyspath, DPB_QUAKEWORLD, 0); + CL_PlayDemoStream(f, demoname, issyspath, DPB_QUAKEWORLD, 0, 0); return; } @@ -2436,7 +2479,7 @@ void CL_PlayDemoFile(vfsfile_t *f, char *demoname, qboolean issyspath) else cls.demotrack = -1; - CL_PlayDemoStream(f, demoname, issyspath, DPB_NETQUAKE, 0); + CL_PlayDemoStream(f, demoname, issyspath, DPB_NETQUAKE, 0, 0); return; } VFS_SEEK(f, start); @@ -2477,7 +2520,7 @@ void CL_PlayDemoFile(vfsfile_t *f, char *demoname, qboolean issyspath) if (protocol >= PROTOCOL_VERSION_Q2_DEMO_MIN && protocol <= PROTOCOL_VERSION_Q2_DEMO_MAX) { VFS_SEEK(f, start); - CL_PlayDemoStream(f, demoname, issyspath, DPB_QUAKE2, 0); + CL_PlayDemoStream(f, demoname, issyspath, DPB_QUAKE2, 0, 0); return; } break; @@ -2493,7 +2536,7 @@ void CL_PlayDemoFile(vfsfile_t *f, char *demoname, qboolean issyspath) //could also be .qwz or .dmz or whatever that nq extension is. we don't support either. //mvd and qwd have no identifying markers, other than the extension. - CL_PlayDemoStream(f, demoname, issyspath, DPB_QUAKEWORLD, 0); + CL_PlayDemoStream(f, demoname, issyspath, DPB_QUAKEWORLD, 0, 0); } #ifdef WEBCLIENT void CL_PlayDownloadedDemo(struct dl_download *dl) @@ -2566,7 +2609,7 @@ void CL_Demo_ClientCommand(char *commandtext) #ifdef warningmsg #pragma warningmsg("this needs buffering safely") #endif - if (cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD && cls.demoeztv_ext) { VFS_WRITE(cls.demoinfile, &len, sizeof(len)); VFS_WRITE(cls.demoinfile, &b, sizeof(b)); @@ -2947,8 +2990,9 @@ fail: //eztv extensions to v1.0 else if (!strcmp(s, "QTV_EZQUAKE_EXT")) { - iseztv = true; - Con_Printf("Warning: eztv extensions %s\n", colon); + iseztv = atoi(colon); + if (iseztv & ~(EZTV_DOWNLOAD|EZTV_SETINFO|EZTV_QTVUSERLIST)) + Con_Printf(CON_WARNING"Warning: unknown eztv extensions %s\n", colon); } //v1.1 sourcelist response includes SRCSRV, SRCHOST, SRCPLYRS, SRCVIEWS, SRCID @@ -3013,7 +3057,7 @@ fail: Con_Printf("streaming \"%s\" from qtv\n", streamavailable); else Con_Printf("qtv connection established to %s\n", qtv->hostname); - CL_PlayDemoStream(qtv->stream, NULL, false, iseztv?DPB_EZTV:DPB_MVD, BUFFERTIME); + CL_PlayDemoStream(qtv->stream, NULL, false, DPB_MVD, BUFFERTIME, iseztv); qtv->stream = NULL; demo_resetcache(qtv->requestsize - (tail-qtv->requestbuffer), tail); *link = qtv->next; @@ -3194,8 +3238,8 @@ void CL_QTVPlay_f (void) if (qtvcl_eztvextensions.ival) { Q_snprintfz(msg+msglen, sizeof(msg)-msglen, - "QTV_EZQUAKE_EXT: 3\n" - "USERINFO: "); + "QTV_EZQUAKE_EXT: %u\n" + "USERINFO: ", EZTV_DOWNLOAD|EZTV_SETINFO|EZTV_QTVUSERLIST); msglen += strlen(msg+msglen); InfoBuf_ToString(&cls.userinfo[0], msg+msglen, sizeof(msg)-msglen-1, basicuserinfos, NULL, NULL, NULL, NULL); msglen += strlen(msg+msglen); diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index c8afc989a..9e41d87d8 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -925,7 +925,7 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * if (bits & UF_DRAWFLAGS) { news->hexen2flags = MSG_ReadByte(); - if ((news->hexen2flags & MLS_MASK) == MLS_ABSLIGHT) + if ((news->hexen2flags & MLS_MASK) >= MLS_ADDLIGHT) news->abslight = MSG_ReadByte(); else news->abslight = 0; @@ -1052,7 +1052,7 @@ void CLFTE_ParseEntities(void) // Con_Printf("CL: Dropped %i\n", i); // } - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) { cls.netchan.incoming_sequence++; cls.netchan.incoming_acknowledged++; @@ -1291,7 +1291,7 @@ void CLQW_ParsePacketEntities (qboolean delta) cl.inframes[newpacket].frameid = cls.netchan.incoming_sequence; cl.inframes[newpacket].receivedtime = realtime; - if (cls.protocol == CP_QUAKEWORLD && (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV)) + if (cls.protocol == CP_QUAKEWORLD && cls.demoplayback == DPB_MVD) { extern float olddemotime; //time from the most recent demo packet cl.oldgametime = cl.gametime; @@ -1317,7 +1317,7 @@ void CLQW_ParsePacketEntities (qboolean delta) from = MSG_ReadByte (); // Con_Printf("%i %i from %i\n", cls.netchan.outgoing_sequence, cls.netchan.incoming_sequence, from); - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) from = oldpacket = cls.netchan.incoming_sequence - 1; oldpacket = cl.inframes[from & UPDATE_MASK].frameid; @@ -1851,6 +1851,209 @@ void CLDP_ParseDarkPlaces5Entities(void) //the things I do.. :o( } } +#ifdef HEXEN2 +#define UH2_MOREBITS (1u<<0) +#define UH2_ORIGIN1 (1u<<1) +#define UH2_ORIGIN2 (1u<<2) +#define UH2_ORIGIN3 (1u<<3) +#define UH2_ANGLE2 (1u<<4) +#define UH2_STEP (1u<<5) +#define UH2_FRAME (1u<<6) +#define UH2_SIGNAL (1u<<7) + +#define UH2_ANGLE1 (1u<<8) +#define UH2_ANGLE3 (1u<<9) +#define UH2_MODEL (1u<<10) +//#define UH2_ (1u<<11) +//#define UH2_ (1u<<12) +//#define UH2_ (1u<<13) +#define UH2_LONGENTITY (1u<<14) +#define UH2_EVENMORE (1u<<15) + +#define UH2_SKIN (1u<<16) +#define UH2_EFFECTS (1u<<17) +#define UH2_SCALE (1u<<18) +#define UH2_COLORMAP (1u<<19) + +void CLH2_ParseEntities(void) +{ + //h2mp apparently uses some sort of delta compression + //there's three parts to this, the start, the updates, and the removes at the end. + //so we can be a bit lazy and parse the 'fast updates' here and assert they end with a final clear. + //entities are ordered. + + packet_entities_t *oldpack, *newpack; + + entity_state_t *to, *from; + unsigned int read, bits; + int oldi; + unsigned int removecount; + + int frame = MSG_ReadByte(); + int seq = MSG_ReadByte(); + + //not really sure what to do with this. + (void)frame; + (void)seq; + + //server->client sequence +// if (cl.numackframes == sizeof(cl.ackframes)/sizeof(cl.ackframes[0])) +// cl.numackframes--; +// cl.ackframes[cl.numackframes++] = MSG_ReadLong(); /*server sequence to be acked*/ + + //client->server sequence ack +// if (cls.protocol_nq >= CPNQ_DP7) +// CL_AckedInputFrame(cls.netchan.incoming_sequence, MSG_ReadLong(), true); /*client input sequence which has been acked*/ + + if (cl.validsequence) + oldpack = &cl.inframes[(cl.validsequence)&UPDATE_MASK].packet_entities; + else + oldpack = NULL; + cl.validsequence = cls.netchan.incoming_sequence; + cl.inframes[(cls.netchan.incoming_sequence)&UPDATE_MASK].receivedtime = realtime; + cl.inframes[(cls.netchan.incoming_sequence)&UPDATE_MASK].frameid = cls.netchan.incoming_sequence; + newpack = &cl.inframes[(cls.netchan.incoming_sequence)&UPDATE_MASK].packet_entities; + newpack->servertime = cl.gametime; + + //copy old state to new state + if (newpack != oldpack) + { + if (oldpack) + { + newpack->num_entities = oldpack->num_entities; + newpack->max_entities = newpack->num_entities+16; //for slop for new ents, to reduce reallocs + newpack->entities = BZ_Realloc(newpack->entities, sizeof(entity_state_t)*newpack->max_entities); + memcpy(newpack->entities, oldpack->entities, sizeof(entity_state_t)*newpack->num_entities); + } + else + newpack->num_entities = 0; + newpack->bonedatacur = 0; + + //flag them all as having old bones + //they'll be renewed after parsing + for (oldi=0 ; oldinum_entities ; oldi++) + newpack->entities[oldi].boneoffset |= 0x80000000; + } + + for (;;) + { + bits = MSG_ReadByte(); + if ((bits&0x80) == 0) + break; //no fast-update bit! + if (bits & UH2_MOREBITS) + bits |= MSG_ReadByte()<<8; + if (bits & UH2_EVENMORE) + bits |= MSG_ReadByte()<<16; + if (bits & UH2_LONGENTITY) + read = MSG_ReadUInt16(); + else + read = MSG_ReadByte(); + + if (msg_badread) + Host_EndGame("Corrupt entity message packet\n"); + + if (!read) + break; //remove world signals end of packet. + + if (read >= MAX_EDICTS) + Host_EndGame("Too many entities.\n"); + + if (!CL_CheckBaselines(read)) + Host_EndGame("CLNQ_ParseEntity: check baselines failed with size %i", read); + from = &cl_baselines[read]; + to = NULL; + + for (oldi=0 ; oldinum_entities ; oldi++) + { + if (read == newpack->entities[oldi].number) + { + from = &newpack->entities[oldi]; + to = &newpack->entities[oldi]; + break; + } + } + + if (!to) + { //okay, so this is new + if (newpack->num_entities==newpack->max_entities) + { + newpack->max_entities = newpack->num_entities+16; + newpack->entities = BZ_Realloc(newpack->entities, sizeof(entity_state_t)*newpack->max_entities); + } + + to = &newpack->entities[newpack->num_entities]; + newpack->num_entities++; + + if (cl_shownet.ival >= 3) + Con_Printf("%3i: New %i %x\n", MSG_GetReadCount(), to->number, bits); + } + else if (cl_shownet.ival >= 3) + Con_Printf("%3i: Update %i %x\n", MSG_GetReadCount(), to->number, bits); + + memcpy(to, from, sizeof(*to)); + to->number = read; + + if (bits & UH2_MODEL) to->modelindex = MSG_ReadShort(); + if (bits & UH2_FRAME) to->frame = MSG_ReadByte(); + if (bits & UH2_COLORMAP)to->colormap = MSG_ReadByte(); + if (bits & UH2_SKIN) to->skinnum = MSG_ReadByte(); + if (bits & UH2_SKIN) to->hexen2flags = MSG_ReadByte(); //yes, shared with skin + if (bits & UH2_EFFECTS) to->effects = MSG_ReadByte(); + if (bits & UH2_ORIGIN1) to->origin[0] = MSG_ReadCoord(); + if (bits & UH2_ANGLE1) to->angles[0] = MSG_ReadAngle(); + if (bits & UH2_ORIGIN2) to->origin[1] = MSG_ReadCoord(); + if (bits & UH2_ANGLE2) to->angles[1] = MSG_ReadAngle(); + if (bits & UH2_ORIGIN3) to->origin[2] = MSG_ReadCoord(); + if (bits & UH2_ANGLE3) to->angles[2] = MSG_ReadAngle(); + if (bits & UH2_SCALE) to->scale = MSG_ReadByte()*(16.0/100); + if (bits & UH2_SCALE) to->abslight = MSG_ReadByte(); + + to->sequence = cls.netchan.incoming_sequence; + to->inactiveflag = 0; + } + + //handle the removes + if (bits != 48) + Host_EndGame("Corrupt entity message packet\n"); + removecount = (qbyte)MSG_ReadByte(); + while (removecount --> 0) + { + read = MSG_ReadUInt16(); + for (oldi=0 ; oldinum_entities ; oldi++) + { + if (read == newpack->entities[oldi].number) + { + newpack->entities[oldi].inactiveflag = true; + break; + } + } + } + + //sort them, just in case. the removes will bubble to the end. + qsort(newpack->entities, newpack->num_entities, sizeof(entity_state_t), CLDP_SortEntities); + while (newpack->num_entities) + { //pop those removes. + if (newpack->entities[newpack->num_entities-1].inactiveflag) + newpack->num_entities--; + else + break; + } + + + //make sure any bone states are refreshed + for (oldi=0, to = newpack->entities; oldinum_entities ; oldi++, to++) + { + if (to->bonecount && (to->boneoffset & 0x80000000)) + { + unsigned int oldoffset = to->boneoffset & 0x7fffffff; + void *dest = AllocateBoneSpace(newpack, to->bonecount, &to->boneoffset); + void *src = GetBoneSpace(oldpack, oldoffset); + memcpy(dest, src, to->bonecount * sizeof(short)*7); + } + } +} +#endif + void CLNQ_ParseEntity(unsigned int bits) { int i; @@ -3934,7 +4137,7 @@ static qboolean CL_ChooseInterpolationFrames(int *newf, int *oldf, float servert qboolean CL_MayLerp(void) { //force lerping when playing low-framerate demos. - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) return true; #ifdef NQPROT if (cls.demoplayback == DPB_NETQUAKE) @@ -3956,13 +4159,13 @@ void CL_TransitionEntities (void) qboolean nolerp; float servertime, frac; - if (cls.protocol == CP_QUAKEWORLD && (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV)) + if (cls.protocol == CP_QUAKEWORLD && cls.demoplayback == DPB_MVD) { nolerp = false; } else { - nolerp = !CL_MayLerp() && cls.demoplayback != DPB_MVD && cls.demoplayback != DPB_EZTV; + nolerp = !CL_MayLerp() && cls.demoplayback != DPB_MVD; } if (cl.demonudge < 0) @@ -4825,8 +5028,26 @@ void CLQW_ParsePlayerinfo (void) oldstate = &cl.inframes[oldparsecountmod].playerstate[num]; state = &cl.inframes[parsecountmod].playerstate[num]; - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) { +#ifdef QUAKESTATS + int i; + const char *viewmodel = NULL; + static struct { + const char *vmdl; + const char *vwep; + } haxx[] = + { + {"progs/v_axe.mdl", "w_axe"}, + {"progs/v_shot.mdl", "w_shot"}, + {"progs/v_shot2.mdl", "w_shot2"}, + {"progs/v_nail.mdl", "w_nail"}, + {"progs/v_nail2.mdl", "w_nail2"}, + {"progs/v_rock.mdl", "w_rock"}, + {"progs/v_rock2.mdl", "w_rock2"}, + {"progs/v_light.mdl", "w_light"}, + }; +#endif player_state_t dummy; if (!cl.parsecount || info->prevcount > cl.parsecount || cl.parsecount - info->prevcount >= UPDATE_BACKUP - 1) { @@ -4853,11 +5074,38 @@ void CLQW_ParsePlayerinfo (void) state->messagenum = cl.parsecount; state->command.msec = 0; + state->command.impulse = 0; + +#ifdef QUAKESTATS + i = cl.players[num].stats[STAT_WEAPONMODELI]; + if (i>0&&icommand.impulse = i; + break; + } + } + break; + } + } + } +#endif state->frame = MSG_ReadByte (); state->state_time = parsecounttime; - state->command.msec = 0; for (i = 0; i < 3; i++) { @@ -5365,7 +5613,7 @@ void CL_LinkPlayers (void) frame = &cl.inframes[displayseq&UPDATE_MASK]; predictplayers = cl_predict_players.ival; - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) predictplayers = false; for (j=0, info=cl.players, state=frame->playerstate ; j < cl.allocated_client_slots diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index ae5672137..49617fb81 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -1332,6 +1332,8 @@ void CL_AdjustAngles (int pnum, double frametime) if (!cl_instantrotate.ival) quant *= speed*frametime; in_rotate -= quant; + if (r_xflip.ival) + quant *= -1; if (ruleset_allow_frj.ival) cl.playerview[pnum].viewanglechange[YAW] += quant; } @@ -1342,6 +1344,8 @@ void CL_AdjustAngles (int pnum, double frametime) if ((cl.fpd & FPD_LIMIT_YAW) || !ruleset_allow_frj.ival) quant = bound(-900, quant, 900); quant *= frametime; + if (r_xflip.ival) + quant *= -1; cl.playerview[pnum].viewanglechange[YAW] -= quant * CL_KeyState (&in_right, pnum, false); cl.playerview[pnum].viewanglechange[YAW] += quant * CL_KeyState (&in_left, pnum, false); } @@ -1410,6 +1414,9 @@ static void CL_BaseMove (vec3_t moves, int pnum) if ((in_speed.state[pnum] & 1) ^ cl_run.ival) scale *= cl_movespeedkey.value; + if (r_xflip.ival) + sidespeed *= -1; + moves[0] = 0; if (! (in_klook.state[pnum] & 1) ) { @@ -1819,7 +1826,7 @@ static qboolean CLFTE_SendVRCmd (sizebuf_t *buf, unsigned int seats) if (flags & VRM_LOSS) MSG_WriteByte (buf, (qbyte)lost); if (flags & VRM_DELAY) - MSG_WriteByte (buf, bound(0,cldelay,255)); //a byte should always be enough for any framerate above 40. + MSG_WriteByte (buf, bound(0,cldelay,255)); //a byte should always be enough for any framerate above 40, and we don't want peole to be able to lie so easily. if (flags & VRM_ACKS) { MSG_WriteUInt64(buf, cl.numackframes); @@ -2122,7 +2129,7 @@ void VARGS CL_SendSeatClientCommand(qboolean reliable, unsigned int seat, char * char string[2048]; clcmdbuf_t *buf, *prev; - if (cls.demoplayback && cls.demoplayback != DPB_EZTV) + if (cls.demoplayback && !(cls.demoplayback == DPB_MVD && cls.demoeztv_ext)) return; //no point. va_start (argptr, format); @@ -2783,7 +2790,7 @@ void CL_SendCmd (double frametime, qboolean mainloop) if (cls.demoplayback != DPB_NONE || cls.state <= ca_demostart) { cursor_active = false; - if (!cls.state || cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (!cls.state || cls.demoplayback == DPB_MVD) { extern cvar_t cl_splitscreen; cl.ackedmovesequence = cl.movesequence; diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 22bdd3e50..ab6f2231b 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -73,7 +73,8 @@ cvar_t cl_nolerp = CVARD("cl_nolerp", "0", "Disables interpolation. If set, miss cvar_t cl_nolerp_netquake = CVARD("cl_nolerp_netquake", "0", "Disables interpolation when connected to an NQ server. Does affect players, even the local player. You probably don't want to set this."); static cvar_t cl_fullpitch_nq = CVARAFD("cl_fullpitch", "0", "pq_fullpitch", CVAR_SEMICHEAT, "When set, attempts to unlimit the default view pitch. Note that some servers will screw over your angles if you use this, resulting in terrible gameplay, while some may merely clamp your angle serverside. This is also considered a cheat in quakeworld, ^1so this will not function there^7. For the equivelent in quakeworld, use serverinfo minpitch+maxpitch instead, which applies to all players fairly."); #endif -static cvar_t cl_forcevrui = CVARD("cl_forcevrui", "0", "Force the use of VR UIs, even with no VR headset active."); +static cvar_t cl_vrui_force = CVARD("cl_vrui_force", "0", "Force the use of VR UIs, even with no VR headset active."); +cvar_t cl_vrui_lock = CVARD("cl_vrui_lock", "1", "Controls how the UI is positioned when using VR/XR. 0: Repositioned infront of the head when the console/menus are toggled. 1: Locked infront of the reference position (which may require the user to turn around to find it)."); cvar_t *hud_tracking_show; cvar_t *hud_miniscores_show; extern cvar_t net_compress; @@ -119,14 +120,18 @@ cvar_t cl_noblink = CVARD("cl_noblink", "0", "Disable the ^^b text blinking feat cvar_t cl_servername = CVARFD("cl_servername", "", CVAR_NOSET, "The hostname of the last server you connected to"); cvar_t cl_serveraddress = CVARD("cl_serveraddress", "none", "The address of the last server you connected to"); cvar_t qtvcl_forceversion1 = CVAR("qtvcl_forceversion1", "0"); -cvar_t qtvcl_eztvextensions = CVAR("qtvcl_eztvextensions", "0"); +cvar_t qtvcl_eztvextensions = CVAR("qtvcl_eztvextensions", "1"); cvar_t record_flush = CVARD("record_flush", "0", "If set, explicitly flushes demo data to disk while recording. This may be inefficient, depending on how your operating system is configured."); cvar_t cl_demospeed = CVARF("cl_demospeed", "1", 0); cvar_t cl_demoreel = CVARFD("cl_demoreel", "0", CVAR_SAVE, "When enabled, the engine will begin playing a demo loop on startup."); cvar_t cl_loopbackprotocol = CVARD("cl_loopbackprotocol", "qw", "Which protocol to use for single-player/the internal client. Should be one of: qw, qwid, nqid, nq, fitz, bjp3, dp6, dp7, auto. If 'auto', will use qw protocols for qw mods, and nq protocols for nq mods."); +#ifdef FTE_TARGET_WEB +static cvar_t cl_verify_urischeme = CVARAFD("cl_verify_urischeme", "2", "cl_verify_qwprotocol"/*ezquake, inappropriate for misc schemes*/, CVAR_NOSAVE/*checked at startup, so its only really default.cfg that sets it*/, "0: Do nothing.\n1: Check whether our protocol scheme is registered and prompt the user to register associations.\n2: Always re-register on every startup, without prompting. Sledgehammer style."); +#else static cvar_t cl_verify_urischeme = CVARAFD("cl_verify_urischeme", "0", "cl_verify_qwprotocol"/*ezquake, inappropriate for misc schemes*/, CVAR_NOSAVE/*checked at startup, so its only really default.cfg that sets it*/, "0: Do nothing.\n1: Check whether our protocol scheme is registered and prompt the user to register associations.\n2: Always re-register on every startup, without prompting. Sledgehammer style."); +#endif cvar_t cl_threadedphysics = CVARD("cl_threadedphysics", "0", "When set, client input frames are generated and sent on a worker thread"); @@ -192,7 +197,6 @@ cvar_t cl_sendguid = CVARD("cl_sendguid", "", "Send a randomly generated 'glo cvar_t cl_downloads = CVARAFD("cl_downloads", "1", /*q3*/"cl_allowDownload", CVAR_NOTFROMSERVER|CVAR_ARCHIVE, "Allows you to block all automatic downloads."); cvar_t cl_download_csprogs = CVARFD("cl_download_csprogs", "1", CVAR_NOTFROMSERVER|CVAR_ARCHIVE, "Download updated client gamecode if available. Warning: If you clear this to avoid downloading vm code, you should also clear cl_download_packages."); cvar_t cl_download_redirection = CVARFD("cl_download_redirection", "2", CVAR_NOTFROMSERVER|CVAR_ARCHIVE, "Follow download redirection to download packages instead of individual files. Also allows the server to send nearly arbitary download commands.\n2: allows redirection only to named packages files (and demos/*.mvd), which is a bit safer."); -cvar_t cl_download_mapsrc = CVARFD("cl_download_mapsrc", "", CVAR_ARCHIVE, "Specifies an http location prefix for map downloads. EG: \"http://example.com/path/gamemaps/\""); cvar_t cl_download_packages = CVARFD("cl_download_packages", "1", CVAR_NOTFROMSERVER, "0=Do not download packages simply because the server is using them. 1=Download and load packages as needed (does not affect games which do not use this package). 2=Do download and install permanently (use with caution!)"); cvar_t requiredownloads = CVARAFD("cl_download_wait", "1", /*old*/"requiredownloads", CVAR_ARCHIVE, "0=join the game before downloads have even finished (might be laggy). 1=wait for all downloads to complete before joining."); cvar_t mod_precache = CVARD("mod_precache","1", "Controls when models are loaded.\n0: Load them only when they're actually needed.\n1: Load them upfront.\n2: Lazily load them to shorten load times at the risk of brief stuttering during only the start of the map."); @@ -302,7 +306,7 @@ static struct } ext; int qport; int challenge; //tracked as part of guesswork based upon what replies we get. - int clchallenge; + int clchallenge; //generated by the client, to ensure the response wasn't spoofed/spammed. double time; //for connection retransmits qboolean clogged; //ignore time... enum coninfomode_e @@ -361,7 +365,10 @@ void VRUI_SnapAngle(void) { // VectorCopy(cl.playerview[0].viewangles, vrui.angles); vrui.angles[0] = 0; - vrui.angles[1] = cl.playerview[0].aimangles[1]; + if (cl_vrui_lock.ival) //if its locked, then its locked infront of the unpitched player entity + vrui.angles[1] = cl.playerview[0].viewangles[1]; + else //otherwise its moved to infront of the player's head any time the menu is displayed. + vrui.angles[1] = cl.playerview[0].aimangles[1]; vrui.angles[2] = 0; } @@ -796,6 +803,8 @@ static void CL_SendConnectPacket (netadr_t *to) //cl.splitclients = 1; if (q3) q3->cl.SendConnectPacket(cls.sockets, to, connectinfo.challenge, connectinfo.qport, cls.userinfo); + else + CL_ConnectAbort("Q3 plugin not loaded, cannot connect to q3 servers without it.\n"); return; } #endif @@ -926,7 +935,7 @@ char *CL_TryingToConnect(void) return cls.servername; } -#ifdef NQPROT +#if defined(NQPROT) && defined(HAVE_SERVER) static void CL_NullReadPacket(void) { //just drop it all } @@ -942,9 +951,7 @@ struct resolvectx_s }; static void CL_ResolvedServer(void *vctx, void *data, size_t a, size_t b) { -#ifdef HAVE_DTLS size_t i; -#endif struct resolvectx_s *ctx = vctx; //something screwed us over... @@ -971,7 +978,7 @@ static void CL_ResolvedServer(void *vctx, void *data, size_t a, size_t b) if (connectinfo.mode == CIM_Q2EONLY) { for (i = 0; i < ctx->found; i++) - { //if we've already established a dtls connection, stick with it + { //if we've already established a dtls connection, stick with it, otherwise 'upgrade' from udp to 'kexlan' transport layer. if (ctx->adr[i].prot == NP_DGRAM) ctx->adr[i].prot = NP_KEXLAN; } @@ -986,8 +993,12 @@ static void CL_ResolvedServer(void *vctx, void *data, size_t a, size_t b) static void CL_ResolveServer(void *vctx, void *data, size_t a, size_t b) { struct resolvectx_s *ctx = vctx; + + //stupid logic for targ@prox2@prox1 chaining. just disable it if there's weird ws:// or whatever in there. + //FIXME: really shouldn't be in there + const char *res = strrchr(ctx->servername, '/'); const char *host = strrchr(ctx->servername+1, '@'); - if (host) + if (host && !res) host++; else host = ctx->servername; @@ -1464,8 +1475,10 @@ void CL_CheckForResend (void) if (contype & 1) { char tmp[256]; - //vanilla: Q_snprintfz (data, sizeof(data), "%c%c%c%cgetchallenge\n", 255, 255, 255, 255); - Q_snprintfz (data, sizeof(data), "%c%c%c%cgetchallenge %i %s\n", 255, 255, 255, 255, connectinfo.clchallenge, COM_QuotedString(com_protocolname.string, tmp, sizeof(tmp), false)); + if (strrchr(cls.servername, '@')) //if we're apparently using qwfwd then strictly adhere to vanilla's protocol so that qwfwd does not bug out. this will pollute gamedirs if stuff starts autodownloading from the wrong type of server, and probably show up vanilla-vs-rerelease glitches everywhere.. + Q_snprintfz (data, sizeof(data), "%c%c%c%cgetchallenge\n", 255, 255, 255, 255); + else + Q_snprintfz (data, sizeof(data), "%c%c%c%cgetchallenge %i %s\n", 255, 255, 255, 255, connectinfo.clchallenge, COM_QuotedString(com_protocolname.string, tmp, sizeof(tmp), false)); switch(NET_SendPacket (cls.sockets, strlen(data), data, to)) { case NETERR_CLOGGED: //temporary failure @@ -1511,7 +1524,7 @@ void CL_CheckForResend (void) if (*e) pwd = CalcHashInt(&hash_md4, password.string, strlen(password.string)); } - MSG_WriteByte(&sb, 1); /*'mod'*/ + MSG_WriteByte(&sb, MOD_PROQUAKE); /*'mod'*/ MSG_WriteByte(&sb, 34); /*'mod' version*/ MSG_WriteByte(&sb, 0); /*flags*/ MSG_WriteLong(&sb, pwd); /*password*/ @@ -1584,9 +1597,9 @@ static void CL_BeginServerConnect(char *host, int port, qboolean noproxy, enum c //the scheme is either a network scheme in which case we use it directly, or a game-specific scheme. scheme = NET_IsURIScheme(schemestart); - if (scheme->prot == NP_INVALID) + if (scheme && scheme->prot == NP_INVALID) scheme = NULL; //qw:// or q3:// something that's just noise here. - if (scheme->flags&URISCHEME_NEEDSRESOURCE) + if (scheme && scheme->flags&URISCHEME_NEEDSRESOURCE) { Q_strncpyz (cls.servername, schemestart, sizeof(cls.servername)); //oh. will probably be okay then arglist = NULL; @@ -1612,6 +1625,13 @@ static void CL_BeginServerConnect(char *host, int port, qboolean noproxy, enum c Con_Printf("Ignoring 'join'\n"); memmove(sl, sl+5, strlen(sl+5)+1); } + else if (!strncmp(sl, "/qtvplay", 5)) + { + char buf[256]; + *sl = 0; + Cmd_ExecuteString(va("qtvplay %s\n", COM_QuotedString(schemeend+3, buf,sizeof(buf), false)), RESTRICT_LOCAL); + return; + } else if (!strncmp(sl, "/", 1) && (sl[1] == 0 || sl[1]=='?')) { //current spectator mode @@ -1649,6 +1669,7 @@ static void CL_BeginServerConnect(char *host, int port, qboolean noproxy, enum c connectinfo.protocol = CP_UNKNOWN; connectinfo.mode = mode; connectinfo.spec = spec; + connectinfo.clchallenge = rand()^(rand()<<16); connectinfo.peercred.name = cls.servername; if (arglist) @@ -1716,6 +1737,7 @@ void CL_BeginServerReconnect(void) connectinfo.time = 0; connectinfo.tries = 0; //re-ensure routes. connectinfo.nextadr = 0; //should at least be consistent, other than packetloss. yay. + connectinfo.clchallenge = rand()^(rand()<<16); NET_InitClient(false); } @@ -1785,11 +1807,11 @@ static void CL_Connect_f (void) server = Cmd_Argv (1); server = strcpy(alloca(strlen(server)+1), server); -#ifdef HAVE_SERVER +/*#ifdef HAVE_SERVER if (sv.state == ss_clustermode) CL_Disconnect (NULL); else -#endif +#endif*/ CL_Disconnect_f (); CL_BeginServerConnect(server, 0, false, CIM_DEFAULT, CIS_DEFAULT); @@ -1821,11 +1843,11 @@ static void CL_ConnectBestRoute_f (void) else Con_TPrintf ("Routing table favours chaining through %i proxies (%ims vs %ims)\n", proxies, chainedcost, directcost); -#ifdef HAVE_SERVER +/*#ifdef HAVE_SERVER if (sv.state == ss_clustermode) CL_Disconnect (NULL); else -#endif +#endif*/ CL_Disconnect_f (); CL_BeginServerConnect(server, 0, true, CIM_DEFAULT, CIS_DEFAULT); } @@ -2265,6 +2287,17 @@ void CL_ClearState (qboolean gamestart) if (cl.particle_csname[i]) free(cl.particle_csname[i]); } + for (i = 0; i < countof(cl.model_name); i++) + if (cl.model_name[i]) + BZ_Free(cl.model_name[i]); +#ifdef HAVE_LEGACY + for (i = 0; i < countof(cl.model_name_vwep); i++) + if (cl.model_name_vwep[i]) + BZ_Free(cl.model_name_vwep[i]); +#endif + for (i = 0; i < countof(cl.sound_name); i++) + if (cl.sound_name[i]) + BZ_Free(cl.sound_name[i]); #ifdef Q2CLIENT for (i = 0; i < Q2MAX_IMAGES; i++) if (cl.image_name[i]) @@ -2521,6 +2554,13 @@ void CL_Disconnect (const char *reason) cls.findtrack = false; cls.realserverip.type = NA_INVALID; + while (cls.qtvviewers) + { + struct qtvviewers_s *v = cls.qtvviewers; + cls.qtvviewers = v->next; + Z_Free(v); + } + #ifdef TCPCONNECT //disconnects it, without disconnecting the others. FTENET_AddToCollection(cls.sockets, "conn", NULL, NA_INVALID, NP_DGRAM); @@ -2618,6 +2658,7 @@ void CL_Users_f (void) { int i; int c; + struct qtvviewers_s *v; c = 0; Con_TPrintf ("userid frags name\n"); @@ -2631,6 +2672,11 @@ void CL_Users_f (void) } } + for (v = cls.qtvviewers; v; v = v->next) + { + Con_Printf ("%6s %4s ^[%s^]\n", "", "-", v->name); + } + Con_TPrintf ("%i total users\n", c); } @@ -2927,7 +2973,7 @@ void CL_PakDownloads(int mode) mode&4 download even packages that are not referenced. */ char local[256]; - char *pname; + char *pname, *sep; char *s = cl.serverpackhashes; int i; @@ -2946,6 +2992,10 @@ void CL_PakDownloads(int mode) else if (!(mode & 4)) continue; + sep = strchr(pname, '/'); + if (!sep || strchr(sep+1, '/')) + continue; //don't try downloading weird ones here... paks inside paks is screwy stuff! + if ((mode&3) != 2) { /*if we already have such a file, this is a no-op*/ @@ -3773,6 +3823,7 @@ void CL_Reconnect_f (void) static void CL_ConnectionlessPacket_Connection(char *tokens) { + unsigned int ncflags; int qportsize = -1; if (net_from.type == NA_INVALID) return; //I've found a qizmo demo that contains one of these. its best left ignored. @@ -3848,19 +3899,16 @@ static void CL_ConnectionlessPacket_Connection(char *tokens) cls.fteprotocolextensions2 = connectinfo.ext.fte2; cls.ezprotocolextensions1 = connectinfo.ext.ez1; cls.challenge = connectinfo.challenge; - Netchan_Setup (NS_CLIENT, &cls.netchan, &net_from, connectinfo.qport); + ncflags = NCF_CLIENT; + if (connectinfo.ext.mtu) + ncflags |= NCF_FRAGABLE; + if (connectinfo.ext.fte2&PEXT2_STUNAWARE) + ncflags |= NCF_STUNAWARE; + Netchan_Setup (ncflags, &cls.netchan, &net_from, connectinfo.qport, connectinfo.ext.mtu); cls.protocol_q2 = (cls.protocol == CP_QUAKE2)?connectinfo.subprotocol:0; if (qportsize>=0) cls.netchan.qportsize = qportsize; - cls.netchan.pext_fragmentation = connectinfo.ext.mtu?true:false; - cls.netchan.pext_stunaware = !!(connectinfo.ext.fte2&PEXT2_STUNAWARE); - if (connectinfo.ext.mtu >= 64) - { - cls.netchan.mtu = connectinfo.ext.mtu; - cls.netchan.message.maxsize = sizeof(cls.netchan.message_buf); - } - else - cls.netchan.mtu = MAX_QWMSGLEN; + #ifdef HUFFNETWORK cls.netchan.compresstable = Huff_CompressionCRC(connectinfo.ext.compresscrc); #else @@ -4069,7 +4117,7 @@ void CL_ConnectionlessPacket (void) if (!strcmp(com_token, "hallengeResponse")) { - /*Quake3*/ + /*Quake3 - "\xff\xff\xff\xffchallengeResponse challenge [clchallenge protover]" (no \n)*/ #ifdef Q3CLIENT if (connectinfo.protocol == CP_QUAKE3 || connectinfo.protocol == CP_UNKNOWN) { @@ -4262,12 +4310,12 @@ void CL_ConnectionlessPacket (void) #ifdef HAVE_DTLS if ((candtls && net_enable_dtls.ival) && net_from.prot == NP_DGRAM && (net_enable_dtls.ival>1 || candtls > 1) && !NET_IsEncrypted(&net_from)) { - //c2s getchallenge //s2c c%u\0DTLS=$candtls //<> //c2s dtlsconnect %u [REALTARGET] //s2c dtlsopened - //c2s DTLS(getchallenge) + //c2s DTLS(getchallenge) //DTLS(etc) //NOTE: the dtlsconnect/dtlsopened parts are redundant and the non-dtls parts are now entirely optional (and should be skipped if the client requries/knows the server supports dtls) @@ -4284,7 +4332,7 @@ void CL_ConnectionlessPacket (void) char *pkt; //qwfwd proxy routing. it doesn't support it yet, but hey, if its willing to forward the dtls packets its all good. char *at; - if ((at = strrchr(cls.servername, '@'))) + if ((at = strrchr(cls.servername, '@')) && !strchr(cls.servername, '/')) { *at = 0; pkt = va("%c%c%c%c""dtlsconnect %i %s", 255, 255, 255, 255, connectinfo.challenge, cls.servername); @@ -4391,7 +4439,7 @@ void CL_ConnectionlessPacket (void) } Validation_Apply_Ruleset(); - Netchan_Setup(NS_CLIENT, &cls.netchan, &net_from, connectinfo.qport); + Netchan_Setup(NCF_CLIENT, &cls.netchan, &net_from, connectinfo.qport, 0); CL_ParseEstablished(); cls.netchan.isnqprotocol = true; @@ -4674,7 +4722,7 @@ void CLNQ_ConnectionlessPacket(void) cls.fteprotocolextensions = connectinfo.ext.fte1; cls.fteprotocolextensions2 = connectinfo.ext.fte2; cls.ezprotocolextensions1 = connectinfo.ext.ez1; - Netchan_Setup (NS_CLIENT, &cls.netchan, &net_from, connectinfo.qport); + Netchan_Setup (NCF_CLIENT, &cls.netchan, &net_from, connectinfo.qport, 0); CL_ParseEstablished(); cls.netchan.isnqprotocol = true; cls.netchan.compresstable = NULL; @@ -4767,7 +4815,7 @@ void CL_ReadPacket(void) return; //ignore it. We arn't connected. } - if (net_message.cursize < 6 && (cls.demoplayback != DPB_MVD && cls.demoplayback != DPB_EZTV)) //MVDs don't have the whole sequence header thing going on + if (net_message.cursize < 6 && cls.demoplayback != DPB_MVD) //MVDs don't have the whole sequence header thing going on { char adr[MAX_ADR_SIZE]; if (net_message.cursize == 1 && net_message.data[0] == A2A_ACK) @@ -4791,7 +4839,7 @@ void CL_ReadPacket(void) return; } - if (cls.netchan.pext_stunaware) //should be safe to do this here. + if (cls.netchan.flags&NCF_STUNAWARE) //should be safe to do this here. if (NET_WasSpecialPacket(cls.sockets)) return; @@ -4836,7 +4884,7 @@ void CL_ReadPacket(void) #endif break; case CP_QUAKEWORLD: - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) { MSG_BeginReading(&net_message, cls.netchan.netprim); cls.netchan.last_received = realtime; @@ -4891,7 +4939,7 @@ void CL_ReadPackets (void) } } - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) { CL_MVDUpdateSpectator(); } @@ -4908,7 +4956,7 @@ qboolean CL_AllowArbitaryDownload(const char *oldname, const char *localfile) strstr(localfile, "\\") || strstr(localfile, "..") || strstr(localfile, "./") || strstr(localfile, ":") || strstr(localfile, "//") || //certain path patterns are just bad Q_strcasestr(localfile, ".qvm") || Q_strcasestr(localfile, ".dll") || Q_strcasestr(localfile, ".so") || Q_strcasestr(localfile, ".dylib")) //disallow any native code { //yes, I know the user can use a different progs from the one that is specified. If you leave it blank there will be no problem. (server isn't allowed to stuff progs cvar) - Con_Printf("Ignoring arbitary download to \"%s\" due to possible security risk\n", localfile); + Con_Printf("Ignoring arbitrary download to \"%s\" due to possible security risk\n", localfile); return false; } allow = cl_download_redirection.ival; @@ -4941,10 +4989,12 @@ static void CL_Curl_f(void) int i, argc = Cmd_Argc(); const char *arg, *gamedir, *localterse/*no dlcache*/= NULL; char localname[MAX_QPATH]; + char localnametmp[MAX_QPATH]; int usage = 0; qboolean alreadyhave = false; extern char *cl_dp_packagenames; unsigned int dlflags = DLLF_VERBOSE|DLLF_ALLOWWEB; + const char *ext; if (argc < 2) { Con_Printf("%s: No args\n", Cmd_Argv(0)); @@ -5015,12 +5065,29 @@ static void CL_Curl_f(void) } arg = Cmd_Argv(argc-1); if (!localterse) + { + char *t; + localterse = strrchr(arg, '/'); + if (!localterse) + localterse = arg; + t = strchr(localterse, '?'); + if (t) + *t = 0; + if (t-localterse < countof(localnametmp)) + { + memcpy(localnametmp, localterse, t-localterse); + localnametmp[t-localterse] = 0; + localterse = localnametmp; + } + } + if (!localterse) { //for compat, we should look for the last / and truncate on a ?. Con_Printf("%s: skipping download of %s, as the local name was not explicitly given\n", Cmd_Argv(0), arg); return; } - if (usage == 1) + ext = COM_GetFileExtension(localterse, NULL); + if (usage == 1 && (!strcmp(ext, ".pk3") || !strcmp(ext, ".pak"))) { dlflags |= DLLF_NONGAME; gamedir = FS_GetGamedir(true); @@ -5084,7 +5151,7 @@ void CL_Download_f (void) if (!*localname) localname = url; - if ((cls.state == ca_disconnected || cls.demoplayback) && cls.demoplayback != DPB_EZTV) + if ((cls.state == ca_disconnected || cls.demoplayback) && !(cls.demoplayback == DPB_MVD && (cls.demoeztv_ext&EZTV_DOWNLOAD))) { Con_TPrintf ("Must be connected.\n"); return; @@ -5134,14 +5201,14 @@ void CL_DownloadSize_f(void) size = Cmd_Argv(2); if (!strcmp(size, "e")) { - Con_Printf("Download of \"%s\" failed. Not found.\n", rname); + Con_Printf(CON_ERROR"Download of \"%s\" failed. Not found.\n", rname); CL_DownloadFailed(rname, NULL, DLFAIL_SERVERFILE); } else if (!strcmp(size, "p")) { if (cls.download && stricmp(cls.download->remotename, rname)) { - Con_Printf("Download of \"%s\" failed. Not allowed.\n", rname); + Con_Printf(CON_ERROR"Download of \"%s\" failed. Not allowed.\n", rname); CL_DownloadFailed(rname, NULL, DLFAIL_SERVERCVAR); } } @@ -5359,21 +5426,11 @@ void CL_Fog_f(void) #ifdef _DEBUG void CL_FreeSpace_f(void) { + char buf[32]; quint64_t freespace; const char *freepath = Cmd_Argv(1); if (Sys_GetFreeDiskSpace(freepath, &freespace)) - { - if (freespace > 512.0*1024*1024*1024) - Con_Printf("%s: %g tb available\n", freepath, freespace/(1024.0*1024*1024*1024)); - else if (freespace > 512.0*1024*1024) - Con_Printf("%s: %g gb available\n", freepath, freespace/(1024.0*1024*1024)); - else if (freespace > 512.0*1024) - Con_Printf("%s: %g mb available\n", freepath, freespace/(1024.0*1024)); - else if (freespace > 512.0) - Con_Printf("%s: %g kb available\n", freepath, freespace/1024.0); - else - Con_Printf("%s: %"PRIu64" bytes available\n", freepath, freespace); - } + Con_Printf("%s: %s available\n", freepath, FS_AbbreviateSize(buf,sizeof(buf),freespace)); else Con_Printf("%s: disk free not queryable\n", freepath); } @@ -5434,7 +5491,24 @@ void CL_Status_f(void) if (cls.state) { + char cert[8192]; + qbyte fp[DIGEST_MAXSIZE+1]; + char b64[(DIGEST_MAXSIZE*4)/3+1]; + if (NET_GetConnectionCertificate(cls.sockets, &cls.netchan.remote_address, QCERT_ISENCRYPTED, NULL, 0)) + Q_strncpyz(b64, "", sizeof(b64)); + else + { + int sz = NET_GetConnectionCertificate(cls.sockets, &cls.netchan.remote_address, QCERT_PEERCERTIFICATE, cert, sizeof(cert)); + if (sz<0) + Q_strncpyz(b64, "", sizeof(b64)); + else + { + sz = Base64_EncodeBlockURI(fp, CalcHash(&hash_certfp, fp,sizeof(fp), cert, sz), b64, sizeof(b64)); + b64[sz] = 0; + } + } Con_Printf("Server address : %s\n", NET_AdrToString(adr, sizeof(adr), &cls.netchan.remote_address)); //not relevent as a limit. + Con_Printf("Server cert fp : %s\n", b64); //not relevent as a limit. switch(cls.protocol) { default: @@ -5466,6 +5540,9 @@ void CL_Status_f(void) case CPNQ_BJP3: Con_Printf("Network Protocol : BJP3\n"); break; + case CPNQ_H2MP: + Con_Printf("Network Protocol : H2MP\n"); + break; case CPNQ_FITZ666: Con_Printf("Network Protocol : FitzQuake\n"); break; @@ -5555,10 +5632,10 @@ void CL_Status_f(void) count++; } Con_Printf("csqc entities : %i/%i/%i (mem: %.1f%%)\n", count, csqc_world.num_edicts, csqc_world.max_edicts, 100*(float)(csqc_world.progs->stringtablesize/(double)csqc_world.progs->stringtablemaxsize)); - for (count = 1; count < MAX_PRECACHE_MODELS; count++) + for (count = 1; count < MAX_CSMODELS; count++) if (!*cl.model_csqcname[count]) break; - Con_Printf("csqc models : %i/%i\n", count, MAX_PRECACHE_MODELS); + Con_Printf("csqc models : %i/%i\n", count, MAX_CSMODELS); Con_Printf("client sounds : %i\n", num_sfx); //there is a limit, its just private. :( for (count = 1; count < MAX_SSPARTICLESPRE; count++) @@ -5678,7 +5755,8 @@ void CL_Init (void) Cvar_Register (&cl_idlefps, cl_screengroup); Cvar_Register (&cl_yieldcpu, cl_screengroup); Cvar_Register (&cl_timeout, cl_controlgroup); - Cvar_Register (&cl_forcevrui, cl_controlgroup); + Cvar_Register (&cl_vrui_force, cl_controlgroup); + Cvar_Register (&cl_vrui_lock, cl_controlgroup); Cvar_Register (&lookspring, cl_inputgroup); Cvar_Register (&lookstrafe, cl_inputgroup); Cvar_Register (&sensitivity, cl_inputgroup); @@ -5794,7 +5872,6 @@ void CL_Init (void) Cvar_Register (&cl_threadedphysics, cl_controlgroup); hud_tracking_show = Cvar_Get("hud_tracking_show", "1", 0, "statusbar"); hud_miniscores_show = Cvar_Get("hud_miniscores_show", "1", 0, "statusbar"); - Cvar_Register (&cl_download_mapsrc, cl_controlgroup); Cvar_Register (&cl_dlemptyterminate, cl_controlgroup); @@ -5863,7 +5940,6 @@ void CL_Init (void) Cmd_AddCommandAD ("connectbr", CL_ConnectBestRoute_f, CL_Connect_c, "connect address:port\nConnect to a qw server using the best route we can detect."); #endif Cmd_AddCommandAD("connect", CL_Connect_f, CL_Connect_c, "connect scheme://address:port\nConnect to a server. " - #if defined(FTE_TARGET_WEB) "Use a scheme of rtc[s]://broker/gamename to connect via a webrtc broker." "Use a scheme of ws[s]://server to connect via websockets." @@ -5896,16 +5972,22 @@ void CL_Init (void) Cmd_AddCommandD ("cl_transfer", CL_Transfer_f, "Connect to a different server, disconnecting from the current server only when the new server replies."); #ifdef TCPCONNECT Cmd_AddCommandAD ("connecttcp", CL_TCPConnect_f, CL_Connect_c, "Connect to a server using the tcp:// prefix"); + Cmd_AddCommandAD ("tcpconnect", CL_TCPConnect_f, CL_Connect_c, "Connect to a server using the tcp:// prefix"); #endif #ifdef IRCCONNECT Cmd_AddCommand ("connectirc", CL_IRCConnect_f); #endif #ifdef NQPROT Cmd_AddCommandD ("connectnq", CLNQ_Connect_f, "Connects to the specified server, defaulting to port "STRINGIFY(PORT_NQSERVER)". Also disables QW/Q2/Q3/DP handshakes preventing them from being favoured, so should only be used when you actually want NQ protocols specifically."); + #ifdef HAVE_DTLS Cmd_AddCommandD ("connectqe", CLNQ_Connect_f, "Connects to the specified server, defaulting to port "STRINGIFY(PORT_NQSERVER)". Also forces the use of DTLS and QE-specific handshakes. You will also need to ensure the dtls_psk_* cvars are set properly or the server will refuse the connection."); + #endif #endif #ifdef Q2CLIENT - Cmd_AddCommandD ("connectq2e", CLQ2E_Connect_f, "Connects to the specified server, defaulting to port "STRINGIFY(PORT_Q2ESERVER)"."); + Cmd_AddCommandD ("connectq2e", CLQ2E_Connect_f, "Connects to the specified server, defaulting to port "STRINGIFY(PORT_Q2EXSERVER)"."); +#endif +#ifdef HAVE_LEGACY + Cmd_AddCommandAD("qwurl", CL_Connect_f, CL_Connect_c, "For compat with ezquake."); #endif Cmd_AddCommand ("reconnect", CL_Reconnect_f); Cmd_AddCommandAD ("join", CL_Join_f, CL_Connect_c, "Switches away from spectator mode, optionally connecting to a different server."); @@ -5924,7 +6006,7 @@ void CL_Init (void) Cmd_AddCommandAD ("color", CL_Color_f, CL_Color_c, NULL); #if defined(NQPROT) && defined(HAVE_LEGACY) - Cmd_AddCommand ("curl", CL_Curl_f); + Cmd_AddCommandD ("curl", CL_Curl_f, "For use by xonotic."); #endif Cmd_AddCommand ("download", CL_Download_f); Cmd_AddCommandD ("dlsize", CL_DownloadSize_f, "For internal use"); @@ -6074,7 +6156,7 @@ void Host_WriteConfiguration (void) f = FS_OpenVFS(savename, "wb", FS_GAMEONLY); if (!f) { - FS_NativePath(savename, FS_GAMEONLY, sysname, sizeof(sysname)); + FS_DisplayPath(savename, FS_GAMEONLY, sysname, sizeof(sysname)); Con_TPrintf (CON_ERROR "Couldn't write %s.\n", sysname); return; } @@ -6084,7 +6166,7 @@ void Host_WriteConfiguration (void) VFS_CLOSE (f); - FS_NativePath(savename, FS_GAMEONLY, sysname, sizeof(sysname)); + FS_DisplayPath(savename, FS_GAMEONLY, sysname, sizeof(sysname)); Con_Printf("Wrote %s\n", savename); } } @@ -6118,15 +6200,6 @@ qboolean Host_SimulationTime(float time) } #endif -void Host_RunFileNotify(struct dl_download *dl) -{ - if (dl->file) - { - Host_RunFile(dl->url, strlen(dl->url), dl->file); - dl->file = NULL; - } -} - #include "fs.h" #define HRF_OVERWRITE (1<<0) #define HRF_NOOVERWRITE (1<<1) @@ -6136,7 +6209,7 @@ void Host_RunFileNotify(struct dl_download *dl) #define HRF_OPENED (1<<4) #define HRF_DOWNLOADED (1<<5) //file was actually downloaded, and not from the local system #define HRF_WAITING (1<<6) //file looks important enough that we should wait for it to start to download or something before we try doing other stuff. -// (1<<7) +#define HRF_DECOMPRESS (1<<7) //need to degzip it, which prevents streaming. #define HRF_DEMO_MVD (1<<8) #define HRF_DEMO_QWD (1<<9) @@ -6165,7 +6238,7 @@ typedef struct { extern int waitingformanifest; void Host_DoRunFile(hrf_t *f); -void CL_PlayDemoStream(vfsfile_t *file, char *filename, qboolean issyspath, int demotype, float bufferdelay); +void CL_PlayDemoStream(vfsfile_t *file, char *filename, qboolean issyspath, int demotype, float bufferdelay, unsigned int eztv_ext); void CL_ParseQTVDescriptor(vfsfile_t *f, const char *name); //guesses the file type based upon its file extension. mdl/md3/iqm distinctions are not important, so we can usually get away with this in the context of quake. @@ -6201,13 +6274,13 @@ unsigned int Host_GuessFileType(const char *mimetype, const char *filename) { //demo formats {HRF_DEMO_QWD, "qwd"}, - {HRF_DEMO_QWD, "qwd.gz"}, + {HRF_DEMO_QWD|HRF_DECOMPRESS, "qwd.gz"}, {HRF_DEMO_MVD, "mvd"}, - {HRF_DEMO_MVD, "mvd.gz"}, + {HRF_DEMO_MVD|HRF_DECOMPRESS, "mvd.gz"}, {HRF_DEMO_DM2, "dm2"}, - {HRF_DEMO_DM2, "dm2.gz"}, + {HRF_DEMO_DM2|HRF_DECOMPRESS, "dm2.gz"}, {HRF_DEMO_DEM, "dem"}, - {HRF_DEMO_DEM, "dem.gz"}, + {HRF_DEMO_DEM|HRF_DECOMPRESS, "dem.gz"}, {HRF_QTVINFO, "qtv"}, //other stuff {HRF_MANIFEST, "fmf"}, @@ -6215,6 +6288,7 @@ unsigned int Host_GuessFileType(const char *mimetype, const char *filename) {HRF_BSP, "map"}, {HRF_CONFIG, "cfg"}, {HRF_CONFIG, "rc"}, + {HRF_PACKAGE, "kpf"}, {HRF_PACKAGE, "pak"}, {HRF_PACKAGE, "pk3"}, {HRF_PACKAGE, "pk4"}, @@ -6319,20 +6393,29 @@ qboolean Host_BeginFileDownload(struct dl_download *dl, char *mimetype) } } +#ifdef AVAIL_GZDEC //seeking means we can rewind - if (f->flags & HRF_DEMO_QWD) - CL_PlayDemoStream((dl->file = VFSPIPE_Open(2, true)), f->fname, true, DPB_QUAKEWORLD, 0); + if (f->flags & HRF_DECOMPRESS) + { //if its a gzip, we'll probably need to decompress it ourselves... in case the server doesn't use content-encoding:gzip + //our demo playback should decompress it when its fin ally available. + dl->file = VFSPIPE_Open(1, true); + return true; + } + else +#endif + if (f->flags & HRF_DEMO_QWD) + CL_PlayDemoStream((dl->file = VFSPIPE_Open(2, true)), f->fname, true, DPB_QUAKEWORLD, 0, 0); else if (f->flags & HRF_DEMO_MVD) - CL_PlayDemoStream((dl->file = VFSPIPE_Open(2, true)), f->fname, true, DPB_MVD, 0); + CL_PlayDemoStream((dl->file = VFSPIPE_Open(2, true)), f->fname, true, DPB_MVD, 0, 0); #ifdef Q2CLIENT else if (f->flags & HRF_DEMO_DM2) - CL_PlayDemoStream((dl->file = VFSPIPE_Open(2, true)), f->fname, true, DPB_QUAKE2, 0); + CL_PlayDemoStream((dl->file = VFSPIPE_Open(2, true)), f->fname, true, DPB_QUAKE2, 0, 0); #endif #ifdef NQPROT else if (f->flags & HRF_DEMO_DEM) { //fixme: the demo code can't handle the cd track with streamed/missing-so-far writes. dl->file = VFSPIPE_Open(1, true); //make sure the reader will be seekable, so we can rewind. -// CL_PlayDemoStream((dl->file = VFSPIPE_Open(2, true)), f->fname, DPB_NETQUAKE, 0); +// CL_PlayDemoStream((dl->file = VFSPIPE_Open(2, true)), f->fname, DPB_NETQUAKE, 0, 0); } #endif else if (f->flags & (HRF_MANIFEST | HRF_QTVINFO)) @@ -6402,6 +6485,7 @@ static qboolean isurl(char *url) #endif qboolean FS_FixupGamedirForExternalFile(char *input, char *filename, size_t fnamelen); +void CL_PlayDemoFile(vfsfile_t *f, char *demoname, qboolean issyspath); void Host_DoRunFile(hrf_t *f) { @@ -6466,7 +6550,7 @@ done: //if we still don't know what it is, give up. if (!(f->flags & HRF_FILETYPES)) { - Con_Printf("Host_DoRunFile: unknown filetype\n"); + Con_Printf("Host_DoRunFile: unknown filetype for \"%s\"\n", f->fname); goto done; } @@ -6485,18 +6569,22 @@ done: if (f->srcfile) { VFS_SEEK(f->srcfile, 0); +#ifdef AVAIL_GZDEC + f->srcfile = FS_DecompressGZip(f->srcfile, NULL); +#endif + if (f->flags & HRF_DEMO_QWD) - CL_PlayDemoStream(f->srcfile, f->fname, true, DPB_QUAKEWORLD, 0); + CL_PlayDemoStream(f->srcfile, f->fname, true, DPB_QUAKEWORLD, 0, 0); #ifdef Q2CLIENT else if (f->flags & HRF_DEMO_DM2) - CL_PlayDemoStream(f->srcfile, f->fname, true, DPB_QUAKE2, 0); + CL_PlayDemoStream(f->srcfile, f->fname, true, DPB_QUAKE2, 0, 0); #endif #ifdef NQPROT else if (f->flags & HRF_DEMO_DEM) - CL_PlayDemoStream(f->srcfile, f->fname, true, DPB_NETQUAKE, 0); + CL_PlayDemoFile(f->srcfile, f->fname, true); //should be able to handle the cd-track header. #endif else //if (f->flags & HRF_DEMO_MVD) - CL_PlayDemoStream(f->srcfile, f->fname, true, DPB_MVD, 0); + CL_PlayDemoStream(f->srcfile, f->fname, true, DPB_MVD, 0, 0); f->srcfile = NULL; } else @@ -6657,8 +6745,12 @@ done: if (f->flags & HRF_PACKAGE) { #ifdef PACKAGEMANAGER - Z_Free(f->packageinfo); + if (f->packageinfo) + Z_Free(f->packageinfo); //sucks that we have to do this again, to recompute the proper qname+qroot + Q_strncpyz(qname, COM_SkipPath(f->fname), sizeof(qname)); f->packageinfo = PM_GeneratePackageFromMeta(f->srcfile, qname,sizeof(qname), &qroot); + if (!f->packageinfo) + goto done; #endif } else if (f->flags & HRF_MANIFEST) @@ -6718,7 +6810,10 @@ done: } else if (isnew) { - Menu_Prompt(Host_RunFilePrompted, f, va(localtext("File appears new.\nWould you like to install\n%s\n"), displayname), "Install!", "", "Cancel", true); + if (f->packageinfo && strstr(f->packageinfo, "\nguessed")) + Menu_Prompt(Host_RunFilePrompted, f, va(localtext("File appears new.\nWould you like to install\n%s\n"CON_ERROR"File contains no metadata so will be installed to\n%s"), displayname, qname), "Install!", "", "Cancel", true); + else + Menu_Prompt(Host_RunFilePrompted, f, va(localtext("File appears new.\nWould you like to install\n%s\n"), displayname), "Install!", "", "Cancel", true); return; } else @@ -6729,9 +6824,9 @@ done: } else if (f->flags & HRF_OVERWRITE) { - char buffer[8192]; + char buffer[65536]; int len; - f->dstfile = FS_OpenVFS(qname, "wb", qroot); + f->dstfile = FS_OpenVFS(qname, (f->flags & HRF_PACKAGE)?"wbp":"wb", qroot); if (f->dstfile) { #ifdef FTE_TARGET_WEB @@ -6753,10 +6848,13 @@ done: #ifdef PACKAGEMANAGER if (f->flags & HRF_PACKAGE) - PM_FileInstalled(COM_SkipPath(f->fname), qroot, f->packageinfo, true); + PM_FileInstalled(qname, qroot, f->packageinfo, true); #endif - Cbuf_AddText(loadcommand, RESTRICT_LOCAL); + if (!strcmp(loadcommand, "fs_restart\n")) + FS_ReloadPackFiles(); + else + Cbuf_AddText(loadcommand, RESTRICT_LOCAL); } goto done; @@ -6779,6 +6877,8 @@ qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file) if (!Sys_ResolveFileURL(fname, nlen, utf8, sizeof(utf8))) { Con_Printf("Cannot resolve file url\n"); + if(file) + VFS_CLOSE(file); return false; } fname = utf8; @@ -6892,7 +6992,11 @@ qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file) if (file) f->flags |= HRF_OPENED; - Con_TPrintf("Opening external file: %s\n", f->fname); + { + char dpath[MAX_OSPATH]; + FS_DisplayPath(f->fname, FS_SYSTEM, dpath,sizeof(dpath)); + Con_TPrintf("Opening external file: %s\n", dpath); + } Host_DoRunFile(f); return true; @@ -6924,7 +7028,7 @@ double Host_Frame (double time) qboolean idle; extern int r_blockvidrestart; static qboolean hadwork; - qboolean vrsync; + unsigned int vrflags; qboolean mustrenderbeforeread; RSpeedLocals(); @@ -6934,7 +7038,7 @@ double Host_Frame (double time) return 0; // something bad happened, or the server disconnected } - vrsync = vid.vr?vid.vr->SyncFrame(&time):false; //fiddle with frame timings + vrflags = vid.vr?vid.vr->SyncFrame(&time):0; //fiddle with frame timings newrealtime = Media_TweekCaptureFrameTime(realtime, time); //fiddle with time some more time = newrealtime - realtime; @@ -6978,7 +7082,7 @@ double Host_Frame (double time) if (!cls.timedemo) CL_ReadPackets (); - if (idle && cl_idlefps.value > 0 && !vrsync) + if (idle && cl_idlefps.value > 0 && !(vrflags&VRF_OVERRIDEFRAMETIME)) { double idlesec = 1.0 / cl_idlefps.value; if (idlesec > 0.1) @@ -7053,7 +7157,7 @@ double Host_Frame (double time) #ifdef HAVE_MEDIA_ENCODER && Media_Capturing() != 2 #endif - && !vrsync) + && !(vrflags&VRF_OVERRIDEFRAMETIME)) { spare = CL_FilterTime((realtime - oldrealtime)*1000, maxfps, 1.5, maxfpsignoreserver); if (!spare) @@ -7128,7 +7232,7 @@ double Host_Frame (double time) CL_UseIndepPhysics(cls.state != ca_disconnected && !!cl_threadedphysics.ival); //starts/stops the input frame thread. - cl.do_lerp_players = cl_lerp_players.ival || (cls.demoplayback==DPB_MVD || cls.demoplayback == DPB_EZTV) || (cls.demoplayback && !cl_nolerp.ival && !cls.timedemo); + cl.do_lerp_players = cl_lerp_players.ival || cls.demoplayback==DPB_MVD || (cls.demoplayback && !cl_nolerp.ival && !cls.timedemo); CL_AllowIndependantSendCmd(false); mustrenderbeforeread = cls.protocol == CP_QUAKE2; //FIXME: quake2 MUST render a frame (or a later one) before it can read any acks from the server, otherwise its prediction screws up. I'm too lazy to rewrite that right now. @@ -7213,6 +7317,8 @@ double Host_Frame (double time) Con_Printf("R2D_Flush was set outside of SCR_UpdateScreen\n"); } + cl.mouseplayerview = NULL; + cl.mousenewtrackplayer = -1; for (i = 0; i < MAX_SPLITS; i++) { cl.playerview[i].audio.defaulted = true; @@ -7235,7 +7341,7 @@ double Host_Frame (double time) { RSpeedMark(); vid.ime_allow = false; - vrui.enabled = cl_forcevrui.ival; + vrui.enabled |= cl_vrui_force.ival || (vrflags&VRF_UIACTIVE); if (SCR_UpdateScreen()) fps_count++; if (R2D_Flush) @@ -7350,6 +7456,7 @@ void CL_StartCinematicOrMenu(void) startuppending = true; return; } + Cmd_StuffCmds(); if (startuppending) { if (startuppending == 2) //installer finished. @@ -7379,7 +7486,7 @@ void CL_StartCinematicOrMenu(void) } if (!sv_state && !cls.demoinfile && !cls.state && !*cls.servername) - { + { //this is so default.cfg can define startup commands to exec if the engine starts up with no +connect / +map etc arg TP_ExecTrigger("f_startup", true); Cbuf_Execute (); } @@ -7590,6 +7697,7 @@ static void Host_URIPrompt(void *ctx, promptbutton_t btn) void Host_FinishLoading(void) { + int i; extern qboolean r_forceheadless; extern int r_blockvidrestart; if (r_blockvidrestart == true) @@ -7615,8 +7723,7 @@ void Host_FinishLoading(void) Con_History_Load(); - Cmd_StuffCmds(); - Cbuf_Execute (); + r_blockvidrestart = 2; CL_ArgumentOverrides(); #ifdef HAVE_SERVER @@ -7641,14 +7748,15 @@ void Host_FinishLoading(void) Sys_Quit(); #endif - r_blockvidrestart = 2; - Menu_Download_Update(); #ifdef IPLOG IPLog_Merge_File("iplog.txt"); IPLog_Merge_File("iplog.dat"); //legacy crap, for compat with proquake #endif + + if (!PM_IsApplying()) + Cmd_StuffCmds(); } if (PM_IsApplying() == 1) @@ -7659,6 +7767,16 @@ void Host_FinishLoading(void) return; } + //open any files specified on the commandline (urls, paks, models, I dunno). + for (i = 1; i < com_argc; i++) + { + if (!com_argv[i]) + continue; + if (*com_argv[i] == '+' || *com_argv[i] == '-') + break; + Host_RunFile(com_argv[i], strlen(com_argv[i]), NULL); + } + //android may find that it has no renderer at various points. if (r_forceheadless) return; diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index e1744260c..e62875a06 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -26,6 +26,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. void CL_GetNumberedEntityInfo (int num, float *org, float *ang); void CLDP_ParseDarkPlaces5Entities(void); +void CLH2_ParseEntities(void); static void CL_SetStatNumeric (int pnum, int stat, int ivalue, float fvalue); #define CL_SetStatInt(pnum,stat,ival) do{int thevalue=ival; CL_SetStatNumeric(pnum,stat,thevalue,thevalue);}while(0) #define CL_SetStatFloat(pnum,stat,fval) do{float thevalue=fval; CL_SetStatNumeric(pnum,stat,thevalue,thevalue);}while(0) @@ -35,6 +36,7 @@ static char *CLNQ_ParseProQuakeMessage (char *s); #endif static void DLC_Poll(qdownload_t *dl); static void CL_ProcessUserInfo (int slot, player_info_t *player); +static void CL_ParseStuffCmd(char *msg, int destsplit); static void Con_HexDump(qbyte *packet, size_t len, size_t badoffset); #ifdef NQPROT @@ -253,12 +255,12 @@ static const char *svc_nqstrings[] = "dp_downloaddata / neh_skyboxsize / qex_servervars", //50 "dp_updatestatubyte / neh_fog / qex_seq", //51 "dp_effect / qex_achievement", //52 - "dp_effect2", //53 - "dp6_precache / dp5_sound2", //54 - "dp_spawnbaseline2", //55 - "dp_spawnstatic2", //56 obsolete - "dp_entities", //57 - "dp_csqcentities", //58 + "dp_effect2 / qex_chat", //53 + "dp6_precache / dp5_sound2 / qex_levelcompleted", //54 + "dp_spawnbaseline2 / qex_backtolobby", //55 + "dp_spawnstatic2 / qex_localsound", //56 obsolete + "dp_entities / qex_prompt", //57 + "dp_csqcentities / qex_loccenterprint", //58 "dp_spawnstaticsound2", //59 "dp_trailparticles", //60 "dp_pointparticles", //61 @@ -564,7 +566,7 @@ qboolean CL_EnqueDownload(const char *filename, const char *localname, unsigned if (cls.state < ca_connected) return false; - if (cls.demoplayback && cls.demoplayback != DPB_EZTV) + if (cls.demoplayback && !(cls.demoplayback == DPB_MVD && (cls.demoeztv_ext&EZTV_DOWNLOAD))) return false; } COM_FileExtension(localname, ext, sizeof(ext)); @@ -809,7 +811,7 @@ void CL_DownloadFinished(qdownload_t *dl) DL_Abort(dl, QDL_COMPLETED); - FS_FlushFSHashWritten(dl->tempname); + FS_FlushFSHashWritten(filename); COM_FileExtension(filename, ext, sizeof(ext)); @@ -830,22 +832,9 @@ void CL_DownloadFinished(qdownload_t *dl) Mod_FileWritten(filename); { -/* - extern int mod_numknown; - extern model_t *mod_known; - for (i = 0; i < mod_numknown; i++) //go and load this model now. - { - if (!strcmp(mod_known[i].name, filename)) - { - Mod_ForName(mod_known[i].name, false); //throw away result. - break; - } - } -*/ - for (i = 0; i < MAX_PRECACHE_MODELS; i++) //go and load this model now. { - if (!strcmp(cl.model_name[i], filename)) + if (cl.model_name[i] && !strcmp(cl.model_name[i], filename)) { if (cl.model_precache[i] && cl.model_precache[i]->loadstate == MLS_FAILED) cl.model_precache[i]->loadstate = MLS_NOTLOADED; @@ -875,7 +864,7 @@ void CL_DownloadFinished(qdownload_t *dl) #ifdef HAVE_LEGACY for (i = 0; i < MAX_VWEP_MODELS; i++) { - if (!strcmp(cl.model_name_vwep[i], filename)) + if (cl.model_name_vwep[i] && !strcmp(cl.model_name_vwep[i], filename)) { if (cl.model_precache_vwep[i] && cl.model_precache_vwep[i]->loadstate == MLS_FAILED) cl.model_precache_vwep[i]->loadstate = MLS_NOTLOADED; @@ -1236,7 +1225,7 @@ static void Model_CheckDownloads (void) } #endif - for (i = 1; cl.model_name[i][0]; i++) + for (i = 1; i < countof(cl.model_name) && cl.model_name[i]; i++) { s = cl.model_name[i]; if (s[0] == '*') @@ -1258,11 +1247,9 @@ static void Model_CheckDownloads (void) for (i = 0; i < MAX_VWEP_MODELS; i++) { s = cl.model_name_vwep[i]; - - if (!stricmp(COM_FileExtension(s, ext, sizeof(ext)), "dsp")) //doom sprites are weird, and not really downloadable via this system + if (!s) continue; - - if (!*s) + if (!stricmp(COM_FileExtension(s, ext, sizeof(ext)), "dsp")) //doom sprites are weird, and not really downloadable via this system continue; CL_CheckOrEnqueDownloadFile(s, s, DLLF_ALLOWWEB); @@ -1382,7 +1369,7 @@ static int CL_LoadModels(int stage, qboolean dontactuallyload) { for (i=1 ; itype == mod_dummy) { - if (!cl.model_name[1][0]) + if (!cl.model_name[1]) Host_EndGame("Worldmodel name wasn't sent\n"); // else // return stage; @@ -1457,7 +1443,7 @@ static int CL_LoadModels(int stage, qboolean dontactuallyload) for (i=1 ; ifile) { - char native[MAX_OSPATH]; - FS_NativePath(dl->tempname+dl->prefixbytes, dl->fsroot, native, sizeof(native)); - Con_TPrintf("Unable to open \"%s\"\n", native); + char displaypath[MAX_OSPATH]; + FS_DisplayPath(dl->tempname+dl->prefixbytes, dl->fsroot, displaypath, sizeof(displaypath)); + Con_TPrintf("Unable to open \"%s\"\n", displaypath); return false; } @@ -2341,11 +2326,15 @@ static void CL_ParseChunkedDownload(qdownload_t *dl) if (!strncmp(svname, "package/", 8)) { int i, c; + char *pn; Cmd_TokenizeString(cl.serverpacknames, false, false); c = Cmd_Argc(); for (i = 0; i < c; i++) { - if (!strcmp(Cmd_Argv(i), svname+8)) + pn = Cmd_Argv(i); + if (*pn == '*') + pn++; //'required'... so shouldn't really be missing. + if (!strcmp(pn, svname+8)) break; } if (i == c) @@ -2637,10 +2626,10 @@ void DL_Abort(qdownload_t *dl, enum qdlabort aborttype) FS_Remove(dl->localname+dl->prefixbytes, dl->fsroot); if (!FS_Rename(dl->tempname+dl->prefixbytes, dl->localname+dl->prefixbytes, dl->fsroot)) { - char nativetmp[MAX_OSPATH], nativefinal[MAX_OSPATH]; - FS_NativePath(dl->tempname+dl->prefixbytes, dl->fsroot, nativetmp, sizeof(nativetmp)); - FS_NativePath(dl->localname+dl->prefixbytes, dl->fsroot, nativefinal, sizeof(nativefinal)); - Con_Printf("Couldn't rename %s to %s\n", nativetmp, nativefinal); + char displaytmp[MAX_OSPATH], displayfinal[MAX_OSPATH]; + FS_DisplayPath(dl->tempname+dl->prefixbytes, dl->fsroot, displaytmp, sizeof(displaytmp)); + FS_DisplayPath(dl->localname+dl->prefixbytes, dl->fsroot, displayfinal, sizeof(displayfinal)); + Con_Printf("Couldn't rename %s to %s\n", displaytmp, displayfinal); } } #ifdef PACKAGEMANAGER @@ -2754,7 +2743,7 @@ static void CL_ParseDownload (qboolean zlib) #ifdef PEXT_CHUNKEDDOWNLOADS if (cls.fteprotocolextensions & PEXT_CHUNKEDDOWNLOADS) { - if (cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD && cls.demoeztv_ext) Host_EndGame("CL_ParseDownload: chunked download on qtv proxy."); CL_ParseChunkedDownload(dl); return; @@ -2785,7 +2774,7 @@ static void CL_ParseDownload (qboolean zlib) return; } - if (cls.demoplayback && cls.demoplayback != DPB_EZTV) + if (cls.demoplayback && !cls.demoeztv_ext) { if (size > 0) MSG_ReadSkip(size); @@ -3366,6 +3355,9 @@ static void CLQW_ParseServerData (void) if (cls.ezprotocolextensions1 & ~EZPEXT1_CLIENTSUPPORT) Con_TPrintf (CON_WARNING"Using unknown ezquake extensions (%#x)\n", cls.ezprotocolextensions1&~EZPEXT1_CLIENTSUPPORT); + if ((cls.ezprotocolextensions1 & EZPEXT1_HIDDEN_MESSAGES) && !cls.demoplayback) + Con_TPrintf (CON_WARNING"Server's EZPEXT1_HIDDEN_MESSAGES extension does not make sense outside of demos\n"); //we do not request this at all. its safe to blame the server. + if (cls.fteprotocolextensions & PEXT_FLOATCOORDS) { cls.netchan.netprim.coordtype = COORDTYPE_FLOAT_32; @@ -3413,7 +3405,7 @@ static void CLQW_ParseServerData (void) } /*mvds have different parsing*/ - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) { extern float olddemotime; int i,j; @@ -3571,7 +3563,7 @@ static void CLQW_ParseServerData (void) } memset(cl.sound_name, 0, sizeof(cl.sound_name)); - if (cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD && (cls.demoeztv_ext&EZTV_DOWNLOAD)) { if (CL_RemoveClientCommands("qtvsoundlist")) Con_DPrintf("Multiple soundlists\n"); @@ -3830,6 +3822,9 @@ void CL_ParseEstablished(void) if (cls.netchan.remote_address.type != NA_LOOPBACK) { + char fp[DIGEST_MAXSIZE*3+1+4]; + char dig[DIGEST_MAXSIZE+1]; + char cert[8192]; const char *security; switch(cls.protocol) { @@ -3839,13 +3834,26 @@ void CL_ParseEstablished(void) case CP_QUAKE3: Con_Printf (S_COLOR_GRAY"Q3 "); break; default: break; } + *fp = 0; if (NET_IsEncrypted(&cls.netchan.remote_address)) - security = localtext("^["S_COLOR_GREEN"encrypted\\tip\\Any passwords will be sent securely, but will still be readable by the server admin^]"); + { + if (cls.netchan.remote_address.prot == NP_DTLS) + { + int sz = NET_GetConnectionCertificate(cls.sockets, &cls.netchan.remote_address, QCERT_PEERCERTIFICATE, cert,sizeof(cert)); + if (sz >= 0) + { + Q_strncpyz(fp, "?fp=",sizeof(fp)); + sz = 4+Base64_EncodeBlockURI(dig, CalcHash(&hash_certfp, dig,sizeof(dig), cert, sz), fp+4,sizeof(fp)-4); + fp[sz] = 0; + } + } + security = localtext("^["S_COLOR_GREEN"encrypted\\tip\\Any passwords will be sent securely, but will still be readable by the server admin\n^]"); + } else security = localtext("^["S_COLOR_RED"plain-text\\tip\\"CON_WARNING"Do not type passwords as they can potentially be seen by network sniffers^]"); Con_Printf ("\r"); - Con_TPrintf ("Connected to ^["S_COLOR_BLUE"%s\\type\\connect %s^] (%s).\n", cls.servername, cls.servername, security); + Con_TPrintf ("Connected to ^["S_COLOR_BLUE"%s"S_COLOR_GRAY"%s\\type\\connect %s%s^] (%s).\n", cls.servername, fp, cls.servername, fp, security); } } @@ -3953,7 +3961,10 @@ static void CLNQ_ParseProtoVersion(void) } else if (protover == PROTOCOL_VERSION_H2) { - Host_EndGame ("\nUnable to connect to standard Hexen2 servers. Host the game with "DISTRIBUTION"\n"); + if (cls.demoplayback) + cls.protocol_nq = CPNQ_H2MP; + else + Host_EndGame ("\nUnable to connect to standard Hexen2 servers. Host the game with "DISTRIBUTION"\n"); } else if (protover == PROTOCOL_VERSION_BJP1) { @@ -4067,7 +4078,14 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut Surf_PreNewMap(); - memset (cl.model_name, 0, sizeof(cl.model_name)); + for (nummodels=0; nummodels < countof(cl.model_name); nummodels++) + { + if (cl.model_name[nummodels]) + { + Z_Free(cl.model_name[nummodels]); + cl.model_name[nummodels] = NULL; + } + } for (nummodels=1 ; ; nummodels++) { str = MSG_ReadString (); @@ -4078,7 +4096,7 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut Con_TPrintf ("Server sent too many model precaches\n"); return; } - Q_strncpyz(cl.model_name[nummodels], str, sizeof(cl.model_name[nummodels])); + Z_StrDupPtr(&cl.model_name[nummodels], str); if (*str != '*' && strcmp(str, "null")) //not inline models! CL_CheckOrEnqueDownloadFile(str, NULL, ((nummodels==1)?DLLF_REQUIRED|DLLF_ALLOWWEB:0)); @@ -4088,7 +4106,7 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut if (!strcmp(cl.model_name[nummodels],"progs/player.mdl")) cl_playerindex = nummodels; #ifdef HAVE_LEGACY - if (*cl.model_name_vwep[0] && !strcmp(cl.model_name[nummodels],cl.model_name_vwep[0]) && cl_playerindex == -1) + if (cl.model_name_vwep[0] && !strcmp(cl.model_name[nummodels],cl.model_name_vwep[0]) && cl_playerindex == -1) cl_playerindex = nummodels; #endif if (!strcmp(cl.model_name[nummodels],"progs/h_player.mdl")) @@ -4113,7 +4131,14 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut Mod_TouchModel (str); } - memset (cl.sound_name, 0, sizeof(cl.sound_name)); + for (numsounds=0; numsounds < countof(cl.sound_name); numsounds++) + { + if (cl.sound_name[numsounds]) + { + Z_Free(cl.sound_name[numsounds]); + cl.sound_name[numsounds] = NULL; + } + } for (numsounds=1 ; ; numsounds++) { str = MSG_ReadString (); @@ -4124,7 +4149,7 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut Con_TPrintf ("Server sent too many sound precaches\n"); return; } - Q_strncpyz(cl.sound_name[numsounds], str, sizeof(cl.sound_name[numsounds])); + Z_StrDupPtr(&cl.sound_name[numsounds], str); cl.sound_precache[numsounds] = S_FindName(cl.sound_name[numsounds], true, false); Sound_CheckDownload(str); @@ -4351,24 +4376,27 @@ static void CLNQ_ParseClientdata (void) if (bits & SU_VIEWHEIGHT) CL_SetStatInt(0, STAT_VIEWHEIGHT, MSG_ReadChar ()); - else if (!CPNQ_IS_DP || cls.protocol_nq <= CPNQ_DP5) + else if ((!CPNQ_IS_DP || cls.protocol_nq <= CPNQ_DP5) && cls.protocol_nq != CPNQ_H2MP) CL_SetStatInt(0, STAT_VIEWHEIGHT, DEFAULT_VIEWHEIGHT); if (bits & SU_IDEALPITCH) CL_SetStatInt(0, STAT_IDEALPITCH, MSG_ReadChar ()); - else + else if (cls.protocol_nq != CPNQ_H2MP) CL_SetStatInt(0, STAT_IDEALPITCH, 0); + if (cls.protocol_nq == CPNQ_H2MP && (bits & (1<<8)/*SU_IDEALROLL*/)) + MSG_ReadChar (); + for (i=0 ; i<3 ; i++) { if (bits & (SU_PUNCH1<velocity[i] = MSG_ReadChar()*16; } - else + else if (cls.protocol_nq != CPNQ_H2MP) pl->velocity[i] = 0; } @@ -4412,6 +4440,12 @@ static void CLNQ_ParseClientdata (void) { /*nothing in dp6+*/ } + else if (cls.protocol_nq == CPNQ_H2MP) + { //only changed stuff + if (bits & SU_WEAPONFRAME) CL_SetStatInt(0, STAT_WEAPONFRAME, MSG_ReadByte()); + if (bits & SU_ARMOR) CL_SetStatInt(0, STAT_ARMOR, MSG_ReadByte()); + if (bits & SU_WEAPONMODEL) CL_SetStatInt(0, STAT_WEAPONMODELI, MSG_ReadUInt16()); + } //nothing else. else { int weaponmodel = 0, armour = 0, weaponframe = 0, health = 0, currentammo = 0, shells = 0, nails = 0, rockets = 0, cells = 0, activeweapon = 0; @@ -4525,14 +4559,16 @@ static void CL_ParseSoundlist (qboolean lots) // if (!strcmp(str+strlen(str)-4, ".mp3")) //don't let the server send us a specific mp3. convert it to wav and this way we know not to look outside the quake path for it. // strcpy(str+strlen(str)-4, ".wav"); - strcpy (cl.sound_name[numsounds], str); + Z_StrDupPtr(&cl.sound_name[numsounds], str); } n = (cl.protocol_qw>=26)?MSG_ReadByte():0; if (n) { - if (cls.demoplayback != DPB_EZTV) + if (cls.demoplayback == DPB_MVD && (cls.demoeztv_ext&EZTV_DOWNLOAD)) + ; + else { if (CL_RemoveClientCommands("soundlist") && !(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)) Con_DPrintf("Multiple soundlists\n"); @@ -4553,7 +4589,7 @@ static void CL_ParseSoundlist (qboolean lots) else #endif { - if (cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD && cls.demoeztv_ext) { if (CL_RemoveClientCommands("qtvmodellist")) Con_DPrintf("Multiple modellists\n"); @@ -4592,11 +4628,11 @@ static void CL_ParseModellist (qboolean lots) if (!str[0]) break; nummodels++; - if (nummodels==1) - SCR_ImageName(cl.model_name[nummodels]); if (nummodels>=MAX_PRECACHE_MODELS) Host_EndGame ("Server sent too many model_precache"); - strcpy (cl.model_name[nummodels], str); + Z_StrDupPtr(&cl.model_name[nummodels], str); + if (nummodels==1) + SCR_ImageName(cl.model_name[nummodels]); //qw has a special network protocol for spikes. if (!strcmp(cl.model_name[nummodels],"progs/spike.mdl")) @@ -4604,7 +4640,7 @@ static void CL_ParseModellist (qboolean lots) if (!strcmp(cl.model_name[nummodels],"progs/player.mdl")) cl_playerindex = nummodels; #ifdef HAVE_LEGACY - if (*cl.model_name_vwep[0] && !strcmp(cl.model_name[nummodels],cl.model_name_vwep[0]) && cl_playerindex == -1) + if (cl.model_name_vwep[0] && !strcmp(cl.model_name[nummodels],cl.model_name_vwep[0]) && cl_playerindex == -1) cl_playerindex = nummodels; #endif if (!strcmp(cl.model_name[nummodels],"progs/h_player.mdl")) @@ -4635,7 +4671,9 @@ static void CL_ParseModellist (qboolean lots) if (n) { - if (cls.demoplayback != DPB_EZTV) + if (cls.demoplayback == DPB_MVD && cls.demoeztv_ext) + ; + else { if (CL_RemoveClientCommands("modellist") && !(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)) Con_DPrintf("Multiple modellists\n"); @@ -4645,10 +4683,15 @@ static void CL_ParseModellist (qboolean lots) return; } +#ifdef QUAKESTATS + if (cls.demoplayback == DPB_MVD && !cl.model_name_vwep[0] && !(cls.fteprotocolextensions2&PEXT2_REPLACEMENTDELTAS)) + CL_ParseStuffCmd("//vwep vwplayer w_axe w_shot w_shot2 w_nail w_nail2 w_rock w_rock2 w_light", 0); +#endif + SCR_SetLoadingFile("loading data"); //we need to try to load it now if we can, so any embedded archive will be loaded *before* we start looking for other content... - cl.model_precache[1] = Mod_ForName (cl.model_name[1], MLV_SILENTSYNC); + cl.model_precache[1] = cl.model_name[1]?Mod_ForName (cl.model_name[1], MLV_SILENTSYNC):NULL; if (cl.model_precache[1] && cl.model_precache[1]->loadstate == MLS_LOADED) FS_LoadMapPackFile(cl.model_precache[1]->name, cl.model_precache[1]->archive); @@ -4840,7 +4883,7 @@ parsemodelindex: s++; //*sigh* if (i == 255) s = "*playermodel"; //something special. - Q_strncpyz(cl.model_name[i], s, MAX_QPATH); + Z_StrDupPtr(&cl.model_name[i], s); if (cl.model_name[i][0] == '#') { if (cl.numq2visibleweapons < Q2MAX_VISIBLE_WEAPONS) @@ -4861,7 +4904,7 @@ parsesoundindex: return; if (*s == '/') s++; //*sigh* - Q_strncpyz(cl.sound_name[i], s, MAX_QPATH); + Z_StrDupPtr(&cl.sound_name[i], s); if (cl.contentstage) cl.sound_precache[i] = S_PrecacheSound (s); } @@ -4870,24 +4913,21 @@ parsesoundindex: i -= Q2EXCS_IMAGES; if ((unsigned int)i >= countof(cl.image_name)) return; - Z_Free(cl.image_name[i]); - cl.image_name[i] = Z_StrDup(s); + Z_StrDupPtr(&cl.image_name[i], s); } else if (i >= Q2EXCS_ITEMS && i < Q2EXCS_ITEMS+Q2MAX_ITEMS) { i -= Q2EXCS_ITEMS; if ((unsigned int)i >= countof(cl.item_name)) return; - Z_Free(cl.item_name[i]); - cl.item_name[i] = Z_StrDup(s); + Z_StrDupPtr(&cl.item_name[i], s); } else if (i >= Q2EXCS_GENERAL && i < Q2EXCS_GENERAL+Q2EXMAX_GENERAL) { i -= Q2EXCS_GENERAL; if ((unsigned int)i >= Q2MAX_CLIENTS)//countof(cl.configstring_general)) return; - Z_Free(cl.configstring_general[i]); - cl.configstring_general[i] = Z_StrDup(s); + Z_StrDupPtr(&cl.configstring_general[i], s); } else if (i >= Q2EXCS_PLAYERSKINS && i < Q2EXCS_PLAYERSKINS+Q2EXMAX_CLIENTS) { @@ -4895,8 +4935,7 @@ parsesoundindex: i += Q2EXMAX_CLIENTS; if ((unsigned int)i >= countof(cl.configstring_general)) return; - Z_Free(cl.configstring_general[i]); - cl.configstring_general[i] = Z_StrDup(s); + Z_StrDupPtr(&cl.configstring_general[i], s); CLQ2_ParseClientinfo (i, s); } @@ -4985,6 +5024,8 @@ static void CL_ParseBaseline (entity_state_t *es, int baselinetype2) bits = FITZ_B_LARGEMODEL|FITZ_B_LARGEFRAME; //dp's baseline2 always has these (regular baseline is unmodified) else if (cls.protocol == CP_NETQUAKE && CPNQ_IS_BJP) bits = FITZ_B_LARGEMODEL; //bjp always uses shorts for models. + else if (cls.protocol == CP_NETQUAKE && cls.protocol_nq == CPNQ_H2MP) + bits = FITZ_B_LARGEMODEL; //urgh else bits = 0; //vanilla nq or qw @@ -4993,6 +5034,13 @@ static void CL_ParseBaseline (entity_state_t *es, int baselinetype2) es->colormap = MSG_ReadByte(); es->skinnum = MSG_ReadByte(); + if (cls.protocol == CP_NETQUAKE && cls.protocol_nq == CPNQ_H2MP) + { + es->scale = MSG_ReadByte(); + es->hexen2flags = MSG_ReadByte(); + es->abslight = MSG_ReadByte(); + } + for (i=0 ; i<3 ; i++) { es->origin[i] = MSG_ReadCoord (); @@ -5199,7 +5247,7 @@ static void CL_ParseStaticSound (unsigned int flags) for (i=0 ; i<3 ; i++) org[i] = MSG_ReadCoord (); - if (flags || (cls.protocol == CP_NETQUAKE && cls.protocol_nq == CPNQ_BJP2)) + if (flags || (cls.protocol == CP_NETQUAKE && (cls.protocol_nq == CPNQ_BJP2 || cls.protocol_nq == CPNQ_H2MP))) { if (cls.fteprotocolextensions2&PEXT2_LERPTIME) sound_num = (unsigned short)MSG_ReadULEB128(); @@ -5388,7 +5436,7 @@ static void CLNQ_ParseStartSoundPacket(void) { vec3_t pos, vel; int channel, ent; - int sound_num; + unsigned int sound_num; int volume; int field_mask; float attenuation; @@ -5456,12 +5504,17 @@ static void CLNQ_ParseStartSoundPacket(void) else sound_num = (unsigned char)MSG_ReadByte (); - if (ent > MAX_EDICTS) - Host_EndGame ("CL_ParseStartSoundPacket: ent = %i", ent); - for (i=0 ; i<3 ; i++) pos[i] = MSG_ReadCoord (); + if (ent > MAX_EDICTS) + Host_EndGame ("CL_ParseStartSoundPacket: ent = %i", ent); + if (sound_num >= MAX_PRECACHE_SOUNDS) + Host_EndGame ("CL_ParseStartSoundPacket: sndidx = %i", sound_num); + + if (!cl.sound_name[sound_num]) + return; //nope, not precached yet... silly raqces. + #ifdef CSQC_DAT if (!CSQC_StartSound(ent, channel, cl.sound_name[sound_num], pos, volume/255.0, attenuation, pitchadj, timeofs, flags)) #endif @@ -5511,7 +5564,7 @@ void CL_ParseClientdata (void) } else #endif - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) { cl.oldparsecount = i - 1; oldparsecountmod = cl.oldparsecount & UPDATE_MASK; @@ -6054,7 +6107,7 @@ static void CL_SetStatNumeric (int pnum, int stat, int ivalue, float fvalue) } #endif - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) { extern int cls_lastto; cl.players[cls_lastto].stats[stat]=ivalue; @@ -6098,13 +6151,13 @@ static void CL_SetStatNumeric (int pnum, int stat, int ivalue, float fvalue) #endif } -static void CL_SetStatString (int pnum, int stat, char *value) +static void CL_SetStatString (int pnum, int stat, const char *value) { if (stat < 0 || stat >= MAX_CL_STATS) return; // Host_EndGame ("CL_SetStat: %i is invalid", stat); - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) { extern int cls_lastto; //Z_Free(cl.players[cls_lastto].statsstr[stat]); @@ -6236,7 +6289,7 @@ static char *CL_ParseChat(char *text, player_info_t **player, int *msgflags) if (!cls.demoplayback) Sys_ServerActivity(); //chat always flashes the screen.. - if (Ignore_Message((*player)->name, s, flags)) + if (*player && Ignore_Message((*player)->name, s, flags)) return NULL; //check f_ stuff @@ -6715,13 +6768,17 @@ static char printtext[4096]; static void CL_ParsePrint(const char *msg, int level) { char n, *e; - if (strlen(printtext) + strlen(msg) >= sizeof(printtext)) + if (strlen(printtext) + strlen(msg)+2 >= sizeof(printtext)) { Con_Printf("%s", printtext); Q_strncpyz(printtext, msg, sizeof(printtext)); } else strcat(printtext, msg); //safe due to size on if. +#ifdef HAVE_LEGACY //eztv is fucking nasty. + if (level == PRINT_CHAT && *printtext == '#' && printtext[1] >= '0' && printtext[1] <= '9' && !strchr(printtext, '\n')) + strcat(printtext, "\n"); +#endif while((e = strchr(printtext, '\n')) || (e = strchr(printtext, '\r'))) { n = e[1]; @@ -6994,13 +7051,18 @@ static void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds if (strcmp(mname, "-")) { mname = va("progs/%s.mdl", Cmd_Argv(i)); - Q_strncpyz(cl.model_name_vwep[i], mname, sizeof(cl.model_name_vwep[i])); + Z_StrDupPtr(&cl.model_name_vwep[i], mname); if (cls.state == ca_active) { CL_CheckOrEnqueDownloadFile(cl.model_name_vwep[i], NULL, 0); cl.model_precache_vwep[i] = Mod_ForName(cl.model_name_vwep[i], MLV_WARN); } } + else + { + Z_Free(cl.model_name_vwep[i]); + cl.model_name_vwep[i] = NULL; + } } } #endif @@ -7109,6 +7171,42 @@ static void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds CL_ProcessUserInfo (slot, player); } } + else if (!strncmp(stufftext, "//qul ", 6)) //qtv user list + { + unsigned int cmd, id; + const char *name; + struct qtvviewers_s **link, *v; + Cmd_TokenizeString(stufftext+5, false, false); + cmd = atoi(Cmd_Argv(0)); + id = atoi(Cmd_Argv(1)); + name = Cmd_Argv(2); + for (link = &cls.qtvviewers; (v=*link); link = &v->next) + { + if (v->userid == id) + break; + } + switch(cmd) + { + case 3://del + if (v) + { + *link = v->next; + Z_Free(v); + } + break; + case 2://change + case 1://add + if (!v) + { //new... + v = Z_Malloc(sizeof(*v)); + v->next = cls.qtvviewers; + cls.qtvviewers = v; + v->userid = id; + } + Q_strncpyz(v->name, name, sizeof(v->name)); + break; + } + } #ifdef PLUGINS else if (!strncmp(stufftext, "//tinfo ", 8)) //ktx-team-info { @@ -7146,12 +7244,13 @@ static void CL_ParsePrecache(void) if (i >= 1 && i < MAX_PRECACHE_MODELS) { model_t *model; + Z_StrDupPtr(&cl.model_name[i], s); + CL_CheckOrEnqueDownloadFile(s, s, DLLF_ALLOWWEB); model = Mod_ForName(Mod_FixName(s, cl.model_name[1]), (i == 1&&!cl.sendprespawn)?MLV_ERROR:MLV_WARN); // if (!model) // Con_Printf("svc_precache: Mod_ForName(\"%s\") failed\n", s); cl.model_precache[i] = model; - Q_strncpyz (cl.model_name[i], s, sizeof(cl.model_name[i])); } else Con_Printf("svc_precache: model index %i outside range %i...%i\n", i, 1, MAX_PRECACHE_MODELS); @@ -7168,7 +7267,7 @@ static void CL_ParsePrecache(void) // if (!sfx) // Con_Printf("svc_precache: S_PrecacheSound(\"%s\") failed\n", s); cl.sound_precache[i] = sfx; - Q_strncpyz (cl.sound_name[i], s, sizeof(cl.sound_name[i])); + Z_StrDupPtr(&cl.sound_name[i], s); } else Con_Printf("svc_precache: sound index %i outside range %i...%i\n", i, 1, MAX_PRECACHE_SOUNDS); @@ -7176,9 +7275,7 @@ static void CL_ParsePrecache(void) case PC_PARTICLE: if (i >= 1 && i < MAX_SSPARTICLESPRE) { - if (cl.particle_ssname[i]) - free(cl.particle_ssname[i]); - cl.particle_ssname[i] = strdup(s); + Z_StrDupPtr(&cl.particle_ssname[i], s); cl.particle_ssprecache[i] = P_FindParticleType(s); cl.particle_ssprecaches = true; } @@ -7344,6 +7441,71 @@ static void CL_ParseBaseAngle(int seat) VRUI_SnapAngle(); } +void CLEZ_ParseHiddenDemoMessage(void) +{ + for(;;) + { + static float throttle; + int size = MSG_ReadLong(); + unsigned int cmd; + if (size == -1 || msg_badread) + break; + + cmd = MSG_ReadUInt16(); + switch(cmd) + { + case 0xFFFF://mvdhidden_extended + return; //can't handle it... protocol is stupid. + + case 0x0003://mvdhidden_demoinfo + MSG_ReadUInt16(); //'more' + MSG_ReadSkip(size-2); //probably json + break; + case 0x0007://mvdhidden_dmgdone + { + lerpents_t *le; + unsigned short typeandflags = MSG_ReadUInt16(); + unsigned short attacker = MSG_ReadUInt16(); + unsigned short targ = MSG_ReadUInt16(); + short dmg = MSG_ReadShort(); + unsigned short issplash = !!(typeandflags&0x8000); + unsigned short isteamdamage = (attacker==targ) || (cl.teamplay && attacker-1origin[0], le->origin[1], le->origin[2], typeandflags, dmg, issplash, isteamdamage), cmd); + } + } + break; + +/* case 0x0000://mvdhidden_antilag_position + case 0x0001://mvdhidden_usercmd // + case 0x0002://mvdhidden_usercmd_weapons // + case 0x0004://mvdhidden_commentary_track + case 0x0005://mvdhidden_commentary_data + case 0x0006://mvdhidden_commentary_text_segment + case 0x0008://mvdhidden_usercmd_weapons_ss // (same format as mvdhidden_usercmd_weapons) + case 0x0009://mvdhidden_usercmd_weapon_instruction // + case 0x000A://mvdhidden_paused_duration +*/ + default: + Con_ThrottlePrintf(&throttle, 1, "CL_ParseHiddenDemoMessage: Unknown cmd %i\n", cmd); + MSG_ReadSkip(size); + break; + } + } +} + #define SHOWNETEOM(x) if(cl_shownet.value>=2)Con_Printf ("%3i:%s\n", MSG_GetReadCount(), x); #define SHOWNET(x) if(cl_shownet.value>=2)Con_Printf ("%3i:%s\n", MSG_GetReadCount()-1, x); #define SHOWNET2(x, y) if(cl_shownet.value>=2)Con_Printf ("%3i:%3i:%s\n", MSG_GetReadCount()-1, y, x); @@ -7469,11 +7631,17 @@ void CLQW_ParseServerMessage (void) case svc_nop: // Con_Printf ("svc_nop\n"); + //If we're using cl_shownet 2, combine large padding blocks into a single line, otherwise we'll get 1000+ lines from a single packet. + while (svc_nop == MSG_PeekByte()) + MSG_ReadByte(); break; case svc_disconnect: - if (cls.demoplayback == DPB_EZTV) //eztv fails to detect the end of demos. - MSG_ReadString(); + if (cls.demoplayback == DPB_MVD) //eztv fails to detect the end of demos. + { + s = MSG_ReadString(); + Con_Printf(CON_WARNING"svc_disconnect: %s\n", s); + } else if (cls.demoplayback) { CL_Disconnect_f(); @@ -7555,7 +7723,7 @@ void CLQW_ParseServerMessage (void) VectorCopy (cl.playerview[destsplit].viewangles, cl.playerview[destsplit].intermissionangles); break; case svc_setangle: - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) { //I really don't get the point of fixangles in an mvd. just to disable interpolation for that frame? int pl = MSG_ReadByte(); @@ -8802,6 +8970,296 @@ static void CLNQ_CheckPlayerIsSpectator(int i) cl.players[i].spectator = false; } +#ifdef HEXEN2 +#define svch2_particle2 34 +#define svch2_cutscene 35 +#define svch2_midiname 36 +#define svch2_updateclass 37 +#define svch2_particle3 38 +#define svch2_particle4 39 +#define svch2_setviewflags 40 +#define svch2_clearviewflags 41 +#define svch2_starteffect 42 +#define svch2_endeffect 43 +#define svch2_plaque 44 +#define svch2_particleexplosion 45 +#define svch2_setviewtint 46 +#define svch2_ref 47 +#define svch2_clearedicts 48 +#define svch2_updateinv 49 +#define svch2_setanglelerp 50 +#define svch2_updatekoth 51 +#define svch2_togglestatbar 52 +#define svch2_soundpos 53 +static qboolean CLH2_ParseServerSubMessage (int cmd) +{ + const int destsplit = 0; + int i,j; + const int svch2_first = svch2_particle2; + static const char *svc_h2strings[] = + { + "h2_particle2", + "h2_cutscene", + "h2_midiname", + "h2_updateclass", + "h2_particle3", + "h2_particle4", + "h2_setviewflags", + "h2_clearviewflags", + "h2_starteffect", + "h2_endeffect", + "h2_plaque", + "h2_particleexplosion", + "h2_setviewtint", + "h2_ref", + "h2_clearedicts", + "h2_updateinv", + "h2_setanglelerp", + "h2_updatekoth", + "h2_togglestatbar", + "h2_soundpos", + }; + //no fastupdate checks needed here. + SHOWNET2((cmd>=svch2_first&&cmd(sizeof(svc_nqstrings)/sizeof(char*))?0:cmd], cmd); + + switch (cmd) + { + case svch2_midiname: + MSG_ReadString(); + break; + case svch2_particle2: + CL_ParseParticleEffect2 (); + break; + case svch2_particle3: + CL_ParseParticleEffect3 (); + break; + case svch2_particle4: + CL_ParseParticleEffect4 (); + break; + case svch2_starteffect: + i = MSG_ReadByte(); //handle + j = MSG_ReadByte(); //type + switch(j) + { + case 1 /*ce_rain*/: + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadShort(); + MSG_ReadShort(); + MSG_ReadFloat(); + break; + case 4/*ce_white_smoke*/: + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadFloat(); + MSG_ReadFloat(); + MSG_ReadFloat(); + MSG_ReadFloat(); + MSG_ReadFloat(); + break; + case 5/*ce_bluespark*/: + case 6/*ce_yellowspark*/: + case 9/*ce_sm_white_flash*/: + case 13/*ce_sm_blue_flash*/: + case 15/*ce_sm_explosion*/: + case 16/*ce_lg_explosion*/: + case 17/*ce_floor_explosion*/: + case 19/*ce_blue_explosion*/: + case 32/*ce_xbow_explosion*/: + case 33/*ce_new_explosion*/: + case 34/*ce_magic_missile_explosion*/: + case 38/*ce_teleporterpuffs*/: + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + break; + case 39/*ce_teleporterbody*/: + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadFloat(); + MSG_ReadFloat(); + MSG_ReadFloat(); + MSG_ReadFloat(); + break; + case 40/*ce_boneshard*/: + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + + MSG_ReadFloat(); + MSG_ReadFloat(); + MSG_ReadFloat(); + + MSG_ReadFloat(); + MSG_ReadFloat(); + MSG_ReadFloat(); + + MSG_ReadFloat(); + MSG_ReadFloat(); + MSG_ReadFloat(); + break; + case 43/*ce_snow*/: + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadByte(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadByte(); + break; + case 55/*ce_chunk*/: + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadByte(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadCoord(); + MSG_ReadByte(); + break; + default: + Host_EndGame ("svch2_starteffect: unknown type %i", j); + return false; + } + break; + case svch2_endeffect: + MSG_ReadByte(); //handle + break; + case svch2_updateclass: + i = MSG_ReadByte(); + j = MSG_ReadByte(); + if (i < MAX_CLIENTS) + InfoBuf_SetValueForKey(&cl.players[i].userinfo, "cl_playerclass", va("%i", j)); + break; + case svch2_updateinv: + cmd = MSG_ReadByte(); + i = j = 0; + if (cmd&(1<<0)) i |= MSG_ReadByte()<<0; + if (cmd&(1<<1)) i |= MSG_ReadByte()<<8; + if (cmd&(1<<2)) i |= MSG_ReadByte()<<16; + if (cmd&(1<<3)) i |= MSG_ReadByte()<<24; + if (cmd&(1<<4)) j |= MSG_ReadByte()<<0; + if (cmd&(1<<5)) j |= MSG_ReadByte()<<8; + if (cmd&(1<<6)) j |= MSG_ReadByte()<<16; + if (cmd&(1<<7)) j |= MSG_ReadByte()<<24; + if (i & (1u<< 0)) CL_SetStatInt(destsplit, STAT_HEALTH, MSG_ReadShort()); + if (i & (1u<< 1)) CL_SetStatInt(destsplit, STAT_H2_LEVEL, MSG_ReadByte()); + if (i & (1u<< 2)) CL_SetStatInt(destsplit, STAT_H2_INTELLIGENCE, MSG_ReadByte()); + if (i & (1u<< 3)) CL_SetStatInt(destsplit, STAT_H2_WISDOM, MSG_ReadByte()); + if (i & (1u<< 4)) CL_SetStatInt(destsplit, STAT_H2_STRENGTH, MSG_ReadByte()); + if (i & (1u<< 5)) CL_SetStatInt(destsplit, STAT_H2_DEXTERITY, MSG_ReadByte()); + if (i & (1u<< 6)) CL_SetStatInt(destsplit, STAT_ACTIVEWEAPON, MSG_ReadByte()); + if (i & (1u<< 7)) CL_SetStatInt(destsplit, STAT_H2_BLUEMANA, MSG_ReadByte()); + if (i & (1u<< 8)) CL_SetStatInt(destsplit, STAT_H2_GREENMANA, MSG_ReadByte()); + if (i & (1u<< 9)) CL_SetStatInt(destsplit, STAT_H2_EXPERIENCE, MSG_ReadLong()); + if (i & (1u<<10)) CL_SetStatInt(destsplit, STAT_H2_CNT_TORCH, MSG_ReadByte()); + if (i & (1u<<11)) CL_SetStatInt(destsplit, STAT_H2_CNT_H_BOOST, MSG_ReadByte()); + if (i & (1u<<12)) CL_SetStatInt(destsplit, STAT_H2_CNT_SH_BOOST, MSG_ReadByte()); + if (i & (1u<<13)) CL_SetStatInt(destsplit, STAT_H2_CNT_MANA_BOOST, MSG_ReadByte()); + if (i & (1u<<14)) CL_SetStatInt(destsplit, STAT_H2_CNT_TELEPORT, MSG_ReadByte()); + if (i & (1u<<15)) CL_SetStatInt(destsplit, STAT_H2_CNT_TOME, MSG_ReadByte()); + if (i & (1u<<16)) CL_SetStatInt(destsplit, STAT_H2_CNT_SUMMON, MSG_ReadByte()); + if (i & (1u<<17)) CL_SetStatInt(destsplit, STAT_H2_CNT_INVISIBILITY, MSG_ReadByte()); + if (i & (1u<<18)) CL_SetStatInt(destsplit, STAT_H2_CNT_GLYPH, MSG_ReadByte()); + if (i & (1u<<19)) CL_SetStatInt(destsplit, STAT_H2_CNT_HASTE, MSG_ReadByte()); + if (i & (1u<<20)) CL_SetStatInt(destsplit, STAT_H2_CNT_BLAST, MSG_ReadByte()); + if (i & (1u<<21)) CL_SetStatInt(destsplit, STAT_H2_CNT_POLYMORPH, MSG_ReadByte()); + if (i & (1u<<22)) CL_SetStatInt(destsplit, STAT_H2_CNT_FLIGHT, MSG_ReadByte()); + if (i & (1u<<23)) CL_SetStatInt(destsplit, STAT_H2_CNT_CUBEOFFORCE, MSG_ReadByte()); + if (i & (1u<<24)) CL_SetStatInt(destsplit, STAT_H2_CNT_INVINCIBILITY, MSG_ReadByte()); + if (i & (1u<<25)) CL_SetStatFloat(destsplit, STAT_H2_ARTIFACT_ACTIVE, MSG_ReadFloat()); + if (i & (1u<<26)) CL_SetStatFloat(destsplit, STAT_H2_ARTIFACT_LOW, MSG_ReadFloat()); + if (i & (1u<<27)) CL_SetStatInt(destsplit, STAT_H2_MOVETYPE, MSG_ReadByte()); + if (i & (1u<<28)) CL_SetStatInt(destsplit, STAT_H2_CAMERAMODE, MSG_ReadByte()); + if (i & (1u<<29)) CL_SetStatFloat(destsplit, STAT_H2_HASTED, MSG_ReadFloat()); + if (i & (1u<<30)) CL_SetStatInt(destsplit, STAT_H2_INVENTORY, MSG_ReadByte()); + if (i & (1u<<31)) CL_SetStatFloat(destsplit, STAT_H2_RINGS_ACTIVE, MSG_ReadFloat()); + if (j & (1u<< 0)) CL_SetStatFloat(destsplit, STAT_H2_RINGS_LOW, MSG_ReadFloat()); + if (j & (1u<< 1)) CL_SetStatInt(destsplit, STAT_H2_ARMOUR1, MSG_ReadByte()); + if (j & (1u<< 2)) CL_SetStatInt(destsplit, STAT_H2_ARMOUR2, MSG_ReadByte()); + if (j & (1u<< 3)) CL_SetStatInt(destsplit, STAT_H2_ARMOUR3, MSG_ReadByte()); + if (j & (1u<< 4)) CL_SetStatInt(destsplit, STAT_H2_ARMOUR4, MSG_ReadByte()); + if (j & (1u<< 5)) CL_SetStatInt(destsplit, STAT_H2_FLIGHT_T, MSG_ReadByte()); + if (j & (1u<< 6)) CL_SetStatInt(destsplit, STAT_H2_WATER_T, MSG_ReadByte()); + if (j & (1u<< 7)) CL_SetStatInt(destsplit, STAT_H2_TURNING_T, MSG_ReadByte()); + if (j & (1u<< 8)) CL_SetStatInt(destsplit, STAT_H2_REGEN_T, MSG_ReadByte()); + if (j & (1u<< 9)) /*CL_SetStatFloat(destsplit, STAT_H2_HASTE_T,*/( MSG_ReadFloat()); + if (j & (1u<<10)) /*CL_SetStatFloat(destsplit, STAT_H2_TOMB_T,*/( MSG_ReadFloat()); + if (j & (1u<<11)) CL_SetStatString(destsplit, STAT_H2_PUZZLE1, MSG_ReadString()); + if (j & (1u<<12)) CL_SetStatString(destsplit, STAT_H2_PUZZLE2, MSG_ReadString()); + if (j & (1u<<13)) CL_SetStatString(destsplit, STAT_H2_PUZZLE3, MSG_ReadString()); + if (j & (1u<<14)) CL_SetStatString(destsplit, STAT_H2_PUZZLE4, MSG_ReadString()); + if (j & (1u<<15)) CL_SetStatString(destsplit, STAT_H2_PUZZLE5, MSG_ReadString()); + if (j & (1u<<16)) CL_SetStatString(destsplit, STAT_H2_PUZZLE6, MSG_ReadString()); + if (j & (1u<<17)) CL_SetStatString(destsplit, STAT_H2_PUZZLE7, MSG_ReadString()); + if (j & (1u<<18)) CL_SetStatString(destsplit, STAT_H2_PUZZLE8, MSG_ReadString()); + if (j & (1u<<19)) CL_SetStatInt(destsplit, STAT_H2_MAXHEALTH, MSG_ReadShort()); + if (j & (1u<<20)) CL_SetStatInt(destsplit, STAT_H2_MAXMANA, MSG_ReadByte()); + if (j & (1u<<21)) CL_SetStatFloat(destsplit, STAT_H2_FLAGS, MSG_ReadFloat()); + if (j & (1u<<22)) CL_SetStatInt(destsplit, STAT_H2_OBJECTIVE1, MSG_ReadLong()); + if (j & (1u<<23)) CL_SetStatInt(destsplit, STAT_H2_OBJECTIVE2, MSG_ReadLong()); + //if (j & (1u<<24)) CL_SetStat(destsplit, STAT_H2_, MSG_Read()); + //if (j & (1u<<25)) CL_SetStat(destsplit, STAT_H2_, MSG_Read()); + //if (j & (1u<<26)) CL_SetStat(destsplit, STAT_H2_, MSG_Read()); + //if (j & (1u<<27)) CL_SetStat(destsplit, STAT_H2_, MSG_Read()); + //if (j & (1u<<28)) CL_SetStat(destsplit, STAT_H2_, MSG_Read()); + //if (j & (1u<<29)) CL_SetStat(destsplit, STAT_H2_, MSG_Read()); + //if (j & (1u<<30)) CL_SetStat(destsplit, STAT_H2_, MSG_Read()); + //if (j & (1u<<31)) CL_SetStat(destsplit, STAT_H2_, MSG_Read()); + break; + case svch2_ref: + if (cls.signon == 4 - 1) + { // first update is the final signon stage + cls.signon = 4; + CLNQ_SignonReply (); + } + CLH2_ParseEntities(); + break; + case svch2_clearedicts: //should have been handled by CLH2_ParseEntities (this also applies to fastupdates) + Host_EndGame ("CLNQ_ParseServerMessage: Unexpected server message (%i@%i)", cmd, MSG_GetReadCount()-1); + return false; + case svch2_togglestatbar: + break; //wtf + case svch2_setanglelerp: //urgh... demo playback has its own angle values, and with greater precision. + MSG_ReadAngle(); + MSG_ReadAngle(); + MSG_ReadAngle(); + break; + case svch2_cutscene: + case svch2_setviewflags: + case svch2_clearviewflags: + case svch2_plaque: + case svch2_particleexplosion: + case svch2_setviewtint: + case svch2_updatekoth: + case svch2_soundpos: + //we're only aiming for demo compat, so we ignore this tat. + Host_EndGame ("CLH2_ParseServerMessage: Unimplemented server message (%i@%i)", cmd, MSG_GetReadCount()-1); + return false; + default: + return false; + } + return true; //handled. +} +#endif + + void CLNQ_ParseServerMessage (void) { const int destsplit = 0; @@ -8841,20 +9299,32 @@ void CLNQ_ParseServerMessage (void) break; } - if (cmd & 128) +#ifdef HEXEN2 + if (cls.protocol_nq == CPNQ_H2MP) { - SHOWNET("fast update"); - CLNQ_ParseEntity(cmd&127); - continue; + if (CLH2_ParseServerSubMessage(cmd)) + continue; + //handle as a regular nq packet. } + else +#endif + { + if (cmd & 128) + { + SHOWNET("fast update"); + CLNQ_ParseEntity(cmd&127); + continue; + } - SHOWNET2(svc_nqstrings[cmd>(sizeof(svc_nqstrings)/sizeof(char*))?0:cmd], cmd); + SHOWNET2(svc_nqstrings[cmd>(sizeof(svc_nqstrings)/sizeof(char*))?0:cmd], cmd); + } // other commands switch (cmd) { default: badsvc: + case svc_bad: CL_DumpPacket(); Host_EndGame ("CLNQ_ParseServerMessage: Illegible server message (%i@%i)", cmd, MSG_GetReadCount()-1); return; @@ -9058,7 +9528,7 @@ void CLNQ_ParseServerMessage (void) cl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK].receivedtime = realtime; cl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK].frameid = cls.netchan.incoming_sequence; - if (CPNQ_IS_DP) + if (CPNQ_IS_DP || cls.protocol_nq == CPNQ_H2MP) { int n = cls.netchan.incoming_sequence&UPDATE_MASK, o = (cls.netchan.incoming_sequence-1)&UPDATE_MASK; cl.inframes[n].packet_entities.num_entities = cl.inframes[o].packet_entities.num_entities; diff --git a/engine/client/cl_plugin.inc b/engine/client/cl_plugin.inc index afa9cb04e..570cc0bf0 100644 --- a/engine/client/cl_plugin.inc +++ b/engine/client/cl_plugin.inc @@ -638,7 +638,6 @@ static void QDECL Plug_GetServerInfoRaw(char *outptr, size_t outlen) case DPB_NONE: break; case DPB_MVD: - case DPB_EZTV: Q_strncatz(outptr, "\\demotype\\mvd", outlen); break; case DPB_QUAKEWORLD: @@ -692,7 +691,6 @@ static size_t QDECL Plug_GetServerInfoBlob(const char *key, void *outptr, size_t case DPB_NONE: break; case DPB_MVD: - case DPB_EZTV: blob = "mvd"; break; case DPB_QUAKEWORLD: @@ -888,7 +886,7 @@ static size_t QDECL Plug_GetTeamInfo(teamplayerinfo_t *players, size_t maxplayer players->health = cl.playerview[seat].statsf[STAT_HEALTH]; Q_strncpyz(players->nick, "", sizeof(players->nick)); } - else if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + else if (cls.demoplayback == DPB_MVD) { //scrape it from the mvd (assuming there is one... players->items = cl.players[i].stats[STAT_ITEMS]; players->armor = cl.players[i].statsf[STAT_ARMOR]; diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index 597c88bac..19ca04095 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -633,7 +633,7 @@ void CL_CalcClientTime(void) //qw code can drift (but oh noes! my latency!) //FIXME: nq code should be able to drift, but is apparently buggy somewhere and ends up uncomfortably stuttery right now. //default is to drift in demos+SP but not live (oh noes! added latency!) - if (cls.protocol == CP_QUAKE2 || cls.protocol==CP_NETQUAKE/*FIXME*/ || (cls.protocol != CP_QUAKE3 && (!cl_lerp_smooth.ival || (cl_lerp_smooth.ival == 2 && !(cls.demoplayback || cl.allocated_client_slots == 1 || cl.playerview[0].spectator))) && cls.demoplayback != DPB_MVD)) + if (cls.protocol == CP_QUAKE2 || cls.protocol==CP_NETQUAKE/*FIXME*/ || (cls.protocol != CP_QUAKE3 && (!cl_lerp_smooth.ival || (cl_lerp_smooth.ival == 2 && !(cls.demoplayback || cl.allocated_client_slots == 1 || cl.playerview[0].spectator))) && cls.demoplayback!=DPB_MVD)) { //no drift logic double f; extern cvar_t cl_demospeed; @@ -1018,7 +1018,7 @@ void CL_PredictMovePNum (int seat) } #endif - if (cl.paused && !(cls.demoplayback!=DPB_MVD && cls.demoplayback!=DPB_EZTV) && pv->cam_state == CAM_FREECAM) + if (cl.paused && !(cls.demoplayback!=DPB_MVD) && pv->cam_state == CAM_FREECAM) return; if (!cl.validsequence) @@ -1086,7 +1086,7 @@ void CL_PredictMovePNum (int seat) nopred = true; //these things also force-disable prediction - if ((cls.demoplayback==DPB_MVD || cls.demoplayback == DPB_EZTV) || + if (cls.demoplayback==DPB_MVD || cl.intermissionmode != IM_NONE || cl.paused || pv->pmovetype == PM_NONE || pv->pmovetype == PM_FREEZE || CAM_ISLOCKED(pv)) { nopred = true; @@ -1169,7 +1169,7 @@ void CL_PredictMovePNum (int seat) } else { - if (cls.demoplayback==DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback==DPB_MVD) { pv->nolocalplayer = false; from.state = &cl.inframes[cl.ackedmovesequence & UPDATE_MASK].playerstate[pv->playernum]; diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index 86088d40e..fe77d9d4f 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -2445,7 +2445,7 @@ void SCR_ImageName (const char *mapname) strcpy(levelshotname, "levelshots/"); COM_FileBase(mapname, levelshotname + strlen(levelshotname), sizeof(levelshotname)-strlen(levelshotname)); - if (qrenderer && scr_loadingscreen_aspect.ival >= 0) + if (qrenderer && scr_loadingscreen_aspect.ival >= 0 && *mapname) { R_LoadHiResTexture(levelshotname, NULL, IF_NOWORKER|IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP); @@ -2624,7 +2624,7 @@ SCR_ScreenShot_f */ static void SCR_ScreenShot_f (void) { - char sysname[1024]; + char displayname[1024]; char pcxname[MAX_QPATH]; int i; vfsfile_t *vfs; @@ -2672,7 +2672,7 @@ static void SCR_ScreenShot_f (void) } } - FS_NativePath(pcxname, FS_GAMEONLY, sysname, sizeof(sysname)); + FS_DisplayPath(pcxname, FS_GAMEONLY, displayname, sizeof(displayname)); rgbbuffer = VID_GetRGBInfo(&stride, &width, &height, &fmt); if (rgbbuffer) @@ -2680,12 +2680,12 @@ static void SCR_ScreenShot_f (void) //regarding metadata - we don't really know what's on the screen, so don't write something that may be wrong (eg: if there's only a console, don't claim that its a 360 image) if (SCR_ScreenShot(pcxname, FS_GAMEONLY, &rgbbuffer, 1, stride, width, height, fmt, false)) { - Con_Printf ("Wrote %s\n", sysname); + Con_Printf ("Wrote %s\n", displayname); BZ_Free(rgbbuffer); return; } BZ_Free(rgbbuffer); - Con_Printf (CON_ERROR "Couldn't write %s\n", sysname); + Con_Printf (CON_ERROR "Couldn't write %s\n", displayname); } else Con_Printf (CON_ERROR "Couldn't get colour buffer for screenshot\n"); @@ -2881,9 +2881,9 @@ static void SCR_ScreenShot_Mega_f(void) { if (SCR_ScreenShot(filename, FS_GAMEONLY, buffers, numbuffers, stride[0], width[0], height[0], fmt[0], true)) { - char sysname[1024]; - FS_NativePath(filename, FS_GAMEONLY, sysname, sizeof(sysname)); - Con_Printf ("Wrote %s\n", sysname); + char displayname[1024]; + FS_DisplayPath(filename, FS_GAMEONLY, displayname, sizeof(displayname)); + Con_Printf ("Wrote %s\n", displayname); } } else @@ -3040,9 +3040,9 @@ static void SCR_ScreenShot_VR_f(void) Con_Printf ("Unable to capture suitable screen image\n"); else if (SCR_ScreenShot(filename, FS_GAMEONLY, buffer, (stereo?2:1), stride, width, height*(stereo?1:2), TF_BGRX32, true)) { - char sysname[1024]; - FS_NativePath(filename, FS_GAMEONLY, sysname, sizeof(sysname)); - Con_Printf ("Wrote %s\n", sysname); + char displayname[1024]; + FS_DisplayPath(filename, FS_GAMEONLY, displayname, sizeof(displayname)); + Con_Printf ("Wrote %s\n", displayname); } BZ_Free(buffer[0]); @@ -3058,7 +3058,7 @@ void SCR_ScreenShot_Cubemap_f(void) int stride, fbwidth, fbheight; uploadfmt_t fmt; char filename[MAX_QPATH]; - char sysname[1024]; + char displayname[1024]; char *fname = Cmd_Argv(1); int i, firstside; char olddrawviewmodel[64]; //hack, so we can set r_drawviewmodel to 0 so that it doesn't appear in screenshots even if the csqc is generating new data. @@ -3189,8 +3189,8 @@ void SCR_ScreenShot_Cubemap_f(void) { if (Image_WriteDDSFile(filename, FS_GAMEONLY, &mips)) { - FS_NativePath(filename, FS_GAMEONLY, sysname, sizeof(sysname)); - Con_Printf ("Wrote %s\n", sysname); + FS_DisplayPath(filename, FS_GAMEONLY, displayname, sizeof(displayname)); + Con_Printf ("Wrote %s\n", displayname); } } #endif @@ -3199,8 +3199,8 @@ void SCR_ScreenShot_Cubemap_f(void) { if (Image_WriteKTXFile(filename, FS_GAMEONLY, &mips)) { - FS_NativePath(filename, FS_GAMEONLY, sysname, sizeof(sysname)); - Con_Printf ("Wrote %s\n", sysname); + FS_DisplayPath(filename, FS_GAMEONLY, displayname, sizeof(displayname)); + Con_Printf ("Wrote %s\n", displayname); } } #endif @@ -3249,13 +3249,13 @@ void SCR_ScreenShot_Cubemap_f(void) if (SCR_ScreenShot(filename, FS_GAMEONLY, &buffer, 1, stride, fbwidth, fbheight, fmt, false)) { - FS_NativePath(filename, FS_GAMEONLY, sysname, sizeof(sysname)); - Con_Printf ("Wrote %s\n", sysname); + FS_DisplayPath(filename, FS_GAMEONLY, displayname, sizeof(displayname)); + Con_Printf ("Wrote %s\n", displayname); } else { - FS_NativePath(filename, FS_GAMEONLY, sysname, sizeof(sysname)); - Con_Printf ("Failed to write %s\n", sysname); + FS_DisplayPath(filename, FS_GAMEONLY, displayname, sizeof(displayname)); + Con_Printf ("Failed to write %s\n", displayname); } BZ_Free(buffer); } diff --git a/engine/client/client.h b/engine/client/client.h index d8f9bc1d3..c3a4f0ce7 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -475,6 +475,7 @@ typedef struct CPNQ_BJP1, //16bit models, strict 8bit sounds (otherwise based on nehahra) CPNQ_BJP2, //16bit models, strict 16bit sounds CPNQ_BJP3, //16bit models, flagged 16bit sounds, 8bit static sounds. + CPNQ_H2MP, //urgh CPNQ_FITZ666, /*and rmqe999 protocol, which is a strict superset*/ CPNQ_DP5, CPNQ_DP6, @@ -519,7 +520,7 @@ typedef struct // entering a map (and clearing client_state_t) vfsfile_t *demooutfile; - enum{DPB_NONE,DPB_QUAKEWORLD,DPB_MVD,DPB_EZTV, + enum{DPB_NONE,DPB_QUAKEWORLD,DPB_MVD, #ifdef NQPROT DPB_NETQUAKE, #endif @@ -527,12 +528,16 @@ typedef struct DPB_QUAKE2 #endif } demoplayback, demorecording; + unsigned int demoeztv_ext; + #define EZTV_DOWNLOAD (1u<<0) //also changes modellist/soundlist stuff to keep things synced + #define EZTV_SETINFO (1u<<1) //proxy wants setinfo + ptrack commands + #define EZTV_QTVUSERLIST (1u<<2) //'//qul cmd id [name]' commands from proxy. qboolean demohadkeyframe; //q2 needs to wait for a packet with a key frame, supposedly. qboolean demoseeking; float demoseektime; int demotrack; qboolean timedemo; - char lastdemoname[MAX_OSPATH]; + char lastdemoname[MAX_OSPATH]; //empty if is a qtv stream qboolean lastdemowassystempath; vfsfile_t *demoinfile; float td_lastframe; // to meter out one message a frame @@ -540,6 +545,13 @@ typedef struct float td_starttime; // realtime at second frame of timedemo float demostarttime; // the time of the first frame, so we don't get weird results with qw demos + struct qtvviewers_s + { //(other) people on a qtv. in case people give a damn. + struct qtvviewers_s *next; + int userid; + char name[128]; + } *qtvviewers; + int challenge; float latency; // rolling average @@ -904,12 +916,12 @@ typedef struct // information that is static for the entire time connected to a server // #ifdef HAVE_LEGACY - char model_name_vwep[MAX_VWEP_MODELS][MAX_QPATH]; + char *model_name_vwep[MAX_VWEP_MODELS]; struct model_s *model_precache_vwep[MAX_VWEP_MODELS]; #endif - char model_name[MAX_PRECACHE_MODELS][MAX_QPATH]; + char *model_name[MAX_PRECACHE_MODELS]; struct model_s *model_precache[MAX_PRECACHE_MODELS]; - char sound_name[MAX_PRECACHE_SOUNDS][MAX_QPATH]; + char *sound_name[MAX_PRECACHE_SOUNDS]; struct sfx_s *sound_precache[MAX_PRECACHE_SOUNDS]; char *particle_ssname[MAX_SSPARTICLESPRE]; int particle_ssprecache[MAX_SSPARTICLESPRE]; //these are actually 1-based, so 0 can be used to lazy-init them. I cheat. @@ -994,6 +1006,8 @@ typedef struct float currentpacktime; qboolean do_lerp_players; + playerview_t *mouseplayerview; //for mouse/scoreboard interaction when playing mvds. + int mousenewtrackplayer; int teamplay; int deathmatch; @@ -1363,6 +1377,7 @@ void CL_Parse_Disconnected(void); void CL_DumpPacket(void); void CL_ParseEstablished(void); void CLQW_ParseServerMessage (void); +void CLEZ_ParseHiddenDemoMessage (void); void CLNQ_ParseServerMessage (void); #ifdef Q2CLIENT void CLQ2_ParseServerMessage (void); diff --git a/engine/client/console.c b/engine/client/console.c index 94c84af44..b19ca2cba 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -1956,7 +1956,8 @@ static int Con_DrawProgress(int left, int right, int y) sprintf(progresspercenttext, " (%ukB/s)", CL_DownloadRate()/1000); else { - sprintf(progresspercenttext, " (%u%sKiB)", (int)(total/1024), extra?"+":""); + char tmp[64]; + sprintf(progresspercenttext, " (%s%s)", FS_AbbreviateSize(tmp,sizeof(tmp), total), extra?"+":""); } //do some marquee thing, so the user gets the impression that SOMETHING is happening. diff --git a/engine/client/image.c b/engine/client/image.c index deaa5e2ca..092cf2e72 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -1792,7 +1792,7 @@ error: int Image_WritePNG (const char *filename, enum fs_relative fsroot, int compression, void **buffers, int numbuffers, qintptr_t bufferstride, int width, int height, enum uploadfmt fmt, qboolean writemetadata) { - char name[MAX_OSPATH]; + char systemname[MAX_OSPATH]; //FIXME: replace with png_set_write_fn int i; FILE *fp; png_structp png_ptr; @@ -1882,7 +1882,7 @@ int Image_WritePNG (const char *filename, enum fs_relative fsroot, int compressi } Image_BlockSizeForEncoding(fmt, &pxsize, &bw, &bh, &bd); - if (!FS_NativePath(filename, fsroot, name, sizeof(name))) + if (!FS_SystemPath(filename, fsroot, systemname, sizeof(systemname))) return false; if (numbuffers == 2) @@ -1898,10 +1898,10 @@ int Image_WritePNG (const char *filename, enum fs_relative fsroot, int compressi if (!LibPNG_Init()) return false; - if (!(fp = fopen (name, "wb"))) + if (!(fp = fopen (systemname, "wb"))) { - FS_CreatePath (filename, FS_GAMEONLY); - if (!(fp = fopen (name, "wb"))) + FS_CreatePath (systemname, FS_SYSTEM); + if (!(fp = fopen (systemname, "wb"))) return false; } @@ -7176,6 +7176,7 @@ static struct pendingtextureinfo *Image_ReadBLPFile(unsigned int flags, const ch //This is for the version command void Image_PrintInputFormatVersions(void) { + int i; #ifndef S_COLOR_YELLOW #define S_COLOR_YELLOW "" #define S_COLOR_WHITE "" @@ -7185,6 +7186,9 @@ void Image_PrintInputFormatVersions(void) #ifdef IMAGEFMT_DDS Con_Printf(" dds"); + #ifndef DECOMPRESS_S3TC + Con_Printf("(hw-only)"); + #endif #endif #ifdef IMAGEFMT_KTX Con_Printf(" ktx"); @@ -7231,6 +7235,9 @@ void Image_PrintInputFormatVersions(void) #endif #ifdef IMAGEFMT_ASTC Con_Printf(" astc"); + #ifndef DECOMPRESS_ASTC + Con_Printf("(hw-only)"); + #endif #endif #ifdef IMAGEFMT_PKM Con_Printf(" pkm"); @@ -7278,6 +7285,10 @@ void Image_PrintInputFormatVersions(void) Con_Printf(" lmp"); #endif + //now properly registered ones. + for (i = 0; i < imageloader_count; i++) + Con_Printf(" ^[%s^]", imageloader[i].funcs->loadername); + Con_Printf("\n"); } @@ -13757,10 +13768,10 @@ qboolean Image_LocateHighResTexture(image_t *tex, flocation_t *bestloc, char *be { COM_FileExtension(altname, nicename, sizeof(nicename)); e = 0; - if (strcmp(nicename, "lmp") && strcmp(nicename, "wal")) + if (Q_strcasecmp(nicename, "lmp") && Q_strcasecmp(nicename, "wal")) for (; e < tex_extensions_count; e++) { - if (!strcmp(nicename, (*tex_extensions[e].name=='.')?tex_extensions[e].name+1:tex_extensions[e].name)) + if (!Q_strcasecmp(nicename, (*tex_extensions[e].name=='.')?tex_extensions[e].name+1:tex_extensions[e].name)) break; } } @@ -13844,7 +13855,7 @@ qboolean Image_LocateHighResTexture(image_t *tex, flocation_t *bestloc, char *be for (e = firstex; e < tex_extensions_count; e++) { if (tex->flags & IF_NOPCX) - if (!strcmp(tex_extensions[e].name, ".pcx")) + if (!Q_strcasecmp(tex_extensions[e].name, ".pcx")) continue; Q_snprintfz(fname, sizeof(fname), tex_path[i].path, subpath, basename, tex_extensions[e].name); depth = FS_FLocateFile(fname, locflags, &loc); @@ -13863,7 +13874,7 @@ qboolean Image_LocateHighResTexture(image_t *tex, flocation_t *bestloc, char *be for (e = firstex; e < tex_extensions_count; e++) { if (tex->flags & IF_NOPCX) - if (!strcmp(tex_extensions[e].name, ".pcx")) + if (!Q_strcasecmp(tex_extensions[e].name, ".pcx")) continue; Q_snprintfz(fname, sizeof(fname), tex_path[i].path, nicename, tex_extensions[e].name); depth = FS_FLocateFile(fname, locflags, &loc); @@ -13906,7 +13917,7 @@ qboolean Image_LocateHighResTexture(image_t *tex, flocation_t *bestloc, char *be *b = 0; for (e = firstex; e < tex_extensions_count; e++) { - if (!strcmp(tex_extensions[e].name, ".tga")) + if (!Q_strcasecmp(tex_extensions[e].name, ".tga")) { Q_snprintfz(fname, sizeof(fname), tex_path[i].path, bumpname, tex_extensions[e].name); depth = FS_FLocateFile(fname, locflags, &loc); @@ -14747,7 +14758,10 @@ void Image_Formats_f(void) } Image_BlockSizeForEncoding(i, &blockbytes, &blockwidth, &blockheight, &blockdepth); bpp = blockbytes*8.0/(blockwidth*blockheight*blockdepth); - Con_Printf("%20s: %s"S_COLOR_GRAY" (%s%.3g-bpp)\n", Image_FormatName(i), sh_config.texfmt[i]?S_COLOR_GREEN"Enabled":S_COLOR_RED"Disabled", (blockdepth!=1)?"3d, ":"", bpp); + if (blockbytes) + Con_Printf("%20s: %s"S_COLOR_GRAY" (%s%.3g-bpp)\n", Image_FormatName(i), sh_config.texfmt[i]?S_COLOR_GREEN"Enabled":S_COLOR_RED"Disabled", (blockdepth!=1)?"3d, ":"", bpp); + else + Con_Printf("%20s: %s\n", Image_FormatName(i), sh_config.texfmt[i]?S_COLOR_GREEN"Enabled":S_COLOR_RED"Disabled"); } } diff --git a/engine/client/in_generic.c b/engine/client/in_generic.c index fd6f1ae7c..20977da08 100644 --- a/engine/client/in_generic.c +++ b/engine/client/in_generic.c @@ -491,7 +491,7 @@ void IN_Commands(void) Key_Event(ev->devid, ev->keyboard.scancode, ev->keyboard.unicode, true); break; case IEV_JOYAXIS: - if (ev->devid < MAXJOYSTICKS && ev->joy.axis < MAXJOYAXIS) + if (ev->devid < MAXJOYSTICKS && ev->joy.axis>=0 && ev->joy.axisjoyaxis && topmenu->joyaxis(topmenu, ev->devid, ev->joy.axis, ev->joy.value)) joy[ev->devid].axis[ev->joy.axis] = 0; @@ -884,6 +884,9 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum, float frame return; } + if (r_xflip.ival) + mouse_x *= -1; + // add mouse X/Y movement to cmd if (strafe_x) movements[1] += m_side.value * mouse_x; @@ -1040,6 +1043,8 @@ void IN_MoveJoystick(struct joy_s *joy, float *movements, int pnum, float framet VectorClear(jlook); VectorClear(jstrafe); } + if (r_xflip.ival) + jlook[0] *= -1, jstrafe[0] *= -1; if (in_speed.state[pnum] & 1) { diff --git a/engine/client/in_sdl.c b/engine/client/in_sdl.c index 617d6ce30..14c43b7fa 100644 --- a/engine/client/in_sdl.c +++ b/engine/client/in_sdl.c @@ -394,6 +394,44 @@ static void J_JoystickButton(SDL_JoystickID jid, int button, qboolean pressed) } } +int INS_GetControllerType(int id) +{ +#if SDL_VERSION_ATLEAST(2,0,12) + int i; + for (i = 0; i < MAX_JOYSTICKS; i++) + { + if (sdljoy[i].qdevid == id) + { + switch(SDL_GameControllerTypeForIndex(sdljoy[i].id)) + { + default: //for the future... +#if SDL_VERSION_ATLEAST(2,0,14) + case SDL_CONTROLLER_TYPE_VIRTUAL: //don't really know... assume steaminput and thus steamdeck and thus xbox-like. +#endif + return 1; + case SDL_CONTROLLER_TYPE_UNKNOWN: + return 0; + case SDL_CONTROLLER_TYPE_XBOX360: + case SDL_CONTROLLER_TYPE_XBOXONE: +#if SDL_VERSION_ATLEAST(2,0,16) + case SDL_CONTROLLER_TYPE_GOOGLE_STADIA: //close enough + case SDL_CONTROLLER_TYPE_AMAZON_LUNA: //it'll do. I guess we're starting to see a standard here. +#endif + return 1; //a on bottom, b('cancel') to right + case SDL_CONTROLLER_TYPE_PS3: + case SDL_CONTROLLER_TYPE_PS4: +#if SDL_VERSION_ATLEAST(2,0,14) + case SDL_CONTROLLER_TYPE_PS5: +#endif + return 2; //weird indecipherable shapes. + case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO: + return 3; //b on bottom, a('cancel') to right + } + } + } +#endif + return 0; +} void INS_Rumble(int id, quint16_t amp_low, quint16_t amp_high, quint32_t duration) { #if SDL_VERSION_ATLEAST(2,0,9) @@ -459,7 +497,8 @@ void INS_SetLEDColor(int id, vec3_t color) void INS_SetTriggerFX(int id, const void *data, size_t size) { #if SDL_VERSION_ATLEAST(2,0,15) - for (int i = 0; i < MAX_JOYSTICKS; i++) + int i; + for (i = 0; i < MAX_JOYSTICKS; i++) { if (sdljoy[i].qdevid == id) { diff --git a/engine/client/in_win.c b/engine/client/in_win.c index 1086274f5..ec5bfaa3e 100644 --- a/engine/client/in_win.c +++ b/engine/client/in_win.c @@ -34,8 +34,6 @@ void INS_Accumulate (void); #define AVAIL_XINPUT #ifdef AVAIL_XINPUT -//#define AVAIL_XINPUT_DLL "xinput9_1_0.dll" -#define AVAIL_XINPUT_DLL "xinput1_3.dll" typedef struct _XINPUT_GAMEPAD { WORD wButtons; BYTE bLeftTrigger; @@ -55,7 +53,9 @@ typedef struct _XINPUT_VIBRATION { } XINPUT_VIBRATION, *PXINPUT_VIBRATION; DWORD (WINAPI *pXInputGetState)(DWORD dwUserIndex, XINPUT_STATE *pState); DWORD (WINAPI *pXInputSetState)(DWORD dwUserIndex, XINPUT_VIBRATION *pState); -DWORD (WINAPI *pXInputGetDSoundAudioDeviceGuids)(DWORD dwUserIndex, GUID* pDSoundRenderGuid, GUID* pDSoundCaptureGuid); +DWORD (WINAPI *pXInputGetDSoundAudioDeviceGuids)(DWORD dwUserIndex, GUID* pDSoundRenderGuid, GUID* pDSoundCaptureGuid); //xi 1.3 +DWORD (WINAPI *pXInputGetAudioDeviceIds)(DWORD dwUserIndex, LPWSTR pRenderDeviceId, UINT *pRenderCount, LPWSTR pCaptureDeviceId, UINT *pCaptureCount); //xi 1.4 + enum { XINPUT_GAMEPAD_DPAD_UP = 0x0001, @@ -382,6 +382,51 @@ static const int mmjbuttons[32] = K_JOY8 }; + +static HANDLE powercontext = INVALID_HANDLE_VALUE; +static qboolean powersaveblocked = false; +static HANDLE (WINAPI *pPowerCreateRequest) (REASON_CONTEXT *Context); +static BOOL (WINAPI *pPowerSetRequest) (HANDLE Context, POWER_REQUEST_TYPE RequestType); +static BOOL (WINAPI *pPowerClearRequest) (HANDLE Context, POWER_REQUEST_TYPE RequestType); +static void INS_ScreenSaver_Init(void) +{ + dllfunction_t functable[] = + { + {(void*)&pPowerCreateRequest, "PowerCreateRequest"}, + {(void*)&pPowerSetRequest, "PowerSetRequest"}, + {(void*)&pPowerClearRequest, "PowerClearRequest"}, + {NULL} + }; + + if (Sys_LoadLibrary("kernel32.dll", functable)) + { //we don't do microsoft's interpretation of localisation, so this is lame. + REASON_CONTEXT reason = {POWER_REQUEST_CONTEXT_VERSION, POWER_REQUEST_CONTEXT_SIMPLE_STRING}; + reason.Reason.SimpleReasonString = L"Demo Playback"; + powercontext = pPowerCreateRequest(&reason); + + //FIXME: be prepared to regenerate the reason when our lang changes... + } +} +static void INS_ScreenSaver_UpdateBlock(qboolean block) +{ + if (powercontext != INVALID_HANDLE_VALUE && block != powersaveblocked) + { + powersaveblocked = block; + if (block) + { + pPowerSetRequest(powercontext, PowerRequestDisplayRequired); //keep the screen on... + pPowerSetRequest(powercontext, PowerRequestSystemRequired); //and don't go to sleep mid-video, too... + } + else + { + pPowerClearRequest(powercontext, PowerRequestSystemRequired); + pPowerClearRequest(powercontext, PowerRequestDisplayRequired); + } + } +} + + + // forward-referenced functions void INS_StartupJoystick (void); void INS_JoyMove (void); @@ -706,6 +751,8 @@ void INS_UpdateGrabs(int fullscreen, int activeapp) { int grabmouse; + INS_ScreenSaver_UpdateBlock(cls.demoplayback && activeapp); + if (!activeapp) grabmouse = false; else if (fullscreen || in_simulatemultitouch.ival || in_windowed_mouse.value) @@ -1376,6 +1423,8 @@ void INS_Init (void) Cvar_Register (&in_rawinput_keyboard, "Input Controls"); Cvar_Register (&in_rawinput_rdp, "Input Controls"); #endif + + INS_ScreenSaver_Init(); } /* @@ -1854,16 +1903,29 @@ static void IN_XInput_SetupAudio(struct wjoy_s *joy) if (joy->devid == DEVID_UNSET) return; - if (pXInputGetDSoundAudioDeviceGuids(joy->id, &gplayback, &gcapture) != ERROR_SUCCESS) - return; //probably not plugged in + if (pXInputGetDSoundAudioDeviceGuids) + { + if (pXInputGetDSoundAudioDeviceGuids(joy->id, &gplayback, &gcapture) != ERROR_SUCCESS) + return; //probably not plugged in - if (!memcmp(&gplayback, &GUID_NULL, sizeof(gplayback))) - return; //we have a controller, but no headset. + if (!memcmp(&gplayback, &GUID_NULL, sizeof(gplayback))) + return; //we have a controller, but no headset. - StringFromGUID2(&gplayback, mssuck, sizeof(mssuck)/sizeof(mssuck[0])); - narrowen(audiodevicename, sizeof(audiodevicename), mssuck); - Con_Printf("Controller %i uses audio device %s\n", joy->id, audiodevicename); - joy->audiodev = S_SetupDeviceSeat("DirectSound", audiodevicename, joy->devid); + StringFromGUID2(&gplayback, mssuck, sizeof(mssuck)/sizeof(mssuck[0])); + narrowen(audiodevicename, sizeof(audiodevicename), mssuck); + Con_Printf("Controller %i uses directsound device %s\n", joy->id, audiodevicename); + joy->audiodev = S_SetupDeviceSeat("DirectSound", audiodevicename, joy->devid); + } + else if (pXInputGetAudioDeviceIds) + { + UINT wccount = countof(mssuck); + if (!FAILED(pXInputGetAudioDeviceIds(joy->id, mssuck, &wccount, NULL,NULL/*we don't have separate mics, sadly, which also makes config awkward*/))) + { + narrowen(audiodevicename, sizeof(audiodevicename), mssuck); + Con_Printf("Controller %i uses xaudio2 device %s\n", joy->id, audiodevicename); + joy->audiodev = S_SetupDeviceSeat("XAudio2", audiodevicename, joy->devid); + } + } #endif } void INS_SetupControllerAudioDevices(qboolean enabled) @@ -1871,9 +1933,6 @@ void INS_SetupControllerAudioDevices(qboolean enabled) #ifdef AVAIL_XINPUT int i; - if (!pXInputGetDSoundAudioDeviceGuids) - return; - xinput_useaudio = enabled; for (i = 0; i < joy_count; i++) IN_XInput_SetupAudio(&wjoy[i]); @@ -1891,6 +1950,12 @@ void INS_StartupJoystick (void) if (in_xinput.ival) { static dllhandle_t *xinput; + static const char *dllnames[] = + { + "xinput1_4.dll", //win8+ only. does xaudio2 instead of dsound. + "xinput1_3.dll", //dxsdk (vista+). does dsound stuff. + "xinput9_1_0.dll" //vista+. doesn't do audio stuff. + }; if (!xinput) { dllfunction_t funcs[] = @@ -1899,10 +1964,17 @@ void INS_StartupJoystick (void) {(void**)&pXInputSetState, "XInputSetState"}, {NULL} }; - xinput = Sys_LoadLibrary(AVAIL_XINPUT_DLL, funcs); + pXInputGetDSoundAudioDeviceGuids = NULL; + pXInputGetAudioDeviceIds = NULL; + for (id = 0; id < countof(dllnames); id++) + { + xinput = Sys_LoadLibrary(dllnames[id], funcs); + if (xinput) + break; + } - if (xinput) - pXInputGetDSoundAudioDeviceGuids = Sys_GetAddressForName(xinput, "XInputGetDSoundAudioDeviceGuids"); + pXInputGetDSoundAudioDeviceGuids = xinput?Sys_GetAddressForName(xinput, "XInputGetDSoundAudioDeviceGuids"):NULL; + pXInputGetAudioDeviceIds = xinput?Sys_GetAddressForName(xinput, "XInputGetAudioDeviceIds"):NULL; } if (pXInputGetState) { @@ -1925,7 +1997,7 @@ void INS_StartupJoystick (void) Con_DPrintf("XInput is enabled (%i controllers found)\n", numdevs); } else - Con_Printf("XInput not installed\n"); + Con_Printf("XInput (%s) not installed\n", dllnames[1]); } #endif diff --git a/engine/client/keys.c b/engine/client/keys.c index c4ce80113..c70f82769 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -944,7 +944,7 @@ void Key_DefaultLinkClicked(console_t *con, char *text, char *info) if (*cl.players[player].ip) Con_Footerf(con, true, "\n%s", cl.players[player].ip); - if (cl.playerview[0].spectator || cls.demoplayback==DPB_MVD||cls.demoplayback==DPB_EZTV) + if (cl.playerview[0].spectator || cls.demoplayback==DPB_MVD) { //we're spectating, or an mvd Con_Footerf(con, true, " ^[Spectate\\player\\%i\\action\\spec^]", player); @@ -3079,7 +3079,7 @@ void Key_Event (unsigned int devid, int key, unsigned int unicode, qboolean down // // during demo playback, most keys bring up the main menu // - if (cls.demoplayback && cls.demoplayback != DPB_MVD && cls.demoplayback != DPB_EZTV && conkey && !Key_Dest_Has(~kdm_game)) + if (cls.demoplayback && cls.demoplayback != DPB_MVD && conkey && !Key_Dest_Has(~kdm_game)) { switch (key) { //these keys don't force the menu to appear while playing the demo reel @@ -3243,6 +3243,8 @@ defaultedbind: if (key == K_TOUCH || (key == K_MOUSE1 && IN_Touch_MouseIsAbs(devid))) { const char *button = SCR_ShowPics_ClickCommand(mousecursor_x, mousecursor_y, key == K_TOUCH); + if (!button && cl.mouseplayerview && cl.mousenewtrackplayer>=0) + Cam_Lock(cl.mouseplayerview, cl.mousenewtrackplayer); if (button) { dc = button; diff --git a/engine/client/m_download.c b/engine/client/m_download.c index 7b207319a..1816ad84c 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -368,7 +368,7 @@ static qboolean PM_HasDep(package_t *p, int deptype, const char *depname) //no dupes. for (d = p->deps; d ; d = d->next) { - if (d->dtype == deptype && !strcmp(d->name, depname)) + if (d->dtype == deptype && (!depname || !strcmp(d->name, depname))) return true; } return false; @@ -561,7 +561,7 @@ static void PM_ValidatePackage(package_t *p) else continue; if (!(p->flags & (DPF_NATIVE|DPF_CACHED))) - Con_Printf("WARNING: %s (%s) no longer exists\n", p->name, n); + Con_Printf("WARNING: (%i) %s (%s) no longer exists\n", dep->dtype, p->name, n); } //if no files were present, unmark it. @@ -637,7 +637,7 @@ static void PM_ValidatePackage(package_t *p) { unsigned int fqhash; pf = NULL; - fqhash = archive->GeneratePureCRC(archive, 0, 0); + fqhash = archive->GeneratePureCRC(archive, NULL); archive->ClosePath(archive); if (fqhash == (unsigned int)strtoul(p->qhash, NULL, 0)) @@ -833,7 +833,7 @@ static package_t *PM_InsertPackage(package_t *p) package_t **link; int v; - for (link = &availablepackages; *link; link = &(*link)->next) + for (link = &availablepackages; *link; ) { package_t *prev = *link; if (((prev->flags|p->flags) & DPF_GUESSED) && prev->extract == p->extract && !strcmp(prev->gamedir, p->gamedir) && prev->fsroot == p->fsroot && !strcmp(prev->qhash?prev->qhash:"", p->qhash?p->qhash:"")) @@ -858,16 +858,21 @@ static package_t *PM_InsertPackage(package_t *p) break; } if (differs) + { + link = &(*link)->next; continue; + } else { - if (p->flags & DPF_GUESSED) - { //the new one was guessed. just return the existing package instead. + if ((p->flags & DPF_GUESSED) && (prev->flags & DPF_PRESENT)) + { //the new one was also guessed. just return the existing package instead. + prev->flags |= p->flags&(DPF_PRESENT|DPF_ALLMARKED|DPF_ENABLED); PM_FreePackage(p); return prev; } - - //FIXME: replace prev... + PM_FreePackage(prev); + link = &availablepackages; + continue; } } v = PM_PackageSortOrdering(&prev, &p); @@ -891,13 +896,19 @@ static package_t *PM_InsertPackage(package_t *p) p->next = prev->alternative; prev->alternative = p; p->link = &prev->alternative; - return prev; + if (p->next) + p->next->link = &p->next; + return p; } //something major differs, display both independantly. p->flags |= DPF_DISPLAYVERSION; prev->flags |= DPF_DISPLAYVERSION; + + //there might be more such dupes. } + + link = &(*link)->next; } PM_ValidatePackage(p); @@ -919,6 +930,7 @@ static package_t *PM_InsertPackage(package_t *p) p->link = link; *link = p; numpackages++; + return p; } @@ -1017,7 +1029,13 @@ static void PM_AddSubListModule(void *module, plugupdatesourcefuncs_t *funcs, co Z_Free(pm_source[i].prefix); pm_source[i].prefix = Z_StrDup(prefix); } + pm_source[i].flags &= ~SRCFL_HISTORIC; pm_source[i].flags |= flags & SRCFL_UNSAFE; + if ((flags & SRCFL_USER) && (flags & (SRCFL_ENABLED|SRCFL_DISABLED))) + { + pm_source[i].flags &= ~SRCFL_ENABLED|SRCFL_DISABLED; + pm_source[i].flags |= (SRCFL_ENABLED|SRCFL_DISABLED) & flags; + } break; } } @@ -1062,11 +1080,15 @@ static void PM_RemSubList(const char *url) //don't actually remove it, that'd mess up the indexes which could break stuff like PM_ListDownloaded callbacks. :( pm_source[i].flags = SRCFL_HISTORIC; //forget enablement state etc. we won't bother writing it. pm_sequence++; //make sure any menus hide it. - break; + + Con_Printf("Source %s removed\n", url); + return; } else i++; } + + Con_Printf("Source %s not known, cannot remove\n", url); } #endif @@ -1083,7 +1105,7 @@ struct packagesourceinfo_s char mirror[MAXMIRRORS][MAX_OSPATH]; int nummirrors; }; -static const char *PM_ParsePackage(struct packagesourceinfo_s *source, const char *tokstart, package_t **outpackage, int wantvariation) +static const char *PM_ParsePackage(struct packagesourceinfo_s *source, const char *tokstart, package_t **outpackage, int wantvariation, const char *defaultpkgname) { package_t *p; struct packagedep_s *dep; @@ -1095,12 +1117,11 @@ static const char *PM_ParsePackage(struct packagesourceinfo_s *source, const cha { char pathname[256]; - char *fullname = (source->version >= 3)?NULL:Z_StrDup(com_token); + char *fullname = defaultpkgname?Z_StrDup(defaultpkgname):NULL; char *file = NULL; char *url = NULL; char *category = NULL; unsigned int flags = source->parseflags; - int i; if (source->version > 2) //v3 has an 'installed' token to say its enabled. v2 has a 'stale' token to say its old, which was weird and to try to avoid conflicts. flags &= ~DPF_ENABLED; @@ -1110,7 +1131,7 @@ static const char *PM_ParsePackage(struct packagesourceinfo_s *source, const cha p->priority = PM_DEFAULTPRIORITY; p->fsroot = FS_ROOT; Q_strncpyz(p->gamedir, source->gamedir, sizeof(p->gamedir)); - for (i = 1; tokstart; i++) + while (tokstart) { char *eq; char key[8192]; @@ -1240,7 +1261,7 @@ static const char *PM_ParsePackage(struct packagesourceinfo_s *source, const cha } else if (!strcmp(key, "map")) PM_AddDep(p, DEP_MAP, val); - else if (!strcmp(key, "depend")) + else if (!strcmp(key, "depend") || !strcmp(key, "depends")) PM_AddDep(p, DEP_REQUIRE, val); else if (!strcmp(key, "conflict")) PM_AddDep(p, DEP_CONFLICT, val); @@ -1329,7 +1350,7 @@ static const char *PM_ParsePackage(struct packagesourceinfo_s *source, const cha { p->mirror[0] = Z_StrDup(url); - if (!file) + if (!file && p->extract != EXTRACT_ZIP) { FS_PathURLCache(url, pathname, sizeof(pathname)); PM_AddDep(p, DEP_CACHEFILE, pathname+10); @@ -1413,7 +1434,7 @@ static const char *PM_ParsePackage(struct packagesourceinfo_s *source, const cha if (wantvariation == 0) //only the first! { while (++wantvariation < variation) - if (tokstart != PM_ParsePackage(source, start, NULL, wantvariation)) + if (tokstart != PM_ParsePackage(source, start, NULL, wantvariation, defaultpkgname)) { Con_Printf(CON_ERROR"%s: Unable to parse package variation...\n", source->url); break; //erk? @@ -1657,13 +1678,13 @@ static qboolean PM_ParsePackageList(const char *f, unsigned int parseflags, cons if (!strcmp(com_token, "{")) { linestart = COM_StringParse (linestart, com_token, sizeof(com_token), false, false); - f = PM_ParsePackage(&source, linestart, NULL, 0); + f = PM_ParsePackage(&source, linestart, NULL, 0, NULL); if (!f) break; //erk! } else if (source.version < 3) { //old single-line gibberish - PM_ParsePackage(&source, tokstart, NULL, -1); + PM_ParsePackage(&source, tokstart, NULL, -1, com_token); } else { @@ -1765,6 +1786,7 @@ static char *PM_GetMetaTextFromFile(vfsfile_t *file, const char *filename, char archive = FS_OpenPackByExtension(file, NULL, filename, filename, ""); if (archive) { + int crc; flocation_t loc; vfsfile_t *metafile = NULL; if (archive->FindFile(archive, &loc, "fte.meta", NULL)) @@ -1775,13 +1797,69 @@ static char *PM_GetMetaTextFromFile(vfsfile_t *file, const char *filename, char { size_t sz = VFS_GETLEN(metafile); ret = BZ_Malloc(sz+1); - VFS_READ(metafile, ret, sz); - ret[sz] = 0; + if (ret) + { + VFS_READ(metafile, ret, sz); + ret[sz] = 0; + } VFS_CLOSE(metafile); } + crc = archive->GeneratePureCRC(archive, NULL); + + if (!ret) + { //it'd be really nice if creators were embedding metadata into their packages... but creators suck... especially when their creations predate our engine... + //anyway, we kinda need this stuff in order to get the gamedirs right when drag+dropping. we also get nicer titles out of it. +#ifdef HAVE_LEGACY + if (0) + ; + //quake + else if (crc == 0x92c8b832 && !Q_strcasecmp(filename, "pak0.pak")) ret = Z_StrDup("{\npackage quake_shareware\ntitle \"Quake Shareware Data\"\ngamedir id1\nver 1.01\n}\n"); + else if (crc == 0x4f069cac && !Q_strcasecmp(filename, "pak0.pak")) ret = Z_StrDup("{\npackage quake_shareware\ntitle \"Quake Shareware Data\"\ngamedir id1\nver 1.06\n}\n"); + else if (crc == 0x329050a6 && !Q_strcasecmp(filename, "pak1.pak")) ret = Z_StrDup("{\npackage quake_registered\ntitle \"Quake Registered Data\"\ngamedir id1\n}\n"); + else if (crc == 0x31bef951 && !Q_strcasecmp(filename, "pak0.pak")) ret = Z_StrDup("{\npackage quake_hipnotic\ntitle \"Scourge of Armagon\"\ngamedir hipnotic\n}\n"); + else if (crc == 0x799d60c0 && !Q_strcasecmp(filename, "pak0.pak")) ret = Z_StrDup("{\npackage quake_rogue\ntitle \"Dissolution of Eternity\"\ngamedir rogue\n}\n"); + //quake rerelease + else if (crc == 0xefdb8a92 && !Q_strcasecmp(filename, "QuakeEX.kpf"))ret= Z_StrDup("{\npackage quakeex_data\ntitle \"Quake Rerelease Misc Data\"\ngamedir \"\"\n}\n"); //p4 + else if (crc == 0xbb51cd43 && !Q_strcasecmp(filename, "pak0.pak")) ret = Z_StrDup("{\npackage quake_rerel\ntitle \"Quake Rerelease Data\"\ngamedir id1\nrequire quakeex_data\n}\n"); //p4 + else if (crc == 0xda8e9bb8 && !Q_strcasecmp(filename, "pak0.pak")) ret = Z_StrDup("{\npackage quake_rerel_hipnotic\ntitle \"Scourge of Armagon (Rerelease)\"\ngamedir hipnotic\nrequire quakeex_data\n}\n"); + else if (crc == 0x016eac0c && !Q_strcasecmp(filename, "pak0.pak")) ret = Z_StrDup("{\npackage quake_rerel_rogue\ntitle \"Dissolution of Eternity (Rerelease)\"\ngamedir rogue\nrequire quakeex_data\n}\n"); + else if (crc == 0xbdfb74e2 && !Q_strcasecmp(filename, "pak0.pak")) ret = Z_StrDup("{\npackage quake_rerel_ctf\ntitle \"Capture The Flag (Rerelease)\"\ngamedir ctf\nrequire quakeex_data\n}\n"); + else if (crc == 0x8751abb3 && !Q_strcasecmp(filename, "pak0.pak")) ret = Z_StrDup("{\npackage quake_rerel_dopa\ntitle \"Dimensions Of The Past (Rerelease)\"\ngamedir dopa\nrequire quakeex_data\n}\n"); + else if (crc == 0x73f34d24 && !Q_strcasecmp(filename, "pak0.pak")) ret = Z_StrDup("{\npackage quake_rerel_mg1\ntitle \"Dimensions Of The Machine\"\ngamedir mg1\nrequire quakeex_data\n}\n"); + //hexen2 + else if (crc == 0xae67217a && !Q_strcasecmp(filename, "pak0.pak")) ret = Z_StrDup("{\npackage hexen2_registered_p0\ntitle \"Quake2 Data (pak0)\"\ngamedir data1\n}\n"); + else if (crc == 0xee6fabd1 && !Q_strcasecmp(filename, "pak1.pak")) ret = Z_StrDup("{\npackage hexen2_registered_p1\ntitle \"Quake2 Data (pak1)\"\ngamedir data1\n}\n"); + else if (crc == 0x400848b4 && !Q_strcasecmp(filename, "pak3.pak")) ret = Z_StrDup("{\npackage hexen2_portals\ntitle \"Portals Data\"\ngamedir portals\n}\n"); + //quake2 + else if (crc == 0x03ffdce0 && !Q_strcasecmp(filename, "pak0.pak")) ret = Z_StrDup("{\npackage quake2_p0\ntitle \"Quake2 Data (pak0)\"\ngamedir baseq2\n}\n"); + else if (crc == 0x7d40ac55 && !Q_strcasecmp(filename, "pak1.pak")) ret = Z_StrDup("{\npackage quake2_p1\ntitle \"Quake2 Data (pak1)\"\ngamedir baseq2\n}\n"); + else if (crc == 0xdc6fabf9 && !Q_strcasecmp(filename, "pak2.pak")) ret = Z_StrDup("{\npackage quake2_p2\ntitle \"Quake2 Data (pak2)\"\ngamedir baseq2\n}\n"); +// else if (crc == 0x && !Q_strcasecmp(filename, "pak0.pak")) ret = Z_StrDup("{\npackage quake2_xatrixp2\ntitle \"The Reckoning\"\ngamedir baseq2\nconflict quake2_release\n}\n"); +// else if (crc == 0x && !Q_strcasecmp(filename, "pak0.pak")) ret = Z_StrDup("{\npackage quake2_rogue\ntitle \"Ground Zero\"\ngamedir baseq2\nconflict quake2_release\n}\n"); +// else if (crc == 0x && !Q_strcasecmp(filename, "pak0.pak")) ret = Z_StrDup("{\npackage quake2_release\ntitle \"Quake2 Rerelease Data\"\ngamedir baseq2\nrequire quake2ex_data\n}\n"); +// else if (crc == 0x && !Q_strcasecmp(filename, "Q2Game.kpf"))ret = Z_StrDup("{\npackage quake2ex_data\ntitle \"Quake2 Rerelease Misc Data\"\ngamedir \"\"\n}\n"); + //quake3 + else if (crc == 0xb1f4d354 && !Q_strcasecmp(filename, "pak0.pk3")) ret = Z_StrDup("{\npackage quake3_demo\ntitle \"Quake3 Demo Data\"\ngamedir baseq3\n}\n"); + else if (crc == 0x5d626b5f && !Q_strcasecmp(filename, "pak0.pk3")) ret = Z_StrDup("{\npackage quake3_p0\ntitle \"Quake3 Data (pak0)\"\ngamedir baseq3\n}\n"); + else if (crc == 0x11c4fe9b && !Q_strcasecmp(filename, "pak1.pk3")) ret = Z_StrDup("{\npackage quake3_p1\ntitle \"Quake3 Data (pak1)\"\ngamedir baseq3\n}\n"); + else if (crc == 0x18912474 && !Q_strcasecmp(filename, "pak2.pk3")) ret = Z_StrDup("{\npackage quake3_p2\ntitle \"Quake3 Data (pak2)\"\ngamedir baseq3\n}\n"); + else if (crc == 0xb24e9894 && !Q_strcasecmp(filename, "pak3.pk3")) ret = Z_StrDup("{\npackage quake3_p3\ntitle \"Quake3 Data (pak3)\"\ngamedir baseq3\n}\n"); + else if (crc == 0x476700a6 && !Q_strcasecmp(filename, "pak4.pk3")) ret = Z_StrDup("{\npackage quake3_p4\ntitle \"Quake3 Data (pak4)\"\ngamedir baseq3\n}\n"); + else if (crc == 0xf39bc355 && !Q_strcasecmp(filename, "pak5.pk3")) ret = Z_StrDup("{\npackage quake3_p5\ntitle \"Quake3 Data (pak5)\"\ngamedir baseq3\n}\n"); + else if (crc == 0xdd13d69b && !Q_strcasecmp(filename, "pak6.pk3")) ret = Z_StrDup("{\npackage quake3_p6\ntitle \"Quake3 Data (pak6)\"\ngamedir baseq3\n}\n"); + else if (crc == 0x362c0725 && !Q_strcasecmp(filename, "pak7.pk3")) ret = Z_StrDup("{\npackage quake3_p7\ntitle \"Quake3 Data (pak7)\"\ngamedir baseq3\n}\n"); + else if (crc == 0x3a3dc1a6 && !Q_strcasecmp(filename, "pak8.pk3")) ret = Z_StrDup("{\npackage quake3_p8\ntitle \"Quake3 Data (pak8)\"\ngamedir baseq3\n}\n"); + else if (crc == 0x90dc1501 && !Q_strcasecmp(filename, "pak0.pk3")) ret = Z_StrDup("{\npackage quake3_ta_p0\ntitle \"TeamArena Data (pak0)\"\ngamedir missionpack\n}\n"); + else if (crc == 0x1e757510 && !Q_strcasecmp(filename, "pak1.pk3")) ret = Z_StrDup("{\npackage quake3_ta_p1\ntitle \"TeamArena Data (pak1)\"\ngamedir missionpack\n}\n"); + else if (crc == 0x9eb4a591 && !Q_strcasecmp(filename, "pak2.pk3")) ret = Z_StrDup("{\npackage quake3_ta_p2\ntitle \"TeamArena Data (pak2)\"\ngamedir missionpack\n}\n"); + else if (crc == 0x55c0476a && !Q_strcasecmp(filename, "pak3.pk3")) ret = Z_StrDup("{\npackage quake3_ta_p3\ntitle \"TeamArena Data (pak3)\"\ngamedir missionpack\n}\n"); + else +#endif + ret = Z_StrDup("{\nguessed 1\n}\n"); + } //get the archive's qhash. - Q_snprintfz(qhash, hashsize, "0x%x", archive->GeneratePureCRC(archive, 0, 0)); + Q_snprintfz(qhash, hashsize, "0x%x", crc); line = ret; do @@ -1814,17 +1892,31 @@ void *PM_GeneratePackageFromMeta(vfsfile_t *file, char *fname, size_t fnamesize, struct packagesourceinfo_s pkgsrc = {DPF_ENABLED}; pkgsrc.version = 3; pkgsrc.categoryprefix = ""; + Q_strncpyz(pkgsrc.gamedir, FS_GetGamedir(true), sizeof(pkgsrc.gamedir)); COM_StripAllExtensions(COM_SkipPath(fname), pkgname,sizeof(pkgname)); PM_PreparePackageList(); //just in case. //see if we can make any sense of it. - PM_ParsePackage(&pkgsrc, pkgdata, &p, 1); + PM_ParsePackage(&pkgsrc, pkgdata, &p, 1, fname); if (p) { - if (p->extract == EXTRACT_COPY) + /*if (!(p->fsroot == FS_ROOT && *p->gamedir) && FS_RELATIVE_ISSPECIAL(p->fsroot)) + { //the metadata is useless if it says to put it somewhere unsafe, though signed packages mean this is at least mostly safe... + Z_Free(pkgdata); + pkgdata = NULL; + Menu_PromptOrPrint(va(localtext("Refusing to install to requested location\n%s\n"), fname), "Cancel", true); + } + else*/ if (!PM_SignatureOkay(p)) + { //only allow if its trustworthy (or paks/pk3s) + Z_Free(pkgdata); + pkgdata = NULL; + Menu_PromptOrPrint(va(localtext("Bad MetaData Signature\n%s\n"), fname), "Cancel", true); + } + else if (p->extract == EXTRACT_COPY) { + vfsfile_t *vfs; const char *f = PM_GetDepSingle(p, DEP_FILE); if (!f) f = va("%s", fname); //erk? @@ -1833,12 +1925,27 @@ void *PM_GeneratePackageFromMeta(vfsfile_t *file, char *fname, size_t fnamesize, f = va("%s/%s", p->gamedir, f); Z_StrDupPtr(&p->qhash, qhash); - if (PM_TryGenCachedName(f, p, fname, fnamesize)) + + vfs = FS_OpenVFS(f, "rb", p->fsroot); //if they're dropping pak0.pak, don't overwrite anything. + if (vfs) + { + VFS_CLOSE(vfs); + Z_Free(pkgdata); + pkgdata = NULL; + Menu_PromptOrPrint(va(localtext("File is already installed\n%s\n"), fname), "Cancel", true); + } + if (!vfs && PM_TryGenCachedName(f, p, fname, fnamesize)) ; else Q_strncpyz(fname, f, fnamesize); *fsroot = p->fsroot; } + else + { //we're doing drag+drop single-file logic here, so don't want to handle extracting everything with the risk of overwriting. + Z_Free(pkgdata); + pkgdata = NULL; + Menu_PromptOrPrint(va(localtext("Package installation too complex\n%s\n"), fname), "Cancel", true); + } //okay, seems there's something in it. PM_FreePackage(p); @@ -1861,7 +1968,7 @@ static qboolean PM_FileInstalled_Internal(const char *package, const char *categ pkgsrc.version = 3; pkgsrc.categoryprefix = ""; - PM_ParsePackage(&pkgsrc, metainfo, &p, 1); + PM_ParsePackage(&pkgsrc, metainfo, &p, 1, package); if (!p) return false; } @@ -1872,9 +1979,16 @@ static qboolean PM_FileInstalled_Internal(const char *package, const char *categ strcpy(p->version, "?" "?" "?" "?"); } - p->deps = Z_Malloc(sizeof(*p->deps) + strlen(filename)); - p->deps->dtype = DEP_FILE; - strcpy(p->deps->name, filename); + if (PM_HasDep(p, DEP_FILE, filename)) + ; //no dupes + else if (PM_GetDepSingle(p, DEP_FILE)) + { //a filename was specified, but it differs from what we expected... + Con_Printf(CON_WARNING"PM_FileInstalled: filename does not match metadata info\n"); + PM_FreePackage(p); + return false; + } + else + PM_AddDep(p, DEP_FILE, filename); dlcache = strstr(p->deps->name+1, "/dlcache/"); if (dlcache) @@ -1932,8 +2046,12 @@ static qboolean PM_FileInstalled_Internal(const char *package, const char *categ if (enable) p->flags |= DPF_USERMARKED|DPF_ENABLED; - if (PM_InsertPackage(p)) + p = PM_InsertPackage(p); + if (p) { + if (enable && !(p->flags&DPF_ENABLED)) + Con_Printf(CON_WARNING"PM_FileInstalled: package failed to enable\n"); +// Cmd_ExecuteString("menu_download\n", RESTRICT_LOCAL); PM_ResortPackages(); PM_WriteInstalledPackages(); } @@ -1944,7 +2062,7 @@ void PM_FileInstalled(const char *filename, enum fs_relative fsroot, void *metai { char pkgname[MAX_QPATH]; COM_StripAllExtensions(COM_SkipPath(filename), pkgname,sizeof(pkgname)); - PM_FileInstalled_Internal(pkgname, "", pkgname, filename, fsroot, DPF_GUESSED, metainfo, enable); + PM_FileInstalled_Internal(pkgname, "", pkgname, filename, fsroot, /*metainfo?0:*/DPF_GUESSED, metainfo, enable); } #ifdef PLUGINS @@ -2102,21 +2220,29 @@ static void PM_PreparePackageList(void) fl |= SRCFL_DISABLED; //don't trust it, don't even prompt. while ((s = COM_Parse(s))) - PM_AddSubList(com_token, NULL, fl); //enable it by default. functionality is kinda broken otherwise. + { +#ifdef FTE_TARGET_WEB + if (*com_token == '/' && !(fl&SRCFL_DISABLED)) + PM_AddSubList(com_token, NULL, fl|SRCFL_ENABLED); //enable it by default, its too annoying otherwise. + else +#endif + PM_AddSubList(com_token, NULL, fl); + } } #ifdef PLUGINS { - char nat[MAX_OSPATH]; - if (FS_NativePath("", FS_BINARYPATH, nat, sizeof(nat))) + char sys[MAX_OSPATH]; + char disp[MAX_OSPATH]; + if (FS_DisplayPath("", FS_BINARYPATH, disp, sizeof(disp))&&FS_SystemPath("", FS_BINARYPATH, sys, sizeof(sys))) { - Con_DPrintf("Loading plugins from \"%s\"\n", nat); - Sys_EnumerateFiles(nat, PLUGINPREFIX"*" ARCH_DL_POSTFIX, PM_EnumeratedPlugin, (void*)FS_BINARYPATH, NULL); + Con_DPrintf("Loading plugins from \"%s\"\n", disp); + Sys_EnumerateFiles(sys, PLUGINPREFIX"*" ARCH_DL_POSTFIX, PM_EnumeratedPlugin, (void*)FS_BINARYPATH, NULL); } - if (FS_NativePath("", FS_LIBRARYPATH, nat, sizeof(nat))) + if (FS_DisplayPath("", FS_LIBRARYPATH, disp, sizeof(disp))&&FS_SystemPath("", FS_LIBRARYPATH, sys, sizeof(sys))) { - Con_DPrintf("Loading plugins from \"%s\"\n", nat); - Sys_EnumerateFiles(nat, PLUGINPREFIX"*" ARCH_DL_POSTFIX, PM_EnumeratedPlugin, (void*)FS_LIBRARYPATH, NULL); + Con_DPrintf("Loading plugins from \"%s\"\n", disp); + Sys_EnumerateFiles(sys, PLUGINPREFIX"*" ARCH_DL_POSTFIX, PM_EnumeratedPlugin, (void*)FS_LIBRARYPATH, NULL); } } #endif @@ -2134,6 +2260,51 @@ void PM_ManifestChanged(ftemanifest_t *man) PM_UpdatePackageList(false); } +qboolean PM_HandleRedirect(const char *package, /*char *cachename, size_t cachesize,*/ char *url, size_t urlsize) +{ //given a "GAMEDIR/PACKAGEFILE", swap it out for a real path/url that a client can download. + //eg "package/GAMEDIR/dlcache/PACKAGEFILE.xxxxxxxx" or http://whereeverweoriginallydownloadeditfrom" + char gamedir[16]; + char *sep; + package_t *p; +char *cachename = NULL; size_t cachesize=0; + sep = strchr(package, '/'); + *cachename = 0; + if (!sep) + return false; //no gamedir? that doesn't seem right. tell em to fuck off. + if (sep-package >= sizeof(gamedir)) + return false; //overflow + memcpy(gamedir, package, sep-package); + gamedir[sep-package] = 0; + sep++; + for (p = availablepackages; p; p = p->next) + { + if (!(p->flags & DPF_PRESENT)) + continue; //dunno, shouldn't really be trying to download it if its not even on the server. + if (strcmp(p->gamedir, gamedir)) + continue; //nope, some other gamedir perhaps. + if (PM_HasDep(p, DEP_FILE, sep)) + { + if (!(p->flags & DPF_CACHED) || !PM_TryGenCachedName(package, p, cachename, cachesize)) + *cachename = 0; + if (p->mirror[0]) + { + Q_strncpyz(url, p->mirror[0], urlsize); + return true; + } + if (p->flags & DPF_CACHED) + { + Q_snprintfz(url, urlsize, "package/%s", cachename); + return true; + } + return false; //native? don't redirect it locally. + } + /*if (PM_HasDep(p, DEP_CACHEFILE, sep)) + { + Q_snprintfz(cachename, sizeof(cachename), "downloads/%s", d->name); + }*/ + } + return false; +} void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const char *parent_logical, searchpath_t *search, unsigned int loadstuff, int minpri, int maxpri) { package_t *p; @@ -2157,7 +2328,11 @@ void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const cha if ((p->flags & (DPF_ENABLED|DPF_MANIMARKED)) && p->priority==pri && !Q_strcasecmp(parent_pure, p->gamedir)) { char *qhash = (p->qhash&&*p->qhash)?p->qhash:NULL; - unsigned int fsfl = SPF_COPYPROTECTED; + unsigned int fsfl = 0; + if (p->flags & DPF_CACHED) + fsfl |= 0; //package is in the dlcache dir... if its downloaded from somewhere then its probably a bit rich to block it from downloading from elsewhere. + else + fsfl |= SPF_COPYPROTECTED; if (qhash && (p->flags&DPF_TRUSTED)) ; else @@ -2239,7 +2414,7 @@ static package_t *PM_FindExactPackage(const char *packagename, const char *arch, { if (arch && (!p->arch||strcmp(p->arch, arch))) continue; //wrong arch. - if (!arch && p->arch && *p->arch && strcmp(p->arch, THISARCH)) + if (!arch && p->arch && *p->arch && Q_strcasecmp(p->arch, THISARCH)) continue; //wrong arch. if (flags && !(p->flags & flags)) continue; @@ -2400,6 +2575,21 @@ static void PM_UnmarkPackage(package_t *package, unsigned int markflag) } } +static package_t *PM_RootForAlt(package_t *package) +{ + package_t *p, *a; + for (p = availablepackages; p; p = p->next) + { + if (p == package) + return p; + for (a = p->alternative; a; a = a->next) + { + if (a == package) + return p; + } + } + return NULL; //some kind of error +} //just flags, doesn't install //returns true if it was marked (or already enabled etc), false if we're not allowed. static qboolean PM_MarkPackage(package_t *package, unsigned int markflag) @@ -2407,9 +2597,13 @@ static qboolean PM_MarkPackage(package_t *package, unsigned int markflag) package_t *o; struct packagedep_s *dep, *dep2; qboolean replacing = false; + package_t *root; //if its an alt package, we want to do some switcheroos if (pkg_updating) + { + Con_Printf("PM_MarkPackage: busy...\n"); return false; + } if (package->flags & DPF_MARKED) { @@ -2422,11 +2616,15 @@ static qboolean PM_MarkPackage(package_t *package, unsigned int markflag) if (!(package->flags & DPF_PRESENT)) { //though we can at least unmark it for deletion... package->flags &= ~DPF_PURGE; + Con_Printf("PM_MarkPackage: unable to download, and not already present.\n"); return false; } #else if (!PM_SignatureOkay(package)) + { + Con_Printf(CON_WARNING"%s: package not trusted, refusing to mark.\n", package->name); return false; + } #endif //any file-conflicts prevent the package from being installable. @@ -2441,10 +2639,41 @@ static qboolean PM_MarkPackage(package_t *package, unsigned int markflag) else n = dep->name; if (PM_CheckFile(n, package->fsroot)) + { + char tmp[MAX_OSPATH]; + FS_DisplayPath(n, package->fsroot, tmp,sizeof(tmp)); + Con_Printf(CON_WARNING"%s: conflicts with existing file \"%s\"\n", package->name, tmp); return false; + } } } + root = PM_RootForAlt(package); + if (root != package && (package->flags&DPF_TRUSTED) >= (root->flags&DPF_TRUSTED)) + { + //unlink the new. + *package->link = package->next; + if (package->next) + package->next->link = package->link; + + //replace the old root with the new + *root->link = package; + package->next = root->next; + if (package->next) + package->next->link = &package->next; + + //insert the old into the new's alt + root->next = package->alternative; + if (root->next) + root->next->link = &root->next; + root->link = &package->alternative; + *root->link = root; + + root->flags &= ~DPF_ALLMARKED; + } + else if (root != package) + return false; + package->flags |= markflag; //first check to see if we're replacing a different version of the same package @@ -2506,6 +2735,8 @@ static qboolean PM_MarkPackage(package_t *package, unsigned int markflag) else PM_MarkPackage(d, DPF_AUTOMARKED); } + else if (dep->dtype == DEP_REQUIRE) + Con_Printf(CON_WARNING"Couldn't find dependancy \"%s\"\n", dep->name); else Con_DPrintf("Couldn't find dependancy \"%s\"\n", dep->name); } @@ -2555,8 +2786,8 @@ unsigned int PM_MarkUpdates (void) char *strings = fs_manifest->installupd; while (strings && *strings) { - qboolean isunwanted = (*tok=='!'); - strings = COM_ParseStringSetSep(strings, ';', tok+isunwanted, sizeof(tok)); + qboolean isunwanted = (*strings=='!'); + strings = COM_ParseStringSetSep(strings+isunwanted, ';', tok, sizeof(tok)); p = PM_MarkedPackage(tok, DPF_MARKED); if (!p) @@ -3279,11 +3510,11 @@ static void PM_WriteInstalledPackages(void) int i; char *s; package_t *p; - vfsfile_t *f = FS_OpenVFS(INSTALLEDFILES, "wb", FS_ROOT); + vfsfile_t *f = FS_OpenVFS(INSTALLEDFILES, "wbp", FS_ROOT); qboolean v3 = false; if (!f) { - if (FS_NativePath(INSTALLEDFILES, FS_ROOT, buf, sizeof(buf))) + if (FS_DisplayPath(INSTALLEDFILES, FS_ROOT, buf, sizeof(buf))) Con_Printf("package manager: Can't write %s\n", buf); else Con_Printf("package manager: Can't update installed list\n"); @@ -3394,7 +3625,7 @@ static void PM_PackageEnabled(package_t *p) #define Menu_Prompt(cb,ctx,msg,yes,no,cancel,highpri) Con_Printf(CON_WARNING "%s\n", msg) #endif - if (FS_NativePath(ef->name, p->fsroot, native, sizeof(native)) && Sys_SetUpdatedBinary(native)) + if (FS_SystemPath(ef->name, p->fsroot, native, sizeof(native)) && Sys_SetUpdatedBinary(native)) { Q_strncpyz(enginerevision, p->version, sizeof(enginerevision)); //make sure 'revert' picks up the new binary... Menu_Prompt(NULL, NULL, localtext("Engine binary updated.\nRestart to use."), NULL, NULL, NULL, true); @@ -3440,7 +3671,7 @@ static int QDECL PM_ExtractFiles(const char *fname, qofs_t fsize, time_t mtime, static qboolean PM_Download_Got_Extract(package_t *p, searchpathfuncs_t *archive, const char *tempname, enum fs_relative temproot) { //EXTRACT_EXPLICITZIP is very special. qboolean success = false; - char native[MAX_OSPATH]; + char displayname[MAX_OSPATH]; char ext[8]; char *destname; struct packagedep_s *dep, *srcname = p->deps; @@ -3490,7 +3721,11 @@ static qboolean PM_Download_Got_Extract(package_t *p, searchpathfuncs_t *archive { while (srcname && srcname->dtype != DEP_EXTRACTNAME) srcname = srcname->next; - if (archive) + if (!archive) + Con_Printf(CON_ERROR"archive %s is not an archive!\n", tempname); + else if (!srcname) + Con_Printf(CON_ERROR"archive %s is EXTRACT_EXPLICITZIP but filename not specified!\n", tempname); + else { flocation_t loc; @@ -3502,38 +3737,46 @@ static qboolean PM_Download_Got_Extract(package_t *p, searchpathfuncs_t *archive archive->ReadFile(archive, &loc, f); if (FS_WriteFile(destname, f, loc.len, p->fsroot)) { - if (!FS_NativePath(destname, p->fsroot, native, sizeof(native))) - Q_strncpyz(native, destname, sizeof(native)); - Con_Printf("Extracted %s (to %s)\n", p->name, native); + if (!FS_DisplayPath(destname, p->fsroot, displayname, sizeof(displayname))) + Q_strncpyz(displayname, destname, sizeof(displayname)); + Con_Printf(CON_WARNING"Extracted %s (to %s)\n", p->name, displayname); p->flags = nfl; success = true; continue; } + else + { + if (!FS_DisplayPath(destname, p->fsroot, displayname, sizeof(displayname))) + Q_strncpyz(displayname, destname, sizeof(displayname)); + Con_Printf(CON_ERROR"Failed to write %s (extracting %s)\n", displayname, p->name); + } } } + else + Con_Printf(CON_ERROR"Failed to find %s in archive %s\n", srcname->name, tempname); } - if (!FS_NativePath(destname, p->fsroot, native, sizeof(native))) - Q_strncpyz(native, destname, sizeof(native)); - Con_Printf("Couldn't extract %s/%s to %s. Removed instead.\n", tempname, dep->name, native); + if (!FS_DisplayPath(destname, p->fsroot, displayname, sizeof(displayname))) + Q_strncpyz(displayname, destname, sizeof(displayname)); + Con_Printf(CON_ERROR"Couldn't extract %s/%s to %s. Removed instead.\n", tempname, dep->name, displayname); success = false; } else if (!FS_Rename2(tempname, destname, temproot, p->fsroot)) { //error! - if (!FS_NativePath(destname, p->fsroot, native, sizeof(native))) - Q_strncpyz(native, destname, sizeof(native)); - Con_Printf("Couldn't rename %s to %s. Removed instead.\n", tempname, native); + if (!FS_DisplayPath(destname, p->fsroot, displayname, sizeof(displayname))) + Q_strncpyz(displayname, destname, sizeof(displayname)); + Con_Printf(CON_ERROR"Couldn't rename %s to %s. Removed instead.\n", tempname, displayname); success = false; } else { //success! - if (!FS_NativePath(destname, p->fsroot, native, sizeof(native))) - Q_strncpyz(native, destname, sizeof(native)); - Con_Printf("Downloaded %s (to %s)\n", p->name, native); + if (!FS_DisplayPath(destname, p->fsroot, displayname, sizeof(displayname))) + Q_strncpyz(displayname, destname, sizeof(displayname)); + Con_Printf("Downloaded %s (to %s)\n", p->name, displayname); p->flags = nfl; @@ -3930,7 +4173,7 @@ static qboolean PM_SignatureOkay(package_t *p) //only allow .pak/.pk3/.zip without a signature, and only when they have a qhash specified (or the .fmf specified it without a qhash...). ext = COM_GetFileExtension(dep->name, NULL); - if ((!stricmp(ext, ".pak") || !stricmp(ext, ".pk3") || !stricmp(ext, ".zip")) && (p->qhash || dep->dtype == DEP_CACHEFILE || (p->flags&DPF_MANIFEST))) + if ((!stricmp(ext, ".kpf") || !stricmp(ext, ".pak") || !stricmp(ext, ".pk3") || !stricmp(ext, ".zip")) && (p->qhash || dep->dtype == DEP_CACHEFILE || (p->flags&DPF_MANIFEST))) ; else return false; @@ -4076,14 +4319,18 @@ static void PM_StartADownload(void) if (i == countof(p->mirror)) { //this appears to be a meta package with no download //just directly install it. - //FIXME: make sure there's no files... - p->flags &= ~(DPF_NATIVE|DPF_CACHED|DPF_CORRUPT); - if (p->flags & DPF_MARKED) - p->flags |= DPF_ENABLED; + if (PM_HasDep(p, DEP_FILE, NULL)) + Con_Printf("Unable to install package %s due to lack of any mirrors\n", p->name); + else + { + p->flags &= ~(DPF_NATIVE|DPF_CACHED|DPF_CORRUPT); + if (p->flags & DPF_MARKED) + p->flags |= DPF_ENABLED; - Con_Printf("Enabled meta package %s\n", p->name); - PM_WriteInstalledPackages(); - PM_PackageEnabled(p); + Con_Printf("Enabled meta package %s\n", p->name); + PM_WriteInstalledPackages(); + PM_PackageEnabled(p); + } } continue; } @@ -4168,8 +4415,8 @@ static void PM_StartADownload(void) } else { - char syspath[MAX_OSPATH]; - Con_Printf("Unable to write %s. Fix permissions before trying to download %s\n", FS_NativePath(temp, temproot, syspath, sizeof(syspath))?syspath:p->name, p->name); + char displaypath[MAX_OSPATH]; + Con_Printf("Unable to write %s. Fix permissions before trying to download %s\n", FS_DisplayPath(temp, temproot, displaypath, sizeof(displaypath))?displaypath:p->name, p->name); p->trymirrors = 0; //don't bother trying other mirrors if we can't write the file or understand its type. } if (p->download) @@ -4598,6 +4845,175 @@ qboolean PM_CanInstall(const char *packagename) return false; } +static void PM_Pkg_ListSources(qboolean onlyenabled) +{ + size_t i, c=0, e=0; + for (i = 0; i < pm_numsources; i++) + { + if ((pm_source[i].flags & SRCFL_HISTORIC) && !developer.ival) + continue; //hidden ones were historically enabled/disabled. remember the state even when using a different fmf, but don't confuse the user. + + c++; + if (pm_source[i].flags & SRCFL_ENABLED) + e++; + else if (onlyenabled) + continue; + + if (pm_source[i].flags & SRCFL_ENABLED) + Con_Printf("^&02 "); + else if (pm_source[i].flags & SRCFL_DISABLED) + Con_Printf("^&04 "); + else + Con_Printf("^&0E "); + + if (pm_source[i].flags & SRCFL_DISABLED) + Con_Printf("%s ", pm_source[i].url); //enable + else + Con_Printf("%s ", pm_source[i].url); //disable + + if (pm_source[i].flags & SRCFL_USER) + Con_Printf("- ^[[Delete]\\type\\pkg remsource \"%s\"^]\n", pm_source[i].url); + else + Con_Printf("(implicit)\n"); + } + if (c != e) + Con_Printf("<%u sources, %u enabled>\n", (unsigned)c, (unsigned)e); + else + Con_Printf("<%u sources>\n", (unsigned)c); +} +static void PM_Pkg_ListPackage(package_t *p, const char **category) +{ + const char *newcat; + char quoted[8192]; + const char *status; + char *markup; + struct packagedep_s *dep; + + if (p->flags & DPF_ENABLED) + markup = S_COLOR_GREEN; + else if (p->flags & DPF_CORRUPT) + markup = S_COLOR_RED; + else if (p->flags & (DPF_CACHED)) + markup = S_COLOR_YELLOW; //downloaded but not active + else + markup = S_COLOR_WHITE; + + if (!(p->flags & DPF_MARKED) != !(p->flags & DPF_ENABLED) || (p->flags & DPF_PURGE)) + { + if (p->flags & DPF_MARKED) + { + if (p->flags & DPF_PURGE) + status = S_COLOR_CYAN""; + else + status = S_COLOR_CYAN""; + } + else if ((p->flags & DPF_PURGE) || !(p->qhash && (p->flags & DPF_CACHED))) + status = S_COLOR_CYAN""; + else + status = S_COLOR_CYAN""; + } + else if ((p->flags & (DPF_ENABLED|DPF_CACHED)) == DPF_CACHED) + status = S_COLOR_CYAN""; + else if (p->flags & DPF_USERMARKED) + status = S_COLOR_GRAY""; + else if (p->flags & DPF_AUTOMARKED) + status = S_COLOR_GRAY""; + else + status = ""; + + //show category banner + newcat = p->category?p->category:""; + if (strcmp(*category, newcat)) + { + *category = newcat; + Con_Printf("%s\n", *category); + } + + //show quick status display + if (p->flags & DPF_ENABLED) + Con_Printf("^&02 "); + else if (p->flags & DPF_PRESENT) + Con_Printf("^&0E "); + else + Con_Printf("^&04 "); + if (p->flags & DPF_MARKED) + Con_Printf("^&02 "); + else if (!(p->flags & DPF_PURGE) && (p->flags&DPF_PRESENT)) + Con_Printf("^&0E "); + else + Con_Printf("^&04 "); + + //show the package details. + Con_Printf("\t^["S_COLOR_GRAY"%s%s%s%s^] %s"S_COLOR_GRAY" %s (%s%s)", markup, p->name, p->arch?":":"", p->arch?p->arch:"", status, strcmp(p->name, p->title)?p->title:"", p->version, (p->flags&DPF_TESTING)?"-testing":""); + + for (dep = p->deps; dep; dep = dep->next) + { + if (dep->dtype == DEP_SOURCE) + Con_Printf(S_COLOR_MAGENTA" %s", dep->name); + } + + if (!(p->flags&DPF_MARKED) && p == PM_FindPackage(p->name)) + Con_Printf(" ^[[Add]\\type\\pkg add %s;pkg apply^]", COM_QuotedString(p->name, quoted, sizeof(quoted), false)); + if ((p->flags&DPF_MARKED) && p == PM_MarkedPackage(p->name, DPF_MARKED)) + Con_Printf(" ^[[Remove]\\type\\pkg rem %s;pkg apply^]", COM_QuotedString(p->name, quoted, sizeof(quoted), false)); + + + if (p->flags & DPF_SIGNATUREACCEPTED) + Con_Printf(" ^&02Trusted"); + else if (p->flags & DPF_SIGNATUREREJECTED) + Con_Printf(" ^&04Untrusted"); + else if (p->flags & DPF_SIGNATUREUNKNOWN) + Con_Printf(" ^&0EUnverified"); + else + Con_Printf(" ^&0EUnsigned"); + + if (p->download) + { + if (p->download->totalsize==0) + Con_Printf(" Downloading %s/unknown", FS_AbbreviateSize(quoted,sizeof(quoted), p->download->completed)); + else + Con_Printf(" Downloading %i%% (%s total)", (int)(100*(double)p->download->completed/(double)p->download->totalsize), FS_AbbreviateSize(quoted,sizeof(quoted), p->download->totalsize)); + } + else if (p->trymirrors) + Con_Printf(" Pending..."); + + Con_Printf("\n"); +} +static void PM_Pkg_ListPackages(qboolean onlyenabled) +{ + const char *category = ""; + int i, count; + package_t **sorted, *p, *a; + + for (count = 0, p = availablepackages; p; p=p->next) + count++; + sorted = Z_Malloc(sizeof(*sorted)*count); + for (count = 0, p = availablepackages; p; p=p->next) + { + if ((p->flags & DPF_HIDDEN) && !(p->flags & (DPF_MARKED|DPF_ENABLED|DPF_PURGE|DPF_CACHED))) + continue; + + if (p->flags & DPF_ALLMARKED) + ; + else if (onlyenabled) + continue; + + sorted[count++] = p; + } + qsort(sorted, count, sizeof(*sorted), PM_PackageSortOrdering); + for (i = 0; i < count; i++) + { + PM_Pkg_ListPackage(sorted[i], &category); + for(a=sorted[i]->alternative; a; a = a->next) + { + Con_Printf(" "); + PM_Pkg_ListPackage(a, &category); + } + } + Z_Free(sorted); + Con_Printf("\n"); +} + void PM_Command_f(void) { package_t *p; @@ -4617,37 +5033,13 @@ void PM_Command_f(void) act += 6; } - if (!strcmp(act, "sources") || !strcmp(act, "addsource")) + if (!strcmp(act, "listenabledsources")) + PM_Pkg_ListSources(true); + else if (!strcmp(act, "sources") || !strcmp(act, "addsource")) { #ifdef WEBCLIENT if (Cmd_Argc() == 2) - { - size_t i, c=0; - for (i = 0; i < pm_numsources; i++) - { - if ((pm_source[i].flags & SRCFL_HISTORIC) && !developer.ival) - continue; //hidden ones were historically enabled/disabled. remember the state even when using a different fmf, but don't confuse the user. - - if (pm_source[i].flags & SRCFL_ENABLED) - Con_Printf("^&02 "); - else if (pm_source[i].flags & SRCFL_DISABLED) - Con_Printf("^&04 "); - else - Con_Printf("^&0E "); - - if (pm_source[i].flags & SRCFL_DISABLED) - Con_Printf("%s ", pm_source[i].url); //enable - else - Con_Printf("%s ", pm_source[i].url); //disable - - if (pm_source[i].flags & SRCFL_USER) - Con_Printf("- ^[[Delete]\\type\\pkg remsource \"%s\"^]\n", pm_source[i].url); - else - Con_Printf("(implicit)\n"); - c++; - } - Con_Printf("<%u sources>\n", (unsigned)c); - } + PM_Pkg_ListSources(false); else { #ifdef HAVE_CLIENT @@ -4661,123 +5053,23 @@ void PM_Command_f(void) } else if (!strcmp(act, "remsource")) { - #ifdef WEBCLIENT - PM_RemSubList(Cmd_Argv(2)); - PM_WriteInstalledPackages(); - #endif + #ifdef WEBCLIENT + PM_RemSubList(Cmd_Argv(2)); + PM_WriteInstalledPackages(); + #endif } else { - if (!loadedinstalled) PM_UpdatePackageList(false); if (!strcmp(act, "list")) { - int i, count; - package_t **sorted; - const char *category = "", *newcat; - struct packagedep_s *dep; - for (count = 0, p = availablepackages; p; p=p->next) - count++; - sorted = Z_Malloc(sizeof(*sorted)*count); - for (count = 0, p = availablepackages; p; p=p->next) - { - if ((p->flags & DPF_HIDDEN) && !(p->flags & (DPF_MARKED|DPF_ENABLED|DPF_PURGE|DPF_CACHED))) - continue; - sorted[count++] = p; - } - qsort(sorted, count, sizeof(*sorted), PM_PackageSortOrdering); - for (i = 0; i < count; i++) - { - char quoted[8192]; - const char *status; - char *markup; - p = sorted[i]; - - if (p->flags & DPF_ENABLED) - markup = S_COLOR_GREEN; - else if (p->flags & DPF_CORRUPT) - markup = S_COLOR_RED; - else if (p->flags & (DPF_CACHED)) - markup = S_COLOR_YELLOW; //downloaded but not active - else - markup = S_COLOR_WHITE; - - if (!(p->flags & DPF_MARKED) != !(p->flags & DPF_ENABLED) || (p->flags & DPF_PURGE)) - { - if (p->flags & DPF_MARKED) - { - if (p->flags & DPF_PURGE) - status = S_COLOR_CYAN""; - else - status = S_COLOR_CYAN""; - } - else if ((p->flags & DPF_PURGE) || !(p->qhash && (p->flags & DPF_CACHED))) - status = S_COLOR_CYAN""; - else - status = S_COLOR_CYAN""; - } - else if ((p->flags & (DPF_ENABLED|DPF_CACHED)) == DPF_CACHED) - status = S_COLOR_CYAN""; - else if (p->flags & DPF_USERMARKED) - status = S_COLOR_GRAY""; - else if (p->flags & DPF_AUTOMARKED) - status = S_COLOR_GRAY""; - else - status = ""; - - //show category banner - newcat = p->category?p->category:""; - if (strcmp(category, newcat)) - { - category = newcat; - Con_Printf("%s\n", category); - } - - //show quick status display - if (p->flags & DPF_ENABLED) - Con_Printf("^&02 "); - else if (p->flags & DPF_PRESENT) - Con_Printf("^&0E "); - else - Con_Printf("^&04 "); - if (p->flags & DPF_MARKED) - Con_Printf("^&02 "); - else if (!(p->flags & DPF_PURGE) && (p->flags&DPF_PRESENT)) - Con_Printf("^&0E "); - else - Con_Printf("^&04 "); - - //show the package details. - Con_Printf("\t^["S_COLOR_GRAY"%s%s%s%s^] %s"S_COLOR_GRAY" %s (%s%s)", markup, p->name, p->arch?":":"", p->arch?p->arch:"", status, strcmp(p->name, p->title)?p->title:"", p->version, (p->flags&DPF_TESTING)?"-testing":""); - - for (dep = p->deps; dep; dep = dep->next) - { - if (dep->dtype == DEP_SOURCE) - Con_Printf(S_COLOR_MAGENTA" %s", dep->name); - } - - if (!(p->flags&DPF_MARKED) && p == PM_FindPackage(p->name)) - Con_Printf(" ^[[Add]\\type\\pkg add %s;pkg apply^]", COM_QuotedString(p->name, quoted, sizeof(quoted), false)); - if ((p->flags&DPF_MARKED) && p == PM_MarkedPackage(p->name, DPF_MARKED)) - Con_Printf(" ^[[Remove]\\type\\pkg rem %s;pkg apply^]", COM_QuotedString(p->name, quoted, sizeof(quoted), false)); - - - if (p->flags & DPF_SIGNATUREACCEPTED) - Con_Printf(" ^&02Trusted"); - else if (p->flags & DPF_SIGNATUREREJECTED) - Con_Printf(" ^&04Untrusted"); - else if (p->flags & DPF_SIGNATUREUNKNOWN) - Con_Printf(" ^&0EUnverified"); - else - Con_Printf(" ^&0EUnsigned"); - - Con_Printf("\n"); - } - Z_Free(sorted); - Con_Printf("\n"); + PM_Pkg_ListSources(false); + PM_Pkg_ListPackages(false); } + else if (!strcmp(act, "listmarked")) + PM_Pkg_ListPackages(true); else if (!strcmp(act, "internal")) { char buf[65536]; @@ -4865,6 +5157,8 @@ void PM_Command_f(void) Con_Printf(" package is an engine update\n"); if (p->flags & DPF_TESTING) Con_Printf(S_COLOR_YELLOW" package is untested\n"); + if (p->flags & DPF_TRUSTED) + Con_Printf(" package is trusted\n"); #ifdef WEBCLIENT if (!PM_SignatureOkay(p)) { @@ -4925,7 +5219,12 @@ void PM_Command_f(void) for (i = 0; i < pm_numsources; i++) { if (!(pm_source[i].flags & SRCFL_ENABLED)) - continue; + { + if (isDedicated && !(pm_source[i].flags & SRCFL_DISABLED)) + pm_source[i].flags |= SRCFL_ONCE; + else + continue; + } pm_source[i].status = SRCSTAT_PENDING; } if (!allowphonehome) @@ -4954,7 +5253,8 @@ void PM_Command_f(void) p = PM_FindPackage(key); if (p) { - PM_MarkPackage(p, DPF_USERMARKED); + if (!PM_MarkPackage(p, DPF_USERMARKED)) + Con_Printf("%s: unable to activate/mark %s\n", Cmd_Argv(0), key); p->flags &= ~DPF_PURGE; } else @@ -5060,7 +5360,7 @@ qboolean PM_FindUpdatedEngine(char *syspath, size_t syspathsize) if (pfname && PM_CheckFile(pfname, p->fsroot)) { - if (FS_NativePath(pfname, p->fsroot, syspath, syspathsize)) + if (FS_SystemPath(pfname, p->fsroot, syspath, syspathsize)) { e = p; best = them; @@ -5115,7 +5415,7 @@ static const char *FS_RelativeURL(const char *base, const char *file, char *buff } //this function is called by the filesystem code to start downloading the packages listed by each manifest. -void PM_AddManifestPackages(ftemanifest_t *man) +void PM_AddManifestPackages(ftemanifest_t *man, qboolean mayapply) { package_t *p, *m; size_t i; @@ -5248,7 +5548,7 @@ void PM_AddManifestPackages(ftemanifest_t *man) } PM_AddDep(p, dtype, path); - PM_ValidateAuthenticity(p, VH_UNSUPPORTED); + PM_ValidateAuthenticity(p, (man->security == MANIFEST_SECURITY_INSTALLER)?VH_CORRECT:VH_UNSUPPORTED); if (p->flags & DPF_SIGNATUREACCEPTED) p->flags |= DPF_TRUSTED; //user confirmed it, engine trusts it, we're all okay with any exploits it may have... @@ -5267,7 +5567,8 @@ void PM_AddManifestPackages(ftemanifest_t *man) } PM_ResortPackages(); - PM_ApplyChanges(); + if (mayapply) + PM_ApplyChanges(); //we reverted changes earlier, so the only packages added should have been from here. } #ifdef HAVE_CLIENT @@ -5328,17 +5629,17 @@ void QCBUILTIN PF_cl_getpackagemanagerinfo(pubprogfuncs_t *prinst, struct global case GPMI_AVAILABLE: #ifdef WEBCLIENT if (PM_SignatureOkay(p) && !(p->flags&DPF_FORGETONUNINSTALL)) - RETURN_TSTRING("1"); + RETURN_SSTRING("1"); #endif break; case GPMI_INSTALLED: if (p->flags & DPF_CORRUPT) - RETURN_TSTRING("corrupt"); //some sort of error + RETURN_SSTRING("corrupt"); //some sort of error else if (p->flags & DPF_ENABLED) - RETURN_TSTRING("enabled"); //its there (and in use) + RETURN_SSTRING("enabled"); //its there (and in use) else if (p->flags & DPF_PRESENT) - RETURN_TSTRING("present"); //its there (but ignored) + RETURN_SSTRING("present"); //its there (but ignored) #ifdef WEBCLIENT else if (p->download) { //we're downloading it @@ -5348,7 +5649,7 @@ void QCBUILTIN PF_cl_getpackagemanagerinfo(pubprogfuncs_t *prinst, struct global RETURN_TSTRING(va("%i%%", (int)p->download->qdownload.percent)); //we're downloading it. } else if (p->trymirrors) - RETURN_TSTRING("pending"); //its queued. + RETURN_SSTRING("pending"); //its queued. #endif break; case GPMI_GAMEDIR: @@ -5359,20 +5660,44 @@ void QCBUILTIN PF_cl_getpackagemanagerinfo(pubprogfuncs_t *prinst, struct global if (p->flags & DPF_PURGE) { if (p->flags & DPF_MARKED) - RETURN_TSTRING("reinstall"); //user wants to install it + RETURN_SSTRING("reinstall"); //user wants to install it else - RETURN_TSTRING("purge"); //wiping it out + RETURN_SSTRING("purge"); //wiping it out } else if (p->flags & DPF_USERMARKED) - RETURN_TSTRING("user"); //user wants to install it + RETURN_SSTRING("user"); //user wants to install it else if (p->flags & (DPF_AUTOMARKED|DPF_MANIMARKED)) - RETURN_TSTRING("auto"); //enabled to satisfy a dependancy + RETURN_SSTRING("auto"); //enabled to satisfy a dependancy else if (p->flags & DPF_ENABLED) - RETURN_TSTRING("disable"); //change from enabled to cached. + RETURN_SSTRING("disable"); //change from enabled to cached. else if (p->flags & DPF_PRESENT) - RETURN_TSTRING("retain"); //keep it in cache + RETURN_SSTRING("retain"); //keep it in cache //else not installed and don't want it. break; + case GPMI_MAPS: + { + char buf[256]; + char *maps = NULL; + struct packagedep_s *d; + for (d = p->deps; d; d = d->next) + { + if (d->dtype == DEP_MAP) + { + *buf = ' '; + COM_QuotedString(d->name, buf+1, sizeof(buf)-1, false); + if (maps) + Z_StrCat(&maps, buf); + else + Z_StrCat(&maps, buf+1); + } + } + if (maps) + { //at least one map found. + RETURN_TSTRING(maps); + Z_Free(maps); + } + } + break; } return; } @@ -6032,16 +6357,10 @@ static int MD_AddItemsToDownloadMenu(emenu_t *m, int y, const char *pathprefix, head = NULL; if (p->filesize) { + char buf[32]; if (!head) head = ""; - if (p->filesize < 1024) - head = va("%s^asize:^U00A0^a%.4f bytes\n", head, (double)p->filesize); - else if (p->filesize < 1024*1024) - head = va("%s^asize:^U00A0^a%.4f KB\n", head, (p->filesize/(1024.0))); - else if (p->filesize < 1024*1024*1024) - head = va("%s^asize:^U00A0^a%.4f MB\n", head, (p->filesize/(1024.0*1024))); - else - head = va("%s^asize:^U00A0^a%.4f GB\n", head, (p->filesize/(1024.0*1024*1024))); + head = va("%s^asize:^U00A0^a%s\n", head, FS_AbbreviateSize(buf,sizeof(buf), p->filesize)); } for (dep = p->deps; dep; dep = dep->next) diff --git a/engine/client/m_items.c b/engine/client/m_items.c index 11ac18abe..67d32c1c6 100644 --- a/engine/client/m_items.c +++ b/engine/client/m_items.c @@ -480,7 +480,7 @@ static qboolean M_MouseMoved(emenu_t *menu) if (opt2->common.posy + opt2->common.height > maxy) maxy = opt2->common.posy + opt2->common.height; } - maxy -= vid.height-8; + maxy -= vid.height; framescroll += option->frame.frac * maxy; ypos -= option->frame.frac * maxy; } @@ -682,16 +682,16 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *men int maxy = option->frame.common.posy; option->frame.common.width = 16; option->frame.common.posx = vid.width - option->frame.common.width - xpos; - option->frame.common.height = vid.height-8-maxy - ypos; + option->frame.common.height = vid.height-maxy - ypos; for (opt2 = option->common.next; opt2; opt2 = opt2->common.next) { if (opt2->common.posy + opt2->common.height > maxy) maxy = opt2->common.posy + opt2->common.height; } - maxy -= vid.height-8; + maxy -= vid.height; framescrollheight = maxy; - if (maxy < 0) + if (maxy <= 0) { option->frame.mousedown = false; option->frame.frac = 0; @@ -892,7 +892,10 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *men static void MenuDraw(emenu_t *menu) { if (!menu->dontexpand) - menu->xpos = ((vid.width - 320)>>1); + { + menu->width = min(vid.width,320); + menu->xpos = ((vid.width - menu->width)>>1); + } if (menu->predraw) menu->predraw(menu); if (menu->selecteditem && menu->selecteditem->common.type == mt_text) @@ -1771,8 +1774,8 @@ void MC_CheckBox_Key(menucheck_t *option, emenu_t *menu, int key) else Cvar_SetValue(option->var, !option->var->value); } - S_LocalSound ("misc/menu2.wav"); } + S_LocalSound ("misc/menu2.wav"); } void MC_EditBox_Key(menuedit_t *edit, int key, unsigned int unicode) @@ -2401,10 +2404,15 @@ static int M_Main_AddExtraOptions(emenu_t *mainm, int y) #endif } if (Cmd_Exists("menu_mods")) - { - MC_AddConsoleCommandQBigFont(mainm, 72, y, localtext("Mods "), "menu_mods\n"); y += 20; - y += 20; - } + {MC_AddConsoleCommandQBigFont(mainm, 72, y, localtext("Mods "), "menu_mods\n"); y += 20;} + + if (Cmd_Exists("sys_openfile")) + {MC_AddConsoleCommandQBigFont(mainm, 72, y, localtext("Open File "), "sys_openfile\n"); y += 20;} + +#ifdef FTE_TARGET_WEB + if (Cmd_Exists("xr_toggle")) + {MC_AddConsoleCommandQBigFont(mainm, 72, y, localtext("Toggle WebXR "), "xr_toggle\n"); y += 20;} +#endif return y; } @@ -2716,8 +2724,10 @@ void M_Menu_Main_f (void) b = NULL; if (!b && !m_preset_chosen.ival) b = M_FindButton(mainm, "menu_options\n"); +#ifdef PACKAGEMANAGER if (!b && PM_AreSourcesNew(false)) b = M_FindButton(mainm, "menu_download\n"); +#endif if (b) { mainm->selecteditem = (menuoption_t*)b; diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index f21e8d5ae..75dc1d408 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -2958,7 +2958,7 @@ static void *QDECL capture_raw_begin (char *streamname, int videorate, int width else Q_strncpyz(ctx->videonameextension, "tga", sizeof(ctx->videonameextension)); - if (!FS_NativePath(va("%s", streamname), FS_GAMEONLY, ctx->videonameprefix, sizeof(ctx->videonameprefix))) + if (!FS_SystemPath(va("%s", streamname), FS_GAMEONLY, ctx->videonameprefix, sizeof(ctx->videonameprefix))) { Z_Free(ctx); return NULL; @@ -2970,8 +2970,7 @@ static void *QDECL capture_raw_begin (char *streamname, int videorate, int width } ctx->fsroot = FS_SYSTEM; - if (FS_NativePath(ctx->videonameprefix, ctx->fsroot, filename, sizeof(filename))) - FS_CreatePath(filename, ctx->fsroot); + FS_CreatePath(ctx->videonameprefix, ctx->fsroot); ctx->audio = NULL; if (*sndkhz) @@ -3012,7 +3011,7 @@ static void QDECL capture_raw_video (void *vctx, int frame, void *data, int stri { char base[MAX_QPATH]; Q_strncpyz(base, ctx->videonameprefix, sizeof(base)); - if (FS_NativePath(base, ctx->fsroot, filename, sizeof(filename))) + if (FS_SystemPath(base, ctx->fsroot, filename, sizeof(filename))) { quint64_t diskfree = 0; if (Sys_GetFreeDiskSpace(filename, &diskfree)) @@ -3111,7 +3110,7 @@ static void *QDECL capture_avi_begin (char *streamname, int videorate, int width COM_StripExtension(streamname, aviname, sizeof(aviname)); COM_DefaultExtension (aviname, ".avi", sizeof(aviname)); /*find the system location of that*/ - FS_NativePath(aviname, FS_GAMEONLY, nativepath, sizeof(nativepath)); + FS_SystemPath(aviname, FS_GAMEONLY, nativepath, sizeof(nativepath)); //wipe it. f = fopen(nativepath, "rb"); diff --git a/engine/client/m_native.c b/engine/client/m_native.c index 4627c787b..336508867 100644 --- a/engine/client/m_native.c +++ b/engine/client/m_native.c @@ -407,7 +407,7 @@ qboolean MN_Init(void) VFS_GETS, VFS_PRINTF, COM_EnumerateFiles, - FS_NativePath, + FS_SystemPath, // Drawing stuff MN_DrawSetClipArea, diff --git a/engine/client/m_options.c b/engine/client/m_options.c index e9f028638..56657dfc8 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -1376,7 +1376,10 @@ void M_Menu_Preset_f (void) emenu_t *menu; int y; menuoption_t *presetoption[7]; - extern cvar_t r_nolerp, sv_nqplayerphysics, r_loadlits; + extern cvar_t r_nolerp, r_loadlits; +#ifdef HAVE_SERVER + extern cvar_t sv_nqplayerphysics; +#endif #if defined(RTLIGHTS) && (defined(GLQUAKE) || defined(VKQUAKE)) extern cvar_t r_bloom, r_shadow_realtime_world_importlightentitiesfrommap; #endif @@ -2608,7 +2611,9 @@ static const char *mapoptions_h2[] = qboolean M_Apply_SP_Cheats_H2 (union menuoption_s *op,struct emenu_s *menu,int key) { +#ifdef HAVE_SERVER singleplayerh2info_t *info = menu->data; +#endif if (key != K_ENTER && key != K_KP_ENTER && key != K_GP_DIAMOND_CONFIRM && key != K_MOUSE1 && key != K_TOUCHTAP) return false; @@ -2642,21 +2647,20 @@ qboolean M_Apply_SP_Cheats_H2 (union menuoption_s *op,struct emenu_s *menu,int k void M_Menu_Singleplayer_Cheats_Hexen2 (void) { - static const char *skilloptions[] = - { - "Easy", - "Normal", - "Hard", - "Nightmare", - "None Set", - NULL - }; - singleplayerh2info_t *info; int cursorpositionY; - int currentmap; #ifdef HAVE_SERVER + int currentmap; int currentskill; + static const char *skilloptions[] = + { + "Easy", + "Normal", + "Hard", + "Nightmare", + "None Set", + NULL + }; extern cvar_t sv_gravity, sv_cheats, sv_maxspeed, skill; #endif int y; @@ -3382,6 +3386,7 @@ typedef struct char modelname[MAX_QPATH]; char skinname[MAX_QPATH]; + char animname[MAX_QPATH]; char shaderfile[MAX_QPATH]; char *shadertext; @@ -3498,12 +3503,14 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu skinfile_t *skin; texnums_t *texnums; qboolean boneanimsonly; + model_t *animmodel = NULL; if (R2D_Flush) R2D_Flush(); memset(&pv, 0, sizeof(pv)); + Alias_FlushCache(); //doesn't like us using stack... CL_DecayLights (); CL_ClearEntityLists(); V_ClearRefdef(&pv); @@ -3571,7 +3578,12 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu return; //panic! if (ent.model->type == mod_alias) //should we even bother with this here? + { + if (*mods->animname) + animmodel = Mod_ForName(mods->animname, MLV_WARN); + AngleVectorsMesh(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]); + } else AngleVectors(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]); VectorInverse(ent.axis[1]); @@ -3608,7 +3620,14 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu ent.framestate.g[FS_REG].frametime[0] = ent.framestate.g[FS_REG].frametime[1] = realtime - mods->framechangetime; ent.framestate.g[FS_REG].endbone = 0x7fffffff; if (*mods->skinname) + { ent.customskin = Mod_RegisterSkinFile(mods->skinname); //explicit .skin file to use + if (!ent.customskin) + { + Con_Printf(CON_WARNING"Named skinfile not loaded\n"); + *mods->skinname = 0; //don't spam. + } + } else { ent.customskin = Mod_RegisterSkinFile(va("%s_%i.skin", mods->modelname, ent.skinnum)); @@ -3700,6 +3719,18 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu } #endif + if (animmodel)// && Mod_GetNumBones(ent.model, false)==Mod_GetNumBones(animmodel, false)) + { + int numbones = Mod_GetNumBones(ent.model, false); + galiasbone_t *boneinfo = Mod_GetBoneInfo(ent.model, &numbones); + float *bonematrix = alloca(numbones*sizeof(*bonematrix)*12); + ent.framestate.bonecount = Mod_GetBoneRelations(animmodel, 0, numbones, boneinfo, &ent.framestate, bonematrix); + ent.framestate.bonestate = bonematrix; + ent.framestate.skeltype = SKEL_RELATIVE; + } + else + animmodel = ent.model; //not using it. sorry. warn? + if (mods->mode == MV_NORMALS) { @@ -3898,8 +3929,10 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu float duration = 0; qboolean loop = false; int act = -1; - if (!Mod_FrameInfoForNum(ent.model, mods->surfaceidx, mods->framegroup, &fname, &numframes, &duration, &loop, &act)) + if (!Mod_FrameInfoForNum(animmodel, mods->surfaceidx, mods->framegroup, &fname, &numframes, &duration, &loop, &act)) fname = "Unknown Sequence"; + if (animmodel != ent.model) + fname = va("^[%s^] %s", animmodel->name, fname); //tag it properly if its from our animmodel if (act != -1) Draw_FunString(0, y, va("Frame%i[%i]: %s (%i poses, %f of %f secs, %s)", mods->framegroup, act, fname, numframes, ent.framestate.g[FS_REG].frametime[0], duration, loop?"looped":"unlooped")); else @@ -4032,17 +4065,18 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu char *data = NULL; Draw_FunString(0, y, va("Events: ")); y+=8; - for (i = 0; Mod_GetModelEvent(ent.model, mods->framegroup, i, ×tamp, &code, &data); y+=8, i++) + for (i = 0; Mod_GetModelEvent(animmodel, mods->framegroup, i, ×tamp, &code, &data); y+=8, i++) { Draw_FunString(0, y, va("%i %f: %i %s", i, timestamp, code, data)); } - Draw_FunString(0, y, va("%f: ", Mod_GetFrameDuration(ent.model, 0, mods->framegroup))); + Draw_FunString(0, y, va("%f: ", Mod_GetFrameDuration(animmodel, 0, mods->framegroup))); } break; case MV_SHADER: { if (!mods->shadertext) { + char *cr; char *body = Shader_GetShaderBody(Mod_ShaderForSkin(ent.model, mods->surfaceidx, mods->skingroup, r_refdef.time, &texnums), mods->shaderfile, sizeof(mods->shaderfile)); if (!body) { @@ -4052,9 +4086,12 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu if (*mods->shaderfile) mods->shadertext = Z_StrDupf("\n\nPress space to view+edit the shader\n\n%s", body); else - mods->shadertext = Z_StrDupf("{%s",body); + mods->shadertext = Z_StrDupf("{ %s",body); + + while ((cr = strchr(mods->shadertext, '\r'))) + *cr = ' '; } - R_DrawTextField(r_refdef.grect.x, r_refdef.grect.y+16, r_refdef.grect.width, r_refdef.grect.height-16, mods->shadertext, CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, fs); + R_DrawTextField(r_refdef.grect.x, r_refdef.grect.y+24, r_refdef.grect.width, r_refdef.grect.height-16, mods->shadertext, CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, fs); //fixme: draw the shader's textures. } @@ -4333,6 +4370,12 @@ void M_Menu_ModelViewer_f(void) menucustom_t *c; emenu_t *menu; + if (!*Cmd_Argv(1)) + { + Con_Printf("modelviewer [SKINFILE] [ANIMATIONFILE]\n"); + return; + } + menu = M_CreateMenu(sizeof(*mv)); menu->menu.persist = true; mv = menu->data; @@ -4345,6 +4388,7 @@ void M_Menu_ModelViewer_f(void) mv->dist = 150; Q_strncpyz(mv->modelname, Cmd_Argv(1), sizeof(mv->modelname)); Q_strncpyz(mv->skinname, Cmd_Argv(2), sizeof(mv->skinname)); + Q_strncpyz(mv->animname, Cmd_Argv(3), sizeof(mv->animname)); mv->framechangetime = realtime; mv->skinchangetime = realtime; @@ -4406,42 +4450,57 @@ static void Mods_Draw(int x, int y, struct menucustom_s *c, struct emenu_s *m) { int i = c->dint; struct modlist_s *mod = Mods_GetMod(i); - c->common.width = vid.width - x - 16; if (!mod && !i) { float scale[] = {8,8}; - R_DrawTextField(0, y, vid.width, vid.height - y, + + m->height = vid.height; + + if (y==0) + { //just take the full screen. + y = m->ypos; + c->common.posy = 0; + c->common.height = m->height; + + m->dontexpand = true; + m->xpos = x = 0; + m->width = vid.width; + } + else + { //at least expand it. + c->common.height = m->height - c->common.posy; + } + + //take the full width of the menu + x = m->xpos; + c->common.posx = 0; + c->common.width = m->width; + + R_DrawTextField(x, y, c->common.width, c->common.height, va( "No games or mods known.\n" -#if defined(FTE_TARGET_WEB) - "Connection issue or bad server config.\n" +#ifdef FTE_TARGET_WEB + "Try providing packages/gamedirs via drag+drop.\n" + "%s", Cmd_Exists("sys_openfile")?"Or click to add a package\n":"" #else #ifndef ANDROID "You may need to use -basedir $PATHTOGAME on the commandline.\n" #endif "\nExpected data path:\n^a%s", com_gamepath #endif - ), CON_WHITEMASK, 0, font_console, scale); + ), CON_WHITEMASK, 0, font_default, scale); return; } + c->common.height = 8; + c->common.width = vid.width - x - 16; if (!mod) return; if (mod->manifest) - { - if (m->selecteditem == (menuoption_t*)c) - Draw_AltFunString(x, y, mod->manifest->formalname); - else - Draw_FunString(x, y, mod->manifest->formalname); - } + Draw_FunStringWidth(x, y, mod->manifest->formalname, c->common.width, 0, m->selecteditem == (menuoption_t*)c); else - { - if (m->selecteditem == (menuoption_t*)c) - Draw_AltFunString(x, y, mod->gamedir); - else - Draw_FunString(x, y, mod->gamedir); - } + Draw_FunStringWidth(x, y, mod->gamedir, c->common.width, 0, m->selecteditem == (menuoption_t*)c); } static qboolean Mods_Key(struct menucustom_s *c, struct emenu_s *m, int key, unsigned int unicode) { @@ -4450,7 +4509,11 @@ static qboolean Mods_Key(struct menucustom_s *c, struct emenu_s *m, int key, uns { qboolean wasgameless = !*FS_GetGamedir(false); if (!Mods_GetMod(c->dint)) + { + if (Cmd_Exists("sys_openfile")) + Cbuf_AddText("sys_openfile\n", RESTRICT_LOCAL); return false; + } M_RemoveMenu(m); Cbuf_AddText(va("\nfs_changegame %u\n", gameidx+1), RESTRICT_LOCAL); @@ -4471,6 +4534,7 @@ void M_Menu_Mods_f (void) menucustom_t *c; emenu_t *menu; size_t i; + int y; //FIXME: sort by mtime? @@ -4479,20 +4543,23 @@ void M_Menu_Mods_f (void) { MC_AddPicture(menu, 16, 4, 32, 144, "gfx/qplaque.lmp"); MC_AddCenterPicture(menu, 0, 24, "gfx/p_option.lmp"); + y = 32; } + else + y = 0; - MC_AddFrameStart(menu, 32); + MC_AddFrameStart(menu, y); for (i = 0; i<1 || Mods_GetMod(i); i++) { struct modlist_s *mod = Mods_GetMod(i); - c = MC_AddCustom(menu, 64, 32+i*8, menu->data, i, (mod&&mod->manifest)?mod->manifest->basedir:NULL); + c = MC_AddCustom(menu, 64, y+i*8, menu->data, i, (mod&&mod->manifest)?mod->manifest->basedir:NULL); // if (!menu->selecteditem) // menu->selecteditem = (menuoption_t*)c; c->common.height = 8; c->draw = Mods_Draw; c->key = Mods_Key; } - MC_AddFrameEnd(menu, 32); + MC_AddFrameEnd(menu, y); } #if 0 @@ -4532,7 +4599,7 @@ static qboolean Installer_Go(menuoption_t *opt, menu_t *menu, int key) #ifdef _WIN32 GetModuleFileNameW(NULL, exepath, sizeof(exepath)); - FS_NativePath(va("%s.exe", fs_manifest->installation), FS_ROOT, newexepath, sizeof(newexepath)); + FS_SystemPath(va("%s.exe", fs_manifest->installation), FS_ROOT, newexepath, sizeof(newexepath)); CopyFileW(exepath, newexepath, FALSE); // SetHookState(false); diff --git a/engine/client/m_script.c b/engine/client/m_script.c index 6356e7649..5200d87c1 100644 --- a/engine/client/m_script.c +++ b/engine/client/m_script.c @@ -20,7 +20,7 @@ void M_Script_Option (emenu_t *menu, char *optionvalue, qboolean isexplicit) Cbuf_AddText("wait\n", execlevel); - if (!*scriptname) + if (!scriptname || !*scriptname) { if (isexplicit) Cbuf_AddText(va("%s\n", optionvalue), execlevel); diff --git a/engine/client/m_single.c b/engine/client/m_single.c index 77801dff4..800d4a950 100644 --- a/engine/client/m_single.c +++ b/engine/client/m_single.c @@ -319,11 +319,10 @@ void M_Menu_Load_f (void) void M_Menu_SinglePlayer_f (void) { emenu_t *menu; -#ifndef CLIENTONLY +#ifdef HAVE_SERVER menubutton_t *b; mpic_t *p; static menuresel_t resel; -#endif #if MAX_SPLITS > 1 static const char *splitopts[] = @@ -344,15 +343,6 @@ void M_Menu_SinglePlayer_f (void) }; #endif -#ifdef CLIENTONLY - menu = M_CreateMenu(0); - - MC_AddWhiteText(menu, 84, 0, 12*8, "This build is unable", false); - MC_AddWhiteText(menu, 84, 0, 13*8, "to start a local game", false); - - MC_AddBox (menu, 60, 11*8, 25*8, 4*8); -#else - switch(M_GameType()) { #ifdef Q2CLIENT @@ -580,6 +570,14 @@ void M_Menu_SinglePlayer_f (void) menu->cursoritem = (menuoption_t*)MC_AddCursor(menu, &resel, 54, 32); } + +#else + menu = M_CreateMenu(0); + + MC_AddWhiteText(menu, 84, 0, 12*8, "This build is unable", false); + MC_AddWhiteText(menu, 84, 0, 13*8, "to start a local game", false); + + MC_AddBox (menu, 60, 11*8, 25*8, 4*8); #endif } @@ -626,9 +624,9 @@ static void M_DemoDraw(int x, int y, menucustom_t *control, emenu_t *menu) demoitem_t *item, *lostit; int ty; - char syspath[MAX_OSPATH]; - if (FS_NativePath(info->fs->path, (info->fs->fsroot==FS_GAME)?FS_GAMEONLY:info->fs->fsroot, syspath, sizeof(syspath))) - Draw_FunString(x, y-16, syspath); + char displaypath[MAX_OSPATH]; + if (FS_DisplayPath(info->fs->path, (info->fs->fsroot==FS_GAME)?FS_GAMEONLY:info->fs->fsroot, displaypath, sizeof(displaypath))) + Draw_FunString(x, y-16, displaypath); ty = vid.height-24; item = info->selected; @@ -1021,7 +1019,7 @@ static void ShowDemoMenu (emenu_t *menu, const char *path) { if (!strcmp(path, "../")) { - FS_NativePath("", FS_ROOT, info->fs->path, sizeof(info->fs->path)); + FS_SystemPath("", FS_ROOT, info->fs->path, sizeof(info->fs->path)); Q_strncatz(info->fs->path, "../", sizeof(info->fs->path)); info->fs->fsroot = FS_SYSTEM; while((s = strchr(info->fs->path, '\\'))) diff --git a/engine/client/menu.c b/engine/client/menu.c index bcce14c23..1ca48fba1 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -21,6 +21,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "winquake.h" #include "shader.h" #include "cl_master.h" +#ifdef FTE_TARGET_WEB +#include +#endif menu_t *topmenu; menu_t *promptmenu; @@ -1302,10 +1305,8 @@ void M_Menu_Quit_f (void) switch(mode) { case 0: -#ifndef FTE_TARGET_WEB CL_Disconnect (NULL); Sys_Quit (); -#endif break; case 2: Menu_Prompt (M_Menu_DoQuitSave, NULL, localtext("You have unsaved settings\nWould you like to\nsave them now?"), "Yes", "No", "Cancel", true); diff --git a/engine/client/menu.h b/engine/client/menu.h index f316e5931..806a32213 100644 --- a/engine/client/menu.h +++ b/engine/client/menu.h @@ -131,7 +131,12 @@ typedef enum PROMPT_NO = 1, PROMPT_CANCEL = -1, } promptbutton_t; +#ifdef HAVE_CLIENT void Menu_Prompt (void (*callback)(void *, promptbutton_t), void *ctx, const char *messages, const char *optionyes, const char *optionno, const char *optioncancel, qboolean highpri); +#define Menu_PromptOrPrint(messages,optioncancel,highpri) Menu_Prompt(NULL, NULL, messages, NULL, NULL, optioncancel, highpri) +#else +#define Menu_PromptOrPrint(messages,optioncancel,highpri) Con_Printf("%s", messages) +#endif #ifndef NOBUILTINMENUS diff --git a/engine/client/merged.h b/engine/client/merged.h index 257c8f766..7d8e75986 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -209,7 +209,7 @@ extern int Mod_TagNumForName (struct model_s *model, const char *name, int f void Mod_AddSingleSurface(struct entity_s *ent, int surfaceidx, shader_t *shader, int mode); int Mod_GetNumBones(struct model_s *model, qboolean allowtags); -int Mod_GetBoneRelations(struct model_s *model, int firstbone, int lastbone, framestate_t *fstate, float *result); +int Mod_GetBoneRelations(struct model_s *model, int firstbone, int lastbone, const struct galiasbone_s *boneinfo, const framestate_t *fstate, float *result); int Mod_GetBoneParent(struct model_s *model, int bonenum); struct galiasbone_s *Mod_GetBoneInfo(struct model_s *model, int *numbones); const char *Mod_GetBoneName(struct model_s *model, int bonenum); @@ -222,11 +222,6 @@ void Draw_FunStringWidthFont(struct font_s *font, float x, float y, const void * extern int r_regsequence; -#ifdef SERVERONLY -#define Mod_Q1LeafPVS Mod_LeafPVS -// qbyte *Mod_LeafPVS (struct mleaf_s *leaf, struct model_s *model, qbyte *buffer); -#endif - enum { TEX_NOTLOADED, @@ -314,13 +309,13 @@ struct pendingtextureinfo uploadfmt_t encoding; //PTI_* formats void *extrafree; //avoids some memcpys - int mipcount; + unsigned int mipcount; struct { void *data; size_t datasize; //ceil(width/blockwidth)*ceil(height/blockheight)*ceil(depth/blockdepth)*blocksize - except that blockdepth is always considered 1 for now. - int width; - int height; + unsigned int width; + unsigned int height; int depth; qboolean needfree; } mip[72]; //enough for a 4096 cubemap. or a really smegging big 2d texture... diff --git a/engine/client/modelgen.h b/engine/client/modelgen.h index 0cfd748ef..6f0dda863 100644 --- a/engine/client/modelgen.h +++ b/engine/client/modelgen.h @@ -53,7 +53,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. typedef enum {ST_SYNC=0, ST_RAND } synctype_t; #endif -typedef enum { ALIAS_SINGLE=0, ALIAS_GROUP, ALIAS_GROUP_SWAPPED=16777216 } aliasframetype_t; +typedef enum { ALIAS_SINGLE=0, ALIAS_GROUP, ALIAS_GROUP_SWAPPED=0x01000000 } aliasframetype_t; typedef enum { ALIAS_SKIN_SINGLE=0, ALIAS_SKIN_GROUP } aliasskintype_t; diff --git a/engine/client/net_master.c b/engine/client/net_master.c index 2dd686fd9..966b86194 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -169,10 +169,10 @@ static net_masterlist_t net_masterlist[] = { // {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "master.teamdamage.com:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "master.teamdamage.com"}, //Total conversions will need to define their own in defaults.cfg or whatever. - {MP_DPMASTER, CVARFC("net_masterextra1", "master.frag-net.com:27950 198.58.111.37:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: Eukara -// {MP_DPMASTER, CVARFC("net_masterextra1", ""/*"ghdigital.com:27950 207.55.114.154:27950"*/, CVAR_NOSAVE, Net_Masterlist_Callback)}, //(was 69.59.212.88) admin: LordHavoc - {MP_DPMASTER, CVARFC("net_masterextra2", "dpmaster.deathmask.net:27950 107.161.23.68:27950 [2604:180::4ac:98c1]:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: Willis - {MP_DPMASTER, CVARFC("net_masterextra3", "dpmaster.tchr.no:27950 92.62.40.73:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: tChr + {MP_DPMASTER, CVARFC("net_masterextra1", "master.frag-net.com:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: Eukara +// {MP_DPMASTER, CVARFC("net_masterextra1", ""/*"ghdigital.com:27950"*/, CVAR_NOSAVE, Net_Masterlist_Callback)}, //(was 69.59.212.88) admin: LordHavoc + {MP_DPMASTER, CVARFC("net_masterextra2", "dpmaster.deathmask.net:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: Willis + {MP_DPMASTER, CVARFC("net_masterextra3", "dpmaster.tchr.no:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: tChr #else {MP_DPMASTER, CVARFC("net_masterextra1", "", CVAR_NOSAVE, Net_Masterlist_Callback)}, {MP_DPMASTER, CVARFC("net_masterextra2", "", CVAR_NOSAVE, Net_Masterlist_Callback)}, @@ -376,8 +376,8 @@ static void SV_Master_SingleHeartbeat(net_masterlist_t *master) { COM_Parse(master->cv.string); Con_TPrintf (S_COLOR_GRAY"Sending heartbeat to %s (%s)\n", NET_AdrToString (adr, sizeof(adr), na), com_token); + master->announced = true; } - master->announced = true; } break; case NETERR_NOROUTE: @@ -387,8 +387,8 @@ static void SV_Master_SingleHeartbeat(net_masterlist_t *master) { COM_Parse(master->cv.string); Con_TPrintf (CON_WARNING"No route for heartbeat to %s (%s)\n", NET_AdrToString (adr, sizeof(adr), na), com_token); + master->announced = true; } - master->announced = true; } break; default: @@ -401,8 +401,8 @@ static void SV_Master_SingleHeartbeat(net_masterlist_t *master) { COM_Parse(master->cv.string); Con_TPrintf (CON_ERROR"Failed to send heartbeat to %s (%s)\n", NET_AdrToString (adr, sizeof(adr), na), com_token); + master->announced = true; } - master->announced = true; } break; } @@ -663,6 +663,17 @@ static void SV_Master_Add(int type, char *stringadr) { int i; + //don't do dupes... + for (i = 0; net_masterlist[i].cv.name; i++) + { + if (net_masterlist[i].protocol == type) + if (!strcmp(net_masterlist[i].cv.string, stringadr)) + { + svs.last_heartbeat = -99999; + return; + } + } + for (i = 0; net_masterlist[i].cv.name; i++) { if (net_masterlist[i].protocol != type) @@ -680,6 +691,7 @@ static void SV_Master_Add(int type, char *stringadr) } Cvar_Set(&net_masterlist[i].cv, stringadr); + Con_Printf(CON_WARNING"setting %s to \"%s\"\n", net_masterlist[i].cv.name, stringadr); svs.last_heartbeat = -99999; } @@ -693,6 +705,8 @@ static void SV_Master_ClearType(int type) { if (net_masterlist[i].cv.flags & CVAR_NOSAVE) continue; //ignore our extras + if (*net_masterlist[i].cv.string) + Con_Printf(CON_WARNING"clearing %s (was \"%s\")\n", net_masterlist[i].cv.name, net_masterlist[i].cv.string); Cvar_Set(&net_masterlist[i].cv, ""); } } @@ -733,7 +747,8 @@ static void SV_SetMaster_f (void) return; } - Cvar_Set(&sv_public, "1"); //go public. + if (sv_public.ival < 1) + Con_Printf(CON_WARNING"%s used on private server (sv_public is \"%s\")\n", Cmd_Argv(0), sv_public.string); if (!strcmp(Cmd_Argv(1), "default")) { for (i = 0; net_masterlist[i].cv.name; i++) @@ -741,8 +756,12 @@ static void SV_SetMaster_f (void) return; } - SV_Master_ClearType(MP_QUAKEWORLD); - for (i=1 ; i= '0' && *s <= '9')) && infostring) + { //if we don't have support for udp packets here, convert any raw address to an rtc:///udp/ADDRESS one instead, via this master's brokering services... hopefully. + brokerid = va("/udp/%s", s); + adr = brokeradr; + } +#endif else { if (!NET_StringToAdr2(s, 80, &adr, 1, &brokerid)) @@ -3752,6 +3778,7 @@ void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad) firstserver = last; } #else +void Master_QueryServer(serverinfo_t *server){} qboolean CL_QueryServers(void) { master_t *mast; diff --git a/engine/client/p_script.c b/engine/client/p_script.c index 13d615af3..f89ce6f38 100644 --- a/engine/client/p_script.c +++ b/engine/client/p_script.c @@ -2651,7 +2651,7 @@ static void P_ExportAllEffects_f(void) outf = FS_OpenVFS(fname, "wb", FS_GAMEONLY); if (!outf) { - FS_NativePath(fname, FS_GAMEONLY, effect, sizeof(effect)); + FS_DisplayPath(fname, FS_GAMEONLY, effect, sizeof(effect)); Con_TPrintf("Unable to open file %s\n", effect); return; } @@ -2679,7 +2679,7 @@ static void P_ExportAllEffects_f(void) } VFS_CLOSE(outf); - FS_NativePath(fname, FS_GAMEONLY, effect, sizeof(effect)); + FS_DisplayPath(fname, FS_GAMEONLY, effect, sizeof(effect)); Con_Printf("Written %s\n", effect); } #endif @@ -3398,7 +3398,7 @@ static void P_ConvertEffectInfo_f(void) outf = FS_OpenVFS(fname, "wb", FS_GAMEONLY); if (!outf) { - FS_NativePath(fname, FS_GAMEONLY, effect, sizeof(effect)); + FS_DisplayPath(fname, FS_GAMEONLY, effect, sizeof(effect)); Con_TPrintf("Unable to open file %s\n", effect); return; } @@ -3435,7 +3435,7 @@ static void P_ConvertEffectInfo_f(void) } VFS_CLOSE(outf); - FS_NativePath(fname, FS_GAMEONLY, effect, sizeof(effect)); + FS_DisplayPath(fname, FS_GAMEONLY, effect, sizeof(effect)); Con_Printf("Written %s\n", effect); } #endif @@ -3534,10 +3534,10 @@ static void PScript_Shutdown (void) Cmd_RemoveCommand("r_exportbuiltinparticles"); Cmd_RemoveCommand("r_importeffectinfo"); -#if _DEBUG +//#if _DEBUG Cmd_RemoveCommand("r_partinfo"); Cmd_RemoveCommand("r_beaminfo"); -#endif +//#endif pe_default = P_INVALID; pe_size2 = P_INVALID; @@ -3809,7 +3809,7 @@ static void QDECL R_ParticleDesc_Callback(struct cvar_s *var, char *oldvalue) if (failure) P_LoadParticleSet("high", true, true); - if (cls.state) + if (cls.state && cl.model_name[1]) { //per-map configs. because we can. memcpy(token, "map_", 4); diff --git a/engine/client/pr_clcmd.c b/engine/client/pr_clcmd.c index 9edaa5705..ce6d0d049 100644 --- a/engine/client/pr_clcmd.c +++ b/engine/client/pr_clcmd.c @@ -542,7 +542,6 @@ void QCBUILTIN PF_cl_playingdemo (pubprogfuncs_t *prinst, struct globalvars_s *p G_FLOAT(OFS_RETURN) = 0; break; case DPB_MVD: - case DPB_EZTV: G_FLOAT(OFS_RETURN) = 2; break; default: diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 22285a3d6..3c86f0fd2 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -221,15 +221,15 @@ static void CSQC_FindGlobals(qboolean nofuncs) #undef globalstring #undef globalfunction -#define ensurefloat(name) if (!csqcg.name) csqcg.name = &junk._float; -#define ensureint(name) if (!csqcg.name) csqcg.name = &junk._int; -#define ensurevector(name) if (!csqcg.name) csqcg.name = junk._vector; -#define ensureentity(name) if (!csqcg.name) csqcg.name = &junk.edict; +#define ensurefloat(name) do{if (!csqcg.name) csqcg.name = &junk._float;}while(0) +#define ensureint(name) do{if (!csqcg.name) csqcg.name = &junk._int; }while(0) +#define ensurevector(name) do{if (!csqcg.name) csqcg.name = junk._vector;}while(0) +#define ensureentity(name) do{if (!csqcg.name) csqcg.name = &junk.edict; }while(0) -#define ensureprivfloat(name) if (!csqcg.name) {static pvec_t f; csqcg.name = &f;} -#define ensureprivint(name) if (!csqcg.name) {static pint_t i; csqcg.name = &i;} -#define ensureprivvector(name) if (!csqcg.name) {static pvec3_t v; csqcg.name = v;} -#define ensurepriventity(name) if (!csqcg.name) {static pint_t e; csqcg.name = &e;} +#define ensureprivfloat(name) do{if (!csqcg.name) {static pvec_t f; csqcg.name = &f;}}while(0) +#define ensureprivint(name) do{if (!csqcg.name) {static pint_t i; csqcg.name = &i;}}while(0) +#define ensureprivvector(name) do{if (!csqcg.name) {static pvec3_t v; csqcg.name = v;}}while(0) +#define ensurepriventity(name) do{if (!csqcg.name) {static pint_t e; csqcg.name = &e;}}while(0) if (csqc_nogameaccess) { @@ -698,6 +698,8 @@ static int CS_FindModel(const char *name, int *free) } for (i = 1; i < MAX_PRECACHE_MODELS; i++) { + if (!cl.model_name[i]) + break; if (!strcmp(cl.model_name[i], name)) return i; } @@ -2733,44 +2735,47 @@ static void QCBUILTIN PF_R_RenderScene(pubprogfuncs_t *prinst, struct globalvars else scissored = false; - R_DrawNameTags(); -#ifdef RTLIGHTS - R_EditLights_DrawInfo(); -#endif - - if (r_refdef.drawsbar) + if (!vrui.enabled) //when we're using the vrui, this stuff needs to be part of the scene, drawn seperately for each eye { -#ifdef PLUGINS - Plug_SBar (r_refdef.playerview); -#else - if (Sbar_ShouldDraw(r_refdef.playerview)) - { - SCR_TileClear (sb_lines); - Sbar_Draw (r_refdef.playerview); - Sbar_DrawScoreboard (r_refdef.playerview); - } - else - SCR_TileClear (0); + R_DrawNameTags(); +#ifdef RTLIGHTS + R_EditLights_DrawInfo(); #endif - if (!Key_Dest_Has(kdm_menu|kdm_cwindows)) + if (r_refdef.drawsbar) { - if (cl.intermissionmode == IM_NQFINALE || cl.intermissionmode == IM_NQCUTSCENE || cl.intermissionmode == IM_H2FINALE) +#ifdef PLUGINS + Plug_SBar (r_refdef.playerview); +#else + if (Sbar_ShouldDraw(r_refdef.playerview)) { - SCR_CheckDrawCenterString (); + SCR_TileClear (sb_lines); + Sbar_Draw (r_refdef.playerview); + Sbar_DrawScoreboard (r_refdef.playerview); } - else if (cl.intermissionmode != IM_NONE) + else + SCR_TileClear (0); +#endif + + if (!Key_Dest_Has(kdm_menu|kdm_cwindows)) { - Sbar_IntermissionOverlay (r_refdef.playerview); + if (cl.intermissionmode == IM_NQFINALE || cl.intermissionmode == IM_NQCUTSCENE || cl.intermissionmode == IM_H2FINALE) + { + SCR_CheckDrawCenterString (); + } + else if (cl.intermissionmode != IM_NONE) + { + Sbar_IntermissionOverlay (r_refdef.playerview); + } } + + SCR_ShowPics_Draw(); } - SCR_ShowPics_Draw(); + if (r_refdef.drawcrosshair) + R2D_DrawCrosshair(); } - if (r_refdef.drawcrosshair) - R2D_DrawCrosshair(); - if (scissored) { if (R2D_Flush) @@ -3100,7 +3105,7 @@ static model_t *csqc_setmodel(pubprogfuncs_t *prinst, csqcedict_t *ent, int mode } else { - if (modelindex >= MAX_PRECACHE_MODELS) + if (modelindex >= MAX_PRECACHE_MODELS || !cl.model_name[modelindex]) return NULL; prinst->SetStringField(prinst, (void*)ent, &ent->v->model, cl.model_name[modelindex], true); model = cl.model_precache[modelindex]; @@ -3180,7 +3185,7 @@ static int PF_cs_PrecacheModel_Internal(pubprogfuncs_t *prinst, const char *mode for (i = 1; i < MAX_PRECACHE_MODELS; i++) //Make sure that the server specified model is loaded.. { - if (!*cl.model_name[i]) + if (!cl.model_name[i]) break; if (!strcmp(cl.model_name[i], modelname)) { @@ -3231,7 +3236,7 @@ static void QCBUILTIN PF_cs_getsoundindex (pubprogfuncs_t *prinst, struct global //look for the server's names first... for (i = 1; i < MAX_PRECACHE_SOUNDS; i++) { - if (!*cl.sound_name[i]) + if (!cl.sound_name[i]) break; if (!strcmp(cl.sound_name[i], s)) { @@ -3265,7 +3270,7 @@ static void QCBUILTIN PF_cs_ModelnameForIndex(pubprogfuncs_t *prinst, struct glo if (modelindex < 0 && (-modelindex) < MAX_CSMODELS) G_INT(OFS_RETURN) = (int)PR_SetString(prinst, cl.model_csqcname[-modelindex]); - else if (modelindex >= 0 && modelindex < MAX_PRECACHE_MODELS) + else if (modelindex >= 0 && modelindex < MAX_PRECACHE_MODELS && cl.model_name[modelindex]) G_INT(OFS_RETURN) = (int)PR_SetString(prinst, cl.model_name[modelindex]); else G_INT(OFS_RETURN) = 0; @@ -3275,7 +3280,7 @@ static void QCBUILTIN PF_cs_SoundnameForIndex(pubprogfuncs_t *prinst, struct glo int soundindex = G_FLOAT(OFS_PARM0); //FIXME: no private indexes. still useful for sending sound names from the ssqc via indexes. - if (soundindex >= 0 && soundindex < MAX_PRECACHE_SOUNDS) + if (soundindex >= 0 && soundindex < MAX_PRECACHE_SOUNDS && cl.sound_name[soundindex]) G_INT(OFS_RETURN) = (int)PR_SetString(prinst, cl.sound_name[soundindex]); else G_INT(OFS_RETURN) = 0; @@ -5616,6 +5621,8 @@ static void QCBUILTIN PF_cs_registercommand (pubprogfuncs_t *prinst, struct glob { const char *str = PR_GetStringOfs(prinst, OFS_PARM0); const char *desc = (prinst->callargc>1)?PR_GetStringOfs(prinst, OFS_PARM1):NULL; + if (desc && !*desc) + desc = NULL; if (!Cmd_Exists(str)) Cmd_AddCommandD(str, CS_ConsoleCommand_f, desc); } @@ -6026,7 +6033,7 @@ static void QCBUILTIN PF_DeltaListen(pubprogfuncs_t *prinst, struct globalvars_s { for (i = 1; i < MAX_PRECACHE_MODELS; i++) { - if (!*cl.model_name[i]) + if (!cl.model_name[i]) break; if (!strcmp(cl.model_name[i], mname)) { @@ -6648,13 +6655,13 @@ static void QCBUILTIN PF_resourcestatus(pubprogfuncs_t *prinst, struct globalvar if (idx < 0) { mod = cl.model_csqcprecache[-idx]; - if (!cl.model_csqcprecache[-idx] && doload) + if (!cl.model_csqcprecache[-idx] && doload && cl.model_csqcname[-idx]) mod = cl.model_csqcprecache[-idx] = Mod_ForName(Mod_FixName(cl.model_csqcname[-idx], cl.model_name[1]), MLV_WARN); } else if (idx > 0) { mod = cl.model_precache[idx]; - if (!cl.model_precache[idx] && doload) + if (!cl.model_precache[idx] && doload && cl.model_name[idx]) mod = cl.model_precache[idx] = Mod_ForName(Mod_FixName(cl.model_name[idx], cl.model_name[1]), MLV_WARN); } else @@ -7119,8 +7126,8 @@ static struct { {"processmodelevents", PF_processmodelevents, 0}, {"getnextmodelevent", PF_getnextmodelevent, 0}, {"getmodeleventidx", PF_getmodeleventidx, 0}, - {"getlocationname", PF_getlocationname, 0}, + {"getlocationname", PF_getlocationname, 0}, {"crossproduct", PF_crossproduct, 0}, {"pushmove", PF_pushmove, 0}, #ifdef TERRAIN @@ -7312,6 +7319,7 @@ static struct { {"memalloc", PF_memalloc, 384}, {"memfree", PF_memfree, 385}, + {"memcmp", PF_memcmp, 0}, {"memcpy", PF_memcpy, 386}, {"memfill8", PF_memfill8, 387}, {"memgetval", PF_memgetval, 388}, @@ -7854,6 +7862,24 @@ static void QDECL CSQC_World_GetFrameState(world_t *w, wedict_t *win, framestate cs_getframestate(in, in->xv->renderflags, out); } +static qboolean CSQC_GenerateMaterial(struct shaderparsestate_s *ps, const char *materialname, void (*LoadMaterialString)(struct shaderparsestate_s *ps, const char *script)) +{ + COM_AssertMainThread("CSQC_GenerateMaterial"); + if (csqcg.CSQC_GenerateMaterial) + { + void *pr_globals = PR_globals(csqcprogs, PR_CURRENT); + (((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, materialname)); + PR_ExecuteProgram(csqcprogs, csqcg.CSQC_GenerateMaterial); + if (G_INT(OFS_RETURN)) + { //we got the script, now pass it to our material system to parse it. + LoadMaterialString(ps, PR_GetStringOfs(csqcprogs, OFS_RETURN)); + return true; + } + } + return false; +} +static plugmaterialloaderfuncs_t csqcmaterialloader = {"csqc", CSQC_GenerateMaterial}; + void CSQC_Shutdown(void) { int i; @@ -7862,6 +7888,8 @@ void CSQC_Shutdown(void) if (csqcg.CSQC_Shutdown) PR_ExecuteProgram(csqcprogs, csqcg.CSQC_Shutdown); + Material_RegisterLoader(&csqc_world, NULL); + key_dest_absolutemouse &= ~kdm_game; CSQC_ForgetThreads(); PR_ReleaseFonts(kdm_game); @@ -7949,11 +7977,11 @@ static void *CSQC_FindMainProgs(size_t *sz, const char *name, unsigned int check if (!file) { - const char *progsname = InfoBuf_ValueForKey(&cl.serverinfo, "*csprogsname"); + const char *progsname = cls.state?InfoBuf_ValueForKey(&cl.serverinfo, "*csprogsname"):"csprogs.dat"; flocation_t loc={0}; vfsfile_t *f; qboolean found = false; - if (!found && *progsname && cls.state) + if (!found && *progsname) found = FS_FLocateFile(progsname, FSLF_IGNOREPURE, &loc); if (!found && strcmp(progsname, "csprogs.dat")) { @@ -7972,7 +8000,11 @@ static void *CSQC_FindMainProgs(size_t *sz, const char *name, unsigned int check if (checksum && !csprogs_promiscuous) { if (!CSQC_ValidateMainCSProgs(file, *sz, checksum, checksize)) - file = NULL; + { //its not a match... maybe we can get a better one from the pure paths instead... + file = COM_LoadTempFile (progsname, 0, sz); + if (!CSQC_ValidateMainCSProgs(file, *sz, checksum, checksize)) + file = NULL; + } //we write the csprogs into our archive if it was loaded from outside of there. //this is to ensure that demos will play on the same machine later on... @@ -8429,6 +8461,9 @@ qboolean CSQC_Init (qboolean anycsqc, const char *csprogsname, unsigned int chec csqcentsize = PR_InitEnts(csqcprogs, pr_csqc_maxedicts.value); + if (csqcg.CSQC_GenerateMaterial) + Material_RegisterLoader(&csqc_world, &csqcmaterialloader); + //world edict becomes readonly worldent = (csqcedict_t *)EDICT_NUM_PB(csqcprogs, 0); worldent->ereftype = ER_ENTITY; @@ -8455,7 +8490,7 @@ qboolean CSQC_Init (qboolean anycsqc, const char *csprogsname, unsigned int chec char *s = InfoBuf_ValueForKey(&cl.serverinfo, "map"); if (!*s) s = cl.model_name[1]; - if (!*s) + if (!s || !*s) s = "unknown"; *str = PR_NewString(csqcprogs, s); } diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 1e58159cb..4a8826ad9 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -1351,6 +1351,7 @@ static struct { evalc_t chain; evalc_t model; + evalc_t modelindex; evalc_t mins; evalc_t maxs; evalc_t origin; @@ -1364,6 +1365,7 @@ static struct evalc_t frame2time; evalc_t renderflags; evalc_t skinobject; + evalc_t skelobject; evalc_t colourmod; evalc_t alpha; } menuc_eval; @@ -2034,6 +2036,23 @@ static void QCBUILTIN PF_m_precache_model(pubprogfuncs_t *prinst, struct globalv const char *modelname = PR_GetStringOfs(prinst, OFS_PARM0); Mod_ForName(modelname, MLV_WARN); } +static model_t *QDECL MP_GetCModel(struct world_s *w, int modelindex) +{ + extern int mod_numknown; + modelindex--; + if (modelindex < 0 || modelindex >= mod_numknown) + return NULL; + return &mod_known[modelindex]; +} +static void QCBUILTIN PF_m_getmodelindex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + const char *modelname = PR_GetStringOfs(prinst, OFS_PARM0); + model_t *m = Mod_ForName(modelname, MLV_WARN); + if (m) + G_FLOAT(OFS_RETURN) = (m-mod_known)+1; + else + G_FLOAT(OFS_RETURN) = 0; +} static void QCBUILTIN PF_m_setmodel(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { menuedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0); @@ -2041,6 +2060,7 @@ static void QCBUILTIN PF_m_setmodel(pubprogfuncs_t *prinst, struct globalvars_s eval_t *modelval = prinst->GetEdictFieldValue(prinst, (void*)ent, "model", ev_string, &menuc_eval.model); eval_t *minsval = prinst->GetEdictFieldValue(prinst, (void*)ent, "mins", ev_vector, &menuc_eval.mins); eval_t *maxsval = prinst->GetEdictFieldValue(prinst, (void*)ent, "maxs", ev_vector, &menuc_eval.maxs); + eval_t *modelidxval = prinst->GetEdictFieldValue(prinst, (void*)ent, "modelindex", ev_float, &menuc_eval.modelindex); model_t *mod = Mod_ForName(modelname, MLV_WARN); if (modelval) modelval->string = G_INT(OFS_PARM1); //lets hope garbage collection is enough. @@ -2050,6 +2070,8 @@ static void QCBUILTIN PF_m_setmodel(pubprogfuncs_t *prinst, struct globalvars_s if (mod) while(mod->loadstate == MLS_LOADING) COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); + if (modelidxval) + modelidxval->_float = mod?(mod-mod_known)+1:0; if (mod && minsval) VectorCopy(mod->mins, minsval->_vector); @@ -2110,6 +2132,44 @@ static void QCBUILTIN PF_m_clearscene(pubprogfuncs_t *prinst, struct globalvars_ V_CalcRefdef(&menuview); //set up the defaults r_refdef.flags |= RDF_NOWORLDMODEL; } +static void QDECL MP_Read_FrameState(pubprogfuncs_t *prinst, wedict_t *ent, framestate_t *fstate) +{ + eval_t *frame1val = prinst->GetEdictFieldValue(prinst, (void*)ent, "frame", ev_float, &menuc_eval.frame1); + eval_t *frame2val = prinst->GetEdictFieldValue(prinst, (void*)ent, "frame2", ev_float, &menuc_eval.frame2); + eval_t *lerpfracval = prinst->GetEdictFieldValue(prinst, (void*)ent, "lerpfrac", ev_float, &menuc_eval.lerpfrac); + eval_t *frame1timeval = prinst->GetEdictFieldValue(prinst, (void*)ent, "frame1time", ev_float, &menuc_eval.frame1time); + eval_t *frame2timeval = prinst->GetEdictFieldValue(prinst, (void*)ent, "frame2time", ev_float, &menuc_eval.frame2time); + eval_t *skelobjectval = prinst->GetEdictFieldValue(prinst, (void*)ent, "skeletonindex", ev_float, &menuc_eval.skelobject); + + fstate->g[FST_BASE].endbone = 0; + fstate->g[FS_REG].endbone = 0x7fffffff; + fstate->g[FS_REG].frame[0] = frame1val?frame1val->_float:0; + fstate->g[FS_REG].frame[1] = frame2val?frame2val->_float:0; + fstate->g[FS_REG].lerpweight[1] = lerpfracval?lerpfracval->_float:0; + fstate->g[FS_REG].frametime[0] = frame1timeval?frame1timeval->_float:0; + fstate->g[FS_REG].frametime[1] = frame2timeval?frame2timeval->_float:0; + +#if FRAME_BLENDS >= 4 + fstate->g[FS_REG].frame[2] = fstate->g[FS_REG].frame[0]; + fstate->g[FS_REG].lerpweight[2] = 0; + fstate->g[FS_REG].frame[3] = fstate->g[FS_REG].frame[0]; + fstate->g[FS_REG].lerpweight[3] = 0; + fstate->g[FS_REG].lerpweight[0] = 1-(fstate->g[FS_REG].lerpweight[1]+fstate->g[FS_REG].lerpweight[2]+fstate->g[FS_REG].lerpweight[3]); +#else + fstate->g[FS_REG].lerpweight[0] = 1-fstate->g[FS_REG].lerpweight[1]; +#endif + +#if defined(SKELETALOBJECTS) || defined(RAGDOLL) + fstate->bonecount = 0; + fstate->bonestate = NULL; + if (skelobjectval && skelobjectval->_float) + skel_lookup(&menu_world, skelobjectval->_float, fstate); +#endif +} +static void QDECL MP_Get_FrameState(struct world_s *w, wedict_t *ent, framestate_t *fstate) +{ + MP_Read_FrameState(w->progs, ent, fstate); +} static qboolean CopyMenuEdictToEntity(pubprogfuncs_t *prinst, menuedict_t *in, entity_t *out) { eval_t *modelval = prinst->GetEdictFieldValue(prinst, (void*)in, "model", ev_string, &menuc_eval.model); @@ -2121,6 +2181,7 @@ static qboolean CopyMenuEdictToEntity(pubprogfuncs_t *prinst, menuedict_t *in, e eval_t *lerpfracval = prinst->GetEdictFieldValue(prinst, (void*)in, "lerpfrac", ev_float, &menuc_eval.lerpfrac); eval_t *frame1timeval = prinst->GetEdictFieldValue(prinst, (void*)in, "frame1time", ev_float, &menuc_eval.frame1time); eval_t *frame2timeval = prinst->GetEdictFieldValue(prinst, (void*)in, "frame2time", ev_float, &menuc_eval.frame2time); + eval_t *skelobjectval = prinst->GetEdictFieldValue(prinst, (void*)in, "skeletonindex", ev_float, &menuc_eval.skelobject); eval_t *colormapval = prinst->GetEdictFieldValue(prinst, (void*)in, "colormap", ev_float, &menuc_eval.colormap); eval_t *renderflagsval = prinst->GetEdictFieldValue(prinst, (void*)in, "renderflags", ev_float, &menuc_eval.renderflags); eval_t *skinobjectval = prinst->GetEdictFieldValue(prinst, (void*)in, "skinobject", ev_float, &menuc_eval.skinobject); @@ -2149,6 +2210,13 @@ static qboolean CopyMenuEdictToEntity(pubprogfuncs_t *prinst, menuedict_t *in, e out->framestate.g[FS_REG].frametime[0] = frame1timeval?frame1timeval->_float:0; out->framestate.g[FS_REG].frametime[1] = frame2timeval?frame2timeval->_float:0; +#if defined(SKELETALOBJECTS) || defined(RAGDOLL) + out->framestate.bonecount = 0; + out->framestate.bonestate = NULL; + if (skelobjectval && skelobjectval->_float) + skel_lookup(&menu_world, skelobjectval->_float, &out->framestate); +#endif + out->customskin = skinobjectval?skinobjectval->_float:0; //FIXME: colourmap @@ -2285,6 +2353,8 @@ static void QCBUILTIN PF_menu_registercommand (pubprogfuncs_t *prinst, struct gl { const char *str = PR_GetStringOfs(prinst, OFS_PARM0); const char *desc = (prinst->callargc>1)?PR_GetStringOfs(prinst, OFS_PARM1):NULL; + if (desc && !*desc) + desc = NULL; if (!Cmd_Exists(str)) Cmd_AddCommandD(str, MP_ConsoleCommand_f, desc); } @@ -2466,6 +2536,8 @@ static struct { {"precache_model", PF_m_precache_model, 91}, {"setorigin", PF_m_setorigin, 92}, //gap + {"getmodelindex", PF_m_getmodelindex, 200}, + //gap {"abort", PF_Abort, 211}, //gap {"strstrofs", PF_strstrofs, 221}, @@ -2484,6 +2556,29 @@ static struct { {"shaderforname", PF_shaderforname, 238}, {"sendpacket", PF_cl_SendPacket, 242}, //gap + {"skel_create", PF_skel_create, 263},//float(float modlindex) skel_create = #263; // (FTE_CSQC_SKELETONOBJECTS) + {"skel_build", PF_skel_build, 264},//float(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone, optional float addition) skel_build = #264; // (FTE_CSQC_SKELETONOBJECTS) + {"skel_build_ptr", PF_skel_build_ptr, 0},//float(float skel, int numblends, __variant *blends, int blendsize) skel_build_ptr = #0; + {"skel_get_numbones", PF_skel_get_numbones, 265},//float(float skel) skel_get_numbones = #265; // (FTE_CSQC_SKELETONOBJECTS) + {"skel_get_bonename", PF_skel_get_bonename, 266},//string(float skel, float bonenum) skel_get_bonename = #266; // (FTE_CSQC_SKELETONOBJECTS) (returns tempstring) + {"skel_get_boneparent", PF_skel_get_boneparent, 267},//float(float skel, float bonenum) skel_get_boneparent = #267; // (FTE_CSQC_SKELETONOBJECTS) + {"skel_find_bone", PF_skel_find_bone, 268},//float(float skel, string tagname) skel_get_boneidx = #268; // (FTE_CSQC_SKELETONOBJECTS) + {"skel_get_bonerel", PF_skel_get_bonerel, 269},//vector(float skel, float bonenum) skel_get_bonerel = #269; // (FTE_CSQC_SKELETONOBJECTS) (sets v_forward etc) + {"skel_get_boneabs", PF_skel_get_boneabs, 270},//vector(float skel, float bonenum) skel_get_boneabs = #270; // (FTE_CSQC_SKELETONOBJECTS) (sets v_forward etc) + {"skel_set_bone", PF_skel_set_bone, 271},//void(float skel, float bonenum, vector org) skel_set_bone = #271; // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc) + {"skel_premul_bone", PF_skel_premul_bone, 272},//void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc) + {"skel_premul_bones", PF_skel_premul_bones, 273},//void(float skel, float startbone, float endbone, vector org) skel_mul_bone = #273; // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc) + {"skel_postmul_bone", PF_skel_postmul_bone, 0},//void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc) +// {"skel_postmul_bones", PF_skel_postmul_bones, 0},//void(float skel, float startbone, float endbone, vector org) skel_mul_bone = #273; // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc) + {"skel_copybones", PF_skel_copybones, 274},//void(float skeldst, float skelsrc, float startbone, float entbone) skel_copybones = #274; // (FTE_CSQC_SKELETONOBJECTS) + {"skel_delete", PF_skel_delete, 275},//void(float skel) skel_delete = #275; // (FTE_CSQC_SKELETONOBJECTS) + {"frameforname", PF_frameforname, 276},//float(float modidx, string framename) frameforname = #276 (FTE_CSQC_SKELETONOBJECTS) + {"frameduration", PF_frameduration, 277},//float(float modidx, float framenum) frameduration = #277 (FTE_CSQC_SKELETONOBJECTS) + {"frameforaction", PF_frameforaction, 0},//float(float modidx, int actionid) frameforaction = #0 + {"processmodelevents", PF_processmodelevents, 0}, + {"getnextmodelevent", PF_getnextmodelevent, 0}, + {"getmodeleventidx", PF_getmodeleventidx, 0}, + //gap {"hash_createtab", PF_hash_createtab, 287}, {"hash_destroytab", PF_hash_destroytab, 288}, {"hash_add", PF_hash_add, 289}, @@ -2555,6 +2650,7 @@ static struct { //gap {"memalloc", PF_memalloc, 384}, {"memfree", PF_memfree, 385}, + {"memcmp", PF_memcmp, 0}, {"memcpy", PF_memcpy, 386}, {"memfill8", PF_memfill8, 387}, {"memgetval", PF_memgetval, 388}, @@ -3163,8 +3259,12 @@ qboolean MP_Init (void) menutime = Sys_DoubleTime(); if (!menu_world.progs) { + vec3_t fwd,rht,up; int mprogs; Con_DPrintf("Initializing menu.dat\n"); + menu_world.Get_CModel = MP_GetCModel; + menu_world.Get_FrameState = MP_Get_FrameState; + menu_world.progs = InitProgs(&menuprogparms); PR_Configure(menu_world.progs, PR_ReadBytesString(pr_menu_memsize.string), 1, pr_enable_profiling.ival); mprogs = PR_LoadProgs(menu_world.progs, "menu.dat"); @@ -3193,6 +3293,10 @@ qboolean MP_Init (void) *menu_world.g.time = Sys_DoubleTime(); menu_world.g.frametime = (float*)PR_FindGlobal(menu_world.progs, "frametime", 0, NULL); + menu_world.g.v_forward = (float*)PR_FindGlobal(menu_world.progs, "v_forward", 0, NULL); if (!menu_world.g.v_forward) menu_world.g.v_forward = fwd; + menu_world.g.v_right = (float*)PR_FindGlobal(menu_world.progs, "v_right", 0, NULL); if (!menu_world.g.v_right) menu_world.g.v_right = rht; + menu_world.g.v_up = (float*)PR_FindGlobal(menu_world.progs, "v_up", 0, NULL); if (!menu_world.g.v_up) menu_world.g.v_up = up; + menu_world.g.drawfont = (float*)PR_FindGlobal(menu_world.progs, "drawfont", 0, NULL); menu_world.g.drawfontscale = (float*)PR_FindGlobal(menu_world.progs, "drawfontscale", 0, NULL); diff --git a/engine/client/pr_skelobj.c b/engine/client/pr_skelobj.c index 5d6ac9d04..c446902a8 100644 --- a/engine/client/pr_skelobj.c +++ b/engine/client/pr_skelobj.c @@ -1625,7 +1625,7 @@ void rag_updatedeltaent(world_t *w, entity_t *ent, lerpents_t *le) sko->numanimated = 0; else if (sko->doll) sko->numanimated = sko->doll->numdefaultanimated; - Mod_GetBoneRelations(mod, 0, skorel.numbones, &ent->framestate, skorel.bonematrix); + Mod_GetBoneRelations(mod, 0, skorel.numbones, NULL, &ent->framestate, skorel.bonematrix); skorel.modelindex = sko->modelindex; skorel.model = sko->model; if (sko->numanimated || sko->doll != mod->dollinfo) @@ -1932,6 +1932,7 @@ void QCBUILTIN PF_skel_build(pubprogfuncs_t *prinst, struct globalvars_s *pr_glo framestate_t fstate; skelobject_t *skelobj; model_t *model; + galiasbone_t *boneinfo; //default to failure G_FLOAT(OFS_RETURN) = 0; @@ -1946,19 +1947,27 @@ void QCBUILTIN PF_skel_build(pubprogfuncs_t *prinst, struct globalvars_s *pr_glo fstate.bonecount = 0; fstate.bonestate = NULL; - numbones = Mod_GetNumBones(model, false); - if (!numbones) - { - return; //this isn't a skeletal model. - } - if (!skelidx) + { + numbones = Mod_GetNumBones(model, false); + if (!numbones) + { + return; //this isn't a skeletal model. + } skelobj = skel_create(w, numbones); + } else skelobj = skel_get(w, skelidx); if (!skelobj) return; //couldn't get one, ran out of memory or something? + if (skelobj->model) + boneinfo = Mod_GetBoneInfo(skelobj->model, &numbones); + else + boneinfo = NULL; + numbones = skelobj->numbones; + + if (lastbone < 0) lastbone = numbones; if (lastbone > numbones) @@ -1980,15 +1989,15 @@ void QCBUILTIN PF_skel_build(pubprogfuncs_t *prinst, struct globalvars_s *pr_glo if (retainfrac == 0) { - if (addition == 1) /*replace everything*/ - Mod_GetBoneRelations(model, firstbone, lastbone, &fstate, skelobj->bonematrix); - else if (addition == 0) /*wipe it*/ + if (addition == 0) /*wipe it*/ memset(skelobj->bonematrix + firstbone*12, 0, sizeof(float)*12*(lastbone-firstbone)); + else if (addition == 1) /*replace everything*/ + Mod_GetBoneRelations(model, firstbone, lastbone, boneinfo, &fstate, skelobj->bonematrix); else { //scale new float relationsbuf[MAX_BONES*12]; - Mod_GetBoneRelations(model, firstbone, lastbone, &fstate, relationsbuf); + Mod_GetBoneRelations(model, firstbone, lastbone, boneinfo, &fstate, relationsbuf); for (i = firstbone; i < lastbone; i++) { for (j = 0; j < 12; j++) @@ -1998,6 +2007,8 @@ void QCBUILTIN PF_skel_build(pubprogfuncs_t *prinst, struct globalvars_s *pr_glo } else { + float relationsbuf[MAX_BONES*12]; + if (retainfrac != 1) { //rescale the existing bones @@ -2007,22 +2018,19 @@ void QCBUILTIN PF_skel_build(pubprogfuncs_t *prinst, struct globalvars_s *pr_glo skelobj->bonematrix[i*12+j] *= retainfrac; } } + + //just add + Mod_GetBoneRelations(model, firstbone, lastbone, boneinfo, &fstate, relationsbuf); if (addition == 1) { - //just add - float relationsbuf[MAX_BONES*12]; - Mod_GetBoneRelations(model, firstbone, lastbone, &fstate, relationsbuf); for (i = firstbone; i < lastbone; i++) { for (j = 0; j < 12; j++) skelobj->bonematrix[i*12+j] += relationsbuf[i*12+j]; } } - else if (addition) + else { - //add+scale - float relationsbuf[MAX_BONES*12]; - Mod_GetBoneRelations(model, firstbone, lastbone, &fstate, relationsbuf); for (i = firstbone; i < lastbone; i++) { for (j = 0; j < 12; j++) @@ -2068,6 +2076,7 @@ void QCBUILTIN PF_skel_build_ptr(pubprogfuncs_t *prinst, struct globalvars_s *pr int numbones, firstbone, lastbone; model_t *model; qboolean noadd; + const galiasbone_t *boneinfo = NULL; //default to failure G_FLOAT(OFS_RETURN) = 0; @@ -2170,7 +2179,7 @@ void QCBUILTIN PF_skel_build_ptr(pubprogfuncs_t *prinst, struct globalvars_s *pr } } else if (blends->prescale == 0) //new data only. directly replace the existing data - Mod_GetBoneRelations(model, firstbone, lastbone, &fstate, skelobj->bonematrix); + Mod_GetBoneRelations(model, firstbone, lastbone, boneinfo, &fstate, skelobj->bonematrix); else { if (blends->prescale != 1) @@ -2183,7 +2192,7 @@ void QCBUILTIN PF_skel_build_ptr(pubprogfuncs_t *prinst, struct globalvars_s *pr } } - Mod_GetBoneRelations(model, firstbone, lastbone, &fstate, relationsbuf); + Mod_GetBoneRelations(model, firstbone, lastbone, boneinfo, &fstate, relationsbuf); for (i = firstbone; i < lastbone; i++) { for (j = 0; j < 12; j++) @@ -2671,10 +2680,10 @@ void QCBUILTIN PF_frametoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_g { world_t *w = prinst->parms->user; int modelindex = G_FLOAT(OFS_PARM0); - unsigned int skinnum = G_FLOAT(OFS_PARM1); + unsigned int animnum = G_FLOAT(OFS_PARM1); int surfaceidx = 0; model_t *mod = w->Get_CModel(w, modelindex); - const char *n = Mod_FrameNameForNum(mod, surfaceidx, skinnum); + const char *n = Mod_FrameNameForNum(mod, surfaceidx, animnum); if (n) RETURN_TSTRING(n); diff --git a/engine/client/quakedef.h b/engine/client/quakedef.h index 50feb7b03..91f02f550 100644 --- a/engine/client/quakedef.h +++ b/engine/client/quakedef.h @@ -208,7 +208,7 @@ extern "C" { #endif #if defined(Q3CLIENT) || defined(Q3SERVER) -#include "q3api.h" +#include "../common/q3api.h" #endif #ifdef __cplusplus diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index 788017f00..c3f8c8b1b 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -876,7 +876,7 @@ static void Surf_StoreLightmap_RGB(qbyte *dest, unsigned int *bl, int smax, int switch(lm->fmt) { default: - Sys_Error("Bad lightmap_fmt\n"); + Sys_Error("Surf_StoreLightmap_RGB: Bad format - %s\n", Image_FormatName(lm->fmt)); break; case PTI_A2BGR10: stride = (lm->width-smax)<<2; @@ -1492,7 +1492,7 @@ static void Surf_BuildLightMap (model_t *model, msurface_t *surf, int map, int s // add all the lightmaps if (src) { - if (model->fromgame == fg_quake3) + if (model->lightmaps.prebaked) Sys_Error("Surf_BuildLightMap: q3bsp"); switch(model->lightmaps.fmt) { @@ -1773,7 +1773,7 @@ static void Surf_BuildLightMap_Worker (model_t *wmodel, msurface_t *surf, int sh // add all the lightmaps if (src) { - if (wmodel->fromgame == fg_quake3) //rgb + if (wmodel->lightmaps.prebaked) //rgb { /*q3 lightmaps are meant to be pre-built this code is misguided, and ought never be executed anyway. @@ -1790,6 +1790,7 @@ static void Surf_BuildLightMap_Worker (model_t *wmodel, msurface_t *surf, int sh bl+=3; } } + Sys_Error("Surf_BuildLightMap_Worker: q3bsp"); } else switch(wmodel->lightmaps.fmt) { @@ -2055,7 +2056,11 @@ dynamic: #ifdef _DEBUG if ((unsigned)fa->lightmaptexturenums[0] >= numlightmaps) - Sys_Error("Invalid lightmap index\n"); + { + static float throttle; + Con_ThrottlePrintf(&throttle, 0, CON_WARNING"Invalid lightmap index\n"); + return; + } #endif @@ -2303,7 +2308,7 @@ void Surf_GenBrushBatches(batch_t **batches, entity_t *ent) // calculate dynamic lighting for bmodel if it's not an // instanced model - if (model->fromgame != fg_quake3 && model->fromgame != fg_doom3 && lightmap && !(webo_blocklightmapupdates&1)) + if (!model->lightmaps.prebaked && lightmap && !(webo_blocklightmapupdates&1)) { int k; @@ -3094,7 +3099,7 @@ void Surf_DrawWorld (void) gennew = true; //generate an initial one, if we can. else { - if (!gennew && currentmodel->fromgame != fg_quake3) + if (!gennew && !currentmodel->lightmaps.prebaked) { int i = cl_max_lightstyles; for (i = 0; i < cl_max_lightstyles; i++) @@ -3449,6 +3454,8 @@ uploadfmt_t Surf_NameToFormat(const char *nam) uploadfmt_t Surf_LightmapMode(model_t *model) { uploadfmt_t fmt = Surf_NameToFormat(r_lightmap_format.string); + if (model && model->lightmaps.prebaked && model->lightmaps.fmt!=LM_RGB8) + fmt = PTI_INVALID; //don't let them force it away if we can't support it. this sucks. if (!sh_config.texfmt[fmt]) { qboolean hdr = (vid.flags&VID_SRGBAWARE), rgb = false; @@ -3755,7 +3762,7 @@ void Surf_BuildModelLightmaps (model_t *m) R_BumpLightstyles(m->lightmaps.maxstyle); //should only really happen with lazy loading - if (m->submodelof && m->fromgame == fg_quake3) //FIXME: should be all bsp formats + if (m->submodelof && m->lightmaps.prebaked) //FIXME: should be all bsp formats { if (m->submodelof->loadstate != MLS_LOADED) return; @@ -3763,7 +3770,7 @@ void Surf_BuildModelLightmaps (model_t *m) } else { - if (!m->lightdata && m->lightmaps.count && m->fromgame == fg_quake3) + if (!m->lightdata && m->lightmaps.count && m->lightmaps.prebaked) { char pattern[MAX_QPATH]; COM_StripAllExtensions(m->name, pattern, sizeof(pattern)); @@ -3787,7 +3794,7 @@ void Surf_BuildModelLightmaps (model_t *m) } } - if (m->fromgame == fg_quake3) + if (m->lightmaps.prebaked) { int j; unsigned char *src, *stop; @@ -3817,24 +3824,38 @@ void Surf_BuildModelLightmaps (model_t *m) if (!m->submodelof) for (i = 0; i < m->lightmaps.count; i++) { - if (lightmap[newfirst+i]->external) + if (lightmap[newfirst+i]->external || !m->lightdata) continue; - dst = lightmap[newfirst+i]->lightmaps; - src = m->lightdata + i*m->lightmaps.width*m->lightmaps.height*3; - stop = m->lightdata + (i+1)*m->lightmaps.width*m->lightmaps.height*3; - if (stop-m->lightdata > m->lightdatasize) - stop = m->lightdata + m->lightdatasize; - if (m->lightdata) + if (lightmap[newfirst+i]->fmt == m->lightmaps.prebaked) { + unsigned int bb,bw,bh,bd; + Image_BlockSizeForEncoding(m->lightmaps.prebaked, &bb,&bw,&bh,&bd); + + dst = lightmap[newfirst+i]->lightmaps; + src = m->lightdata + i*m->lightmaps.width*m->lightmaps.height*bb; + stop = m->lightdata + (i+1)*m->lightmaps.width*m->lightmaps.height*bb; + if (stop-m->lightdata > m->lightdatasize) + stop = m->lightdata + m->lightdatasize; + memcpy(dst, src, stop-src); + } + //FIXME: replace with Image_ChangeFormat here. but the data may be partial for the last mip. + else switch(m->lightmaps.fmt) + { + case LM_RGB8: + dst = lightmap[newfirst+i]->lightmaps; + src = m->lightdata + i*m->lightmaps.width*m->lightmaps.height*3; + stop = m->lightdata + (i+1)*m->lightmaps.width*m->lightmaps.height*3; + if (stop-m->lightdata > m->lightdatasize) + stop = m->lightdata + m->lightdatasize; switch(lightmap[newfirst+i]->fmt) { default: - Sys_Error("Bad lightmap_fmt\n"); + Sys_Error("Surf_BuildModelLightmaps: Bad format - %s\n", Image_FormatName(lightmap[newfirst+i]->fmt)); break; case PTI_A2BGR10: for (; src < stop; dst += 4, src += 3) - *(unsigned int*)dst = (0x3<<30) | (src[2]<<22) | (src[1]<<12) | (src[0]<<2); + *(unsigned int*)dst = (0x3u<<30) | (src[2]<<22) | (src[1]<<12) | (src[0]<<2); break; case PTI_E5BGR9: for (; src < stop; dst += 4, src += 3) @@ -3850,7 +3871,8 @@ void Surf_BuildModelLightmaps (model_t *m) dst[3] = 255; } break; - /*case TF_RGBA32: + case PTI_RGBA8: + case PTI_RGBX8: for (; src < stop; dst += 4, src += 3) { dst[0] = src[0]; @@ -3859,15 +3881,15 @@ void Surf_BuildModelLightmaps (model_t *m) dst[3] = 255; } break; - case TF_BGR24: + case PTI_BGR8: for (; src < stop; dst += 3, src += 3) { dst[0] = src[2]; dst[1] = src[1]; dst[2] = src[0]; } - break;*/ - case TF_RGB24: + break; + case PTI_RGB8: for (; src < stop; dst += 3, src += 3) { dst[0] = src[0]; @@ -3886,6 +3908,23 @@ void Surf_BuildModelLightmaps (model_t *m) } break; } + break; + + case LM_E5BGR9: + dst = lightmap[newfirst+i]->lightmaps; + src = m->lightdata + i*m->lightmaps.width*m->lightmaps.height*4; + stop = m->lightdata + (i+1)*m->lightmaps.width*m->lightmaps.height*4; + if (stop-m->lightdata > m->lightdatasize) + stop = m->lightdata + m->lightdatasize; + + if (lightmap[newfirst+i]->fmt == PTI_E5BGR9) + memcpy(dst, src, stop-src); + else //this can happen on older gpus... + Con_Printf(CON_WARNING"Unsupported lightmap format. set ^[/r_lightmap_format e5bgr9^]\n"); + break; + default: + Con_Printf(CON_WARNING"Unsupported input lightmap format\n"); + break; } } } @@ -4010,7 +4049,7 @@ void Surf_NewMap (model_t *worldmodel) //evil haxx r_dynamic.ival = r_dynamic.value; - if (r_dynamic.ival > 0 && (!cl.worldmodel || cl.worldmodel->fromgame == fg_quake3)) //quake3 has no lightmaps, disable r_dynamic + if (r_dynamic.ival > 0 && (!cl.worldmodel || cl.worldmodel->lightmaps.prebaked)) //quake3 has no lightmaps, disable r_dynamic r_dynamic.ival = 0; memset (&r_worldentity, 0, sizeof(r_worldentity)); diff --git a/engine/client/render.h b/engine/client/render.h index c2ea71b8d..a81f1b93e 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -586,7 +586,7 @@ struct llightinfo_s; void LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *threadctx, lightstyleindex_t surf_styles[MAXCPULIGHTMAPS], unsigned int *surf_expsamples, qbyte *surf_rgbsamples, qbyte *surf_deluxesamples, vec4_t surf_plane, vec4_t surf_texplanes[2], vec2_t exactmins, vec2_t exactmaxs, int texmins[2], int texsize[2], float lmscale); //special version that doesn't know what a face is or anything. struct relight_ctx_s *LightStartup(struct relight_ctx_s *ctx, struct model_s *model, qboolean shadows, qboolean skiplit); void LightReloadEntities(struct relight_ctx_s *ctx, const char *entstring, qboolean ignorestyles); -void LightShutdown(struct relight_ctx_s *ctx); +void LightShutdown(struct relight_ctx_s *ctx, struct model_s *model); extern const size_t lightthreadctxsize; qboolean RelightSetup (struct model_s *model, size_t lightsamples, qboolean generatelit); @@ -699,9 +699,7 @@ extern qboolean r_softwarebanding; extern cvar_t r_lightprepass_cvar; extern int r_lightprepass; //0=off,1=16bit,2=32bit -#ifdef R_XFLIP extern cvar_t r_xflip; -#endif extern cvar_t gl_mindist, gl_maxdist; extern cvar_t r_clear; diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 7cb57a97f..a3ab6e72b 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -161,8 +161,9 @@ cvar_t r_fb_bmodels = CVARAFD("r_fb_bmodels", "1", "gl_fb_bmodels", CVAR_SEMICHEAT|CVAR_RENDERERLATCH, "Enables loading lumas on the map, as well as any external bsp models."); cvar_t r_fb_models = CVARAFD ("r_fb_models", "1", "gl_fb_models", CVAR_SEMICHEAT, "Enables the use of lumas on models. Note that if ruleset_allow_fbmodels is enabled, then all models are unconditionally fullbright in deathmatch, because cheaters would set up their models like that anyway, hurrah for beating them at their own game. QuakeWorld players suck."); -cvar_t r_skin_overlays = CVARF ("r_skin_overlays", "1", - CVAR_SEMICHEAT|CVAR_RENDERERLATCH); +cvar_t gl_overbright_models = CVARFD("gl_overbright_models", "0", CVAR_SEMICHEAT|CVAR_ARCHIVE, "Doubles the brightness of models, to match QuakeSpasm's misfeature of the same name."); +//cvar_t r_skin_overlays = CVARF ("r_skin_overlays", "1", +// CVAR_SEMICHEAT|CVAR_RENDERERLATCH); cvar_t r_globalskin_first = CVARFD ("r_globalskin_first", "100", CVAR_RENDERERLATCH, "Specifies the first .skin value that is a global skin. Entities within this range will use the shader/image called 'gfx/skinSKIN.lmp' instead of their regular skin. See also: r_globalskin_count."); cvar_t r_globalskin_count = CVARFD ("r_globalskin_count", "10", CVAR_RENDERERLATCH, "Specifies how many globalskins there are."); cvar_t r_coronas = CVARFD ("r_coronas", "0", CVAR_ARCHIVE, "Draw coronas on realtime lights. Overrides glquake-esque flashblends."); @@ -323,6 +324,8 @@ cvar_t r_stereo_separation = CVARD("r_stereo_separation", "4", "How far apar cvar_t r_stereo_convergence = CVARD("r_stereo_convergence", "0", "Nudges the angle of each eye inwards when using stereoscopic rendering."); cvar_t r_stereo_method = CVARFD("r_stereo_method", "0", CVAR_ARCHIVE, "Value 0 = Off.\nValue 1 = Attempt hardware acceleration. Requires vid_restart.\nValue 2 = red/cyan.\nValue 3 = red/blue.\nValue 4=red/green.\nValue 5=eye strain."); +cvar_t r_xflip = CVAR("leftisright", "0"); + extern cvar_t r_dodgytgafiles; extern cvar_t r_dodgypcxfiles; extern cvar_t r_keepimages; @@ -581,10 +584,6 @@ void GLRenderer_Init(void) Cvar_Register (&gl_smoothcrosshair, GRAPHICALNICETIES); -#ifdef R_XFLIP - Cvar_Register (&r_xflip, GLRENDEREROPTIONS); -#endif - // Cvar_Register (&gl_lightmapmode, GLRENDEREROPTIONS); Cvar_Register (&gl_picmip, GLRENDEREROPTIONS); @@ -943,6 +942,7 @@ void Renderer_Init(void) Cvar_Register (&scr_allowsnap, SCREENOPTIONS); Cvar_Register (&scr_consize, SCREENOPTIONS); Cvar_Register (&scr_centersbar, SCREENOPTIONS); + Cvar_Register (&r_xflip, GLRENDEREROPTIONS); Cvar_Register(&r_bloodstains, GRAPHICALNICETIES); @@ -1002,8 +1002,9 @@ void Renderer_Init(void) Cvar_Register (&r_fb_bmodels, GRAPHICALNICETIES); Cvar_Register (&r_fb_models, GRAPHICALNICETIES); + Cvar_Register (&gl_overbright_models, GRAPHICALNICETIES); // Cvar_Register (&r_fullbrights, GRAPHICALNICETIES); //dpcompat: 1 if r_fb_bmodels&&r_fb_models - Cvar_Register (&r_skin_overlays, GRAPHICALNICETIES); +// Cvar_Register (&r_skin_overlays, GRAPHICALNICETIES); Cvar_Register (&r_globalskin_first, GRAPHICALNICETIES); Cvar_Register (&r_globalskin_count, GRAPHICALNICETIES); Cvar_Register (&r_shadows, GRAPHICALNICETIES); @@ -1824,7 +1825,7 @@ TRACE(("dbg: R_ApplyRenderer: starting on client state\n")); TRACE(("dbg: R_ApplyRenderer: reloading ALL models\n")); for (i=1 ; ispectator && (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV)) + if (pv->spectator && cls.demoplayback == DPB_MVD) { int t = pv->cam_spec_track; if (t >= 0 && CAM_ISLOCKED(pv) && cl.players[t].statsf[STAT_HEALTH] <= 0) @@ -3544,14 +3544,14 @@ ping time frags name sprintf(num, S_COLOR_WHITE"%4i", p); \ else \ sprintf(num, S_COLOR_GREEN"%4i", p); \ - Draw_FunStringWidth(x, y, num, 4*8, false, false); \ + Draw_FunStringWidth(x, y, num, 4*8+4, false, highlight); \ },NOFILL) #define COLUMN_PL COLUMN(pl, 2*8, \ { \ int p = s->pl; \ sprintf(num, "%2i", p); \ - Draw_FunStringWidth(x, y, num, 2*8, false, false); \ + Draw_FunStringWidth(x, y, num, 2*8+4, false, highlight); \ },NOFILL) #define COLUMN_TIME COLUMN(time, 4*8, \ { \ @@ -3563,14 +3563,14 @@ ping time frags name minutes = (int)total/60; \ sprintf (num, "%4i", minutes); \ } \ - Draw_FunStringWidth(x, y, num, 4*8, false, false); \ + Draw_FunStringWidth(x, y, num, 4*8+4, false, highlight); \ },NOFILL) #define COLUMN_FRAGS COLUMN(frags, 5*8, \ { \ int cx; int cy; \ if (s->spectator && s->spectator != 2) \ { \ - Draw_FunStringWidth(x, y, "spectator", 5*8, false, false); \ + Draw_FunStringWidth(x, y, "spectator", 5*8+4, false, false); \ } \ else \ { \ @@ -3607,7 +3607,7 @@ ping time frags name { \ if (!s->spectator) \ { \ - Draw_FunStringWidth(x, y, s->team, 4*8, false, false); \ + Draw_FunStringWidth(x, y, s->team, 4*8+4, false, highlight); \ } \ },NOFILL) #define COLUMN_STAT(title, width, code, fill) COLUMN(title, width, { \ @@ -3616,13 +3616,13 @@ ping time frags name code \ } \ }, fill) -#define COLUMN_RULESET COLUMN(ruleset, 8*8, {Draw_FunStringWidth(x, y, s->ruleset, 8*8, false, false);},NOFILL) -#define COLUMN_NAME COLUMN(name, namesize, {Draw_FunStringWidth(x, y, s->name, namesize, false, false);},NOFILL) -#define COLUMN_KILLS COLUMN_STAT(kils, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetKills(k)), 4*8, false, false);},NOFILL) -#define COLUMN_TKILLS COLUMN_STAT(tkil, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetTKills(k)), 4*8, false, false);},NOFILL) -#define COLUMN_DEATHS COLUMN_STAT(dths, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetDeaths(k)), 4*8, false, false);},NOFILL) -#define COLUMN_TOUCHES COLUMN_STAT(tchs, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetTouches(k)), 4*8, false, false);},NOFILL) -#define COLUMN_CAPS COLUMN_STAT(caps, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetCaptures(k)), 4*8, false, false);},NOFILL) +#define COLUMN_RULESET COLUMN(ruleset, 8*8, {Draw_FunStringWidth(x, y, s->ruleset, 8*8+4, false, false);},NOFILL) +#define COLUMN_NAME COLUMN(name, namesize, {Draw_FunStringWidth(x, y, s->name, namesize, false, highlight);},NOFILL) +#define COLUMN_KILLS COLUMN_STAT(kils, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetKills(k)), 4*8+4, false, false);},NOFILL) +#define COLUMN_TKILLS COLUMN_STAT(tkil, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetTKills(k)), 4*8+4, false, false);},NOFILL) +#define COLUMN_DEATHS COLUMN_STAT(dths, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetDeaths(k)), 4*8+4, false, false);},NOFILL) +#define COLUMN_TOUCHES COLUMN_STAT(tchs, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetTouches(k)), 4*8+4, false, false);},NOFILL) +#define COLUMN_CAPS COLUMN_STAT(caps, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetCaptures(k)), 4*8+4, false, false);},NOFILL) #define COLUMN_AFK COLUMN(afk, 0, {int cs = atoi(InfoBuf_ValueForKey(&s->userinfo, "chat")); if (cs)Draw_FunStringWidth(x+4, y, (cs&2)?"afk":"msg", 4*8, false, false);},NOFILL) @@ -3658,12 +3658,13 @@ void Sbar_DeathmatchOverlay (playerview_t *pv, int start) int pages; int linesperpage, firstline, lastline; + int highlight; if (!pv) return; // request new ping times every two second - if (realtime - cl.last_ping_request > 2 && cls.demoplayback != DPB_EZTV) + if (realtime - cl.last_ping_request > 2 && !cls.demoplayback) { if (cls.protocol == CP_QUAKEWORLD) { @@ -3944,6 +3945,18 @@ if (showcolumns & (1<cam_state == CAM_FREECAM && k == pv->playernum) || (pv->cam_state != CAM_FREECAM && k == pv->cam_spec_track); + if ((key_dest_absolutemouse & key_dest_mask & ~kdm_game) && + !Key_Dest_Has(~kdm_game) && + mousecursor_x >= startx && mousecursor_x < startx+rank_width && + mousecursor_y >= y && mousecursor_y < y+skip) + { + highlight = 2; + cl.mouseplayerview = pv; + cl.mousenewtrackplayer = k; + } + else + highlight = 0; + x = startx; #define COLUMN(title, width, code, fills) \ if (showcolumns & (1< //output #include //context+input +#ifdef USEEFX +#include +#endif #ifndef AL_API #define AL_API @@ -103,6 +106,13 @@ We also have no doppler with WebAudio. #define palGetProcAddress alGetProcAddress +//voip stuff +#define palcCaptureOpenDevice alcCaptureOpenDevice +#define palcCaptureCloseDevice alcCaptureCloseDevice +#define palcCaptureStart alcCaptureStart +#define palcCaptureStop alcCaptureStop +#define palcCaptureSamples alcCaptureSamples + #ifdef FTE_TARGET_WEB //emscripten sucks. AL_API void (AL_APIENTRY alSpeedOfSound)( ALfloat value ) {} #endif @@ -115,6 +125,8 @@ AL_API void (AL_APIENTRY alSpeedOfSound)( ALfloat value ) {} #endif #define AL_API +#undef AL_ALEXT_PROTOTYPES + typedef int ALint; typedef unsigned int ALuint; @@ -131,6 +143,7 @@ static qboolean openallib_tried; static AL_API ALenum (AL_APIENTRY *palGetError)( void ); static AL_API void (AL_APIENTRY *palSourcef)( ALuint sid, ALenum param, ALfloat value ); static AL_API void (AL_APIENTRY *palSourcei)( ALuint sid, ALenum param, ALint value ); +static AL_API void (AL_APIENTRY *palSource3i)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); static AL_API void (AL_APIENTRY *palSourcePlayv)( ALsizei ns, const ALuint *sids ); static AL_API void (AL_APIENTRY *palSourceStopv)( ALsizei ns, const ALuint *sids ); @@ -251,19 +264,17 @@ static ALC_API void* (ALC_APIENTRY *palcGetProcAddress)(ALCdevice *dev #if defined(VOICECHAT) //capture-specific stuff -static ALC_API void (ALC_APIENTRY *palcGetIntegerv)( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *data ); -static ALC_API ALCdevice * (ALC_APIENTRY *palcCaptureOpenDevice)( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize ); -static ALC_API ALCboolean (ALC_APIENTRY *palcCaptureCloseDevice)( ALCdevice *device ); -static ALC_API void (ALC_APIENTRY *palcCaptureStart)( ALCdevice *device ); -static ALC_API void (ALC_APIENTRY *palcCaptureStop)( ALCdevice *device ); -static ALC_API void (ALC_APIENTRY *palcCaptureSamples)( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); +static void (ALC_APIENTRY *palcGetIntegerv)( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *data ); +static ALCdevice * (ALC_APIENTRY *palcCaptureOpenDevice)( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize ); +static ALCboolean (ALC_APIENTRY *palcCaptureCloseDevice)( ALCdevice *device ); +static void (ALC_APIENTRY *palcCaptureStart)( ALCdevice *device ); +static void (ALC_APIENTRY *palcCaptureStop)( ALCdevice *device ); +static void (ALC_APIENTRY *palcCaptureSamples)( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); #define ALC_CAPTURE_DEVICE_SPECIFIER 0x310 #define ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER 0x311 #define ALC_CAPTURE_SAMPLES 0x312 #endif -#endif - //efx #ifdef USEEFX @@ -312,18 +323,32 @@ static ALC_API void (ALC_APIENTRY *palcCaptureSamples)( ALCdevice *dev #define AL_EAXREVERB_LFREFERENCE 0x0015 #define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR 0x0016 #define AL_EAXREVERB_DECAY_HFLIMIT 0x0017 -static AL_API void (AL_APIENTRY *palSource3i)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); +#endif +#endif -static AL_API void (AL_APIENTRY *palAuxiliaryEffectSloti)(ALuint effectslot, ALenum param, ALint iValue); -static AL_API ALvoid (AL_APIENTRY *palGenAuxiliaryEffectSlots)(ALsizei n, ALuint *effectslots); -static AL_API ALvoid (AL_APIENTRY *palDeleteAuxiliaryEffectSlots)(ALsizei n, const ALuint *effectslots); -static AL_API ALvoid (AL_APIENTRY *palDeleteEffects)(ALsizei n, const ALuint *effects); +#ifdef USEEFX + #if defined(AL_ALEXT_PROTOTYPES) && defined(OPENAL_STATIC) + #define palAuxiliaryEffectSloti alAuxiliaryEffectSloti + #define palGenAuxiliaryEffectSlots alGenAuxiliaryEffectSlots + #define palDeleteAuxiliaryEffectSlots alDeleteAuxiliaryEffectSlots + #define palDeleteEffects alDeleteEffects + #define palGenEffects alGenEffects + #define palEffecti alEffecti +// #define palEffectiv alEffectiv + #define palEffectf alEffectf + #define palEffectfv alEffectfv + #else + static void (AL_APIENTRY *palAuxiliaryEffectSloti)(ALuint effectslot, ALenum param, ALint iValue); + static ALvoid (AL_APIENTRY *palGenAuxiliaryEffectSlots)(ALsizei n, ALuint *effectslots); + static ALvoid (AL_APIENTRY *palDeleteAuxiliaryEffectSlots)(ALsizei n, const ALuint *effectslots); + static ALvoid (AL_APIENTRY *palDeleteEffects)(ALsizei n, const ALuint *effects); -static AL_API ALvoid (AL_APIENTRY *palGenEffects)(ALsizei n, ALuint *effects); -static AL_API ALvoid (AL_APIENTRY *palEffecti)(ALuint effect, ALenum param, ALint iValue); -static AL_API ALvoid (AL_APIENTRY *palEffectiv)(ALuint effect, ALenum param, const ALint *piValues); -static AL_API ALvoid (AL_APIENTRY *palEffectf)(ALuint effect, ALenum param, ALfloat flValue); -static AL_API ALvoid (AL_APIENTRY *palEffectfv)(ALuint effect, ALenum param, const ALfloat *pflValues); + static ALvoid (AL_APIENTRY *palGenEffects)(ALsizei n, ALuint *effects); + static ALvoid (AL_APIENTRY *palEffecti)(ALuint effect, ALenum param, ALint iValue); +// static ALvoid (AL_APIENTRY *palEffectiv)(ALuint effect, ALenum param, const ALint *piValues); + static ALvoid (AL_APIENTRY *palEffectf)(ALuint effect, ALenum param, ALfloat flValue); + static ALvoid (AL_APIENTRY *palEffectfv)(ALuint effect, ALenum param, const ALfloat *pflValues); + #endif #endif //AL_EXT_float32 @@ -1084,13 +1109,10 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, chanupdat palSourcef(src, AL_PITCH, pitch); #ifdef USEEFX - if (palSource3i) - { - if (chan->flags & CF_NOREVERB) //don't do the underwater thing on static sounds. it sounds like arse with all those sources. - palSource3i(src, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); - else - palSource3i(src, AL_AUXILIARY_SEND_FILTER, oali->effectslot, 0, AL_FILTER_NULL); - } + if (chan->flags & CF_NOREVERB) //don't do the underwater thing on static sounds. it sounds like arse with all those sources. + palSource3i(src, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); + else + palSource3i(src, AL_AUXILIARY_SEND_FILTER, oali->effectslot, 0, AL_FILTER_NULL); #endif palSourcei(src, AL_LOOPING, (!stream && ((chan->flags & CF_FORCELOOP)||(sfx->loopstart>=0&&!stream)))?AL_TRUE:AL_FALSE); @@ -1205,6 +1227,7 @@ static qboolean OpenAL_InitLibrary(void) {(void*)&palGetError, "alGetError"}, {(void*)&palSourcef, "alSourcef"}, {(void*)&palSourcei, "alSourcei"}, + {(void*)&palSource3i, "alSource3i"}, {(void*)&palSourcePlayv, "alSourcePlayv"}, {(void*)&palSourceStopv, "alSourceStopv"}, {(void*)&palSourcePlay, "alSourcePlay"}, @@ -1238,6 +1261,7 @@ static qboolean OpenAL_InitLibrary(void) {(void*)&palcMakeContextCurrent, "alcMakeContextCurrent"}, {(void*)&palcProcessContext, "alcProcessContext"}, {(void*)&palcGetString, "alcGetString"}, + {(void*)&palcGetIntegerv, "alcGetIntegerv"}, {(void*)&palcIsExtensionPresent, "alcIsExtensionPresent"}, {(void*)&palcGetProcAddress, "alcGetProcAddress"}, {NULL} @@ -1854,16 +1878,17 @@ static qboolean QDECL OpenAL_InitCard2(soundcardinfo_t *sc, const char *devname, #ifdef USEEFX PrintALError("preeffects"); - palSource3i = palGetProcAddress("alSource3i"); + #ifndef AL_ALEXT_PROTOTYPES palAuxiliaryEffectSloti = palGetProcAddress("alAuxiliaryEffectSloti"); palGenAuxiliaryEffectSlots = palGetProcAddress("alGenAuxiliaryEffectSlots"); palDeleteAuxiliaryEffectSlots = palGetProcAddress("alDeleteAuxiliaryEffectSlots"); palDeleteEffects = palGetProcAddress("alDeleteEffects"); palGenEffects = palGetProcAddress("alGenEffects"); palEffecti = palGetProcAddress("alEffecti"); - palEffectiv = palGetProcAddress("alEffectiv"); +// palEffectiv = palGetProcAddress("alEffectiv"); palEffectf = palGetProcAddress("alEffectf"); palEffectfv = palGetProcAddress("alEffectfv"); + #endif if (palGenAuxiliaryEffectSlots && s_al_use_reverb.ival) palGenAuxiliaryEffectSlots(1, &oali->effectslot); @@ -1915,10 +1940,11 @@ static qboolean OpenAL_InitCapture(void) //if (!palcIsExtensionPresent(NULL, "ALC_EXT_capture")) // return false; +#ifdef OPENAL_STATIC + return true; +#else if(!palcCaptureOpenDevice) { - palcGetIntegerv = Sys_GetAddressForName(openallib, "alcGetIntegerv"); - palcCaptureOpenDevice = Sys_GetAddressForName(openallib, "alcCaptureOpenDevice"); palcCaptureStart = Sys_GetAddressForName(openallib, "alcCaptureStart"); palcCaptureSamples = Sys_GetAddressForName(openallib, "alcCaptureSamples"); @@ -1927,6 +1953,7 @@ static qboolean OpenAL_InitCapture(void) } return palcGetIntegerv&&palcCaptureOpenDevice&&palcCaptureStart&&palcCaptureSamples&&palcCaptureStop&&palcCaptureCloseDevice; +#endif } static qboolean QDECL OPENAL_Capture_Enumerate (void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename)) { @@ -1955,8 +1982,13 @@ static void *QDECL OPENAL_Capture_Init (int samplerate, const char *device) if (!device || !*device) { +#if defined(FTE_TARGET_WEB) && (__EMSCRIPTEN_major__>2 || (__EMSCRIPTEN_major__==2&&__EMSCRIPTEN_tiny__>=14)) + //emscripten, and recent enough to actually work. don't check s_al_disable here as we don't have dsound/alsa fallbacks and we do want to actually use it. + //older versions of emscripten are too buggy to use. +#else if (s_al_disable.ival) return NULL; //no default device +#endif device = palcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER); } diff --git a/engine/client/snd_alsa.c b/engine/client/snd_alsa.c index b4c4d0e55..bba0c1393 100755 --- a/engine/client/snd_alsa.c +++ b/engine/client/snd_alsa.c @@ -38,41 +38,41 @@ static void *alsasharedobject; -int (*psnd_pcm_open) (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode); -int (*psnd_pcm_close) (snd_pcm_t *pcm); -int (*psnd_config_update_free_global)(void); -const char *(*psnd_strerror) (int errnum); -int (*psnd_pcm_hw_params_any) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -int (*psnd_pcm_hw_params_set_access) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access); -int (*psnd_pcm_hw_params_set_format) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val); -int (*psnd_pcm_hw_params_set_channels) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); -int (*psnd_pcm_hw_params_set_rate_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int (*psnd_pcm_hw_params_set_period_size_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir); -int (*psnd_pcm_hw_params) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -int (*psnd_pcm_sw_params_current) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params); -int (*psnd_pcm_sw_params_set_start_threshold) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); -int (*psnd_pcm_sw_params_set_stop_threshold) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); -int (*psnd_pcm_sw_params) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params); -int (*psnd_pcm_hw_params_get_buffer_size) (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); -int (*psnd_pcm_hw_params_set_buffer_size_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); -int (*psnd_pcm_set_params) (snd_pcm_t *pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int channels, unsigned int rate, int soft_resample, unsigned int latency); -snd_pcm_sframes_t (*psnd_pcm_avail_update) (snd_pcm_t *pcm); -snd_pcm_state_t (*psnd_pcm_state) (snd_pcm_t *pcm); -int (*psnd_pcm_start) (snd_pcm_t *pcm); -int (*psnd_pcm_recover) (snd_pcm_t *pcm, int err, int silent); +static int (*psnd_pcm_open) (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode); +static int (*psnd_pcm_close) (snd_pcm_t *pcm); +static int (*psnd_config_update_free_global)(void); +static const char *(*psnd_strerror) (int errnum); +static int (*psnd_pcm_hw_params_any) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params); +static int (*psnd_pcm_hw_params_set_access) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access); +static int (*psnd_pcm_hw_params_set_format) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val); +static int (*psnd_pcm_hw_params_set_channels) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); +static int (*psnd_pcm_hw_params_set_rate_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +static int (*psnd_pcm_hw_params_set_period_size_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir); +static int (*psnd_pcm_hw_params) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params); +static int (*psnd_pcm_sw_params_current) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params); +static int (*psnd_pcm_sw_params_set_start_threshold) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); +static int (*psnd_pcm_sw_params_set_stop_threshold) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); +static int (*psnd_pcm_sw_params) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params); +static int (*psnd_pcm_hw_params_get_buffer_size) (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); +static int (*psnd_pcm_hw_params_set_buffer_size_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); +static int (*psnd_pcm_set_params) (snd_pcm_t *pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int channels, unsigned int rate, int soft_resample, unsigned int latency); +static snd_pcm_sframes_t (*psnd_pcm_avail_update) (snd_pcm_t *pcm); +static snd_pcm_state_t (*psnd_pcm_state) (snd_pcm_t *pcm); +static int (*psnd_pcm_start) (snd_pcm_t *pcm); +static int (*psnd_pcm_recover) (snd_pcm_t *pcm, int err, int silent); -size_t (*psnd_pcm_hw_params_sizeof) (void); -size_t (*psnd_pcm_sw_params_sizeof) (void); +static size_t (*psnd_pcm_hw_params_sizeof) (void); +static size_t (*psnd_pcm_sw_params_sizeof) (void); -int (*psnd_pcm_mmap_begin) (snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames); -snd_pcm_sframes_t (*psnd_pcm_mmap_commit) (snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t frames); +static int (*psnd_pcm_mmap_begin) (snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames); +static snd_pcm_sframes_t (*psnd_pcm_mmap_commit) (snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t frames); -snd_pcm_sframes_t (*psnd_pcm_writei) (snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size); -int (*psnd_pcm_prepare) (snd_pcm_t *pcm); +static snd_pcm_sframes_t (*psnd_pcm_writei) (snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size); +static int (*psnd_pcm_prepare) (snd_pcm_t *pcm); -int (*psnd_device_name_hint) (int card, const char *iface, void ***hints); -char * (*psnd_device_name_get_hint) (const void *hint, const char *id); -int (*psnd_device_name_free_hint) (void **hints); +static int (*psnd_device_name_hint) (int card, const char *iface, void ***hints); +static char * (*psnd_device_name_get_hint) (const void *hint, const char *id); +static int (*psnd_device_name_free_hint) (void **hints); static unsigned int ALSA_MMap_GetDMAPos (soundcardinfo_t *sc) diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index 2b74304cc..3442e3f1e 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -92,13 +92,8 @@ cvar_t snd_precache = CVARAF( "s_precache", "1", cvar_t snd_loadas8bit = CVARAFD( "s_loadas8bit", "0", "loadas8bit", CVAR_ARCHIVE, "Downsample sounds on load as lower quality 8-bit sound, to save memory."); -#ifdef FTE_TARGET_WEB -cvar_t snd_loadasstereo = CVARD( "snd_loadasstereo", "1", - "Force mono sounds to load as if stereo ones, to waste memory. Used to work around stupid browser bugs."); -#else cvar_t snd_loadasstereo = CVARD( "snd_loadasstereo", "0", "Force mono sounds to load as if stereo ones, to waste memory. Not normally useful."); -#endif cvar_t ambient_level = CVARAFD( "s_ambientlevel", "0.3", "ambient_level", CVAR_ARCHIVE, "This controls the volume levels of automatic area-based sounds (like water or sky), and is quite annoying. If you're playing deathmatch you'll definitely want this OFF."); @@ -1091,6 +1086,7 @@ static float S_Voip_Preprocess(short *start, unsigned int samples, float micamp) } static void S_Voip_TryInitCaptureContext(char *driver, char *device, int rate) { + static float throttle; int i; s_voip.cdriver = NULL; @@ -1114,12 +1110,12 @@ static void S_Voip_TryInitCaptureContext(char *driver, char *device, int rate) if (!s_voip.cdriver) { if (!driver) - Con_Printf("No microphone drivers supported\n"); + Con_ThrottlePrintf(&throttle, 0, CON_ERROR"No microphone drivers supported\n"); else - Con_Printf("Microphone driver \"%s\" is not valid\n", driver); + Con_ThrottlePrintf(&throttle, 0, CON_ERROR"Microphone driver \"%s\" is not valid\n", driver); } else - Con_Printf("No microphone detected\n"); + Con_ThrottlePrintf(&throttle, 0, CON_ERROR"No microphone detected\n"); s_voip.cdriver = NULL; } @@ -1388,7 +1384,7 @@ void S_Voip_Transmit(unsigned char clc, sizebuf_t *buf) s_voip.capturepos += s_voip.cdriver->Update(s_voip.cdriverctx, (unsigned char*)s_voip.capturebuf + s_voip.capturepos, s_voip.encframesize*2, sizeof(s_voip.capturebuf) - s_voip.capturepos); - if (!s_voip.wantsend && s_voip.capturepos < s_voip.encframesize*2) + if (!voipsendenable || (!s_voip.wantsend && s_voip.capturepos < s_voip.encframesize*2)) { s_voip.voiplevel = -1; s_voip.capturepos = 0; @@ -1710,10 +1706,10 @@ void S_Voip_MapChange(void) } int S_Voip_Loudness(qboolean ignorevad) { - if (s_voip.voiplevel > 100) - return 100; if (!s_voip.cdriverctx || (!ignorevad && s_voip.dumps)) return -1; + if (s_voip.voiplevel > 100) + return 100; return s_voip.voiplevel; } int S_Voip_ClientLoudness(unsigned int plno) @@ -1770,6 +1766,10 @@ void S_Voip_Parse(void) bytes = MSG_ReadShort(); MSG_ReadSkip(bytes); } +int S_Voip_ClientLoudness(unsigned int plno) +{ + return -1; +} #endif @@ -2188,7 +2188,7 @@ void S_DoRestart (qboolean onlyifneeded) for (i=1 ; isn.numchannels; i++) @@ -2859,7 +2859,7 @@ static void SND_Spatialize(soundcardinfo_t *sc, channel_t *ch) listener_vec[1] = DotProduct(listener[seat].right, world_vec); listener_vec[2] = DotProduct(listener[seat].up, world_vec); - if (snd_leftisright.ival) + if (snd_leftisright.ival^r_xflip.ival) listener_vec[1] = -listener_vec[1]; for (i = 0; i < sc->sn.numchannels; i++) diff --git a/engine/client/snd_mem.c b/engine/client/snd_mem.c index b6987b316..17e574b2a 100644 --- a/engine/client/snd_mem.c +++ b/engine/client/snd_mem.c @@ -37,10 +37,6 @@ typedef struct static wavinfo_t GetWavinfo (char *name, qbyte *wav, int wavlength); -int cache_full_cycle; - -qbyte *S_Alloc (int size); - #define LINEARUPSCALE(in, inrate, insamps, out, outrate, outlshift, outrshift) \ { \ scale = inrate / (double)outrate; \ @@ -822,9 +818,54 @@ static qboolean QDECL S_LoadWavSound (sfx_t *s, qbyte *data, size_t datalen, int return ResampleSfx (s, info.rate, info.numchannels, format, info.samples, info.loopstart, data + info.dataofs); } -qboolean QDECL S_LoadOVSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode); - #ifdef FTE_TARGET_WEB +#if 1 +void S_BrowserDecoded (void *ctx, void *dataptr, int frames, int channels, float rate) +{ + sfx_t *sfx = ctx; + + //make sure we were not restarting at the time... FIXME: make stricter? + extern sfx_t *known_sfx; + extern int num_sfx; + int id = sfx-known_sfx; + if (id < 0 || id >= num_sfx || sfx != &known_sfx[id]) + return; //err... don't crash out! + + sfx->loopstart = -1; + if (dataptr) + { //okay, something loaded. woo. + Z_Free(sfx->decoder.buf); + sfx->decoder.buf = NULL; + sfx->decoder.decodedata = NULL; + ResampleSfx (sfx, rate, channels, QAF_S16, frames, -1, dataptr); + } + else + { + Con_Printf(CON_WARNING"Failed to decode %s\n", sfx->name); + sfx->loadstate = SLS_FAILED; + } +} +static qboolean QDECL S_LoadBrowserFile (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode) +{ + struct sfxcache_s *buf; + + if (datalen > 4 && !strncmp(data, "RIFF", 4)) + return false; //do NOT use this code for wav files. we have no way to read the looping flags which would break things in certain situations. we MUST fall back on our normal loader. + + s->decoder.buf = buf = Z_Malloc(sizeof(*buf)+128); + //fill with a placeholder + buf->length = 128; + buf->speed = snd_speed; + buf->format = QAF_S8; //something basic + buf->numchannels=1; + buf->soundoffset = 0; + buf->data = (qbyte*)(buf+1); + + s->loopstart = 0; //keep looping silence until it actually loads something. + + return emscriptenfte_pcm_loadaudiofile(s, S_BrowserDecoded, data, datalen, sndspeed); +} +#else //web browsers contain their own decoding libraries that our openal stuff can use. static qboolean QDECL S_LoadBrowserFile (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode) { @@ -842,6 +883,9 @@ static qboolean QDECL S_LoadBrowserFile (sfx_t *s, qbyte *data, size_t datalen, return true; } #endif +#endif + +qboolean QDECL S_LoadOVSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode); //highest priority is last. static struct diff --git a/engine/client/sound.h b/engine/client/sound.h index ff6d07a0d..6269de988 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -242,12 +242,12 @@ void S_ResetFailedLoad(void); #ifdef PEXT2_VOICECHAT void S_Voip_Parse(void); #endif +int S_Voip_ClientLoudness(unsigned int plno); #ifdef VOICECHAT extern cvar_t snd_voip_showmeter; void S_Voip_Transmit(unsigned char clc, sizebuf_t *buf); void S_Voip_MapChange(void); int S_Voip_Loudness(qboolean ignorevad); //-1 for not capturing, otherwise between 0 and 100 -int S_Voip_ClientLoudness(unsigned int plno); qboolean S_Voip_Speaking(unsigned int plno); void S_Voip_Ignore(unsigned int plno, qboolean ignore); #else diff --git a/engine/client/sys_linux.c b/engine/client/sys_linux.c index bb800ae4c..e573da0e6 100644 --- a/engine/client/sys_linux.c +++ b/engine/client/sys_linux.c @@ -452,7 +452,7 @@ static void Sys_Register_File_Associations_f(void) if (!strcmp(iconname, "afterquake") || !strcmp(iconname, "nq")) //hacks so that we don't need to create icons. iconname = "quake"; - if (FS_NativePath("icon.png", FS_PUBBASEGAMEONLY, iconsyspath, sizeof(iconsyspath))) + if (FS_SystemPath("icon.png", FS_PUBBASEGAMEONLY, iconsyspath, sizeof(iconsyspath))) iconname = iconsyspath; s = va("%s/applications/fte-%s.desktop", xdgbase, fs_manifest->installation); @@ -1215,7 +1215,7 @@ static void DoSign(const char *fname, int signtype) searchpathfuncs_t *search = FS_OpenPackByExtension(f, NULL, fname, fname, prefix); if (search) { - printf("%#08x", search->GeneratePureCRC(search, 0, 0)); + printf("%#08x", search->GeneratePureCRC(search, NULL)); search->ClosePath(search); } else @@ -1551,15 +1551,6 @@ int main (int c, const char **v) Host_Init(&parms); - for (i = 1; i < parms.argc; i++) - { - if (!parms.argv[i]) - continue; - if (*parms.argv[i] == '+' || *parms.argv[i] == '-') - break; - Host_RunFile(parms.argv[i], strlen(parms.argv[i]), NULL); - } - oldtime = Sys_DoubleTime (); while (1) { diff --git a/engine/client/sys_sdl.c b/engine/client/sys_sdl.c index eda6a47c6..97832d338 100644 --- a/engine/client/sys_sdl.c +++ b/engine/client/sys_sdl.c @@ -948,7 +948,6 @@ int QDECL main(int argc, char **argv) { float time, newtime, oldtime; quakeparms_t parms; - int i; memset(&parms, 0, sizeof(parms)); @@ -975,15 +974,6 @@ int QDECL main(int argc, char **argv) oldtime = Sys_DoubleTime (); - for (i = 1; i < parms.argc; i++) - { - if (!parms.argv[i]) - continue; - if (*parms.argv[i] == '+' || *parms.argv[i] == '-') - break; - Host_RunFile(parms.argv[i], strlen(parms.argv[i]), NULL); - } - //client console should now be initialized. /* main window message loop */ diff --git a/engine/client/sys_win.c b/engine/client/sys_win.c index 30165e58a..6d0db97cb 100644 --- a/engine/client/sys_win.c +++ b/engine/client/sys_win.c @@ -3629,7 +3629,7 @@ static qboolean Sys_DoInstall(void) GetModuleFileNameW(NULL, wide, countof(wide)); narrowen(exepath, sizeof(exepath), wide); - FS_NativePath(va("%s.exe", fs_gamename.string), FS_ROOT, newexepath, sizeof(newexepath)); + FS_SystemPath(va("%s.exe", fs_gamename.string), FS_ROOT, newexepath, sizeof(newexepath)); CopyFileU(exepath, newexepath, FALSE); /*the game can now be run (using regular autoupdate stuff), but most installers are expected to install the data instead of just more downloaders, so lets do that with a 'nice' progress box*/ @@ -4347,16 +4347,6 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin oldtime = Sys_DoubleTime (); - if (qtvfile) - { - if (!Host_RunFile(qtvfile, strlen(qtvfile), NULL)) - { - SetHookState(false); - Host_Shutdown (); - return EXIT_FAILURE; - } - } - //client console should now be initialized. #ifndef MINGW diff --git a/engine/client/valid.c b/engine/client/valid.c index 6863340cf..883c32931 100644 --- a/engine/client/valid.c +++ b/engine/client/valid.c @@ -1437,6 +1437,9 @@ void Validation_Auto_Response(int playernum, char *s) static float cmdlineresponsetime; static float scriptsresponsetime; + if (cls.demoplayback) + return; //noone gives a shit about qtv spectator versions that can't even play without reconnecting. + //quakeworld tends to use f_* //netquake uses the slightly more guessable q_* form if (!strncmp(s, "f_", 2)) diff --git a/engine/client/view.c b/engine/client/view.c index 66b7e7ac6..0dba588e2 100644 --- a/engine/client/view.c +++ b/engine/client/view.c @@ -94,8 +94,8 @@ static cvar_t v_ipitch_level = CVAR("v_ipitch_level", "0.3"); static cvar_t v_idlescale = CVARD("v_idlescale", "0", "Enable swishing of the view (whether idle or otherwise). Often used for concussion effects."); cvar_t crosshair = CVARF("crosshair", "1", CVAR_ARCHIVE); -cvar_t crosshaircolor = CVARF("crosshaircolor", "255 255 255", CVAR_ARCHIVE); -cvar_t crosshairsize = CVARF("crosshairsize", "8", CVAR_ARCHIVE); +cvar_t crosshaircolor = CVARF("crosshaircolor", "255 255 255", CVAR_ARCHIVE); //QSSM misnamed it... +cvar_t crosshairsize = CVARF("crosshairsize", "8", CVAR_ARCHIVE); //QS has scr_crosshairscale, but its a multiplier rather than (vritual) size. cvar_t cl_crossx = CVARF("cl_crossx", "0", CVAR_ARCHIVE); cvar_t cl_crossy = CVARF("cl_crossy", "0", CVAR_ARCHIVE); @@ -1877,7 +1877,7 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam) x = center[0]*r_refdef.vrect.width+r_refdef.vrect.x; y = (1-center[1])*r_refdef.vrect.height+r_refdef.vrect.y; - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (cls.demoplayback == DPB_MVD) { health = pl->statsf[STAT_HEALTH]; armour = pl->statsf[STAT_ARMOR]; @@ -2050,6 +2050,10 @@ void R_DrawNameTags(void) vec3_t targ; vec2_t scale = {12,12}; msurface_t *surf; + shader_t *shader; + const char *shadername; + char *body; + char fname[MAX_QPATH]; VectorMA(r_refdef.vieworg, 8192, vpn, targ); #ifdef CSQC_DAT if (csqc_world.progs) @@ -2063,23 +2067,53 @@ void R_DrawNameTags(void) #endif cl.worldmodel->funcs.NativeTrace(cl.worldmodel, 0, PE_FRAMESTATE, NULL, r_refdef.vieworg, targ, vec3_origin, vec3_origin, false, ~0, &trace); - surf = (trace.fraction == 1)?NULL:Mod_GetSurfaceNearPoint(cl.worldmodel, trace.endpos); - if (surf) + if (trace.fraction >= 1) + str = "hit nothing"; + else { - shader_t *shader = surf->texinfo->texture->shader; - char fname[MAX_QPATH]; - char *body = shader?Shader_GetShaderBody(shader, fname, countof(fname)):NULL; + shader = NULL; + if (cl.worldmodel->terrain && trace.brush_id && (shader = Terr_GetShader(cl.worldmodel, &trace))) + shadername = shader->name; + else if ((surf = (trace.fraction == 1)?NULL:Mod_GetSurfaceNearPoint(cl.worldmodel, trace.endpos))) + { + shadername = surf->texinfo->texture->name; + shader = surf->texinfo->texture->shader; + } + else if (trace.surface && *trace.surface->name) + { + shadername = trace.surface->name; + shader = NULL; + } + else + { + shadername = "the unknown"; + shader = NULL; + } + + body = shader?Shader_GetShaderBody(shader, fname, countof(fname)):NULL; if (body) { -// Q_snprintfz(fname, sizeof(fname), ""); - str = va("^2%s^7\n%s%s\n{%s\n", fname, ruleset_allow_shaders.ival?"":CON_ERROR"WARNING: ruleset_allow_shaders disables external shaders"CON_DEFAULT"\n", surf->texinfo->texture->name, body); + int width, height; + char *cr; + const char *fl = ""; + if (shader->usageflags & SUF_LIGHTMAP) + fl = S_COLOR_GRAY" (lightmapped)"S_COLOR_WHITE; + else if (shader->usageflags & SUF_2D) + fl = S_COLOR_GRAY" (2d)"S_COLOR_WHITE; + else //if (shader->usageflags & SUF_2D) + fl = S_COLOR_GRAY" (auto)"S_COLOR_WHITE; + if (R_GetShaderSizes(shader, &width, &height, false)>0) + fl = va("%s (%ix%i)", fl, width, height); + + while((cr = strchr(body, '\r'))) + *cr = ' '; + + str = va(S_COLOR_GREEN"%s"S_COLOR_WHITE"\n%s%s%s\n{%s\n", fname, ruleset_allow_shaders.ival?"":CON_ERROR"WARNING: ruleset_allow_shaders disables external shaders"CON_DEFAULT"\n", shadername, fl, body); Z_Free(body); } else - str = va("hit '%s'", surf->texinfo->texture->name); + str = va("hit '%s'", shadername); } - else - str = "hit nothing"; R_DrawTextField(r_refdef.vrect.x + r_refdef.vrect.width/4, r_refdef.vrect.y, r_refdef.vrect.width/2, r_refdef.vrect.height, str, CON_WHITEMASK, CPRINT_LALIGN, font_console, scale); } else diff --git a/engine/client/vr.h b/engine/client/vr.h index a0b6a5fd9..416e3c7cc 100644 --- a/engine/client/vr.h +++ b/engine/client/vr.h @@ -67,14 +67,17 @@ typedef struct vrsetup_s }; } vrsetup_t; +#define VRF_OVERRIDEFRAMETIME 1 //the vr interface is responsible for determining frame intervals instead of using regular clocks (so they can fiddle with prediction etc) +#define VRF_UIACTIVE 2 //we're actually rendering through a headset. use 3d rendering exclusively, with VR UI and stuff. + //interface registered by plugins for VR stuff. typedef struct plugvrfuncs_s { const char *description; qboolean (*Prepare) (vrsetup_t *setupinfo); //called before graphics context init qboolean (*Init) (vrsetup_t *setupinfo, rendererstate_t *info); //called after graphics context init - qboolean (*SyncFrame)(double *frametime); //called in the client's main loop, to block/tweak frame times. True means the game should render as fast as possible. - qboolean (*Render) (void(*rendereye)(texid_t tex, vec4_t fovoverride, vec3_t angorg[2])); + unsigned int (*SyncFrame)(double *frametime); //called in the client's main loop, to block/tweak frame times. True means the game should render as fast as possible. + qboolean (*Render) (void(*rendereye)(texid_t tex, const pxrect_t *viewport, const vec4_t fovoverride, const float projmatrix[16], const float eyematrix[12])); void (*Shutdown) (void); #define plugvrfuncs_name "VR" } plugvrfuncs_t; diff --git a/engine/client/winquake.h b/engine/client/winquake.h index ee95c06f6..bf83fae80 100644 --- a/engine/client/winquake.h +++ b/engine/client/winquake.h @@ -128,7 +128,8 @@ extern qboolean mouseinitialized; //extern HANDLE hinput, houtput; extern HCURSOR hArrowCursor, hCustomCursor; -void *WIN_CreateCursor(const qbyte *imagedata, int width, int height, uploadfmt_t format, float hotx, float hoty, float scale); +enum uploadfmt; +void *WIN_CreateCursor(const qbyte *imagedata, int width, int height, enum uploadfmt format, float hotx, float hoty, float scale); qboolean WIN_SetCursor(void *cursor); void WIN_DestroyCursor(void *cursor); void WIN_WindowCreated(HWND window); diff --git a/engine/client/zqtp.c b/engine/client/zqtp.c index ce7127201..5e638c3fd 100644 --- a/engine/client/zqtp.c +++ b/engine/client/zqtp.c @@ -76,92 +76,92 @@ static void QDECL TP_EnemyColor_CB (struct cvar_s *var, char *oldvalue); #define TP_SKIN_CVARS \ TP_CVARAC(cl_teamskin, "", teamskin, TP_SkinCvar_Callback); \ TP_CVARAC(cl_enemyskin, "", enemyskin, TP_SkinCvar_Callback); \ - TP_CVARC(enemycolor, "off", TP_EnemyColor_CB); \ - TP_CVARC(teamcolor, "off", TP_TeamColor_CB); + static TP_CVARC(enemycolor, "off", TP_EnemyColor_CB); \ + static TP_CVARC(teamcolor, "off", TP_TeamColor_CB); #else #define TP_SKIN_CVARS #endif #ifdef QUAKESTATS #define TP_NAME_CVARS \ - TP_CVAR(tp_name_none, ""); \ - TP_CVAR(tp_name_axe, "axe"); \ - TP_CVAR(tp_name_sg, "sg"); \ - TP_CVAR(tp_name_ssg, "ssg"); \ - TP_CVAR(tp_name_ng, "ng"); \ - TP_CVAR(tp_name_sng, "sng"); \ - TP_CVAR(tp_name_gl, "gl"); \ - TP_CVAR(tp_name_rl, "rl"); \ - TP_CVAR(tp_name_lg, "lg"); \ - TP_CVAR(tp_name_ra, "ra"); \ - TP_CVAR(tp_name_ya, "ya"); \ - TP_CVAR(tp_name_ga, "ga"); \ - TP_CVAR(tp_name_quad, "quad"); \ - TP_CVAR(tp_name_pent, "pent"); \ - TP_CVAR(tp_name_ring, "ring"); \ - TP_CVAR(tp_name_suit, "suit"); \ - TP_CVAR(tp_name_shells, "shells"); \ - TP_CVAR(tp_name_nails, "nails"); \ - TP_CVAR(tp_name_rockets, "rockets"); \ - TP_CVAR(tp_name_cells, "cells"); \ - TP_CVAR(tp_name_mh, "mega"); \ - TP_CVAR(tp_name_health, "health"); \ - TP_CVAR(tp_name_backpack, "pack"); \ - TP_CVAR(tp_name_flag, "flag"); \ - TP_CVAR(tp_name_nothing, "nothing"); \ - TP_CVAR(tp_name_at, "at"); \ - TP_CVAR(tp_need_ra, "50"); \ - TP_CVAR(tp_need_ya, "50"); \ - TP_CVAR(tp_need_ga, "50"); \ - TP_CVAR(tp_need_health, "50"); \ - TP_CVAR(tp_need_weapon, "35687"); \ - TP_CVAR(tp_need_rl, "1"); \ - TP_CVAR(tp_need_rockets, "5"); \ - TP_CVAR(tp_need_cells, "20"); \ - TP_CVAR(tp_need_nails, "40"); \ - TP_CVAR(tp_need_shells, "10"); \ - TP_CVAR(tp_name_disp, "dispenser"); \ - TP_CVAR(tp_name_sentry, "sentry gun"); \ - TP_CVAR(tp_name_rune_1, "resistance rune"); \ - TP_CVAR(tp_name_rune_2, "strength rune"); \ - TP_CVAR(tp_name_rune_3, "haste rune"); \ - TP_CVAR(tp_name_rune_4, "regeneration rune"); \ + static TP_CVAR(tp_name_none, ""); \ + static TP_CVAR(tp_name_axe, "axe"); \ + TP_CVAR(tp_name_sg, "sg"); \ + TP_CVAR(tp_name_ssg, "ssg"); \ + TP_CVAR(tp_name_ng, "ng"); \ + TP_CVAR(tp_name_sng, "sng"); \ + TP_CVAR(tp_name_gl, "gl"); \ + TP_CVAR(tp_name_rl, "rl"); \ + TP_CVAR(tp_name_lg, "lg"); \ + static TP_CVAR(tp_name_ra, "ra"); \ + static TP_CVAR(tp_name_ya, "ya"); \ + static TP_CVAR(tp_name_ga, "ga"); \ + static TP_CVAR(tp_name_quad, "quad"); \ + static TP_CVAR(tp_name_pent, "pent"); \ + static TP_CVAR(tp_name_ring, "ring"); \ + static TP_CVAR(tp_name_suit, "suit"); \ + static TP_CVAR(tp_name_shells, "shells"); \ + static TP_CVAR(tp_name_nails, "nails"); \ + static TP_CVAR(tp_name_rockets, "rockets"); \ + static TP_CVAR(tp_name_cells, "cells"); \ + static TP_CVAR(tp_name_mh, "mega"); \ + static TP_CVAR(tp_name_health, "health"); \ + static TP_CVAR(tp_name_backpack, "pack"); \ + static TP_CVAR(tp_name_flag, "flag"); \ + static TP_CVAR(tp_name_nothing, "nothing"); \ + static TP_CVAR(tp_name_at, "at"); \ + static TP_CVAR(tp_need_ra, "50"); \ + static TP_CVAR(tp_need_ya, "50"); \ + static TP_CVAR(tp_need_ga, "50"); \ + static TP_CVAR(tp_need_health, "50"); \ + static TP_CVAR(tp_need_weapon, "35687"); \ + static TP_CVAR(tp_need_rl, "1"); \ + static TP_CVAR(tp_need_rockets, "5"); \ + static TP_CVAR(tp_need_cells, "20"); \ + static TP_CVAR(tp_need_nails, "40"); \ + static TP_CVAR(tp_need_shells, "10"); \ + static TP_CVAR(tp_name_disp, "dispenser"); \ + static TP_CVAR(tp_name_sentry, "sentry gun"); \ + static TP_CVAR(tp_name_rune_1, "resistance rune"); \ + static TP_CVAR(tp_name_rune_2, "strength rune"); \ + static TP_CVAR(tp_name_rune_3, "haste rune"); \ + static TP_CVAR(tp_name_rune_4, "regeneration rune"); \ \ - TP_CVAR(tp_name_status_red, "$R"); \ - TP_CVAR(tp_name_status_green, "$G"); \ - TP_CVAR(tp_name_status_yellow, "$Y"); \ - TP_CVAR(tp_name_status_blue, "$B"); \ + static TP_CVAR(tp_name_status_red, "$R"); \ + static TP_CVAR(tp_name_status_green, "$G"); \ + static TP_CVAR(tp_name_status_yellow, "$Y"); \ + static TP_CVAR(tp_name_status_blue, "$B"); \ \ - TP_CVAR(tp_name_armortype_ga, "g"); \ - TP_CVAR(tp_name_armortype_ya, "y"); \ - TP_CVAR(tp_name_armortype_ra, "r"); \ - TP_CVAR(tp_name_armor, "armor"); \ - TP_CVAR(tp_name_weapon, "weapon"); \ - TP_CVAR(tp_weapon_order, "78654321"); \ + static TP_CVAR(tp_name_armortype_ga, "g"); \ + static TP_CVAR(tp_name_armortype_ya, "y"); \ + static TP_CVAR(tp_name_armortype_ra, "r"); \ + static TP_CVAR(tp_name_armor, "armor"); \ + static TP_CVAR(tp_name_weapon, "weapon"); \ + static TP_CVAR(tp_weapon_order, "78654321"); \ \ - TP_CVAR(tp_name_quaded, "quaded"); \ - TP_CVAR(tp_name_pented, "pented"); \ - TP_CVAR(tp_name_separator, "/"); \ + static TP_CVAR(tp_name_quaded, "quaded"); \ + static TP_CVAR(tp_name_pented, "pented"); \ + static TP_CVAR(tp_name_separator, "/"); \ \ - TP_CVAR(tp_name_enemy, "enemy"); \ - TP_CVAR(tp_name_teammate, ""); \ - TP_CVAR(tp_name_eyes, "eyes"); \ + static TP_CVAR(tp_name_enemy, "enemy"); \ + static TP_CVAR(tp_name_teammate, ""); \ + static TP_CVAR(tp_name_eyes, "eyes"); \ \ - TP_CVAR(loc_name_separator, "-"); \ - TP_CVAR(loc_name_ssg, "ssg"); \ - TP_CVAR(loc_name_ng, "ng"); \ - TP_CVAR(loc_name_sng, "sng"); \ - TP_CVAR(loc_name_gl, "gl"); \ - TP_CVAR(loc_name_rl, "rl"); \ - TP_CVAR(loc_name_lg, "lg"); \ - TP_CVAR(loc_name_ga, "ga"); \ - TP_CVAR(loc_name_ya, "ya"); \ - TP_CVAR(loc_name_ra, "ra"); \ - TP_CVAR(loc_name_mh, "mh"); \ - TP_CVAR(loc_name_quad, "quad"); \ - TP_CVAR(loc_name_pent, "pent"); \ - TP_CVAR(loc_name_ring, "ring"); \ - TP_CVAR(loc_name_suit, "suit"); + static TP_CVAR(loc_name_separator, "-"); \ + static TP_CVAR(loc_name_ssg, "ssg"); \ + static TP_CVAR(loc_name_ng, "ng"); \ + static TP_CVAR(loc_name_sng, "sng"); \ + static TP_CVAR(loc_name_gl, "gl"); \ + static TP_CVAR(loc_name_rl, "rl"); \ + static TP_CVAR(loc_name_lg, "lg"); \ + static TP_CVAR(loc_name_ga, "ga"); \ + static TP_CVAR(loc_name_ya, "ya"); \ + static TP_CVAR(loc_name_ra, "ra"); \ + static TP_CVAR(loc_name_mh, "mh"); \ + static TP_CVAR(loc_name_quad, "quad"); \ + static TP_CVAR(loc_name_pent, "pent"); \ + static TP_CVAR(loc_name_ring, "ring"); \ + static TP_CVAR(loc_name_suit, "suit"); #else #define TP_NAME_CVARS #endif @@ -171,16 +171,16 @@ static void QDECL TP_EnemyColor_CB (struct cvar_s *var, char *oldvalue); #define TP_CVARS \ TP_SKIN_CVARS \ TP_NAME_CVARS \ - TP_CVAR(cl_fakename, ""); \ + static TP_CVAR(cl_fakename, ""); \ TP_CVAR(cl_parseSay, "1"); \ TP_CVAR(cl_parseFunChars, "1"); \ TP_CVAR(cl_triggers, "1"); \ - TP_CVAR(tp_autostatus, ""); /* things which will not always change, but are useful */ \ + static TP_CVAR(tp_autostatus, ""); /* things which will not always change, but are useful */ \ TP_CVAR(tp_forceTriggers, "0"); \ TP_CVAR(tp_loadlocs, "1"); \ - TP_CVAR(tp_soundtrigger, "~"); \ + static TP_CVAR(tp_soundtrigger, "~"); \ \ - TP_CVAR(tp_name_someplace, "someplace") + static TP_CVAR(tp_name_someplace, "someplace") //create the globals for all the TP cvars. #define TP_CVAR(name,def) cvar_t name = CVAR(#name, def) @@ -906,10 +906,6 @@ static char *Macro_demoplayback (void) case DPB_QUAKE2: return "dm2playback"; #endif - - //gcc will warn if we add annother playback and forget here, otherwise I'd use a default. - case DPB_EZTV: - break; } return "1"; //unknown. } @@ -2311,9 +2307,9 @@ int TP_CategorizeMessage (char *s, int *offset, player_info_t **plr) // no team messages in teamplay 0, except for our own if (pv->spectator) { - unsigned int track = Cam_TrackNum(pv); - if (i == track || ( cl.teamplay && - !strcmp(cl.players[track].team, player->team)) ) + int track = Cam_TrackNum(pv); + if (track>=0 && (i == track || ( cl.teamplay && + !strcmp(cl.players[track].team, player->team)) )) { flags |= TPM_OBSERVEDTEAM; } @@ -3710,7 +3706,9 @@ void TP_Init (void) #define TP_CVAR(name,def) Cvar_Register (&name, TEAMPLAYVARS); #define TP_CVARC(name,def,callback) Cvar_Register (&name, TEAMPLAYVARS); #define TP_CVARAC(name,def,name2,callback) Cvar_Register (&name, TEAMPLAYVARS); +#define static TP_CVARS; +#undef static #undef TP_CVAR #undef TP_CVARC #undef TP_CVARAC diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index e6638101e..39b388a46 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -173,10 +173,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define DISTRIBUTIONLONG "Forethought Entertainment" //effectively the 'company' name #endif #ifndef FULLENGINENAME - #define FULLENGINENAME "FTE Quake" //the posh name for the engine + #define FULLENGINENAME "FTE QW" //the posh name for the engine, note that 'Quake' is trademarked so we should not be using it here. #endif #ifndef ENGINEWEBSITE - #define ENGINEWEBSITE "^8http://^4fte^8.^4triptohell^8.^4info" //url for program + #define ENGINEWEBSITE "^8https://^4fte^8.^4triptohell^8.^4info" //url for program #endif #if !defined(_WIN32) || defined(WINRT) @@ -187,9 +187,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef AVAIL_WASAPI #endif -#if !(defined(__linux__) || defined(__CYGWIN__)) || defined(ANDROID) - #undef HAVE_GNUTLS -#endif +//#if !(defined(__linux__) || defined(__CYGWIN__)) || defined(ANDROID) +// #undef HAVE_GNUTLS +//#endif #if !defined(_WIN32) || (defined(_MSC_VER) && (_MSC_VER < 1300)) || defined(FTE_SDL) #undef HAVE_WINSSPI #endif @@ -253,7 +253,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef Q2BSPS #undef Q3BSPS #undef RFBSPS - #undef WEBSERVER //http server #undef FTPSERVER //ftp server #undef WEBCLIENT //http client. #undef FTPCLIENT //ftp client. @@ -285,7 +284,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef HAVE_PACKET //no udp support //try to trim the fat - #undef VOICECHAT //too lazy to compile opus +// #undef VOICECHAT //too lazy to compile opus #undef HLCLIENT //dlls... #undef HLSERVER //dlls... // #undef CL_MASTER //bah. use the site to specify the servers. @@ -293,7 +292,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef RAGDOLL //no ode #undef TCPCONNECT //err... #undef IRCCONNECT //not happening - #undef PLUGINS //pointless + #if !defined(USE_INTERNAL_BULLET) && !defined(USE_INTERNAL_ODE) && !defined(MODELFMT_GLTF) && !defined(STATIC_EZHUD) && !defined(STATIC_OPENSSL) && !defined(STATIC_Q3) + #undef PLUGINS //pointless + #endif #undef VM_Q1 //no dlls #undef MAP_PROC //meh // #undef HALFLIFEMODELS //blurgh @@ -392,15 +393,18 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifdef NO_GNUTLS #undef HAVE_GNUTLS #endif +#ifdef NO_WINSSPI + #undef HAVE_WINSSPI +#endif #ifdef NO_OPENGL #undef GLQUAKE #undef USE_EGL #endif -#if defined(HAVE_GNUTLS) || defined(HAVE_WINSSPI) || defined(HAVE_PLUGINS) +#if (defined(HAVE_GNUTLS) || defined(HAVE_WINSSPI) || defined(PLUGINS)) && !defined(FTE_TARGET_WEB) #define HAVE_SSL #endif -#if defined(HAVE_GNUTLS) || defined(HAVE_WINSSPI) || defined(HAVE_PLUGINS) +#if (defined(HAVE_GNUTLS) || defined(HAVE_WINSSPI) || defined(PLUGINS)) && !defined(FTE_TARGET_WEB) //FIXME: HAVE_WINSSPI does not work as a server. //FIXME: advertising dtls without a valid certificate will probably bug out if a client tries to auto-upgrade. //FIXME: we don't cache server certs @@ -456,7 +460,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef HAVE_TCP #undef TCPCONNECT #undef IRCCONNECT - #undef WEBSERVER //http server #undef FTPSERVER //ftp server #undef FTPCLIENT //ftp client. #if !defined(FTE_TARGET_WEB) @@ -488,7 +491,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef Q2SERVER #undef Q3SERVER #undef HLSERVER - #undef WEBSERVER #undef FTPSERVER #undef SUBSERVERS #undef VM_Q1 @@ -558,6 +560,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #else #define IFMINIMAL(x,y) y #endif +#ifdef FTE_TARGET_WEB + #define IFWEB(x,y) x +#else + #define IFWEB(x,y) y +#endif // defs common to client and server @@ -902,8 +909,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define MAX_NET_LIGHTSTYLES (INVALID_LIGHTSTYLE+1) // 16bit. the last index MAY be used to signify an invalid lightmap in the bsp, but is still valid for rtlights. #define MAX_STANDARDLIGHTSTYLES 64 -#define MAX_PRECACHE_MODELS 4096 // 14bit. -#define MAX_PRECACHE_SOUNDS 2048 // 14bit. +#define MAX_PRECACHE_MODELS 16384 // 14bit. +#define MAX_PRECACHE_SOUNDS 4096 // 14bit. #define MAX_SSPARTICLESPRE 1024 // 14bit. precached particle effect names, for server-side pointparticles/trailparticles. #define MAX_VWEP_MODELS 32 diff --git a/engine/common/bspfile.h b/engine/common/bspfile.h index fd44af050..3886d2971 100644 --- a/engine/common/bspfile.h +++ b/engine/common/bspfile.h @@ -177,19 +177,19 @@ enum q1contents_e Q1CONTENTS_SLIME = -4, Q1CONTENTS_LAVA = -5, Q1CONTENTS_SKY = -6, -//#define Q1CONTENTS_ORIGIN -7 /*not known to engine - origin or something*/ - Q1CONTENTS_CLIP = -8, /*solid to players+monsters, but not tracelines*/ - Q1CONTENTS_CURRENT_0 = -9, /*moves player*/ - Q1CONTENTS_CURRENT_90 = -10, /*moves player*/ - Q1CONTENTS_CURRENT_180 = -11, /*moves player*/ - Q1CONTENTS_CURRENT_270 = -12, /*moves player*/ - Q1CONTENTS_CURRENT_UP = -13, /*moves player*/ - Q1CONTENTS_CURRENT_DOWN = -14, /*moves player*/ - Q1CONTENTS_TRANS = -15, /*should be solid I guess*/ +//#define HLCONTENTS_ORIGIN -7 /*not known to engine - origin or something*/ + HLCONTENTS_CLIP = -8, /*solid to players+monsters, but not tracelines*/ + HLCONTENTS_CURRENT_0 = -9, /*moves player*/ + HLCONTENTS_CURRENT_90 = -10, /*moves player*/ + HLCONTENTS_CURRENT_180 = -11, /*moves player*/ + HLCONTENTS_CURRENT_270 = -12, /*moves player*/ + HLCONTENTS_CURRENT_UP = -13, /*moves player*/ + HLCONTENTS_CURRENT_DOWN = -14, /*moves player*/ + HLCONTENTS_TRANS = -15, /*empty, but blocks pvs (for opaque non-solid windows, like vanilla-q1 water)*/ Q1CONTENTS_LADDER = -16, /*player can climb up/down*/ Q1CONTENTS_MONSTERCLIP = -17, /*solid to monster movement*/ Q1CONTENTS_PLAYERCLIP = -18, /*solid to player movement*/ - Q1CONTENTS_CORPSE = -19, /*solid to tracelines*/ + Q1CONTENTS_CORPSE = -19, /*solid to tracelines but not boxes*/ }; // !!! if this is changed, it must be changed in asm_i386.h too !!! @@ -572,7 +572,7 @@ typedef struct #define Q2CONTENTS_ORIGIN 0x01000000 // removed before bsping an entity #define Q2CONTENTS_MONSTER FTECONTENTS_BODY //0x02000000 // should never be on a brush, only in game #define Q2CONTENTS_DEADMONSTER FTECONTENTS_CORPSE //0x04000000 -#define Q2CONTENTS_DETAIL 0x08000000 // brushes to be added after vis leafs +#define Q2CONTENTS_DETAIL FTECONTENTS_DETAIL // 0x08000000 // brushes to be added after vis leafs #define Q2CONTENTS_TRANSLUCENT 0x10000000 // auto set if any surface has trans #define Q2CONTENTS_LADDER 0x20000000 //0x40000000 @@ -606,7 +606,7 @@ typedef struct #define Q3CONTENTS_ORIGIN Q2CONTENTS_ORIGIN //0x01000000 #define Q3CONTENTS_BODY FTECONTENTS_BODY //0x02000000 #define Q3CONTENTS_CORPSE FTECONTENTS_CORPSE //0x04000000 -#define Q3CONTENTS_DETAIL Q2CONTENTS_DETAIL //0x08000000 +#define Q3CONTENTS_DETAIL FTECONTENTS_DETAIL //0x08000000 #define Q3CONTENTS_STRUCTURAL 0x10000000 #define Q3CONTENTS_TRANSLUCENT 0x20000000 #define Q3CONTENTS_TRIGGER 0x40000000 @@ -649,11 +649,11 @@ typedef struct //#define TI_KINGPIN_WNDW33 0x4000 //#define TI_KINGPIN_WNDW64 0x8000 -#define TI_Q2EX_ALPHATEST (1u<<25) -#define TI_N64_UV (1u<<28) -#define TI_N64_SCROLL_X (1u<<29) -#define TI_N64_SCROLL_Y (1u<<30) -#define TI_N64_SCROLL_FLIP (1u<<31) +#define TI_Q2EX_ALPHATEST (1u<<25) //seems to be a thing in other pre q2e engines too. +#define TI_N64_UV (1u<<28) //2 qu per texel instead of 1... (still 16qu per luxel) +#define TI_N64_SCROLL_X (1u<<29) //scrolls fully right each second. +#define TI_N64_SCROLL_Y (1u<<30) //scrolls fully down each second. +#define TI_N64_SCROLL_FLIP (1u<<31) //reverses the scroll dirs. //Surface flags //#define Q3SURFACEFLAG_NODAMAGE 0x1 // never give falling damage diff --git a/engine/common/cmd.c b/engine/common/cmd.c index ba140d0b0..cf22c1784 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -36,6 +36,7 @@ static const cvar_t dpcompat_console = {0}; int Cmd_ExecLevel; qboolean cmd_didwait; qboolean cmd_blockwait; +static qboolean cmd_stuffedcmdline; void Cmd_ForwardToServer (void); @@ -590,68 +591,67 @@ quake -nosound +cmd amlev1 */ void Cmd_StuffCmds (void) { - int i, j; + int i; int s; - char *text, *build, c; + char *text; + const char *arg; + int pluscmd = false; + if (cmd_stuffedcmdline) + return; //don't do it multiple times // build the combined string to parse from - s = 0; + s = 2; for (i=1 ; i= '0' && arg[1] <= '9')) //we only really want the '+foo arg' commands, split by +/- prefixes. if its a -1 or +1 then that's a numerical parameter, not a separate command! { - Q_strcat (text,"\""); - Q_strcat (text,com_argv[i]); - Q_strcat (text,"\""); + if (*text) + { + Q_strcat (text, "\n"); + Cbuf_AddText (text, RESTRICT_LOCAL); + *text = 0; + } + pluscmd = *arg++ == '+'; + } + + if (pluscmd) + { + if (*text) + Q_strcat (text, " "); + if (strchr(arg, ' ') || strchr(arg, '\t') || strchr(arg, '@') || strchr(arg, '/') || strchr(arg, '\\')) + COM_QuotedString(arg, text+strlen(text),s-strlen(text), false); + else + Q_strcat (text,arg); } - else - Q_strcat (text,com_argv[i]); - if (i != com_argc-1) - Q_strcat (text, " "); } -// pull out the commands - build = (char*)Z_Malloc (s+1); - build[0] = 0; - - for (i=0 ; ifrac[l] = fs->lerpweight[b]; lerps->needsfree[l] = BZ_Malloc(sizeof(float)*12*numbones); - lerps->pose[l] = g->GetRawBones(inf, g, time, lerps->needsfree[l], numbones); + lerps->pose[l] = g->GetRawBones(inf, g, time, lerps->needsfree[l], boneinf, numbones); if (lerps->pose[l]) l++; else @@ -1107,14 +1107,14 @@ static qboolean Alias_BuildSkelLerps(skellerps_t *lerps, const struct framestate { totalweight += lerps->frac[l]; lerps->needsfree[l] = NULL; - lerps->pose[l++] = (float*)g->boneofs + numbones*12*frame1; + lerps->pose[l++] = (float*)g->boneofs + inf->numbones*12*frame1; } lerps->frac[l] = (mlerp)*fs->lerpweight[b]; if (lerps->frac[l]>0) { totalweight += lerps->frac[l]; lerps->needsfree[l] = NULL; - lerps->pose[l++] = (float*)g->boneofs + numbones*12*frame2; + lerps->pose[l++] = (float*)g->boneofs + inf->numbones*12*frame2; } } } @@ -1153,16 +1153,13 @@ static qboolean Alias_BuildSkelLerps(skellerps_t *lerps, const struct framestate /* finds the various blend info. returns number of bone blocks used. */ -static int Alias_FindRawSkelData(galiasinfo_t *inf, const framestate_t *fstate, skellerps_t *lerps, size_t firstbone, size_t lastbone) +static int Alias_FindRawSkelData(galiasinfo_t *inf, const framestate_t *fstate, skellerps_t *lerps, size_t firstbone, size_t lastbone, const galiasbone_t *boneinfo) { int bonegroup; int cbone = 0; int endbone; int numbonegroups=0; - if (lastbone > inf->numbones) - lastbone = inf->numbones; - for (bonegroup = 0; bonegroup < FS_COUNT; bonegroup++) { endbone = fstate->g[bonegroup].endbone; @@ -1176,7 +1173,7 @@ static int Alias_FindRawSkelData(galiasinfo_t *inf, const framestate_t *fstate, if (lerps->firstbone == lerps->endbone) continue; - if (!inf->numanimations || !Alias_BuildSkelLerps(lerps, &fstate->g[bonegroup], inf->numbones, inf)) //if there's no animations in this model, use the base pose instead. + if (!inf->numanimations || !Alias_BuildSkelLerps(lerps, &fstate->g[bonegroup], boneinfo, lerps->endbone, inf)) //if there's no animations in this model, use the base pose instead. { if (!inf->baseframeofs) continue; //nope, not happening. @@ -1200,11 +1197,11 @@ static int Alias_FindRawSkelData(galiasinfo_t *inf, const framestate_t *fstate, return value is the lastbone argument, or less if the model simply doesn't have that many bones. _always_ writes into result */ -static int Alias_BlendBoneData(galiasinfo_t *inf, framestate_t *fstate, float *result, skeltype_t skeltype, int firstbone, int lastbone) +static int Alias_BlendBoneData(galiasinfo_t *inf, const framestate_t *fstate, float *result, skeltype_t skeltype, int firstbone, int lastbone, const galiasbone_t *boneinfo) { skellerps_t lerps[FS_COUNT], *lerp; size_t bone, endbone = 0; - size_t numgroups = Alias_FindRawSkelData(inf, fstate, lerps, firstbone, lastbone); + size_t numgroups = Alias_FindRawSkelData(inf, fstate, lerps, firstbone, lastbone, boneinfo); float *pose, *matrix; int k, b; @@ -1219,7 +1216,7 @@ static int Alias_BlendBoneData(galiasinfo_t *inf, framestate_t *fstate, float *r memcpy(result+bone*12, lerp->pose[0]+bone*12, (endbone-bone)*12*sizeof(float)); else { - //set up the identity matrix + //blend each influence for (; bone < endbone; bone++) { pose = result + 12*bone; @@ -1247,7 +1244,7 @@ static int Alias_BlendBoneData(galiasinfo_t *inf, framestate_t *fstate, float *r only writes targetbuffer if needed. the return value is the only real buffer result. assumes that all blended types are the same. probably buggy, but meh. */ -static const float *Alias_GetBoneInformation(galiasinfo_t *inf, const framestate_t *framestate, skeltype_t targettype, float *targetbuffer, float *targetbufferalt, size_t maxbufferbones) +static const float *Alias_GetBoneInformation(galiasinfo_t *inf, const framestate_t *framestate, skeltype_t targettype, float *targetbuffer, float *targetbufferalt, size_t numbones, const galiasbone_t *boneinfo) { skellerps_t lerps[FS_COUNT], *lerp; size_t numgroups; @@ -1256,7 +1253,7 @@ static const float *Alias_GetBoneInformation(galiasinfo_t *inf, const framestate lerps[0].skeltype = SKEL_IDENTITY; //just in case. #ifdef SKELETALOBJECTS - if (framestate->bonestate && framestate->bonecount >= inf->numbones) + if (framestate->bonestate && framestate->bonecount >= numbones) { lerps[0].skeltype = framestate->skeltype; lerps[0].firstbone = 0; @@ -1270,13 +1267,18 @@ static const float *Alias_GetBoneInformation(galiasinfo_t *inf, const framestate else #endif { - numgroups = Alias_FindRawSkelData(inf, framestate, lerps, 0, inf->numbones); + numgroups = Alias_FindRawSkelData(inf, framestate, lerps, 0, numbones, boneinfo); } //try to return data in-place. if (numgroups==1 && lerps[0].lerpcount == 1) { - ret = Alias_ConvertBoneData(lerps[0].skeltype, lerps[0].pose[0], min(lerps[0].endbone, inf->numbones), inf->ofsbones, targettype, targetbuffer, targetbufferalt, maxbufferbones); + ret = Alias_ConvertBoneData(lerps[0].skeltype, lerps[0].pose[0], min(lerps[0].endbone, inf->numbones), inf->ofsbones, targettype, targetbuffer, targetbufferalt, numbones); + if (ret == lerps[0].needsfree[0]) + { //bum + memcpy(targetbuffer, ret, sizeof(float)*min(lerps[0].endbone, numbones)*12); + ret = targetbuffer; + } BZ_Free(lerps[0].needsfree[0]); return ret; } @@ -1365,7 +1367,7 @@ static const float *Alias_GetBoneInformation(galiasinfo_t *inf, const framestate } } - return Alias_ConvertBoneData(lerps[0].skeltype, targetbuffer, inf->numbones, inf->ofsbones, targettype, targetbuffer, targetbufferalt, maxbufferbones); + return Alias_ConvertBoneData(lerps[0].skeltype, targetbuffer, inf->numbones, inf->ofsbones, targettype, targetbuffer, targetbufferalt, numbones); } static void Alias_BuildSkeletalMesh(mesh_t *mesh, framestate_t *framestate, galiasinfo_t *inf) @@ -1375,9 +1377,9 @@ static void Alias_BuildSkeletalMesh(mesh_t *mesh, framestate_t *framestate, gali const float *morphweights; if (meshcache.bonecachetype != SKEL_INVERSE_ABSOLUTE) - meshcache.usebonepose = Alias_GetBoneInformation(inf, framestate, meshcache.bonecachetype=SKEL_INVERSE_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, MAX_BONES); + meshcache.usebonepose = Alias_GetBoneInformation(inf, framestate, meshcache.bonecachetype=SKEL_INVERSE_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, inf->numbones, NULL); - morphweights = inf->AnimateMorphs?inf->AnimateMorphs(inf, framestate):NULL; + morphweights = inf->AnimateMorphs?inf->AnimateMorphs(inf, framestate, alloca(sizeof(*morphweights)*inf->nummorphs)):NULL; if (morphweights) { size_t m,v; @@ -1820,6 +1822,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in { //if we have skeletal xyz info, but no skeletal weights, then its a partial model that cannot possibly be animated. meshcache.usebonepose = NULL; + meshcache.bonecachetype = -1; mesh->xyz_array = inf->ofs_skel_xyz; mesh->xyz2_array = NULL; mesh->normals_array = inf->ofs_skel_norm; @@ -1875,7 +1878,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in else { if (meshcache.bonecachetype != SKEL_ABSOLUTE) - meshcache.usebonepose = Alias_GetBoneInformation(inf, &e->framestate, meshcache.bonecachetype=SKEL_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, MAX_BONES); + meshcache.usebonepose = Alias_GetBoneInformation(inf, &e->framestate, meshcache.bonecachetype=SKEL_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, MAX_BONES, NULL); #ifndef SERVERONLY if (inf->shares_bones != surfnum && qrenderer) Alias_DrawSkeletalBones(inf->ofsbones, (const float *)meshcache.usebonepose, inf->numbones, e->framestate.g[0].endbone); @@ -1885,7 +1888,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in else { if (meshcache.bonecachetype != SKEL_INVERSE_ABSOLUTE) - meshcache.usebonepose = Alias_GetBoneInformation(inf, &e->framestate, meshcache.bonecachetype=SKEL_INVERSE_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, MAX_BONES); + meshcache.usebonepose = Alias_GetBoneInformation(inf, &e->framestate, meshcache.bonecachetype=SKEL_INVERSE_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, MAX_BONES, NULL); //hardware bone animation mesh->xyz_array = inf->ofs_skel_xyz; @@ -2632,7 +2635,7 @@ static qboolean Mod_Trace(model_t *model, int forcehullnum, const framestate_t * if (curbonesurf != mod->shares_bones) { curbonesurf = mod->shares_bones; - bonepose = Alias_GetBoneInformation(mod, framestate, SKEL_INVERSE_ABSOLUTE, buffer, bufferalt, MAX_BONES); + bonepose = Alias_GetBoneInformation(mod, framestate, SKEL_INVERSE_ABSOLUTE, buffer, bufferalt, MAX_BONES, NULL); } posedata = alloca(mod->numverts*sizeof(vecV_t)); Alias_TransformVerticies_V(bonepose, mod->numverts, mod->ofs_skel_idx[0], mod->ofs_skel_weight[0], mod->ofs_skel_xyz[0], posedata[0]); @@ -3170,7 +3173,7 @@ static void Mod_GenerateMeshVBO(model_t *mod, galiasinfo_t *galias) } } else - Con_DPrintf("\"%s\":\"%s\" exceeds gpu bone limit and will be software-skinned - %i > %i\n", mod->name, galias->surfacename, k, sh_config.max_gpu_bones); + Con_DPrintf(CON_WARNING"PERF: \"%s\":\"%s\" exceeds gpu bone limit and will be software-skinned - %i > %i\n", mod->name, galias->surfacename, k, sh_config.max_gpu_bones); } if (galias->mappedbones) { @@ -3834,8 +3837,6 @@ static void Mod_FloodFillSkin( qbyte *skin, int skinwidth, int skinheight ) static void *Q1MDL_LoadSkins_GL (galiasinfo_t *galias, dmdl_t *pq1inmodel, model_t *loadmodel, daliasskintype_t *pskintype, uploadfmt_t skintranstype) { skinframe_t *frames; - char skinname[MAX_QPATH]; - char alttexpath[MAX_QPATH]; int i; int s, t; float sinter; @@ -3843,8 +3844,6 @@ static void *Q1MDL_LoadSkins_GL (galiasinfo_t *galias, dmdl_t *pq1inmodel, model daliasskininterval_t *intervals; qbyte *data, *saved; galiasskin_t *outskin = galias->ofsskins; - const char *slash; - unsigned int texflags; s = pq1inmodel->skinwidth*pq1inmodel->skinheight; for (i = 0; i < pq1inmodel->numskins; i++) @@ -3857,37 +3856,15 @@ static void *Q1MDL_LoadSkins_GL (galiasinfo_t *galias, dmdl_t *pq1inmodel, model //but only preload it if we have no replacement. outskin->numframes=1; - if (1 || /*!TEXVALID(texture) ||*/ (loadmodel->engineflags & MDLF_NOTREPLACEMENTS)) - { - //we're not using 24bits - frames = ZG_Malloc(&loadmodel->memgroup, sizeof(*frames)+s); - saved = (qbyte*)(frames+1); - frames[0].texels = saved; - memcpy(saved, pskintype+1, s); - if (i == 0) //Vanilla bug: ONLY skin 0 is flood-filled (the vanilla code operates on a cached 'skin' variable that does NOT get updated between skins reflooding skin 0). We still don't like flood fills either. Hexen2 has the same issue. - Mod_FloodFillSkin(saved, outskin->skinwidth, outskin->skinheight); - } - else - { - frames = ZG_Malloc(&loadmodel->memgroup, sizeof(*frames)); - Q_snprintfz(skinname, sizeof(skinname), "%s_%i.lmp", slash, i); - frames[0].texnums.base = R_LoadReplacementTexture(skinname, alttexpath, texflags, frames[0].texels, outskin->skinwidth, outskin->skinheight, skintranstype); - if (r_fb_models.ival) - { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_luma.lmp", slash, i); - frames[0].texnums.fullbright = R_LoadReplacementTexture(skinname, alttexpath, texflags, frames[0].texels, outskin->skinwidth, outskin->skinheight, TF_TRANS8_FULLBRIGHT); - } - if (r_loadbumpmapping) - { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_norm.lmp", slash, i); - frames[0].texnums.bump = R_LoadReplacementTexture(skinname, alttexpath, texflags|IF_TRYBUMP|IF_NOSRGB, frames[0].texels, outskin->skinwidth, outskin->skinheight, TF_HEIGHT8PAL); - } - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_shirt.lmp", slash, i); - frames[0].texnums.upperoverlay = R_LoadReplacementTexture(skinname, alttexpath, texflags, NULL, outskin->skinwidth, outskin->skinheight, TF_INVALID); - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_pants.lmp", slash, i); - frames[0].texnums.loweroverlay = R_LoadReplacementTexture(skinname, alttexpath, texflags, NULL, outskin->skinwidth, outskin->skinheight, TF_INVALID); - } + //the actual texture gets loaded after the shader. + frames = ZG_Malloc(&loadmodel->memgroup, sizeof(*frames)+s); + saved = (qbyte*)(frames+1); + frames[0].texels = saved; + memcpy(saved, pskintype+1, s); + if (i == 0) //Vanilla bug: ONLY skin 0 is flood-filled (the vanilla code operates on a cached 'skin' variable that does NOT get updated between skins reflooding skin 0). We still don't like flood fills either. Hexen2 has the same issue. + Mod_FloodFillSkin(saved, outskin->skinwidth, outskin->skinheight); + Q_snprintfz(frames[0].shadername, sizeof(frames[0].shadername), "%s_%i.lmp", loadmodel->name, i); frames[0].shader = NULL; frames[0].defaultshader = NULL; @@ -5135,11 +5112,11 @@ int Mod_GetNumBones(model_t *model, qboolean allowtags) return 0; } -int Mod_GetBoneRelations(model_t *model, int firstbone, int lastbone, framestate_t *fstate, float *result) +int Mod_GetBoneRelations(model_t *model, int firstbone, int lastbone, const galiasbone_t *boneinfo, const framestate_t *fstate, float *result) { #ifdef SKELETALMODELS if (model && model->type == mod_alias) - return Alias_BlendBoneData(Mod_Extradata(model), fstate, result, SKEL_RELATIVE, firstbone, lastbone); + return Alias_BlendBoneData(Mod_Extradata(model), fstate, result, SKEL_RELATIVE, firstbone, lastbone, boneinfo); #endif #ifdef HALFLIFEMODELS if (model && model->type == mod_halflife) @@ -5156,7 +5133,10 @@ galiasbone_t *Mod_GetBoneInfo(model_t *model, int *numbones) if (!model || model->type != mod_alias) + { + *numbones = 0; return NULL; + } inf = Mod_Extradata(model); @@ -5252,7 +5232,7 @@ qboolean Mod_GetTag(model_t *model, int tagnum, framestate_t *fstate, float *res } else //try getting the data from the frame state { - numbones = Mod_GetBoneRelations(model, 0, tagnum+1, fstate, relatives); + numbones = Mod_GetBoneRelations(model, 0, tagnum+1, NULL, fstate, relatives); lerps = relatives; } @@ -5323,7 +5303,7 @@ qboolean Mod_GetTag(model_t *model, int tagnum, framestate_t *fstate, float *res //try getting the data from the frame state if (!numbonegroups) - numbonegroups = Alias_FindRawSkelData(inf, fstate, lerps, 0, inf->numbones); + numbonegroups = Alias_FindRawSkelData(inf, fstate, lerps, 0, inf->numbones, NULL); //try base pose? if (!numbonegroups && inf->baseframeofs) @@ -5677,7 +5657,7 @@ const char *Mod_FrameNameForNum(model_t *model, int surfaceidx, int num) while(surfaceidx-->0 && inf) inf = inf->nextsurf; - if (!inf || num >= inf->numanimations) + if (!inf || (unsigned)num >= (unsigned)inf->numanimations) return NULL; group = inf->ofsanimations; return group[num].name; diff --git a/engine/common/com_mesh.h b/engine/common/com_mesh.h index 3a6220934..abcde24d7 100644 --- a/engine/common/com_mesh.h +++ b/engine/common/com_mesh.h @@ -49,20 +49,27 @@ typedef struct galiasevent_s char *data; } galiasevent_t; +typedef struct galiasrefpose_s +{ + vec4_t quat; + vec3_t org; + vec3_t scale; +} galiasrefpose_t; + //a frame group (aka: animation) typedef struct galiasanimation_s { #ifdef SKELETALMODELS skeltype_t skeltype; //for models with transforms, states that bones need to be transformed from their parent. //this is actually bad, and can result in bones shortening as they interpolate. - float *(QDECL *GetRawBones)(const struct galiasinfo_s *mesh, const struct galiasanimation_s *a, float time, float *bonematrixstorage, int numbones); + float *(QDECL *GetRawBones)(const struct galiasinfo_s *mesh, const struct galiasanimation_s *a, float time, float *bonematrixstorage, const struct galiasbone_s *boneinf, int numbones); void *boneofs; //numposes*12*numbones #endif qboolean loop; int numposes; //float *poseendtime; //first starts at 0, anim duration is poseendtime[numposes-1] float rate; //average framerate of animation. - int action; + int action; //-1 for none. float actionweight; #ifdef NONSKELETALMODELS galiaspose_t *poseofs; @@ -76,9 +83,15 @@ typedef struct galiasbone_s galiasbone_t; struct galiasbone_s { char name[64]; +#if MAX_BONES>32767 int parent; +#else + short parent; +#endif + unsigned char group; // float radius; float inverse[12]; + galiasrefpose_t ref; }; typedef struct @@ -180,10 +193,10 @@ typedef struct galiasinfo_s #ifdef SKELETALMODELS boneidx_t *bonemap; //filled in automatically if our mesh has more gpu bones than we can support - unsigned int mappedbones; + unsigned int mappedbones; //number of private per-mesh bones. unsigned int nummorphs; //extra data after the xyz/norm/stvect arrays - const float *(QDECL *AnimateMorphs)(const struct galiasinfo_s *surf, const framestate_t *framestate); - int meshrootbone; + const float *(QDECL *AnimateMorphs)(const struct galiasinfo_s *surf, const framestate_t *framestate, float *resultmorphs); //returns a float weight[nummorphs] array (base verts have an implicit weight of 1, so these are purely additive) + int meshrootbone; //unused by engine. for loader callbacks to (ab)use float *baseframeofs; /*non-heirachical*/ int numbones; @@ -246,6 +259,7 @@ typedef struct modplugfuncs_s void (QDECL *VectorAngles)(const float *forward, const float *up, float *result, qboolean meshpitch); void (QDECL *AngleVectors)(const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); void (QDECL *GenMatrixPosQuat4Scale)(const vec3_t pos, const vec4_t quat, const vec3_t scale, float result[12]); + void (QDECL *QuaternionSlerp)(const vec4_t p, vec4_t q, float t, vec4_t qt); //bone stuff void (QDECL *ForceConvertBoneData)(skeltype_t sourcetype, const float *sourcedata, size_t bonecount, galiasbone_t *bones, skeltype_t desttype, float *destbuffer, size_t destbonecount); @@ -267,13 +281,10 @@ typedef struct modplugfuncs_s void (*RenderDynamicLightmaps) (struct msurface_s *surf); entity_t *(*NewSceneEntity) (void); void (*EndSubmodelLoad)(struct model_s *submod, int modelloadstate); -#if sizeof_index_t==2 - #define plugmodfuncs_name "Models" -#else - #define plugmodfuncs_name "Models_IDX" STRINGIFY(sizeof_index_t) -#endif + #define plugmodfuncs_name_idxpostfix "_IDX" STRINGIFY(sizeof_index_t) +#define plugmodfuncs_name "Models" plugmodfuncs_name_idxpostfix } plugmodfuncs_t; -#define MODPLUGFUNCS_VERSION 3 +#define MODPLUGFUNCS_VERSION 4 #ifdef SKELETALMODELS void Alias_TransformVerticies(float *bonepose, galisskeletaltransforms_t *weights, int numweights, vecV_t *xyzout, vec3_t *normout); diff --git a/engine/common/common.c b/engine/common/common.c index 42804c0da..888e04f32 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -1273,7 +1273,7 @@ int MSG_ReadSize16 (sizebuf_t *sb) { int solid = (((ssolid>>7) & 0x1F8) - 32+32768)<<16; /*up can be negative*/ solid|= ((ssolid & 0x1F)<<3); - solid|= ((ssolid & 0x3E0)<<10); + solid|= ((ssolid & 0x3E0)<<6); return solid; } } @@ -2192,6 +2192,20 @@ int MSG_ReadChar (void) return c; } +int MSG_PeekByte(void) +{ + unsigned int msg_readcount; + + if (msg_readmsg->packing!=SZ_RAWBYTES) + return -1; + + msg_readcount = msg_readmsg->currentbit>>3; + if (msg_readcount+1 > msg_readmsg->cursize) + return -1; + + return (unsigned char)msg_readmsg->data[msg_readcount]; +} + int MSG_ReadByte (void) { unsigned char c; @@ -2625,6 +2639,7 @@ void MSGQW_ReadDeltaUsercmd (const usercmd_t *from, usercmd_t *move, int protove } } +#ifdef HAVE_SERVER void MSGQ2_ReadDeltaUsercmd (client_t *cl, const usercmd_t *from, usercmd_t *move) { int bits; @@ -2729,6 +2744,7 @@ void MSGQ2_ReadDeltaUsercmd (client_t *cl, const usercmd_t *from, usercmd_t *mov else move->lightlevel = MSG_ReadByte (); } +#endif void MSG_ReadData (void *data, int len) { @@ -5671,7 +5687,11 @@ static void COM_Version_f (void) Con_Printf("Renderers:"); #ifdef GLQUAKE #ifdef GLESONLY - Con_Printf(" OpenGLES"); + #ifdef FTE_TARGET_WEB //shuld we be just asking the video code for a list?... + Con_Printf(" WebGL"); + #else + Con_Printf(" OpenGLES"); + #endif #else Con_Printf(" OpenGL"); #endif @@ -5711,6 +5731,10 @@ static void COM_Version_f (void) Con_Printf("Compiled with Cygwin\n"); #endif +#ifdef FTE_TARGET_WEB + Con_Printf("Compiled with emscripten %i.%i.%i\n", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__); +#endif + #ifdef __clang__ Con_Printf("Compiled with clang version: %i.%i.%i (%s)\n",__clang_major__, __clang_minor__, __clang_patchlevel__, __VERSION__); #elif defined(__GNUC__) @@ -5831,28 +5855,36 @@ static void COM_Version_f (void) #if !defined(VOICECHAT) Con_Printf(" disabled"); #else - #ifdef SPEEX_STATIC - Con_Printf(" speex"); - Con_DPrintf("^h(static)"); - #else - Con_Printf(" speex^h(dynamic)"); + #ifdef HAVE_SPEEX + #ifdef SPEEX_STATIC + Con_Printf(" speex"); + Con_DPrintf("^h(static)"); + #else + Con_Printf(" speex^h(dynamic)"); + #endif #endif - #ifdef OPUS_STATIC - Con_Printf(" opus"); - Con_DPrintf("^h(static)"); - #else - Con_Printf(" opus^h(dynamic)"); + #ifdef HAVE_OPUS + #ifdef OPUS_STATIC + Con_Printf(" opus"); + Con_DPrintf("^h(static)"); + #else + Con_Printf(" opus^h(dynamic)"); + #endif #endif #endif Con_Printf("\n"); Con_Printf("^3Audio Decoders:^7"); + #ifdef FTE_TARGET_WEB + Con_DPrintf(" javascript"); + #endif #ifndef AVAIL_OGGVORBIS Con_DPrintf(" ^h(disabled: Ogg Vorbis)^7"); #elif defined(LIBVORBISFILE_STATIC) - Con_Printf(" Ogg Vorbis"); + Con_Printf(" Ogg-Vorbis"); + Con_DPrintf("^h(static)"); #else - Con_Printf(" Ogg Vorbis^h(dynamic)"); + Con_Printf(" Ogg-Vorbis^h(dynamic)"); #endif #if defined(AVAIL_MP3_ACM) Con_Printf(" mp3(system)"); @@ -5893,7 +5925,12 @@ static void COM_Version_f (void) Con_DPrintf(" ^h(disabled: freetype2)^7"); #endif #ifdef AVAIL_OPENAL - Con_Printf(" openal^h(dynamic)"); + #ifdef FTE_TARGET_WEB + Con_Printf(" WebAudio"); + Con_DPrintf("^h(static)"); + #else + Con_Printf(" OpenAL^h(dynamic)"); + #endif #else Con_DPrintf(" ^h(disabled: openal)^7"); #endif @@ -5986,12 +6023,19 @@ static void COM_Version_f (void) #ifdef FTPSERVER Con_Printf(" FTPServer"); #endif -#ifdef HAVE_TCP -#ifdef TCPCONNECT - Con_Printf(" TCPConnect"); +#if (defined(SUPPORT_ICE)&&defined(HAVE_DTLS)) || defined(FTE_TARGET_WEB) + Con_Printf(" WebRTC"); #endif +#ifdef FTE_TARGET_WEB + Con_Printf(" WebSocket/WSS"); #else - Con_Printf(" ^h(disabled: TCP)"); + #if defined(HAVE_TCP) + #ifdef TCPCONNECT + Con_Printf(" TCPConnect"); + #endif + #else + Con_Printf(" ^h(disabled: TCP)"); + #endif #endif #ifdef HAVE_GNUTLS //on linux Con_Printf(" GnuTLS"); @@ -6705,9 +6749,6 @@ void COM_Init (void) //random should be random from the start... srand(time(0)); -#ifdef MULTITHREAD - Sys_ThreadsInit(); -#endif #ifdef LOADERTHREAD COM_InitWorkerThread(); #endif diff --git a/engine/common/common.h b/engine/common/common.h index 0de5cc3f4..3d61a08b0 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -31,7 +31,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #define VK_NO_STDINT_H //we're handling this. please don't cause conflicts. grr. -#if __STDC_VERSION__ >= 199901L || defined(__GNUC__) +#if __STDC_VERSION__ >= 199901L || defined(__GNUC__) || _MSC_VER >= 1600 //C99 has a stdint header which hopefully contains an intptr_t //its optional... but if its not in there then its unlikely you'll actually be able to get the engine to a stage where it *can* load anything #include @@ -43,6 +43,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define quint32_t uint32_t #define qint64_t int64_t #define quint64_t uint64_t + #define qintmax_t intmax_t + #define quintmax_t uintmax_t #else #define qint8_t signed char //be explicit with this one. #define quint8_t unsigned char @@ -85,6 +87,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define quint64_t unsigned qint64_t #endif + #define qintmax_t qint64_t + #define quintmax_t quint64_t + #ifndef uint32_t #define int8_t qint8_t #define uint8_t quint8_t @@ -96,6 +101,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define uint64_t quint64_t #define intptr_t qintptr_t #define uintptr_t quintptr_t + #define intmax_t qintmax_t + #define uintmax_t quintmax_t #endif #endif @@ -345,6 +352,8 @@ void MSG_WriteDir (sizebuf_t *sb, float dir[3]); extern qboolean msg_badread; // set if a read goes beyond end of message extern struct netprim_s msg_nullnetprim; +int MSG_PeekByte(void); + void MSG_BeginReading (sizebuf_t *sb, struct netprim_s prim); void MSG_ChangePrimitives(struct netprim_s prim); int MSG_GetReadCount(void); @@ -381,7 +390,6 @@ void MSGQ2_ReadDeltaUsercmd (struct client_s *cl, const struct usercmd_s *from, void MSG_ReadData (void *data, int len); void MSG_ReadSkip (int len); - int MSG_ReadSize16 (sizebuf_t *sb); void MSG_WriteSize16 (sizebuf_t *sb, unsigned int sz); void COM_DecodeSize(int solid, float *mins, float *maxs); @@ -568,6 +576,7 @@ typedef struct searchpath_s char logicalpath[MAX_OSPATH]; //printable hunam-readable location of the package. generally includes a system path, including nested packages. char purepath[256]; //server tracks the path used to load them so it can tell the client char prefix[MAX_QPATH]; //prefix to add to each file within the archive. may also be ".." to mean ignore the top-level path. + int crc_seed; //can skip some hashes if this is cached. int crc_check; //client sorts packs according to this checksum int crc_reply; //client sends a different crc back to the server, for the paks it's actually loaded. int orderkey; //used to check to see if the paths were actually changed or not. @@ -607,6 +616,7 @@ qboolean FS_GetLocMTime(flocation_t *location, time_t *modtime); const char *FS_GetPackageDownloadFilename(flocation_t *loc); //returns only packages (or null) const char *FS_GetRootPackagePath(flocation_t *loc); //favours packages, but falls back on gamedirs. +searchpath_t *FS_GetPackage(const char *package); //for fancy stuff that should probably have its own helper... qboolean FS_GetPackageDownloadable(const char *package); char *FS_GetPackHashes(char *buffer, int buffersize, qboolean referencedonly); char *FS_GetPackNames(char *buffer, int buffersize, int referencedonly, qboolean ext); @@ -660,12 +670,12 @@ char *VFS_GETS(vfsfile_t *vf, char *buffer, size_t buflen); void VARGS VFS_PRINTF(vfsfile_t *vf, const char *fmt, ...) LIKEPRINTF(2); enum fs_relative{ - //note that many of theses paths can map to multiple system locations. FS_NativePath can vary somewhat in terms of what it returns, generally favouring writable locations rather then the path that actually contains a file. + //note that many of theses paths can map to multiple system locations. FS_SystemPath/FS_DisplayPath can vary somewhat in terms of what it returns, generally favouring writable locations rather then the path that actually contains a file. FS_BINARYPATH, //where the 'exe' is located. we'll check here for dlls too. FS_LIBRARYPATH, //for system dlls and stuff FS_ROOT, //./ (effectively -homedir if enabled, otherwise effectively -basedir arg) FS_SYSTEM, //a system path. absolute paths are explicitly allowed and expected, but not required. - +#define FS_RELATIVE_ISSPECIAL(r) ((r)= '0' && x <= '9') || (x >= 'A' && x <= 'F') || x == '-') #define ishexcode(x) ((x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f')) @@ -245,7 +248,7 @@ void VARGS Con_Printf (const char *fmt, ...) LIKEPRINTF(1); void VARGS Con_TPrintf (translation_t text, ...); void VARGS Con_DPrintf (const char *fmt, ...) LIKEPRINTF(1); //developer>=1, for stuff that's probably actually slightly useful void VARGS Con_DLPrintf (int level, const char *fmt, ...) LIKEPRINTF(2); //developer>=2, for spammy stuff -void VARGS Con_ThrottlePrintf (float *timer, int developerlevel, const char *fmt, ...); //for spammed warnings, so they don't spam prints with every single frame/call. the timer arg should be a static local. +void VARGS Con_ThrottlePrintf (float *timer, int developerlevel, const char *fmt, ...) LIKEPRINTF(3); //for spammed warnings, so they don't spam prints with every single frame/call. the timer arg should be a static local. void VARGS Con_SafePrintf (const char *fmt, ...) LIKEPRINTF(1); void Con_Footerf(console_t *con, qboolean append, const char *fmt, ...) LIKEPRINTF(3); void Con_Clear_f (void); diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 6928203b1..4ed8c3eca 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -474,7 +474,7 @@ showhelp: else col = S_COLOR_YELLOW; //cvar is changed, but won't be saved to a config so w/e. if (cmd->flags & CVAR_NOUNSAFEEXPAND) - Con_Printf("^[%s%s\\type\\%s\\tip\\"S_COLOR_YELLOW"%s^]", col, cmd->name, cmd->name, cmd->description?cmd->description:""); //cvar is changed, but won't be saved to a config so w/e. + Con_Printf("^[%s%s\\type\\%s\\tip\\