megacommit.
adds qtv relay support. lots of other misc tweaks.
This commit is contained in:
parent
effee0e45a
commit
ff1a2299f4
69 changed files with 3621 additions and 748 deletions
|
@ -1143,7 +1143,9 @@ ELSE()
|
|||
fteqtv/source.c
|
||||
fteqtv/bsp.c
|
||||
fteqtv/rcon.c
|
||||
fteqtv/relay.c
|
||||
fteqtv/mdfour.c
|
||||
engine/common/md5.c
|
||||
fteqtv/crc.c
|
||||
fteqtv/control.c
|
||||
fteqtv/forward.c
|
||||
|
@ -1155,9 +1157,9 @@ ELSE()
|
|||
)
|
||||
SET_TARGET_PROPERTIES(qtv PROPERTIES COMPILE_DEFINITIONS "${FTE_REVISON}")
|
||||
IF(WIN32)
|
||||
TARGET_LINK_LIBRARIES(qtv ws2_32 winmm ${SYS_LIBS})
|
||||
TARGET_LINK_LIBRARIES(qtv ws2_32 winmm ${SYS_LIBS} ${ZLIB_LIBRARIES})
|
||||
ELSE()
|
||||
TARGET_LINK_LIBRARIES(qtv ${SYS_LIBS})
|
||||
TARGET_LINK_LIBRARIES(qtv ${SYS_LIBS} ${ZLIB_LIBRARIES})
|
||||
ENDIF()
|
||||
SET(INSTALLTARGS ${INSTALLTARGS} qtv)
|
||||
ENDIF()
|
||||
|
|
|
@ -48,8 +48,8 @@ endif
|
|||
|
||||
ifeq ($(SVNREVISION),)
|
||||
#try subversion firstly...
|
||||
SVN_VERSION:=$(shell test -d $(BASE_DIR)/../.svn && svnversion $(BASE_DIR))
|
||||
SVN_DATE:=$(shell test -d $(BASE_DIR)/../.svn && cd $(BASE_DIR) && svn info --show-item last-changed-date --no-newline)
|
||||
# SVN_VERSION:=$(shell test -d $(BASE_DIR)/../.svn && svnversion $(BASE_DIR))
|
||||
# SVN_DATE:=$(shell test -d $(BASE_DIR)/../.svn && cd $(BASE_DIR) && svn info --show-item last-changed-date --no-newline)
|
||||
# ifeq (,$(SVN_VERSION))
|
||||
# #grab the svn version from git-svn (assuming no other modifications). this fails when there's extra commits (probably a good thing).
|
||||
# SVN_VERSION=$(shell test -d $(BASE_DIR)/../.git && git svn find-rev `git rev-parse HEAD`)
|
||||
|
@ -86,7 +86,7 @@ PNGVER=1.6.40
|
|||
OGGVER=1.3.4
|
||||
VORBISVER=1.3.7
|
||||
VULKANVER=1.3.275.0
|
||||
SDL2VER=2.26.4
|
||||
SDL2VER=2.30.4
|
||||
SCINTILLAVER=373
|
||||
OPUSVER=1.3.1
|
||||
SPEEXVER=1.2.0
|
||||
|
@ -1053,6 +1053,7 @@ endif
|
|||
ifeq (1,$(LINK_ZLIB))
|
||||
CLIENTLIBFLAGS+=-DZLIB_STATIC
|
||||
CLIENTLDDEPS+=-lz
|
||||
SERVERLDDEPS+=-lz
|
||||
|
||||
#and deflate64, because why not.
|
||||
ifneq ("$(wildcard $(ARCHLIBS)/infback9.h)","")
|
||||
|
@ -1122,7 +1123,7 @@ GLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidsdl.o snd_sdl.o cd_sdl.
|
|||
GL_EXE_NAME=../$(EXE_NAME)-gl$(FTE_FULLTARGET)
|
||||
GLCL_EXE_NAME=../$(EXE_NAME)cl-gl$(FTE_FULLTARGET)
|
||||
|
||||
#SDLCONFIG:=libs/sdl2_mingw/$(CC_MACHINE)/bin/sdl2-config --prefix=libs/sdl2_mingw/$(CC_MACHINE)
|
||||
#SDLCONFIG:=libs-$(ARCH)/sdl2_mingw/$(CC_MACHINE)/bin/sdl2-config --prefix=libs-$(ARCH)/sdl2_mingw/$(CC_MACHINE)
|
||||
ifdef windir
|
||||
GL_LDFLAGS=$(GLLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`
|
||||
VK_LDFLAGS=$(VKLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`
|
||||
|
@ -1168,6 +1169,7 @@ ifeq (,$(findstring NO_ZLIB,$(CFLAGS)))
|
|||
VK_LDFLAGS+=-lz
|
||||
M_LDFLAGS+=-lz
|
||||
QCC_LDFLAGS+=-L$(ARCHLIBS) -lz
|
||||
QTV_LDFLAGS+=-lz
|
||||
endif
|
||||
|
||||
|
||||
|
@ -1185,24 +1187,28 @@ ifeq (win_SDL,$(findstring win,$(FTE_TARGET))$(findstring _SDL,$(FTE_TARGET)))
|
|||
EXEPOSTFIX=.exe
|
||||
|
||||
CC_MACHINE:=$(shell $(CC) -dumpmachine)
|
||||
ARCH_PREDEP=$(BASE_DIR)/libs/SDL2-$(SDL2VER)/$(CC_MACHINE)/bin/sdl2-config
|
||||
SDLCONFIG=$(ARCH_PREDEP) --prefix=$(BASE_DIR)/libs/SDL2-$(SDL2VER)/$(CC_MACHINE)
|
||||
ARCH_CFLAGS=`$(SDLCONFIG) --cflags`
|
||||
ARCH_PREDEP=$(BASE_DIR)/libs-$(ARCH)/SDL2-$(SDL2VER)/$(CC_MACHINE)/bin/sdl2-config
|
||||
SDLCONFIG=$(ARCH_PREDEP) --prefix=$(BASE_DIR)/libs-$(ARCH)/SDL2-$(SDL2VER)/$(CC_MACHINE)
|
||||
CLIENTLIBFLAGS+=`$(SDLCONFIG) --cflags`
|
||||
|
||||
#the defaults for sdl come first
|
||||
GLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(LTO_END) resources.o $(LTO_START)
|
||||
GL_EXE_NAME=../$(EXE_NAME)-sdl-gl$(BITS)$(EXEPOSTFIX)
|
||||
GLCL_EXE_NAME=../$(EXE_NAME)-sdl-glcl$(BITS)$(EXEPOSTFIX)
|
||||
ifdef windir
|
||||
ifneq ($(SDL_STATIC),0)
|
||||
#statically link by default. dlls suck.
|
||||
GL_LDFLAGS=$(GLLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`
|
||||
VK_LDFLAGS=$(GLLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`
|
||||
M_LDFLAGS=$(MLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`
|
||||
D3D_LDFLAGS=$(MLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`
|
||||
SV_LDFLAGS=-lm -lmingw32 -lws2_32 -lwinmm `$(SDLCONFIG) --static-libs`
|
||||
QCC_LDFLAGS=
|
||||
else
|
||||
#or dynamically link when SDL_STATIC=0
|
||||
GL_LDFLAGS=$(IMAGELDFLAGS) -lws2_32 -lmingw32 $(SDL_LDFLAGS) -mwindows -ldxguid -lwinmm -lole32 $(GLLDFLAGS) `$(SDLCONFIG) --libs`
|
||||
VK_LDFLAGS=$(IMAGELDFLAGS) -lws2_32 -lmingw32 $(SDL_LDFLAGS) -mwindows -ldxguid -lwinmm -lole32 $(GLLDFLAGS) `$(SDLCONFIG) --libs`
|
||||
M_LDFLAGS=$(IMAGELDFLAGS) -lws2_32 -lmingw32 $(SDL_LDFLAGS) -mwindows -ldxguid -lwinmm -lole32 $(MLDFLAGS) `$(SDLCONFIG) --libs`
|
||||
D3D_LDFLAGS=$(IMAGELDFLAGS) -lws2_32 -lmingw32 $(SDL_LDFLAGS) -mwindows -ldxguid -lwinmm -lole32 $(MLDFLAGS) `$(SDLCONFIG) --libs`
|
||||
SV_LDFLAGS=-lm -lmingw32 -lws2_32 -lwinmm `$(SDLCONFIG) --libs`
|
||||
QCC_LDFLAGS=
|
||||
endif
|
||||
|
@ -1212,9 +1218,12 @@ ifeq (win_SDL,$(findstring win,$(FTE_TARGET))$(findstring _SDL,$(FTE_TARGET)))
|
|||
GLB_DIR=gl_mgw_sdl$(BITS)
|
||||
GLCL_DIR=glcl_mgw_sdl$(BITS)
|
||||
|
||||
SV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) $(WINDOWSSERVERONLY_OBJS) $(LTO_END) resources.o $(LTO_START)
|
||||
#don't use sdl at all for dedicated servers. it'd break running it as a console program etc. its not officially supported, but included anyway for ease of use.
|
||||
SV_DIR=sv_winsdl$(BITS)
|
||||
SV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) $(WINDOWSSERVERONLY_OBJS) fs_win32.o $(LTO_END) resources.o $(LTO_START)
|
||||
SV_EXE_NAME=../$(EXE_NAME)-sdl-sv$(BITS)$(EXEPOSTFIX)
|
||||
SV_CFLAGS=$(SERVER_ONLY_CFLAGS) -DFTE_SDL
|
||||
SV_LDFLAGS=-lm -lole32 -lws2_32 -lwinmm
|
||||
SV_CFLAGS=$(SERVER_ONLY_CFLAGS) -mconsole #-DFTE_SDL
|
||||
|
||||
MINGL_DIR=mingl_sdlwin$(BITS)
|
||||
MINGL_EXE_NAME=../$(EXE_NAME)-sdl-mingl$(BITS)$(EXEPOSTFIX)
|
||||
|
@ -1228,10 +1237,10 @@ ifeq (win_SDL,$(findstring win,$(FTE_TARGET))$(findstring _SDL,$(FTE_TARGET)))
|
|||
MCL_OBJS=$(D3DGL_OBJS) $(GLQUAKE_OBJS) $(SOFTWARE_OBJS) gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(LTO_END) resources.o $(LTO_START)
|
||||
M_CFLAGS=$(VKCFLAGS) $(GLCFLAGS) -DFTE_SDL $(CLIENTLIBFLAGS) $(DX7SDK)
|
||||
|
||||
#unsupported, video code has winmsg/input junk in it.
|
||||
D3DCL_OBJS=$(D3DQUAKE_OBJS) snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(D3DGL_OBJS) $(LTO_END) resources.o $(LTO_START)
|
||||
D3D_EXE_NAME=../$(EXE_NAME)-sdl-d3d$(BITS)$(EXEPOSTFIX)
|
||||
D3DCL_EXE_NAME=../$(EXE_NAME)-sdl-d3dcl$(BITS)$(EXEPOSTFIX)
|
||||
D3D_LDFLAGS=$(IMAGELDFLAGS) -lws2_32 -lmingw32 $(SDL_LDFLAGS) -mwindows -ldxguid -lwinmm -lole32
|
||||
D3D_CFLAGS=$(D3DCFLAGS) -DFTE_SDL -DNO_XFLIP $(CLIENTLIBFLAGS) $(DX7SDK)
|
||||
D3DB_DIR=sdl_d3d_mgw$(BITS)
|
||||
D3DCL_DIR=sdl_d3dcl_mgw$(BITS)
|
||||
|
@ -1417,7 +1426,7 @@ ifeq (win,$(findstring win,$(FTE_TARGET))$(findstring _SDL,$(FTE_TARGET)))
|
|||
BASELDFLAGS+=-lcomctl32
|
||||
EXEPOSTFIX=.exe
|
||||
|
||||
QTV_LDFLAGS=-lws2_32 -lwinmm
|
||||
QTV_LDFLAGS+=-lws2_32 -lwinmm
|
||||
|
||||
SV_EXE_NAME=../$(EXE_NAME)sv$(BITS)$(EXEPOSTFIX)
|
||||
SV_LDFLAGS=-lws2_32 -lwinmm -lole32
|
||||
|
@ -2191,7 +2200,6 @@ clean:
|
|||
|
||||
distclean: clean
|
||||
-rm -f droid/ftekeystore
|
||||
-rm -f -r libs/SDL2-$(SDL2VER)
|
||||
|
||||
|
||||
|
||||
|
@ -2328,11 +2336,11 @@ droid-help:
|
|||
@-echo
|
||||
@-echo "Note that 'make droid-rel' will automatically generate a keystore. If you forget the password, just do a 'make dist-clean'."
|
||||
|
||||
$(BASE_DIR)/libs/SDL2-$(SDL2VER)/i686-w64-mingw32/bin/sdl2-config:
|
||||
$(BASE_DIR)/libs-$(ARCH)/SDL2-$(SDL2VER)/i686-w64-mingw32/bin/sdl2-config:
|
||||
wget http://www.libsdl.org/release/SDL2-devel-$(SDL2VER)-mingw.tar.gz -O $(BASE_DIR)/sdl2.tar.gz
|
||||
cd $(BASE_DIR)/libs && tar -xvzf $(BASE_DIR)/sdl2.tar.gz
|
||||
cd $(BASE_DIR)/libs-$(ARCH) && tar -xvzf $(BASE_DIR)/sdl2.tar.gz
|
||||
rm $(BASE_DIR)/sdl2.tar.gz
|
||||
$(BASE_DIR)/libs/SDL2-$(SDL2VER)/x86_64-w64-mingw32/bin/sdl2-config: $(BASE_DIR)/libs/SDL2-$(SDL2VER)/i686-w64-mingw32/bin/sdl2-config
|
||||
$(BASE_DIR)/libs-$(ARCH)/SDL2-$(SDL2VER)/x86_64-w64-mingw32/bin/sdl2-config: $(BASE_DIR)/libs-$(ARCH)/SDL2-$(SDL2VER)/i686-w64-mingw32/bin/sdl2-config
|
||||
|
||||
|
||||
|
||||
|
@ -2456,9 +2464,11 @@ QTV_OBJECTS= \
|
|||
bsp.c \
|
||||
rcon.c \
|
||||
mdfour.c \
|
||||
md5.c \
|
||||
crc.c \
|
||||
control.c \
|
||||
forward.c \
|
||||
relay.c \
|
||||
pmove.c \
|
||||
menu.c \
|
||||
msg.c \
|
||||
|
|
|
@ -2174,28 +2174,29 @@ void CL_Record_f (void)
|
|||
static int QDECL CompleteDemoList (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)
|
||||
{
|
||||
struct xcommandargcompletioncb_s *ctx = parm;
|
||||
const char *ext = NULL;
|
||||
ext = COM_GetFileExtension(name, ext);
|
||||
if (!Q_strcasecmp(ext, ".gz"))
|
||||
ext = COM_GetFileExtension(name, ext);
|
||||
if (
|
||||
#ifdef NQPROT
|
||||
!Q_strcasecmp(ext, ".dem") || !Q_strcasecmp(ext, ".dem.gz") ||
|
||||
#endif
|
||||
#ifdef Q2CLIENT
|
||||
!Q_strcasecmp(ext, ".dm2") || !Q_strcasecmp(ext, ".dm2.gz") ||
|
||||
#endif
|
||||
!Q_strcasecmp(ext, ".qwd") || !Q_strcasecmp(ext, ".qwd.gz") ||
|
||||
!Q_strcasecmp(ext, ".mvd") || !Q_strcasecmp(ext, ".mvd.gz"))
|
||||
//FIXME: enumerate .zip and .dz files too.
|
||||
{
|
||||
ctx->cb(name, NULL, NULL, ctx);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void CL_DemoList_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)
|
||||
{
|
||||
if (argn == 1)
|
||||
{
|
||||
COM_EnumerateFiles(va("%s*.qwd", partial), CompleteDemoList, ctx);
|
||||
COM_EnumerateFiles(va("%s*.qwd.gz", partial), CompleteDemoList, ctx);
|
||||
#ifdef NQPROT
|
||||
COM_EnumerateFiles(va("%s*.dem", partial), CompleteDemoList, ctx);
|
||||
COM_EnumerateFiles(va("%s*.dem.gz", partial), CompleteDemoList, ctx);
|
||||
#endif
|
||||
COM_EnumerateFiles(va("%s*.mvd", partial), CompleteDemoList, ctx);
|
||||
COM_EnumerateFiles(va("%s*.mvd.gz", partial), CompleteDemoList, ctx);
|
||||
|
||||
COM_EnumerateFiles(va("%s*.dm2", partial), CompleteDemoList, ctx);
|
||||
COM_EnumerateFiles(va("%s*.dm2.gz", partial), CompleteDemoList, ctx);
|
||||
|
||||
//fixme: show files in both .zip and .dz
|
||||
// COM_EnumerateFiles(va("%s*.dz", partial), CompleteDemoList, ctx);
|
||||
}
|
||||
COM_EnumerateFiles(va("%s*", partial), CompleteDemoList, ctx);
|
||||
}
|
||||
/*
|
||||
====================
|
||||
|
@ -2302,6 +2303,8 @@ void CL_PlayDemo_f (void)
|
|||
|
||||
if (cls.state == ca_demostart)
|
||||
cls.state = ca_disconnected;
|
||||
else
|
||||
cls.demonum = -1; //not via CL_NextDemo, don't confuse the user by playing random other demos.
|
||||
|
||||
#ifdef WEBCLIENT
|
||||
#if 1
|
||||
|
@ -3322,6 +3325,8 @@ void CL_TimeDemo_f (void)
|
|||
return;
|
||||
}
|
||||
|
||||
cls.demonum = -1; //stop the demo reel. the user will probably want to read the results.
|
||||
|
||||
CL_PlayDemo_f ();
|
||||
|
||||
if (cls.state != ca_demostart)
|
||||
|
|
|
@ -4507,10 +4507,9 @@ void CL_LinkPacketEntities (void)
|
|||
//DP extension. .modelflags (which is sent in the high parts of effects) allows to specify exactly the q1-compatible flags.
|
||||
//the extra bit allows for setting to 0.
|
||||
//note that hexen2 has additional flags which cannot be expressed.
|
||||
if (state->effects & 0xff800000)
|
||||
modelflags = state->effects>>24;
|
||||
else
|
||||
modelflags = model->flags;
|
||||
if (!(state->effects & EF_NOMODELFLAGS))
|
||||
modelflags |= model->flags;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LEGACY
|
||||
|
@ -4990,7 +4989,6 @@ CL_ParsePlayerinfo
|
|||
*/
|
||||
extern int parsecountmod, oldparsecountmod;
|
||||
extern double parsecounttime;
|
||||
int lastplayerinfo;
|
||||
|
||||
void CL_ParseClientdata (void);
|
||||
void CL_MVDUpdateSpectator(void)
|
||||
|
@ -5005,12 +5003,15 @@ void CLQW_ParsePlayerinfo (void)
|
|||
unsigned int flags;
|
||||
player_info_t *info;
|
||||
player_state_t *state, *oldstate;
|
||||
int num;
|
||||
unsigned int num;
|
||||
int i;
|
||||
int newf;
|
||||
vec3_t org, dist;
|
||||
|
||||
lastplayerinfo = num = MSG_ReadByte ();
|
||||
if (cls.fteprotocolextensions2&PEXT2_LONGINDEXES)
|
||||
num = MSG_ReadUInt64 ();
|
||||
else
|
||||
num = MSG_ReadByte ();
|
||||
if (num >= MAX_CLIENTS)
|
||||
Host_EndGame ("CL_ParsePlayerinfo: bad num");
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ static void CL_ForceStopDownload (qboolean finish);
|
|||
|
||||
qboolean noclip_anglehack; // remnant from old quake
|
||||
int startuppending;
|
||||
extern int r_blockvidrestart;
|
||||
|
||||
void Host_FinishLoading(void);
|
||||
|
||||
|
@ -926,12 +927,20 @@ char *CL_TryingToConnect(void)
|
|||
if (!connectinfo.trying)
|
||||
return NULL;
|
||||
|
||||
if (connectinfo.numadr >= 1 && connectinfo.adr[0].prot == NP_KEXLAN)
|
||||
if (connectinfo.numadr < 1)
|
||||
;
|
||||
else if (connectinfo.adr[0].prot == NP_KEXLAN
|
||||
#ifdef SUPPORT_ICE
|
||||
|| connectinfo.adr[0].type == NA_ICE
|
||||
#endif
|
||||
)
|
||||
{
|
||||
char status[1024];
|
||||
if (NET_GetConnectionCertificate(cls.sockets, &connectinfo.adr[0], QCERT_LOBBYSTATUS, status, sizeof(status))>0)
|
||||
return va("%s\n%s", cls.servername, status);
|
||||
}
|
||||
else if (connectinfo.adr[0].prot == NP_RTC_TCP || connectinfo.adr[0].prot == NP_RTC_TLS)
|
||||
return va("%s\n%s", cls.servername, "Waiting for broker connection");
|
||||
return cls.servername;
|
||||
}
|
||||
|
||||
|
@ -994,11 +1003,11 @@ 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.
|
||||
//stupid logic for targ@prox2@[ws[s]://]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 && !res)
|
||||
if (host && (!res || res > host))
|
||||
host++;
|
||||
else
|
||||
host = ctx->servername;
|
||||
|
@ -1038,7 +1047,6 @@ void CL_CheckForResend (void)
|
|||
double t1, t2;
|
||||
int contype = 0;
|
||||
qboolean keeptrying = true;
|
||||
extern int r_blockvidrestart;
|
||||
netadr_t *to;
|
||||
|
||||
#ifdef HAVE_SERVER
|
||||
|
@ -1355,7 +1363,7 @@ void CL_CheckForResend (void)
|
|||
Cvar_ForceSet(&cl_servername, "");
|
||||
return;
|
||||
}
|
||||
if (startuppending || r_blockvidrestart)
|
||||
if (startuppending || r_blockvidrestart || FS_DownloadingPackage())
|
||||
return; //don't send connect requests until we've actually initialised fully. this isn't a huge issue, but makes the startup prints a little more sane.
|
||||
|
||||
if (connectinfo.time && realtime - connectinfo.time < 5.0)
|
||||
|
@ -1406,7 +1414,7 @@ void CL_CheckForResend (void)
|
|||
|
||||
Cvar_ForceSet(&cl_servername, cls.servername);
|
||||
|
||||
if (!connectinfo.numadr || !cls.sockets)
|
||||
if (!connectinfo.numadr || !cls.sockets || connectinfo.resolving)
|
||||
return; //nothing to do yet...
|
||||
if (!connectinfo.clogged)
|
||||
connectinfo.time = realtime+t2-t1; // for retransmit requests
|
||||
|
@ -1439,11 +1447,22 @@ void CL_CheckForResend (void)
|
|||
connectinfo.clogged = false;
|
||||
|
||||
if (connectinfo.tries == 0 && connectinfo.nextadr < connectinfo.numadr)
|
||||
if (!NET_EnsureRoute(cls.sockets, "conn", &connectinfo.peercred, cls.servername, to, true))
|
||||
{
|
||||
//stupid logic for targ@prox2@[ws[s]://]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(cls.servername, '/');
|
||||
const char *host = strrchr(cls.servername+1, '@');
|
||||
if (host && (!res || res > host))
|
||||
host++;
|
||||
else
|
||||
host = cls.servername;
|
||||
|
||||
if (!NET_EnsureRoute(cls.sockets, "conn", &connectinfo.peercred, host, to, true))
|
||||
{
|
||||
CL_ConnectAbort ("Unable to establish connection to %s\n", cls.servername);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (to->prot == NP_DGRAM)
|
||||
connectinfo.nextadr++; //cycle hosts with each ping (if we got multiple).
|
||||
|
@ -1577,13 +1596,31 @@ void CL_CheckForResend (void)
|
|||
}
|
||||
}
|
||||
|
||||
static void CL_BeginServerConnect(char *host, int port, qboolean noproxy, enum coninfomode_e mode, enum coninfospec_e spec)
|
||||
static void CL_BeginServerConnect(char *chain, int port, qboolean noproxy, enum coninfomode_e mode, enum coninfospec_e spec)
|
||||
{
|
||||
const char *schemeend = strstr(host, "://");
|
||||
char *arglist;
|
||||
const char *host = chain;
|
||||
const char *schemeend;
|
||||
char *arglist, *c;
|
||||
size_t presize;
|
||||
|
||||
Q_strncpyz(cls.serverurl, host, sizeof(cls.serverurl));
|
||||
Q_strncpyz(cls.serverurl, chain, sizeof(cls.serverurl));
|
||||
|
||||
for (c = chain; *c; c++)
|
||||
{
|
||||
if (*c == '@')
|
||||
host=c+1;
|
||||
else if (*c == '/' || *c == '?')
|
||||
break; //stop if we find some path weirdness (like an authority).
|
||||
}
|
||||
presize = host-chain;
|
||||
if (presize >= sizeof(cls.servername))
|
||||
{
|
||||
CL_ConnectAbort("server address too long");
|
||||
return; //no, get lost. panic.
|
||||
}
|
||||
memcpy(cls.servername, chain, presize);
|
||||
|
||||
schemeend = strstr(host, "://");
|
||||
if (schemeend)
|
||||
{
|
||||
//"qw:tcp://host/observe"
|
||||
|
@ -1601,7 +1638,7 @@ static void CL_BeginServerConnect(char *host, int port, qboolean noproxy, enum c
|
|||
scheme = NULL; //qw:// or q3:// something that's just noise here.
|
||||
if (scheme && scheme->flags&URISCHEME_NEEDSRESOURCE)
|
||||
{
|
||||
Q_strncpyz (cls.servername, schemestart, sizeof(cls.servername)); //oh. will probably be okay then
|
||||
Q_strncpyz (cls.servername+presize, schemestart, sizeof(cls.servername)-presize); //oh. will probably be okay then
|
||||
arglist = NULL;
|
||||
}
|
||||
else
|
||||
|
@ -1639,9 +1676,9 @@ static void CL_BeginServerConnect(char *host, int port, qboolean noproxy, enum c
|
|||
}
|
||||
}
|
||||
if (scheme) //preserve the scheme, the netchan cares.
|
||||
Q_strncpyz (cls.servername, schemestart, sizeof(cls.servername)); //probably some game-specific mess that we don't know
|
||||
Q_strncpyz (cls.servername+presize, schemestart, sizeof(cls.servername)-presize); //probably some game-specific mess that we don't know
|
||||
else
|
||||
Q_strncpyz (cls.servername, schemeend+3, sizeof(cls.servername)); //probably some game-specific mess that we don't know
|
||||
Q_strncpyz (cls.servername+presize, schemeend+3, sizeof(cls.servername)-presize); //probably some game-specific mess that we don't know
|
||||
arglist = strchr(cls.servername, '?');
|
||||
}
|
||||
}
|
||||
|
@ -1650,10 +1687,10 @@ static void CL_BeginServerConnect(char *host, int port, qboolean noproxy, enum c
|
|||
if (!strncmp(host, "localhost", 9))
|
||||
noproxy = true; //FIXME: resolve the address here or something so that we don't end up using a proxy for lan addresses.
|
||||
|
||||
if (strstr(host, "://") || !*cl_proxyaddr.string || noproxy)
|
||||
Q_strncpyz (cls.servername, host, sizeof(cls.servername));
|
||||
if (strstr(host, "://") || *host == '/' || !*cl_proxyaddr.string || noproxy)
|
||||
Q_strncpyz (cls.servername+presize, host, sizeof(cls.servername)-presize);
|
||||
else
|
||||
Q_snprintfz(cls.servername, sizeof(cls.servername), "%s@%s", host, cl_proxyaddr.string);
|
||||
Q_snprintfz(cls.servername+presize, sizeof(cls.servername)-presize, "%s@%s", host, cl_proxyaddr.string);
|
||||
arglist = strchr(cls.servername, '?');
|
||||
}
|
||||
|
||||
|
@ -3486,6 +3523,10 @@ Contents allows \n escape character
|
|||
*/
|
||||
void CL_Packet_f (void)
|
||||
{
|
||||
#ifdef FTE_TARGET_WEB
|
||||
//either this creates some expensive alternative rtc connection that screws us over, or just generally fails. don't allow it.
|
||||
Con_Printf (CON_WARNING "Ignoring 'packet %s' request.\n", Cmd_Argv(1));
|
||||
#else
|
||||
char send[2048];
|
||||
int i, l;
|
||||
char *in, *out;
|
||||
|
@ -3593,6 +3634,7 @@ void CL_Packet_f (void)
|
|||
cls.realip_ident = atoi(Cmd_Argv(2));
|
||||
Z_Free(temp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -3681,6 +3723,8 @@ void CL_Startdemos_f (void)
|
|||
|
||||
for (i=1 ; i<c+1 ; i++)
|
||||
Q_strncpyz (cls.demos[i-1], Cmd_Argv(i), sizeof(cls.demos[0]));
|
||||
for ( ; i<MAX_DEMOS ; i++)
|
||||
Q_strncpyz (cls.demos[i-1], "", sizeof(cls.demos[0]));
|
||||
|
||||
cls.demonum = -1;
|
||||
//don't start it here - we might have been given a +connect or whatever argument.
|
||||
|
@ -4213,13 +4257,12 @@ void CL_ConnectionlessPacket (void)
|
|||
return;
|
||||
}
|
||||
|
||||
s = COM_Parse(s); //read the challenge.
|
||||
/*throttle connect requests*/
|
||||
if (curtime - lasttime < 500 && NET_CompareAdr(&net_from, &lastadr))
|
||||
if (curtime - lasttime < 500 && NET_CompareAdr(&net_from, &lastadr) && connectinfo.challenge == atoi(com_token))
|
||||
return;
|
||||
lasttime = curtime;
|
||||
lastadr = net_from;
|
||||
|
||||
s = COM_Parse(s);
|
||||
connectinfo.challenge = atoi(com_token);
|
||||
memset(&connectinfo.ext, 0, sizeof(connectinfo.ext));
|
||||
|
||||
|
@ -4308,7 +4351,7 @@ 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))
|
||||
if ((candtls && net_enable_dtls.ival) && net_from.prot == NP_DGRAM && (connectinfo.peercred.hash || net_enable_dtls.ival>1 || candtls > 1) && !NET_IsEncrypted(&net_from))
|
||||
{
|
||||
//c2s getchallenge <no client details, only leaks that its quakelike, something you can maybe guess from port numbers>
|
||||
//s2c c%u\0DTLS=$candtls <may leak server details>
|
||||
|
@ -4640,7 +4683,8 @@ void CL_ConnectionlessPacket (void)
|
|||
//happens in demos
|
||||
if (c == svc_disconnect && cls.demoplayback != DPB_NONE && net_from.type == NA_INVALID)
|
||||
{
|
||||
Host_EndGame ("End of Demo");
|
||||
CL_NextDemo();
|
||||
Host_EndGame (NULL); //end of demo.
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -5405,13 +5449,13 @@ void CL_Fog_f(void)
|
|||
cl.fog[ftype].time += 1;
|
||||
|
||||
//fitz:
|
||||
//if (Cmd_Argc() >= 6) cl.fog_time += atof(Cmd_Argv(5));
|
||||
//if (Cmd_Argc() >= 6) cl.fog[ftype].time += atof(Cmd_Argv(5));
|
||||
//dp:
|
||||
if (Cmd_Argc() >= 6) cl.fog[ftype].alpha = atof(Cmd_Argv(5));
|
||||
if (Cmd_Argc() >= 7) cl.fog[ftype].depthbias = atof(Cmd_Argv(6));
|
||||
//if (Cmd_Argc() >= 8) cl.fog.end = atof(Cmd_Argv(7));
|
||||
//if (Cmd_Argc() >= 9) cl.fog.height = atof(Cmd_Argv(8));
|
||||
//if (Cmd_Argc() >= 10) cl.fog.fadedepth = atof(Cmd_Argv(9));
|
||||
//if (Cmd_Argc() >= 8) cl.fog[ftype].end = atof(Cmd_Argv(7));
|
||||
//if (Cmd_Argc() >= 9) cl.fog[ftype].height = atof(Cmd_Argv(8));
|
||||
//if (Cmd_Argc() >= 10) cl.fog[ftype].fadedepth = atof(Cmd_Argv(9));
|
||||
|
||||
if (Cmd_FromGamecode())
|
||||
cl.fog_locked = !!cl.fog[ftype].density;
|
||||
|
@ -5924,8 +5968,8 @@ void CL_Init (void)
|
|||
Cmd_AddCommandD ("showpic", SCR_ShowPic_Script_f, "showpic <imagename> <placename> <x> <y> <zone> [width] [height] [touchcommand]\nDisplays an image onscreen, that potentially has a key binding attached to it when clicked/touched.\nzone should be one of: TL, TR, BL, BR, MM, TM, BM, ML, MR. This serves as an extra offset to move the image around the screen without any foreknowledge of the screen resolution.");
|
||||
Cmd_AddCommandD ("showpic_removeall", SCR_ShowPic_Remove_f, "removes any pictures inserted with the showpic command.");
|
||||
|
||||
Cmd_AddCommand ("startdemos", CL_Startdemos_f);
|
||||
Cmd_AddCommand ("demos", CL_Demos_f);
|
||||
Cmd_AddCommandD ("startdemos", CL_Startdemos_f, "Sets the demoreel list, but does not start playing them (use the 'demos' command for that)");
|
||||
Cmd_AddCommandD ("demos", CL_Demos_f, "Starts playing the demo reel.");
|
||||
Cmd_AddCommand ("stopdemo", CL_Stopdemo_f);
|
||||
|
||||
Cmd_AddCommand ("skins", Skin_Skins_f);
|
||||
|
@ -6074,16 +6118,24 @@ NORETURN void VARGS Host_EndGame (const char *message, ...)
|
|||
va_list argptr;
|
||||
char string[1024];
|
||||
|
||||
if (message)
|
||||
{
|
||||
va_start (argptr,message);
|
||||
vsnprintf (string,sizeof(string)-1, localtext(message),argptr);
|
||||
va_end (argptr);
|
||||
}
|
||||
else
|
||||
*string = 0;
|
||||
|
||||
COM_AssertMainThread(string);
|
||||
|
||||
SCR_EndLoadingPlaque();
|
||||
|
||||
if (message)
|
||||
{
|
||||
Con_TPrintf ("^&C0Host_EndGame: %s\n", string);
|
||||
Con_Printf ("\n");
|
||||
}
|
||||
|
||||
SCR_EndLoadingPlaque();
|
||||
|
||||
|
@ -7027,7 +7079,6 @@ double Host_Frame (double time)
|
|||
float maxfps;
|
||||
qboolean maxfpsignoreserver;
|
||||
qboolean idle;
|
||||
extern int r_blockvidrestart;
|
||||
static qboolean hadwork;
|
||||
unsigned int vrflags;
|
||||
qboolean mustrenderbeforeread;
|
||||
|
@ -7668,7 +7719,9 @@ void CL_ExecInitialConfigs(char *resetcommand, qboolean fullvidrestart)
|
|||
com_parseutf8.ival = com_parseutf8.value;
|
||||
|
||||
//if the renderer is already up and running, be prepared to reload content to match the new conback/font/etc
|
||||
if (fullvidrestart)
|
||||
if (r_blockvidrestart)
|
||||
;
|
||||
else if (fullvidrestart)
|
||||
Cbuf_AddText ("vid_restart\n", RESTRICT_LOCAL);
|
||||
else if (qrenderer != QR_NONE)
|
||||
Cbuf_AddText ("vid_reload\n", RESTRICT_LOCAL);
|
||||
|
@ -7701,7 +7754,6 @@ void Host_FinishLoading(void)
|
|||
{
|
||||
int i;
|
||||
extern qboolean r_forceheadless;
|
||||
extern int r_blockvidrestart;
|
||||
if (r_blockvidrestart == true)
|
||||
{
|
||||
//1 means we need to init the filesystem
|
||||
|
|
|
@ -34,6 +34,7 @@ enum masterprotocol_e
|
|||
#define SS_KEEPINFO (1<<6u)
|
||||
#define SS_GETINFO (1<<7u) //explicitly query via getinfo
|
||||
#define SS_PROXY (1<<8u) //qizmo/qwfwd/qtv/eztv
|
||||
#define SS_RELAY (1<<9u) //supports the \prx\nexthop relay thing, and pingstatus requests for connectbr.
|
||||
|
||||
#define PING_DEAD 0xffff //default ping value to denote servers that are not responding.
|
||||
#define PING_UNKNOWN 0xfffe //these servers are considered up, but we can't query them directly so can't determine the final ping from here.
|
||||
|
|
|
@ -27,7 +27,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);
|
||||
static void CL_SetStatNumeric (int pnum, unsigned 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)
|
||||
static qboolean CL_CheckModelResources (char *name);
|
||||
|
@ -37,7 +37,10 @@ static char *CLNQ_ParseProQuakeMessage (char *s);
|
|||
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);
|
||||
void Con_HexDump(qbyte *packet, size_t len, size_t badoffset);
|
||||
|
||||
#define MSG_ReadBigIndex() ((cls.fteprotocolextensions2&PEXT2_LONGINDEXES)?(unsigned int)MSG_ReadUInt64():MSG_ReadByte ())
|
||||
#define MSG_ReadPlayer() MSG_ReadBigIndex()
|
||||
|
||||
#ifdef NQPROT
|
||||
char *cl_dp_packagenames;
|
||||
|
@ -3412,10 +3415,13 @@ static void CLQW_ParseServerData (void)
|
|||
|
||||
if (cls.fteprotocolextensions2 & PEXT2_MAXPLAYERS)
|
||||
{
|
||||
cl.allocated_client_slots = MSG_ReadByte();
|
||||
cl.allocated_client_slots = MSG_ReadPlayer();
|
||||
if (cl.allocated_client_slots > MAX_CLIENTS)
|
||||
{
|
||||
Con_Printf(CON_ERROR"Server has too many client slots (%u > %u)\n", cl.allocated_client_slots, MAX_CLIENTS);
|
||||
cl.allocated_client_slots = MAX_CLIENTS;
|
||||
}
|
||||
}
|
||||
|
||||
cl.gametime = MSG_ReadFloat();
|
||||
cl.gametimemark = realtime;
|
||||
|
@ -3441,7 +3447,7 @@ static void CLQW_ParseServerData (void)
|
|||
else if (cls.fteprotocolextensions2 & PEXT2_MAXPLAYERS)
|
||||
{
|
||||
// qboolean spec = false;
|
||||
cl.allocated_client_slots = MSG_ReadByte();
|
||||
cl.allocated_client_slots = MSG_ReadPlayer();
|
||||
if (cl.allocated_client_slots > MAX_CLIENTS)
|
||||
{
|
||||
Con_Printf(CON_ERROR"Server has too many client slots (%u > %u)\n", cl.allocated_client_slots, MAX_CLIENTS);
|
||||
|
@ -4041,14 +4047,14 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut
|
|||
|
||||
if (cls.qex)
|
||||
{
|
||||
cl.allocated_client_slots = MSG_ReadByte();
|
||||
cl.allocated_client_slots = MSG_ReadPlayer();
|
||||
str = MSG_ReadString();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cls.fteprotocolextensions2 & PEXT2_PREDINFO)
|
||||
str = MSG_ReadString();
|
||||
cl.allocated_client_slots = MSG_ReadByte();
|
||||
cl.allocated_client_slots = MSG_ReadPlayer();
|
||||
}
|
||||
if (str)
|
||||
{
|
||||
|
@ -5847,10 +5853,10 @@ CL_UpdateUserinfo
|
|||
*/
|
||||
static void CL_UpdateUserinfo (void)
|
||||
{
|
||||
int slot;
|
||||
unsigned int slot;
|
||||
player_info_t *player;
|
||||
|
||||
slot = MSG_ReadByte ();
|
||||
slot = MSG_ReadPlayer();
|
||||
if (slot >= MAX_CLIENTS)
|
||||
Host_EndGame ("CL_ParseServerMessage: svc_updateuserinfo > MAX_SCOREBOARD");
|
||||
|
||||
|
@ -5874,7 +5880,7 @@ static void CL_UpdateUserinfo (void)
|
|||
|
||||
static void CL_ParseSetInfoBlob (void)
|
||||
{
|
||||
qbyte slot = MSG_ReadByte();
|
||||
unsigned int slot = MSG_ReadPlayer();
|
||||
char *key = MSG_ReadString();
|
||||
size_t keysize;
|
||||
unsigned int offset = MSG_ReadLong();
|
||||
|
@ -5918,12 +5924,12 @@ CL_SetInfo
|
|||
*/
|
||||
static void CL_ParseSetInfo (void)
|
||||
{
|
||||
int slot;
|
||||
unsigned int slot;
|
||||
player_info_t *player;
|
||||
char *val;
|
||||
char key[512];
|
||||
|
||||
slot = MSG_ReadByte ();
|
||||
slot = MSG_ReadPlayer ();
|
||||
|
||||
MSG_ReadStringBuffer(key, sizeof(key));
|
||||
val = MSG_ReadString();
|
||||
|
@ -6100,7 +6106,7 @@ static void CL_SetStatMovevar(int pnum, int stat, int ivalue, float value)
|
|||
#endif
|
||||
|
||||
//the two values are expected to be the same, they're just both provided for precision.
|
||||
static void CL_SetStatNumeric (int pnum, int stat, int ivalue, float fvalue)
|
||||
static void CL_SetStatNumeric (int pnum, unsigned int stat, int ivalue, float fvalue)
|
||||
{
|
||||
if (stat < 0 || stat >= MAX_CL_STATS)
|
||||
return;
|
||||
|
@ -6188,6 +6194,84 @@ static void CL_SetStatString (int pnum, int stat, const char *value)
|
|||
cl.playerview[pnum].statsstr[stat] = Z_StrDup(value);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
//if we're going to 'spend' another byte for longer indexes, we might as well spend an extra 4 bits on the type too, allowing for 64bit types etc.
|
||||
static void CL_ParseExtendedStat(int destsplit)
|
||||
{
|
||||
//float/double/sint/uint
|
||||
//string
|
||||
quint64_t id = MSG_ReadUInt64();
|
||||
unsigned int type;
|
||||
type = id&0xf;
|
||||
id>>=4; //we're never going to have that many stats.
|
||||
switch(type)
|
||||
{
|
||||
case ev_void: //might as well.
|
||||
CL_SetStatNumeric(destsplit, id, 0, 0);
|
||||
break;
|
||||
case ev_string:
|
||||
CL_SetStatString(destsplit, id, MSG_ReadString());
|
||||
break;
|
||||
case ev_float:
|
||||
{
|
||||
float f = MSG_ReadFloat();
|
||||
CL_SetStatNumeric(destsplit, id, f, f);
|
||||
}
|
||||
break;
|
||||
case ev_vector:
|
||||
{
|
||||
float f;
|
||||
f = MSG_ReadFloat();CL_SetStatNumeric(destsplit, id+0, f, f);
|
||||
f = MSG_ReadFloat();CL_SetStatNumeric(destsplit, id+1, f, f);
|
||||
f = MSG_ReadFloat();CL_SetStatNumeric(destsplit, id+2, f, f);
|
||||
}
|
||||
break;
|
||||
case ev_entity:
|
||||
{
|
||||
unsigned int i = MSGCL_ReadEntity();
|
||||
CL_SetStatNumeric(destsplit, id, i, i);
|
||||
}
|
||||
break;
|
||||
// case ev_field:
|
||||
// case ev_function:
|
||||
// case ev_pointer:
|
||||
case ev_integer:
|
||||
{
|
||||
signed int i = MSG_ReadLong();
|
||||
CL_SetStatNumeric(destsplit, id, i, i);
|
||||
}
|
||||
break;
|
||||
case ev_uint:
|
||||
{
|
||||
unsigned int i = MSG_ReadLong();
|
||||
CL_SetStatNumeric(destsplit, id, i, i);
|
||||
}
|
||||
break;
|
||||
case ev_int64:
|
||||
{
|
||||
qint64_t i = MSG_ReadInt64();
|
||||
CL_SetStatNumeric(destsplit, id, i, i);
|
||||
}
|
||||
break;
|
||||
case ev_uint64:
|
||||
{
|
||||
quint64_t i = MSG_ReadUInt64();
|
||||
CL_SetStatNumeric(destsplit, id, i, i);
|
||||
}
|
||||
break;
|
||||
case ev_double:
|
||||
{
|
||||
double f = MSG_ReadDouble();
|
||||
CL_SetStatNumeric(destsplit, id, f, f);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Host_EndGame("CL_ParseExtendedStat: type %i is unsupported", type);
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
|
||||
/*
|
||||
==============
|
||||
CL_MuzzleFlash
|
||||
|
@ -7293,7 +7377,7 @@ static void CL_ParsePrecache(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void Con_HexDump(qbyte *packet, size_t len, size_t badoffset)
|
||||
void Con_HexDump(qbyte *packet, size_t len, size_t badoffset)
|
||||
{
|
||||
int i;
|
||||
int pos;
|
||||
|
@ -7514,6 +7598,7 @@ void CLEZ_ParseHiddenDemoMessage(void)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#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);
|
||||
|
@ -7526,6 +7611,7 @@ void CLQW_ParseServerMessage (void)
|
|||
{
|
||||
int cmd;
|
||||
char *s;
|
||||
unsigned int u;
|
||||
int i, j;
|
||||
int destsplit;
|
||||
vec3_t ang;
|
||||
|
@ -7652,7 +7738,8 @@ void CLQW_ParseServerMessage (void)
|
|||
}
|
||||
else if (cls.demoplayback)
|
||||
{
|
||||
CL_Disconnect_f();
|
||||
CL_Disconnect(NULL);
|
||||
CL_NextDemo();
|
||||
return;
|
||||
}
|
||||
else if (cls.state == ca_connected)
|
||||
|
@ -7832,32 +7919,32 @@ void CLQW_ParseServerMessage (void)
|
|||
|
||||
case svc_updatefrags:
|
||||
Sbar_Changed ();
|
||||
i = MSG_ReadByte ();
|
||||
if (i >= MAX_CLIENTS)
|
||||
u = MSG_ReadPlayer();
|
||||
if (u >= MAX_CLIENTS)
|
||||
Host_EndGame ("CL_ParseServerMessage: svc_updatefrags > MAX_SCOREBOARD");
|
||||
cl.players[i].frags = MSG_ReadShort ();
|
||||
cl.players[u].frags = MSG_ReadShort ();
|
||||
break;
|
||||
|
||||
case svc_updateping:
|
||||
i = MSG_ReadByte ();
|
||||
if (i >= MAX_CLIENTS)
|
||||
u = MSG_ReadPlayer();
|
||||
if (u >= MAX_CLIENTS)
|
||||
Host_EndGame ("CL_ParseServerMessage: svc_updateping > MAX_SCOREBOARD");
|
||||
cl.players[i].ping = MSG_ReadShort ();
|
||||
cl.players[u].ping = MSG_ReadShort ();
|
||||
break;
|
||||
|
||||
case svc_updatepl:
|
||||
i = MSG_ReadByte ();
|
||||
if (i >= MAX_CLIENTS)
|
||||
u = MSG_ReadPlayer();
|
||||
if (u >= MAX_CLIENTS)
|
||||
Host_EndGame ("CL_ParseServerMessage: svc_updatepl > MAX_SCOREBOARD");
|
||||
cl.players[i].pl = MSG_ReadByte ();
|
||||
cl.players[u].pl = MSG_ReadByte ();
|
||||
break;
|
||||
|
||||
case svc_updateentertime:
|
||||
// time is sent over as seconds ago
|
||||
i = MSG_ReadByte ();
|
||||
if (i >= MAX_CLIENTS)
|
||||
u = MSG_ReadPlayer();
|
||||
if (u >= MAX_CLIENTS)
|
||||
Host_EndGame ("CL_ParseServerMessage: svc_updateentertime > MAX_SCOREBOARD");
|
||||
cl.players[i].realentertime = realtime - MSG_ReadFloat ();
|
||||
cl.players[u].realentertime = realtime - MSG_ReadFloat ();
|
||||
break;
|
||||
|
||||
case svc_spawnbaseline:
|
||||
|
@ -7938,6 +8025,9 @@ void CLQW_ParseServerMessage (void)
|
|||
f = MSG_ReadFloat();
|
||||
CL_SetStatNumeric (destsplit, i, f, f);
|
||||
break;
|
||||
/* case svcfte_updatebigstat:
|
||||
CL_ParseExtendedStat();
|
||||
break;*/
|
||||
|
||||
case svc_spawnstaticsound:
|
||||
CL_ParseStaticSound (false);
|
||||
|
@ -8967,7 +9057,7 @@ static qboolean CLNQ_ParseNQPrints(char *s)
|
|||
return false;
|
||||
}
|
||||
|
||||
static void CLNQ_CheckPlayerIsSpectator(int i)
|
||||
static void CLNQ_CheckPlayerIsSpectator(unsigned int i)
|
||||
{
|
||||
cl.players[i].spectator =
|
||||
(cl.players[i].frags==-999) || //DP mods tend to use -999
|
||||
|
@ -9002,6 +9092,7 @@ static void CLNQ_CheckPlayerIsSpectator(int i)
|
|||
static qboolean CLH2_ParseServerSubMessage (int cmd)
|
||||
{
|
||||
const int destsplit = 0;
|
||||
unsigned int u;
|
||||
int i,j;
|
||||
const int svch2_first = svch2_particle2;
|
||||
static const char *svc_h2strings[] =
|
||||
|
@ -9150,10 +9241,10 @@ static qboolean CLH2_ParseServerSubMessage (int cmd)
|
|||
MSG_ReadByte(); //handle
|
||||
break;
|
||||
case svch2_updateclass:
|
||||
i = MSG_ReadByte();
|
||||
u = MSG_ReadPlayer();
|
||||
j = MSG_ReadByte();
|
||||
if (i < MAX_CLIENTS)
|
||||
InfoBuf_SetValueForKey(&cl.players[i].userinfo, "cl_playerclass", va("%i", j));
|
||||
if (u < MAX_CLIENTS)
|
||||
InfoBuf_SetValueForKey(&cl.players[u].userinfo, "cl_playerclass", va("%i", j));
|
||||
break;
|
||||
case svch2_updateinv:
|
||||
cmd = MSG_ReadByte();
|
||||
|
@ -9273,6 +9364,7 @@ void CLNQ_ParseServerMessage (void)
|
|||
const int destsplit = 0;
|
||||
int cmd;
|
||||
char *s;
|
||||
unsigned int u;
|
||||
int i, j;
|
||||
vec3_t ang;
|
||||
unsigned int cmdstart;
|
||||
|
@ -9363,7 +9455,8 @@ void CLNQ_ParseServerMessage (void)
|
|||
break;
|
||||
|
||||
case svc_disconnect:
|
||||
CL_Disconnect("Server disconnected");
|
||||
CL_Disconnect(cls.demoplayback?NULL:"Server disconnected"); //don't show any errors on end-of-demo.
|
||||
CL_NextDemo();
|
||||
return;
|
||||
|
||||
case svc_centerprint:
|
||||
|
@ -9575,57 +9668,57 @@ void CLNQ_ParseServerMessage (void)
|
|||
|
||||
case svc_updatename:
|
||||
Sbar_Changed ();
|
||||
i = MSG_ReadByte ();
|
||||
if (i >= MAX_CLIENTS)
|
||||
u = MSG_ReadPlayer ();
|
||||
if (u >= MAX_CLIENTS)
|
||||
MSG_ReadString();
|
||||
else
|
||||
{
|
||||
strcpy(cl.players[i].name, MSG_ReadString());
|
||||
if (*cl.players[i].name)
|
||||
cl.players[i].userid = i+1;
|
||||
InfoBuf_SetValueForKey(&cl.players[i].userinfo, "name", cl.players[i].name);
|
||||
strcpy(cl.players[u].name, MSG_ReadString());
|
||||
if (*cl.players[u].name)
|
||||
cl.players[u].userid = u+1;
|
||||
InfoBuf_SetValueForKey(&cl.players[u].userinfo, "name", cl.players[u].name);
|
||||
if (!cl.nqplayernamechanged)
|
||||
cl.nqplayernamechanged = realtime+2;
|
||||
|
||||
CLNQ_CheckPlayerIsSpectator(i);
|
||||
CLNQ_CheckPlayerIsSpectator(u);
|
||||
}
|
||||
break;
|
||||
|
||||
case svc_updatefrags:
|
||||
Sbar_Changed ();
|
||||
i = MSG_ReadByte ();
|
||||
if (i >= MAX_CLIENTS)
|
||||
u = MSG_ReadPlayer ();
|
||||
if (u >= MAX_CLIENTS)
|
||||
MSG_ReadShort();
|
||||
else
|
||||
{
|
||||
cl.players[i].frags = MSG_ReadShort();
|
||||
CLNQ_CheckPlayerIsSpectator(i);
|
||||
cl.players[u].frags = MSG_ReadShort();
|
||||
CLNQ_CheckPlayerIsSpectator(u);
|
||||
}
|
||||
break;
|
||||
case svc_updatecolors:
|
||||
{
|
||||
int a;
|
||||
i = MSG_ReadByte ();
|
||||
u = MSG_ReadPlayer ();
|
||||
a = MSG_ReadByte ();
|
||||
if (i < cl.allocated_client_slots)
|
||||
if (u < cl.allocated_client_slots)
|
||||
{
|
||||
// cl.players[i].rtopcolor = a&0x0f;
|
||||
// cl.players[i].rbottomcolor = (a&0xf0)>>4;
|
||||
// sprintf(cl.players[i].team, "%2d", cl.players[i].rbottomcolor);
|
||||
// cl.players[u].rtopcolor = a&0x0f;
|
||||
// cl.players[u].rbottomcolor = (a&0xf0)>>4;
|
||||
// sprintf(cl.players[u].team, "%2d", cl.players[u].rbottomcolor);
|
||||
|
||||
InfoBuf_SetValueForKey(&cl.players[i].userinfo, "topcolor", va("%i", (a&0xf0)>>4));
|
||||
InfoBuf_SetValueForKey(&cl.players[i].userinfo, "bottomcolor", va("%i", (a&0x0f)));
|
||||
InfoBuf_SetValueForKey(&cl.players[i].userinfo, "team", va("%i", (a&0x0f)+1));
|
||||
CL_ProcessUserInfo (i, &cl.players[i]);
|
||||
InfoBuf_SetValueForKey(&cl.players[u].userinfo, "topcolor", va("%i", (a&0xf0)>>4));
|
||||
InfoBuf_SetValueForKey(&cl.players[u].userinfo, "bottomcolor", va("%i", (a&0x0f)));
|
||||
InfoBuf_SetValueForKey(&cl.players[u].userinfo, "team", va("%i", (a&0x0f)+1));
|
||||
CL_ProcessUserInfo (u, &cl.players[u]);
|
||||
|
||||
// CLNQ_CheckPlayerIsSpectator(i);
|
||||
// CLNQ_CheckPlayerIsSpectator(u);
|
||||
|
||||
#ifdef QWSKINS
|
||||
if (cls.state == ca_active)
|
||||
Skin_Find (&cl.players[i]);
|
||||
if (i == cl.playerview[destsplit].playernum)
|
||||
Skin_Find (&cl.players[u]);
|
||||
if (u == cl.playerview[destsplit].playernum)
|
||||
Skin_FlushPlayers();
|
||||
CL_NewTranslation (i);
|
||||
CL_NewTranslation (u);
|
||||
#endif
|
||||
Sbar_Changed ();
|
||||
}
|
||||
|
|
|
@ -2537,7 +2537,12 @@ void SCR_SetUpToDrawConsole (void)
|
|||
else
|
||||
{ //nothing happening, make sure the console is visible or something.
|
||||
if (!scr_drawloading)
|
||||
{
|
||||
if (SCR_GetLoadingStage() == LS_NONE && CL_TryingToConnect()) //if we're trying to connect, make sure there's a loading/connecting screen showing instead of forcing the menu visible
|
||||
SCR_SetLoadingStage(LS_CONNECTION);
|
||||
else
|
||||
Key_Dest_Add(kdm_console);
|
||||
}
|
||||
legacyfullscreen = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -375,7 +375,7 @@ typedef struct
|
|||
|
||||
typedef enum {
|
||||
ca_disconnected, // full screen console with no connection
|
||||
ca_demostart, // starting up a demo
|
||||
ca_demostart, // waiting to start up a demo (still disconnected but there should be a playdemo command in the cbuf somewhere so don't do other stuff)
|
||||
ca_connected, // netchan_t established, waiting for svc_serverdata
|
||||
ca_onserver, // processing data lists, donwloading, etc
|
||||
ca_active // everything is in, so frames can be rendered
|
||||
|
|
|
@ -86,7 +86,7 @@ static cvar_t r_tracker_fadetime = CVARCD("r_tracker_fadetime", "1", TrackerCall
|
|||
static cvar_t r_tracker_x = CVARCD("r_tracker_x", "0.5", TrackerCallback, "left position of the r_tracker messages, as a fraction of the screen's width, eg 0.5\n");
|
||||
static cvar_t r_tracker_y = CVARCD("r_tracker_y", "0.333", TrackerCallback, "top position of the r_tracker messages, as a fraction of the screen's height, eg 0.333\n");
|
||||
static cvar_t r_tracker_w = CVARCD("r_tracker_w", "0.5", TrackerCallback, "width of the r_tracker messages, as a fraction of the screen's width, eg 0.5\n");
|
||||
static cvar_t r_tracker_lines = CVARCD("r_tracker_lines", "8", TrackerCallback, "number of r_tracker messages to display\n");
|
||||
static cvar_t r_tracker_lines = CVARAFCD("r_tracker_lines", "8", "r_tracker_messages", 0, TrackerCallback, "number of r_tracker messages to display\n");
|
||||
static void Tracker_Update(console_t *tracker)
|
||||
{
|
||||
tracker->notif_l = tracker->maxlines = max(1,r_tracker_lines.ival);
|
||||
|
|
|
@ -1051,7 +1051,7 @@ void IN_MoveJoystick(struct joy_s *joy, float *movements, int pnum, float framet
|
|||
VectorScale(jlook, 360*cl_movespeedkey.value, jlook);
|
||||
VectorScale(jstrafe, 360*cl_movespeedkey.value, jstrafe);
|
||||
}
|
||||
VectorScale(jlook, 360*frametime, jlook);
|
||||
VectorScale(jlook, 360*frametime*in_sensitivityscale, jlook);
|
||||
|
||||
if (!movements) //if this is null, gamecode should still get inputs, just no camera looking or anything.
|
||||
return;
|
||||
|
|
|
@ -1127,26 +1127,11 @@ static unsigned int tbl_sdltoquakemouse[] =
|
|||
#include <SDL_misc.h>
|
||||
static qboolean usesteamosk;
|
||||
#endif
|
||||
#endif
|
||||
void Sys_SendKeyEvents(void)
|
||||
{
|
||||
SDL_Event event;
|
||||
int axis, j;
|
||||
|
||||
#ifdef HAVE_SDL_TEXTINPUT
|
||||
void INS_SetOSK(int osk)
|
||||
{
|
||||
static SDL_bool active = false;
|
||||
SDL_bool osk = Key_Dest_Has(kdm_console|kdm_cwindows|kdm_message);
|
||||
if (Key_Dest_Has(kdm_prompt|kdm_menu))
|
||||
{
|
||||
j = Menu_WantOSK();
|
||||
if (j < 0)
|
||||
osk |= sys_osk.ival;
|
||||
else
|
||||
osk |= j;
|
||||
}
|
||||
else if (Key_Dest_Has(kdm_game))
|
||||
osk |= sys_osk.ival;
|
||||
if (osk)
|
||||
if (osk && sdlwindow)
|
||||
{
|
||||
SDL_Rect rect;
|
||||
rect.x = 0;
|
||||
|
@ -1181,8 +1166,41 @@ void Sys_SendKeyEvents(void)
|
|||
// Con_Printf("OSK shown... killed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
void INS_SetOSK(int osk)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
void Sys_SendKeyEvents(void)
|
||||
{
|
||||
SDL_Event event;
|
||||
int axis, j;
|
||||
|
||||
#ifdef HAVE_SERVER
|
||||
if (isDedicated)
|
||||
{
|
||||
SV_GetConsoleCommands ();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SDL_TEXTINPUT
|
||||
{
|
||||
SDL_bool osk = Key_Dest_Has(kdm_console|kdm_cwindows|kdm_message);
|
||||
if (Key_Dest_Has(kdm_prompt|kdm_menu))
|
||||
{
|
||||
j = Menu_WantOSK();
|
||||
if (j < 0)
|
||||
osk |= sys_osk.ival;
|
||||
else
|
||||
osk |= j;
|
||||
}
|
||||
else if (Key_Dest_Has(kdm_game))
|
||||
osk |= sys_osk.ival;
|
||||
INS_SetOSK(osk);
|
||||
}
|
||||
#endif
|
||||
|
||||
while(SDL_PollEvent(&event))
|
||||
{
|
||||
|
@ -1202,9 +1220,10 @@ void Sys_SendKeyEvents(void)
|
|||
SDL_Vulkan_GetDrawableSize(sdlwindow, &window_width, &window_height); //get the proper physical size.
|
||||
if (vid.pixelwidth != window_width || vid.pixelheight != window_height)
|
||||
vk.neednewswapchain = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (qrenderer == QR_OPENGL)
|
||||
{
|
||||
#if SDL_VERSION_ATLEAST(2,0,1)
|
||||
SDL_GL_GetDrawableSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight); //get the proper physical size.
|
||||
|
|
|
@ -1133,7 +1133,7 @@ void Key_DefaultLinkClicked(console_t *con, char *text, char *info)
|
|||
c = Info_ValueForKey(info, "playaudio");
|
||||
if (*c && !strchr(c, ';') && !strchr(c, '\n'))
|
||||
{
|
||||
S_StartSound(0, INT_MAX-1, S_PrecacheSound(c), NULL, NULL, 1, ATTN_NONE, 0, 0, CF_NOSPACIALISE);
|
||||
S_StartSound(0, 0x7ffffffe, S_PrecacheSound(c), NULL, NULL, 1, ATTN_NONE, 0, 0, CF_NOSPACIALISE);
|
||||
return;
|
||||
}
|
||||
c = Info_ValueForKey(info, "desc");
|
||||
|
@ -3081,6 +3081,10 @@ void Key_Event (unsigned int devid, int key, unsigned int unicode, qboolean down
|
|||
//
|
||||
if (cls.demoplayback && cls.demoplayback != DPB_MVD && conkey && !Key_Dest_Has(~kdm_game))
|
||||
{
|
||||
dc = keybindings[key][modifierstate];
|
||||
if (!dc || (strcmp(dc, "toggleconsole") && strncmp(dc, "+show", 5) && strncmp(dc, "demo_", 5)))
|
||||
{
|
||||
extern cvar_t cl_demospeed;
|
||||
switch (key)
|
||||
{ //these keys don't force the menu to appear while playing the demo reel
|
||||
case K_LSHIFT:
|
||||
|
@ -3088,14 +3092,29 @@ void Key_Event (unsigned int devid, int key, unsigned int unicode, qboolean down
|
|||
case K_LALT:
|
||||
case K_RALT:
|
||||
case K_LCTRL:
|
||||
// case K_RCTRL:
|
||||
// case K_RCTRL:
|
||||
break;
|
||||
//demo modifiers...
|
||||
case K_DOWNARROW:
|
||||
case K_GP_DPAD_DOWN:
|
||||
Cvar_SetValue(&cl_demospeed, max(cl_demospeed.value - 0.1, 0));
|
||||
Con_Printf("playback speed: %g%%\n", cl_demospeed.value*100);
|
||||
return;
|
||||
case K_UPARROW:
|
||||
case K_GP_DPAD_UP:
|
||||
Cvar_SetValue(&cl_demospeed, min(cl_demospeed.value + 0.1, 10));
|
||||
Con_Printf("playback speed: %g%%\n", cl_demospeed.value*100);
|
||||
return;
|
||||
case K_LEFTARROW:
|
||||
case K_GP_DPAD_LEFT:
|
||||
Cbuf_AddText("demo_jump -10", RESTRICT_LOCAL); //expensive.
|
||||
return;
|
||||
case K_RIGHTARROW:
|
||||
case K_GP_DPAD_RIGHT:
|
||||
Cbuf_AddText("demo_jump +10", RESTRICT_LOCAL);
|
||||
return;
|
||||
//any other key
|
||||
default:
|
||||
dc = keybindings[key][modifierstate];
|
||||
//toggleconsole or +showFOO keys should do their regular bind action
|
||||
//demo_jump/demo_setspeed/demo_nudge should be allowed too.
|
||||
if (!dc || (strcmp(dc, "toggleconsole") && strncmp(dc, "+show", 5) && strncmp(dc, "demo_", 5)))
|
||||
{
|
||||
M_ToggleMenu_f ();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -407,7 +407,7 @@ static void PM_ValidateAuthenticity(package_t *p, enum hashvalidation_e validate
|
|||
//this is temporary code and should be removed once everything else has been fixed.
|
||||
//ignore the signature (flag as accepted) for any packages with all mirrors on our own update site.
|
||||
//we can get away with this because we enforce a known certificate for the download.
|
||||
if (!COM_CheckParm("-notlstrust"))
|
||||
if (!COM_CheckParm("-notlstrust") && p->mirror[0])
|
||||
{
|
||||
conchar_t musite[256], *e;
|
||||
char site[256];
|
||||
|
@ -962,7 +962,7 @@ static qboolean PM_CheckFeature(const char *feature, const char **featurename, c
|
|||
if (!strcmp(feature, "24bit"))
|
||||
return *featurename="24bit Textures", *concommand="seta gl_load24bit 1\n", gl_load24bit.ival;
|
||||
if (!strcmp(feature, "md3"))
|
||||
return *featurename="Replacement Models", *concommand="seta r_replacemodels md3 md2\n", !!strstr(r_replacemodels.string, "md3");
|
||||
return *featurename="Replacement Models", *concommand="seta r_replacemodels md3 md2 md5mesh\n", !!strstr(r_replacemodels.string, "md3");
|
||||
if (!strcmp(feature, "rtlights"))
|
||||
return *featurename="Realtime Dynamic Lights", *concommand="seta r_shadow_realtime_dlight 1\n", r_shadow_realtime_dlight.ival||r_shadow_realtime_world.ival;
|
||||
if (!strcmp(feature, "rtworld"))
|
||||
|
|
|
@ -3008,7 +3008,7 @@ void M_Menu_Video_f (void)
|
|||
static const char *srgbvalues[] = { "0", "1", "2", "-1", NULL};
|
||||
|
||||
|
||||
#ifdef ANDROID
|
||||
#if defined(ANDROID) && !defined(FTE_SDL)
|
||||
extern cvar_t sys_orientation;
|
||||
static const char *orientationopts[] = {
|
||||
"Auto",
|
||||
|
@ -3027,6 +3027,15 @@ void M_Menu_Video_f (void)
|
|||
NULL
|
||||
};
|
||||
#else
|
||||
extern cvar_t vid_fullscreen;
|
||||
static const char *fullscreenopts[] = {
|
||||
"Windowed",
|
||||
"Fullscreen",
|
||||
"Borderless Windowed",
|
||||
NULL
|
||||
};
|
||||
static const char *fullscreenvalues[] = {"0", "1", "2", NULL};
|
||||
#endif
|
||||
extern cvar_t vid_renderer;
|
||||
static const char *rendererops[] =
|
||||
{
|
||||
|
@ -3087,16 +3096,6 @@ void M_Menu_Video_f (void)
|
|||
NULL
|
||||
};
|
||||
|
||||
extern cvar_t vid_fullscreen;
|
||||
static const char *fullscreenopts[] = {
|
||||
"Windowed",
|
||||
"Fullscreen",
|
||||
"Borderless Windowed",
|
||||
NULL
|
||||
};
|
||||
static const char *fullscreenvalues[] = {"0", "1", "2", NULL};
|
||||
#endif
|
||||
|
||||
static const char *aaopts[] = {
|
||||
"1x",
|
||||
"2x",
|
||||
|
@ -3245,10 +3244,10 @@ void M_Menu_Video_f (void)
|
|||
MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", true),
|
||||
MB_CMD("Apply Settings", M_VideoApply, "Restart video and apply renderer, display, and 2D resolution options."),
|
||||
MB_SPACING(4),
|
||||
#ifdef ANDROID
|
||||
MB_COMBOCVAR("Renderer", vid_renderer, rendererops, renderervalues, NULL),
|
||||
#if defined(ANDROID) && !defined(FTE_SDL)
|
||||
MB_COMBOCVAR("Orientation", sys_orientation, orientationopts, orientationvalues, NULL),
|
||||
#else
|
||||
MB_COMBOCVAR("Renderer", vid_renderer, rendererops, renderervalues, NULL),
|
||||
MB_COMBOCVARRETURN("Display Mode", vid_fullscreen, fullscreenopts, fullscreenvalues, info->dispmode, vid_fullscreen.description),
|
||||
#endif
|
||||
MB_COMBOCVAR("MSAA", vid_multisample, aaopts, aavalues, NULL),
|
||||
|
|
|
@ -316,6 +316,53 @@ void M_Menu_Load_f (void)
|
|||
|
||||
#endif
|
||||
|
||||
static qboolean M_SingleParseMapDBEpisodes(emenu_t *menu, int *y, qboolean bigfont)
|
||||
{ //use the remaster's episode selection lists.
|
||||
size_t sz;
|
||||
char *file = FS_LoadMallocFile("mapdb.json", &sz);
|
||||
if (file)
|
||||
{
|
||||
json_t *j = JSON_Parse(file);
|
||||
json_t *episodes = JSON_FindChild(j, "episodes"), *e;
|
||||
int i = 0;
|
||||
while ((e=JSON_GetIndexed(episodes, i++)))
|
||||
{
|
||||
char namebuf[MAX_QPATH];
|
||||
char cmdbuf[MAX_QPATH];
|
||||
const char *command = JSON_GetString(e, "command", cmdbuf,sizeof(cmdbuf), NULL);
|
||||
const char *name = JSON_GetString(e, "name", namebuf,sizeof(namebuf), NULL);
|
||||
if (!command)
|
||||
{
|
||||
command = JSON_GetString(e, "dir", cmdbuf,sizeof(cmdbuf), NULL);
|
||||
if (command)
|
||||
command = va("gamedir %s; map start", command);
|
||||
}
|
||||
if (!command)
|
||||
continue;
|
||||
name = TL_Translate(com_language, name);
|
||||
if (name && command)
|
||||
{
|
||||
menubutton_t *b;
|
||||
if (bigfont)
|
||||
b = MC_AddConsoleCommandQBigFont(menu, 72, *y, name, va("closemenu;disconnect;maxclients 1;spectator \"\";samelevel \"\";deathmatch \"\";set_calc coop ($cl_splitscreen>0);%s\n", command)), *y += 20-8;
|
||||
else if (JSON_GetInteger(e, "needsClassSelect", false))
|
||||
b = MC_AddConsoleCommand (menu, 64, 260, *y, name, va("menu_single class %s\n", command));
|
||||
else if (JSON_GetInteger(e, "needsSkillSelect", false))
|
||||
b = MC_AddConsoleCommand (menu, 64, 260, *y, name, va("menu_single skill %s\n", command));
|
||||
else
|
||||
b = MC_AddConsoleCommand (menu, 64, 260, *y, name, va("closemenu; skill 0;deathmatch 0; set_calc coop ($cl_splitscreen>0);%s\n",command));
|
||||
*y+=8;
|
||||
if (!menu->selecteditem)
|
||||
menu->selecteditem = (menuoption_t*)b;
|
||||
}
|
||||
}
|
||||
JSON_Destroy(j);
|
||||
FS_FreeFile(file);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void M_Menu_SinglePlayer_f (void)
|
||||
{
|
||||
emenu_t *menu;
|
||||
|
@ -347,25 +394,41 @@ void M_Menu_SinglePlayer_f (void)
|
|||
{
|
||||
#ifdef Q2CLIENT
|
||||
case MGT_QUAKE2:
|
||||
{
|
||||
int y = 40;
|
||||
const char *command = "newgame";
|
||||
menu = M_CreateMenu(0);
|
||||
|
||||
MC_AddCenterPicture(menu, 4, 24, "pics/m_banner_game");
|
||||
|
||||
//quake2 uses the 'newgame' alias, which controls the intro video and then start map.
|
||||
if (!strncmp(Cmd_Argv(1), "skill", 5))
|
||||
command = Cmd_Argv(2);
|
||||
else
|
||||
M_SingleParseMapDBEpisodes(menu, &y, false);
|
||||
|
||||
if (!menu->selecteditem)
|
||||
{ //quake2 uses the 'newgame' alias, which controls the intro video and then start map.
|
||||
menu->selecteditem = (menuoption_t*)
|
||||
MC_AddConsoleCommand (menu, 64, 170, 40, "Easy", va("closemenu; skill 0;deathmatch 0; set_calc coop ($cl_splitscreen>0);newgame\n"));
|
||||
MC_AddConsoleCommand (menu, 64, 170, 48, "Medium", va("closemenu; skill 1;deathmatch 0; set_calc coop ($cl_splitscreen>0);newgame\n"));
|
||||
MC_AddConsoleCommand (menu, 64, 170, 56, "Hard", va("closemenu; skill 2;deathmatch 0; set_calc coop ($cl_splitscreen>0);newgame\n"));
|
||||
MC_AddConsoleCommand (menu, 64, 170, y, "Easy", va("closemenu; skill 0;deathmatch 0; set_calc coop ($cl_splitscreen>0);%s\n",command)); y+=8;
|
||||
MC_AddConsoleCommand (menu, 64, 170, y, "Medium", va("closemenu; skill 1;deathmatch 0; set_calc coop ($cl_splitscreen>0);%s\n",command)); y+=8;
|
||||
MC_AddConsoleCommand (menu, 64, 170, y, "Hard", va("closemenu; skill 2;deathmatch 0; set_calc coop ($cl_splitscreen>0);%s\n",command)); y+=8;
|
||||
}
|
||||
if (strncmp(Cmd_Argv(1), "skill", 5))
|
||||
{
|
||||
#ifdef SAVEDGAMES
|
||||
MC_AddConsoleCommand (menu, 64, 170, 72, "Load Game", "menu_load\n");
|
||||
MC_AddConsoleCommand (menu, 64, 170, 80, "Save Game", "menu_save\n");
|
||||
y+=8;
|
||||
MC_AddConsoleCommand (menu, 64, 170, y, "Load Game", "menu_load\n"); y+=8;
|
||||
MC_AddConsoleCommand (menu, 64, 170, y, "Save Game", "menu_save\n"); y+=8;
|
||||
#endif
|
||||
|
||||
#if MAX_SPLITS > 1
|
||||
b = (menubutton_t*)MC_AddCvarCombo(menu, 72, 170, 96, localtext("Splitscreen"), &cl_splitscreen, splitopts, splitvals);
|
||||
y+=8;
|
||||
MC_AddCvarCombo(menu, 72, 170, y, localtext("Splitscreen"), &cl_splitscreen, splitopts, splitvals);
|
||||
#endif
|
||||
}
|
||||
|
||||
menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 48, 0, 40, NULL, false);
|
||||
}
|
||||
return;
|
||||
#endif
|
||||
#ifdef HEXEN2
|
||||
|
@ -510,15 +573,21 @@ void M_Menu_SinglePlayer_f (void)
|
|||
default:
|
||||
if (QBigFontWorks())
|
||||
{
|
||||
int y = 32;
|
||||
menu = M_CreateMenu(0);
|
||||
MC_AddPicture(menu, 16, 4, 32, 144, "gfx/qplaque.lmp");
|
||||
MC_AddCenterPicture(menu, 4, 24, "gfx/ttl_sgl.lmp");
|
||||
|
||||
if (M_SingleParseMapDBEpisodes(menu, &y, true))
|
||||
y += 20;
|
||||
else
|
||||
{
|
||||
menu->selecteditem = (menuoption_t*)
|
||||
MC_AddConsoleCommandQBigFont (menu, 72, 32, "New Game", "closemenu;disconnect;maxclients 1;spectator \"\";samelevel \"\";deathmatch \"\";set_calc coop ($cl_splitscreen>0);startmap_sp\n");
|
||||
MC_AddConsoleCommandQBigFont (menu, 72, y, "New Game", "closemenu;disconnect;maxclients 1;spectator \"\";samelevel \"\";deathmatch \"\";set_calc coop ($cl_splitscreen>0);startmap_sp\n"); y += 20;
|
||||
}
|
||||
#ifdef SAVEDGAMES
|
||||
MC_AddConsoleCommandQBigFont (menu, 72, 52, "Load Game", "menu_load\n");
|
||||
MC_AddConsoleCommandQBigFont (menu, 72, 72, "Save Game", "menu_save\n");
|
||||
MC_AddConsoleCommandQBigFont (menu, 72, y, "Load Game", "menu_load\n"); y+=20;
|
||||
MC_AddConsoleCommandQBigFont (menu, 72, y, "Save Game", "menu_save\n"); y+=20;
|
||||
#endif
|
||||
|
||||
menu->cursoritem = (menuoption_t*)MC_AddCursor(menu, &resel, 54, 32);
|
||||
|
|
|
@ -938,6 +938,7 @@ void Master_SetupSockets(void)
|
|||
|
||||
static void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad);
|
||||
static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favorite);
|
||||
static void CL_ReadPingList(void);
|
||||
#else
|
||||
void Master_SetupSockets(void)
|
||||
{
|
||||
|
@ -2392,6 +2393,12 @@ void Master_CheckPollSockets(void)
|
|||
continue;
|
||||
}
|
||||
#endif
|
||||
if (!strncmp(s, "pinglist", 8)) //parse a bit more...
|
||||
{
|
||||
net_message.currentbit = (c+8-1)<<3;
|
||||
CL_ReadPingList();
|
||||
continue;
|
||||
}
|
||||
|
||||
net_message.currentbit = c;
|
||||
|
||||
|
@ -2632,6 +2639,23 @@ static qboolean MasterInfo_ReadProtocol(serverinfo_t *info, const char *infostri
|
|||
info->special |= SS_NETQUAKE;
|
||||
else if (*token == 'x')
|
||||
info->special |= SS_QEPROT;
|
||||
|
||||
else if (*token == 't')
|
||||
info->special |= SS_PROXY; //qtv
|
||||
else if (*token == 'r')
|
||||
{
|
||||
#if POLLTOTALSOCKETS>0
|
||||
char *msg = "\xff\xff\xff\xffpingstatus ext";
|
||||
if (info->peers)
|
||||
{ //forget the old, to let them timeout.
|
||||
Z_Free(info->peers);
|
||||
info->peers = NULL;
|
||||
info->numpeers = 0;
|
||||
}
|
||||
NET_SendPollPacket(strlen(msg), msg, info->adr);
|
||||
#endif
|
||||
info->special |= SS_PROXY|SS_RELAY; //qwfwd relay, ask it for its pinglist
|
||||
}
|
||||
else
|
||||
continue;
|
||||
break;
|
||||
|
@ -3297,6 +3321,86 @@ void MasterInfo_AddPlayer(netadr_t *serveradr, char *name, int ping, int frags,
|
|||
mplayers = p;
|
||||
}
|
||||
|
||||
static void CL_ReadPingListEntry(serverinfo_t *info, netadrtype_t type, size_t *maxpeers)
|
||||
{
|
||||
serverinfo_t *peer;
|
||||
unsigned short ping;
|
||||
int i;
|
||||
netadr_t pa;
|
||||
char adr[MAX_ADR_SIZE];
|
||||
memset(&pa, 0, sizeof(pa));
|
||||
|
||||
pa.type = type;
|
||||
if (type == NA_IP)
|
||||
{
|
||||
for (i = 0; i < countof(pa.address.ip); i++)
|
||||
pa.address.ip[i] = MSG_ReadByte();
|
||||
}
|
||||
else if (type == NA_IPV6)
|
||||
{
|
||||
for (i = 0; i < countof(pa.address.ip6); i++)
|
||||
pa.address.ip6[i] = MSG_ReadByte();
|
||||
}
|
||||
else
|
||||
{
|
||||
Sys_Error("CL_ReadPingListEntry: Unsupported netadrtype_t\n");
|
||||
return; //error...
|
||||
}
|
||||
pa.port = htons(MSG_ReadShort()); //little endian... stored into a network-endian variable...
|
||||
ping = MSG_ReadShort();
|
||||
|
||||
if (NET_ClassifyAddress(&pa, NULL) >= ASCOPE_NET)
|
||||
{
|
||||
peer = Master_InfoForServer(&pa, NULL);
|
||||
if (!peer)
|
||||
{
|
||||
//generate some lame peer node that we can use.
|
||||
peer = Z_Malloc(sizeof(serverinfo_t));
|
||||
peer->adr = pa;
|
||||
peer->sends = 1;
|
||||
peer->special = SS_QUAKEWORLD;
|
||||
peer->refreshtime = 0;
|
||||
peer->ping = PING_DEAD;
|
||||
Q_snprintfz(peer->name, sizeof(peer->name), "%s p", Master_ServerToString(adr, sizeof(adr), peer));
|
||||
peer->next = firstserver;
|
||||
firstserver = peer;
|
||||
}
|
||||
|
||||
for (i = 0; i < info->numpeers; i++)
|
||||
{
|
||||
if (info->peers[i].peer == peer)
|
||||
break;
|
||||
}
|
||||
if (i == *maxpeers)
|
||||
{ //need a new one
|
||||
info->numpeers = i+1;
|
||||
Z_ReallocElements((void**)&info->peers, maxpeers, info->numpeers+64, sizeof(*info->peers));
|
||||
}
|
||||
info->peers[i].peer = peer;
|
||||
info->peers[i].ping = ping;
|
||||
}
|
||||
}
|
||||
static void CL_ReadPingList(void)
|
||||
{
|
||||
serverinfo_t *info = Master_InfoForServer(&net_from, NULL);
|
||||
size_t count = info->numpeers;
|
||||
for(;;)
|
||||
{
|
||||
int type = MSG_ReadByte();
|
||||
if (type == '\\')
|
||||
CL_ReadPingListEntry(info, NA_IP, &count);
|
||||
else if (type == '/')
|
||||
CL_ReadPingListEntry(info, NA_IPV6, &count);
|
||||
else
|
||||
break; //don't know, don't corrupt it.
|
||||
}
|
||||
if (count > info->numpeers)
|
||||
{ //trim it...
|
||||
Z_ReallocElements((void**)&info->peers, &count, info->numpeers, sizeof(*info->peers));
|
||||
info->numpeers = count;
|
||||
}
|
||||
}
|
||||
|
||||
//we got told about a server, parse it's info
|
||||
static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favorite)
|
||||
{
|
||||
|
@ -3361,52 +3465,12 @@ static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolea
|
|||
if (!*Info_ValueForKey(msg, "hostname"))
|
||||
{ //qq, you suck
|
||||
//this is a proxy peer list, not an actual serverinfo update.
|
||||
unsigned char *ptr = net_message.data + 5;
|
||||
int remaining = net_message.cursize - 5;
|
||||
struct peers_s *peer;
|
||||
netadr_t pa;
|
||||
memset(&pa, 0, sizeof(pa));
|
||||
remaining /= 8;
|
||||
|
||||
//Master_ServerToString(adr, sizeof(adr), info);
|
||||
|
||||
Z_Free(info->peers);
|
||||
info->numpeers = 0;
|
||||
peer = info->peers = Z_Malloc(sizeof(*peer)*remaining);
|
||||
|
||||
size_t count = info->numpeers;
|
||||
int remaining = (net_message.cursize - 5) / 8;
|
||||
net_message.currentbit = (5)<<3;
|
||||
while (remaining --> 0)
|
||||
{
|
||||
pa.type = NA_IP;
|
||||
pa.address.ip[0] = *ptr++;
|
||||
pa.address.ip[1] = *ptr++;
|
||||
pa.address.ip[2] = *ptr++;
|
||||
pa.address.ip[3] = *ptr++;
|
||||
|
||||
pa.port = *ptr++<<8;
|
||||
pa.port |= *ptr++;
|
||||
peer->ping = *ptr++;
|
||||
peer->ping |= *ptr++<<8;
|
||||
|
||||
if (NET_ClassifyAddress(&pa, NULL) >= ASCOPE_NET)
|
||||
{
|
||||
peer->peer = Master_InfoForServer(&pa, NULL);
|
||||
if (!peer->peer)
|
||||
{
|
||||
//generate some lame peer node that we can use.
|
||||
peer->peer = Z_Malloc(sizeof(serverinfo_t));
|
||||
peer->peer->adr = pa;
|
||||
peer->peer->sends = 1;
|
||||
peer->peer->special = SS_QUAKEWORLD;
|
||||
peer->peer->refreshtime = 0;
|
||||
peer->peer->ping = PING_DEAD;
|
||||
peer->peer->next = firstserver;
|
||||
Q_snprintfz(peer->peer->name, sizeof(peer->peer->name), "%s p", Master_ServerToString(adr, sizeof(adr), peer->peer));
|
||||
firstserver = peer->peer;
|
||||
}
|
||||
peer++;
|
||||
info->numpeers++;
|
||||
}
|
||||
}
|
||||
CL_ReadPingListEntry(info, NA_IP, &count);
|
||||
info->numpeers = count;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -3456,16 +3520,21 @@ static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolea
|
|||
|
||||
if (*Info_ValueForKey(msg, "*qtv") || *Info_ValueForKey(msg, "*QTV"))
|
||||
info->special |= SS_PROXY|SS_FTESERVER; //qtv
|
||||
if (!strcmp(Info_ValueForKey(msg, "*progs"), "666") && !strcmp(Info_ValueForKey(msg, "*version"), "2.91"))
|
||||
else if (!strcmp(Info_ValueForKey(msg, "*progs"), "666") && !strcmp(Info_ValueForKey(msg, "*version"), "2.91"))
|
||||
info->special |= SS_PROXY; //qizmo
|
||||
if (!Q_strncmp(Info_ValueForKey(msg, "*version"), "qwfwd", 5))
|
||||
else if (!Q_strncmp(Info_ValueForKey(msg, "*version"), "qwfwd", 5))
|
||||
{
|
||||
char *msg = "\xff\xff\xff\xffpingstatus";
|
||||
char *msg = "\xff\xff\xff\xffpingstatus ext";
|
||||
NET_SendPollPacket(strlen(msg), msg, info->adr);
|
||||
|
||||
info->special |= SS_PROXY; //qwfwd
|
||||
if (info->peers)
|
||||
{ //let em time out
|
||||
Z_Free(info->peers);
|
||||
info->peers = NULL;
|
||||
info->numpeers = 0;
|
||||
}
|
||||
if (!Q_strncasecmp(Info_ValueForKey(msg, "*version"), "qtv ", 4))
|
||||
info->special |= SS_PROXY|SS_RELAY; //qwfwd
|
||||
}
|
||||
else if (!Q_strncasecmp(Info_ValueForKey(msg, "*version"), "qtv ", 4))
|
||||
info->special |= SS_PROXY; //eztv
|
||||
|
||||
token = Info_ValueForKey(msg, "map");
|
||||
|
|
|
@ -622,6 +622,10 @@ static void QCBUILTIN PF_Fixme (pubprogfuncs_t *prinst, struct globalvars_s *pr_
|
|||
prinst->RunError(prinst, "\nBuiltin %i:%s not implemented.\nCSQC is not compatible.", binum, fname);
|
||||
PR_BIError (prinst, "bulitin not implemented");
|
||||
}
|
||||
static void QCBUILTIN PF_Ignore (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
|
||||
{
|
||||
G_INT(OFS_RETURN) = 0;
|
||||
}
|
||||
static void QCBUILTIN PF_NoCSQC (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
|
||||
{
|
||||
int binum;
|
||||
|
@ -647,7 +651,7 @@ static void QCBUILTIN PF_checkbuiltin (pubprogfuncs_t *prinst, struct globalvars
|
|||
{ //qc defines the function at least. nothing weird there...
|
||||
if (builtinno > 0 && builtinno < prinst->parms->numglobalbuiltins)
|
||||
{
|
||||
if (!prinst->parms->globalbuiltins[builtinno] || prinst->parms->globalbuiltins[builtinno] == PF_Fixme || prinst->parms->globalbuiltins[builtinno] == PF_NoCSQC)
|
||||
if (!prinst->parms->globalbuiltins[builtinno] || prinst->parms->globalbuiltins[builtinno] == PF_Fixme || prinst->parms->globalbuiltins[builtinno] == PF_Ignore || prinst->parms->globalbuiltins[builtinno] == PF_NoCSQC)
|
||||
G_FLOAT(OFS_RETURN) = false; //the builtin with that number isn't defined.
|
||||
else
|
||||
{
|
||||
|
@ -837,7 +841,10 @@ static qboolean CopyCSQCEdictToEntity(csqcedict_t *fte_restrict in, entity_t *ft
|
|||
{
|
||||
VectorCopy(in->v->angles, out->angles);
|
||||
if (model && model->type == mod_alias)
|
||||
{
|
||||
out->angles[0] *= r_meshpitch.value;
|
||||
out->angles[2] *= r_meshroll.value;
|
||||
}
|
||||
AngleVectors(out->angles, out->axis[0], out->axis[1], out->axis[2]);
|
||||
VectorInverse(out->axis[1]);
|
||||
|
||||
|
@ -2713,6 +2720,8 @@ static void QCBUILTIN PF_R_RenderScene(pubprogfuncs_t *prinst, struct globalvars
|
|||
World_RBE_Start(&csqc_world);
|
||||
}
|
||||
|
||||
R2D_ImageColours(1,1,1,1); //apparently does matter.
|
||||
|
||||
if (cl.worldmodel)
|
||||
R_PushDlights ();
|
||||
|
||||
|
|
|
@ -1570,6 +1570,10 @@ void QCBUILTIN PF_clientstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
|
|||
G_FLOAT(OFS_RETURN) = 1/*nq ca_disconnected*/;
|
||||
}
|
||||
|
||||
static void QCBUILTIN PF_Ignore (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
|
||||
{
|
||||
G_INT(OFS_RETURN) = 0;
|
||||
}
|
||||
//too specific to the prinst's builtins.
|
||||
static void QCBUILTIN PF_Fixme (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
|
||||
{
|
||||
|
@ -1595,7 +1599,7 @@ static void QCBUILTIN PF_checkbuiltin (pubprogfuncs_t *prinst, struct globalvars
|
|||
{ //qc defines the function at least. nothing weird there...
|
||||
if (builtinno > 0 && builtinno < prinst->parms->numglobalbuiltins)
|
||||
{
|
||||
if (!prinst->parms->globalbuiltins[builtinno] || prinst->parms->globalbuiltins[builtinno] == PF_Fixme)
|
||||
if (!prinst->parms->globalbuiltins[builtinno] || prinst->parms->globalbuiltins[builtinno] == PF_Fixme || prinst->parms->globalbuiltins[builtinno] == PF_Ignore)
|
||||
G_FLOAT(OFS_RETURN) = false; //the builtin with that number isn't defined.
|
||||
else
|
||||
{
|
||||
|
|
|
@ -2355,7 +2355,7 @@ void Surf_GenBrushBatches(batch_t **batches, entity_t *ent)
|
|||
}
|
||||
|
||||
#ifdef BEF_PUSHDEPTH
|
||||
if (r_pushdepth)
|
||||
if (r_pushdepth && model->submodelof == r_worldentity.model)
|
||||
bef = BEF_PUSHDEPTH;
|
||||
else
|
||||
bef = 0;
|
||||
|
|
|
@ -157,7 +157,7 @@ static void apply_vector_2x2(roq_info *ri, int x, int y, roq_cell_rgba *cell)
|
|||
int idxa = (y * ri->width) + x;
|
||||
int idxb = 0;
|
||||
|
||||
int *ptra = (int*) &ri->rgba[0][idxa][0];
|
||||
int *ptra = (int*) ri->rgba[0][idxa];
|
||||
int *ptrb = (int*) &cell->p[idxb];
|
||||
|
||||
ptra[0] = ptrb[0];
|
||||
|
@ -175,7 +175,7 @@ static void apply_vector_4x4(roq_info *ri, int x, int y, roq_cell_rgba *cell)
|
|||
int idxa = (y * ri->width) + x;
|
||||
int idxb = 0;
|
||||
|
||||
int *ptra = (int*) &ri->rgba[0][idxa][0];
|
||||
int *ptra = (int*) ri->rgba[0][idxa];
|
||||
int *ptrb = (int*) &cell->p[idxb];
|
||||
|
||||
int i;
|
||||
|
@ -202,8 +202,8 @@ static void apply_motion_4x4(roq_info *ri, int x, int y, unsigned char mv, char
|
|||
int idxa = (y * ri->width) + x;
|
||||
int idxb = (my * ri->width) + mx;
|
||||
|
||||
int *ptra = (int*) &ri->rgba[0][idxa][0];
|
||||
int *ptrb = (int*) &ri->rgba[1][idxb][0];
|
||||
int *ptra = (int*) ri->rgba[0][idxa];
|
||||
int *ptrb = (int*) ri->rgba[1][idxb];
|
||||
|
||||
int i;
|
||||
for(i = 0; i < 4; i++) {
|
||||
|
@ -227,8 +227,8 @@ static void apply_motion_8x8(roq_info *ri, int x, int y, unsigned char mv, char
|
|||
int idxa = (y * ri->width) + x;
|
||||
int idxb = (my * ri->width) + mx;
|
||||
|
||||
int *ptra = (int*) &ri->rgba[0][idxa][0];
|
||||
int *ptrb = (int*) &ri->rgba[1][idxb][0];
|
||||
int *ptra = (int*) ri->rgba[0][idxa];
|
||||
int *ptrb = (int*) ri->rgba[1][idxb];
|
||||
|
||||
int i;
|
||||
for(i = 0; i < 8; i++) {
|
||||
|
@ -337,7 +337,7 @@ int i;
|
|||
#define LIMIT(x) ((((x) > 0xffffff) ? 0xff0000 : (((x) <= 0xffff) ? 0 : (x) & 0xff0000)) >> 16)
|
||||
void roq_cells_to_rgba(roq_info *ri)
|
||||
{
|
||||
char *pptr;
|
||||
unsigned char *pptr;
|
||||
int i, r, g, b, y, u, v, t;
|
||||
for(i = 0; i < 256; i++) {
|
||||
pptr = ri->cells_rgba[i].p;
|
||||
|
|
|
@ -58,10 +58,24 @@ void Sys_RecentServer(char *command, char *target, char *title, char *desc)
|
|||
{
|
||||
}
|
||||
|
||||
#if defined(__linux__) || defined(BSD)
|
||||
qboolean Sys_RandomBytes(qbyte *string, int len)
|
||||
{
|
||||
qboolean res = false;
|
||||
int fd = open("/dev/urandom", 0);
|
||||
if (fd != -1)
|
||||
{
|
||||
res = (read(fd, string, len) == len);
|
||||
close(fd);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#else
|
||||
qboolean Sys_RandomBytes(qbyte *string, int len)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ApplyColour(unsigned int chrflags)
|
||||
{
|
||||
|
@ -786,6 +800,9 @@ int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const
|
|||
//blink window if possible (it's not)
|
||||
void Sys_ServerActivity(void)
|
||||
{
|
||||
#if SDL_VERSION_ATLEAST(2,0,16)
|
||||
SDL_FlashWindow(sdlwindow, SDL_FLASH_BRIEFLY);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Sys_CloseLibrary(dllhandle_t *lib)
|
||||
|
@ -909,9 +926,129 @@ int VARGS Sys_DebugLog(char *file, char *fmt, ...)
|
|||
};
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
static qboolean gotconsole;
|
||||
static HANDLE con_stdin;
|
||||
qboolean Sys_InitTerminal(void)
|
||||
{
|
||||
gotconsole = AllocConsole(); //failure is okay if we already had one.
|
||||
con_stdin = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
|
||||
freopen("CON", "w", stdout); //unfuck the stdout too.
|
||||
return true;
|
||||
}
|
||||
char *Sys_ConsoleInput(void)
|
||||
{ //NET_Sleep won't sleep on this handle, so expect it to be sluggish.
|
||||
DWORD numevents, i;
|
||||
while (GetNumberOfConsoleInputEvents(con_stdin, &numevents) && numevents>0)
|
||||
{
|
||||
static char text[256];
|
||||
static int textlen;
|
||||
INPUT_RECORD event[1]; //longer might miss presses. especially with delays.
|
||||
if (numevents > countof(event))
|
||||
numevents = countof(event);
|
||||
if (ReadConsoleInputW(con_stdin, event, numevents, &numevents))
|
||||
{
|
||||
for(i = 0; i < numevents; i++)
|
||||
{
|
||||
if (event[i].EventType == KEY_EVENT && event[i].Event.KeyEvent.bKeyDown && event[i].Event.KeyEvent.uChar.UnicodeChar)
|
||||
{
|
||||
if (textlen >= countof(text))
|
||||
textlen = countof(text)-1; //don't overflow.
|
||||
text[textlen] = event[i].Event.KeyEvent.uChar.UnicodeChar;
|
||||
if (text[textlen] == '\r')
|
||||
{
|
||||
text[textlen] = 0; //caller will add its own \n
|
||||
printf("\r]%s\n", text);
|
||||
textlen = 0; //start from the start
|
||||
fflush(stdout);
|
||||
return text;
|
||||
}
|
||||
textlen++;
|
||||
text[textlen] = 0; //caller will add its own \n
|
||||
printf("\r]%s", text);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
void Sys_CloseTerminal (void)
|
||||
{
|
||||
if (gotconsole)
|
||||
{ //don't close our initial one. don't detach that way.
|
||||
FreeConsole();
|
||||
gotconsole = false;
|
||||
}
|
||||
if (con_stdin)
|
||||
{
|
||||
CloseHandle(con_stdin);
|
||||
con_stdin = NULL;
|
||||
}
|
||||
}
|
||||
#elif defined(__unix__) && !defined(__ANDROID__)
|
||||
static qbyte noconinput;
|
||||
qboolean Sys_InitTerminal(void)
|
||||
{
|
||||
if (COM_CheckParm("-nostdin"))
|
||||
noconinput = true;
|
||||
if (noconinput)
|
||||
return true; //they okayed it, let it start regardless.
|
||||
if (isatty(STDIN_FILENO))
|
||||
return true;
|
||||
Con_Printf(CON_WARNING"Sys_InitTerminal: not started from a tty\n"); //no easy way to kill it otherwise.
|
||||
return false;
|
||||
}
|
||||
char *Sys_ConsoleInput(void)
|
||||
{
|
||||
static char text[256];
|
||||
char *nl;
|
||||
|
||||
if (noconinput)
|
||||
return NULL;
|
||||
|
||||
#if defined(__linux__) && defined(_DEBUG)
|
||||
{
|
||||
int fl = fcntl (STDIN_FILENO, F_GETFL, 0);
|
||||
if (!(fl & FNDELAY))
|
||||
{
|
||||
fcntl(STDIN_FILENO, F_SETFL, fl | FNDELAY);
|
||||
// Sys_Printf(CON_WARNING "stdin flags became blocking - gdb bug?\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!fgets(text, sizeof(text), stdin))
|
||||
{
|
||||
if (errno == EIO)
|
||||
{
|
||||
Sys_Printf(CON_WARNING "Backgrounded, ignoring stdin\n");
|
||||
noconinput |= 2;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
nl = strchr(text, '\n');
|
||||
if (!nl) //err? wut?
|
||||
return NULL;
|
||||
*nl = 0;
|
||||
|
||||
return text;
|
||||
}
|
||||
void Sys_CloseTerminal (void)
|
||||
{
|
||||
}
|
||||
#else
|
||||
qboolean Sys_InitTerminal(void)
|
||||
{
|
||||
Con_Printf(CON_WARNING"Sys_InitTerminal: not implemented in this build.\n");
|
||||
return false; //Sys_ConsoleInput cannot work, so return false here.
|
||||
}
|
||||
char *Sys_ConsoleInput(void)
|
||||
|
@ -921,13 +1058,6 @@ char *Sys_ConsoleInput(void)
|
|||
void Sys_CloseTerminal (void)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef FTE_TARGET_WEB
|
||||
|
@ -970,6 +1100,32 @@ int QDECL main(int argc, char **argv)
|
|||
|
||||
if (parms.binarydir)
|
||||
Sys_Printf("Binary is located at \"%s\"\n", parms.binarydir);
|
||||
|
||||
#ifdef HAVE_CLIENT
|
||||
if (COM_CheckParm ("-dedicated"))
|
||||
isDedicated = true;
|
||||
if (isDedicated) //compleate denial to switch to anything else - many of the client structures are not initialized.
|
||||
{
|
||||
float delay;
|
||||
|
||||
SV_Init (&parms);
|
||||
|
||||
if (!Sys_InitTerminal())
|
||||
Con_Printf(CON_WARNING"Stdin unavailable\n");
|
||||
|
||||
delay = SV_Frame();
|
||||
while (1)
|
||||
{
|
||||
if (!isDedicated)
|
||||
Sys_Error("Dedicated was cleared");
|
||||
NET_Sleep(delay, false);
|
||||
delay = SV_Frame();
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
Host_Init (&parms);
|
||||
|
||||
oldtime = Sys_DoubleTime ();
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef FTE_SDL
|
||||
#include <SDL.h>
|
||||
#endif
|
||||
|
||||
static void Headless_Draw_Init(void)
|
||||
{
|
||||
|
@ -172,7 +175,9 @@ static qboolean Headless_SCR_UpdateScreen (void)
|
|||
{
|
||||
if (!cls.timedemo)
|
||||
{
|
||||
#if defined(_WIN32) && !defined(FTE_SDL)
|
||||
#ifdef FTE_SDL
|
||||
SDL_Delay(100);
|
||||
#elif defined(_WIN32)
|
||||
Sleep(100);
|
||||
#else
|
||||
usleep(100*1000);
|
||||
|
|
|
@ -517,7 +517,7 @@ void V_ParseDamage (playerview_t *pv)
|
|||
if (count < 10)
|
||||
count = 10;
|
||||
|
||||
#ifdef ANDROID
|
||||
#if defined(ANDROID) && !defined(FTE_SDL)
|
||||
//later versions of android might support strength values, but the simple standard interface is duration only.
|
||||
Sys_Vibrate(count);
|
||||
#endif
|
||||
|
|
|
@ -673,6 +673,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
#define FTE_LITTLE_ENDIAN
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(__LITTLE_ENDIAN__)
|
||||
#define FTE_LITTLE_ENDIAN
|
||||
#elif defined(__BIG_ENDIAN__)
|
||||
#define FTE_BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
|
|
@ -874,7 +874,7 @@ static void Cmd_Exec_f (void)
|
|||
s+=3;
|
||||
}
|
||||
|
||||
if (!strcmp(name, "config.cfg") || !strcmp(name, "q3config.cfg") || !strcmp(name, fs_manifest->mainconfig))
|
||||
if (!strcmp(name, "config.cfg") || !strcmp(name, "q3config.cfg") || (*fs_manifest->mainconfig && !strcmp(name, fs_manifest->mainconfig)))
|
||||
{
|
||||
char *restart;
|
||||
//if the config is from id1 and the default.cfg was from some mod, make sure the default.cfg overrides the config.
|
||||
|
@ -4148,7 +4148,10 @@ static void Cmd_WriteConfig_f(void)
|
|||
filename = Cmd_Argv(1);
|
||||
if (!*filename)
|
||||
{
|
||||
if (*fs_manifest->mainconfig)
|
||||
Q_strncpyz(fname, fs_manifest->mainconfig, sizeof(fname));
|
||||
else
|
||||
Q_strncpyz(fname, "config.cfg", sizeof(fname)); //write SOMETHING.
|
||||
|
||||
#if defined(CL_MASTER) && defined(HAVE_CLIENT)
|
||||
MasterInfo_WriteServers();
|
||||
|
|
|
@ -5662,39 +5662,150 @@ static void COM_Version_f (void)
|
|||
Con_Printf("%s\n", version_string());
|
||||
|
||||
#ifdef FTE_BRANCH
|
||||
Con_Printf("Branch: "STRINGIFY(FTE_BRANCH)"\n");
|
||||
Con_Printf("Revision: %s - %s\n",STRINGIFY(SVNREVISION), STRINGIFY(SVNDATE));
|
||||
Con_Printf("^3Branch:^7 "STRINGIFY(FTE_BRANCH)"\n");
|
||||
Con_Printf("^3Revision:^7 %s - %s\n",STRINGIFY(SVNREVISION), STRINGIFY(SVNDATE));
|
||||
#elif defined(SVNREVISION) && defined(SVNDATE)
|
||||
if (!strncmp(STRINGIFY(SVNREVISION), "git-", 4))
|
||||
Con_Printf("GIT Revision: %s - %s\n",STRINGIFY(SVNREVISION), STRINGIFY(SVNDATE));
|
||||
Con_Printf("^3GIT Revision:^7 %s - %s\n",STRINGIFY(SVNREVISION), STRINGIFY(SVNDATE));
|
||||
else
|
||||
Con_Printf("SVN Revision: %s - %s\n",STRINGIFY(SVNREVISION), STRINGIFY(SVNDATE));
|
||||
Con_Printf("^3SVN Revision:^7 %s - %s\n",STRINGIFY(SVNREVISION), STRINGIFY(SVNDATE));
|
||||
#else
|
||||
Con_TPrintf ("Exe: %s %s\n", __DATE__, __TIME__);
|
||||
Con_TPrintf ("^3Exe:^7 %s %s\n", __DATE__, __TIME__);
|
||||
#ifdef SVNREVISION
|
||||
if (!strncmp(STRINGIFY(SVNREVISION), "git-", 4))
|
||||
Con_Printf("GIT Revision: %s\n",STRINGIFY(SVNREVISION));
|
||||
Con_Printf("^3GIT Revision:^7 %s\n",STRINGIFY(SVNREVISION));
|
||||
else if (strcmp(STRINGIFY(SVNREVISION), "-"))
|
||||
Con_Printf("SVN Revision: %s\n",STRINGIFY(SVNREVISION));
|
||||
Con_Printf("^3SVN Revision:^7 %s\n",STRINGIFY(SVNREVISION));
|
||||
#endif
|
||||
#endif
|
||||
#ifdef CONFIG_FILE_NAME
|
||||
Con_Printf("Build config: %s\n\n", COM_SkipPath(STRINGIFY(CONFIG_FILE_NAME)));
|
||||
Con_Printf("^3Build config:^7 %s\n\n", COM_SkipPath(STRINGIFY(CONFIG_FILE_NAME)));
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
Con_Printf("debug build\n");
|
||||
#endif
|
||||
Con_Printf("^3Build type:^7");
|
||||
#ifdef MINIMAL
|
||||
Con_Printf("minimal build\n");
|
||||
Con_Printf("minimal\n");
|
||||
#endif
|
||||
#ifdef CLIENTONLY
|
||||
Con_Printf("client-only build\n");
|
||||
Con_Printf(" client-only\n");
|
||||
#endif
|
||||
#ifdef SERVERONLY
|
||||
Con_Printf("dedicated server build\n");
|
||||
Con_Printf(" dedicated\n");
|
||||
#endif
|
||||
#ifdef _DEBUG
|
||||
Con_Printf(" debug");
|
||||
#else
|
||||
Con_Printf("Renderers:");
|
||||
Con_Printf(" release");
|
||||
#endif
|
||||
Con_Printf("\n");
|
||||
|
||||
#ifdef FTE_SDL
|
||||
Con_Printf("^3SDL version:^7 %d.%d.%d\n", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
|
||||
#endif
|
||||
|
||||
// Don't print both as a 64bit MinGW built client
|
||||
#if defined(__MINGW32__)
|
||||
Con_Printf("Compiled with MinGW32/64 version: %i.%i\n",__MINGW32_MAJOR_VERSION, __MINGW32_MINOR_VERSION);
|
||||
#endif
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
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("^3Compiler:^7 clang %i.%i.%i (%s)\n",__clang_major__, __clang_minor__, __clang_patchlevel__, __VERSION__);
|
||||
#elif defined(__GNUC__)
|
||||
Con_Printf("^3Compiler:^7 GCC %i.%i.%i (%s)\n",__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__, __VERSION__);
|
||||
|
||||
#ifdef __OPTIMIZE__
|
||||
#ifdef __OPTIMIZE_SIZE__
|
||||
Con_Printf("Optimized for size\n");
|
||||
#else
|
||||
Con_Printf("Optimized for speed\n");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __NO_INLINE__
|
||||
Con_Printf("^3GCC Optimization:^7 Functions currently not inlined into their callers\n");
|
||||
#else
|
||||
Con_Printf("^3GCC Optimization:^7 Functions currently inlined into their callers\n");
|
||||
#endif
|
||||
#elif defined(_MSC_VER)
|
||||
if (_MSC_VER == 600) { Con_Printf( "^3Compiler:^7 C Compiler version 6.0\n"); }
|
||||
else if (_MSC_VER == 700) { Con_Printf( "^3Compiler:^7 C/C++ compiler version 7.0\n"); }
|
||||
else if (_MSC_VER == 800) { Con_Printf( "^3Compiler:^7 Visual C++, Windows, version 1.0 or Visual C++, 32-bit, version 1.0\n"); }
|
||||
else if (_MSC_VER == 900) { Con_Printf( "^3Compiler:^7 Visual C++, Windows, version 2.0 or Visual C++, 32-bit, version 2.x\n"); }
|
||||
else if (_MSC_VER == 1000) { Con_Printf("^3Compiler:^7 Visual C++, 32-bit, version 4.0\n"); }
|
||||
else if (_MSC_VER == 1020) { Con_Printf("^3Compiler:^7 Visual C++, 32-bit, version 4.2\n"); }
|
||||
else if (_MSC_VER == 1100) { Con_Printf("^3Compiler:^7 Visual C++, 32-bit, version 5.0\n"); }
|
||||
else if (_MSC_VER == 1200) { Con_Printf("^3Compiler:^7 Visual C++, 32-bit, version 6.0\n"); }
|
||||
else if (_MSC_VER == 1300) { Con_Printf("^3Compiler:^7 Visual C++, version 7.0\n"); }
|
||||
else if (_MSC_VER == 1310) { Con_Printf("^3Compiler:^7 Visual C++ 2003, version 7.1\n"); }
|
||||
else if (_MSC_VER == 1400) { Con_Printf("^3Compiler:^7 Visual C++ 2005, version 8.0\n"); }
|
||||
else if (_MSC_VER == 1500) { Con_Printf("^3Compiler:^7 Visual C++ 2008, version 9.0\n"); }
|
||||
else if (_MSC_VER == 1600) { Con_Printf("^3Compiler:^7 Visual C++ 2010, version 10.0\n"); }
|
||||
else if (_MSC_VER == 1700) { Con_Printf("^3Compiler:^7 Visual C++ 2012, version 11.0\n"); }
|
||||
else if (_MSC_VER == 1800) { Con_Printf("^3Compiler:^7 Visual C++ 2013, version 12.0\n"); }
|
||||
else if (_MSC_VER == 1900) { Con_Printf("^3Compiler:^7 Visual C++ 2015, version 14.0\n"); }
|
||||
else if (_MSC_VER >= 1910 && _MSC_VER < 1920) { Con_Printf("^3Compiler:^7 Visual C++ 2017, version 14.1x\n"); }
|
||||
else if (_MSC_VER >= 1920 && _MSC_VER < 1930) { Con_Printf("^3Compiler:^7 Visual C++ 2019, version 14.2x\n"); }
|
||||
else
|
||||
{
|
||||
#ifdef _MSC_BUILD
|
||||
Con_Printf("^3Compiler:^7 Unknown Microsoft C++ compiler: %i %i %i\n",_MSC_VER, _MSC_FULL_VER, _MSC_BUILD);
|
||||
#else
|
||||
Con_Printf("^3Compiler:^7 Unknown Microsoft C++ compiler: %i %i\n",_MSC_VER, _MSC_FULL_VER);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
Con_Printf("^3CPU Arch:^7 " PLATFORM " " ARCH_CPU_POSTFIX
|
||||
#ifdef ARCH_ALTCPU_POSTFIX
|
||||
"/"ARCH_ALTCPU_POSTFIX
|
||||
#endif
|
||||
);
|
||||
#ifdef __AVX512F__
|
||||
Con_Printf(" AVX512");
|
||||
#elif defined(__AVX2__)
|
||||
Con_Printf(" AVX2");
|
||||
#elif defined (__AVX__)
|
||||
Con_Printf(" AVX");
|
||||
#elif defined (__SSE4_2__)
|
||||
Con_Printf(" SSE4.2");
|
||||
#elif defined (__SSE4_1__)
|
||||
Con_Printf(" SSE4.1");
|
||||
#elif defined (__SSE3__)
|
||||
Con_Printf(" SSE3");
|
||||
#elif defined(_M_IX86_FP) && _M_IX86_FP == 2 //32bit only - always enabled for amd64
|
||||
Con_Printf(" SSE2");
|
||||
#elif defined(_M_IX86_FP) && _M_IX86_FP == 1 //32bit only - always enabled for amd64
|
||||
Con_Printf(" SSE");
|
||||
#elif defined(_M_IX86_FP) && _M_IX86_FP == 0 //32bit only - always enabled for amd64
|
||||
Con_Printf(" x87");
|
||||
#endif
|
||||
Con_Printf("\n");
|
||||
|
||||
#ifdef _M_IX86
|
||||
Con_Printf("^3x86 optimized for:^7 ");
|
||||
|
||||
if (_M_IX86 == 600) { Con_Printf("Blend or Pentium Pro, Pentium II and Pentium III"); }
|
||||
else if (_M_IX86 == 500) { Con_Printf("Pentium"); }
|
||||
else if (_M_IX86 == 400) { Con_Printf("486"); }
|
||||
else if (_M_IX86 == 300) { Con_Printf("386"); }
|
||||
else
|
||||
{
|
||||
Con_Printf("Unknown (%i)\n",_M_IX86);
|
||||
}
|
||||
|
||||
Con_Printf("\n");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CLIENT
|
||||
Con_Printf("^3Renderers:^7");
|
||||
#ifdef GLQUAKE
|
||||
#ifdef GLESONLY
|
||||
#ifdef FTE_TARGET_WEB //shuld we be just asking the video code for a list?...
|
||||
|
@ -5724,120 +5835,14 @@ static void COM_Version_f (void)
|
|||
Con_Printf("\n");
|
||||
#endif
|
||||
|
||||
#ifdef QCJIT
|
||||
Con_Printf("QuakeC just-in-time compiler (QCJIT) enabled\n");
|
||||
#endif
|
||||
|
||||
#ifdef FTE_SDL
|
||||
Con_Printf("SDL version: %d.%d.%d\n", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
|
||||
#endif
|
||||
|
||||
// Don't print both as a 64bit MinGW built client
|
||||
#if defined(__MINGW32__)
|
||||
Con_Printf("Compiled with MinGW32/64 version: %i.%i\n",__MINGW32_MAJOR_VERSION, __MINGW32_MINOR_VERSION);
|
||||
#endif
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
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__)
|
||||
Con_Printf("Compiled with GCC version: %i.%i.%i (%s)\n",__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__, __VERSION__);
|
||||
|
||||
#ifdef __OPTIMIZE__
|
||||
#ifdef __OPTIMIZE_SIZE__
|
||||
Con_Printf("Optimized for size\n");
|
||||
#else
|
||||
Con_Printf("Optimized for speed\n");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __NO_INLINE__
|
||||
Con_Printf("GCC Optimization: Functions currently not inlined into their callers\n");
|
||||
#else
|
||||
Con_Printf("GCC Optimization: Functions currently inlined into their callers\n");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _WIN64
|
||||
Con_Printf("Compiled for 64bit windows\n");
|
||||
#endif
|
||||
#if defined(_M_AMD64) || defined(__amd64__) || defined(__x86_64__)
|
||||
#ifdef __ILP32__
|
||||
Con_Printf("Compiled for AMD64 compatible cpus (x32)\n");
|
||||
#else
|
||||
Con_Printf("Compiled for AMD64 compatible cpus\n");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _M_IX86
|
||||
Con_Printf("x86 optimized for: ");
|
||||
|
||||
if (_M_IX86 == 600) { Con_Printf("Blend or Pentium Pro, Pentium II and Pentium III"); }
|
||||
else if (_M_IX86 == 500) { Con_Printf("Pentium"); }
|
||||
else if (_M_IX86 == 400) { Con_Printf("486"); }
|
||||
else if (_M_IX86 == 300) { Con_Printf("386"); }
|
||||
else
|
||||
{
|
||||
Con_Printf("Unknown (%i)\n",_M_IX86);
|
||||
}
|
||||
|
||||
Con_Printf("\n");
|
||||
#endif
|
||||
|
||||
#ifdef _M_IX86_FP
|
||||
if (_M_IX86_FP == 0) { Con_Printf("SSE & SSE2 instructions disabled\n"); }
|
||||
else if (_M_IX86_FP == 1) { Con_Printf("SSE instructions enabled\n"); }
|
||||
else if (_M_IX86_FP == 2) { Con_Printf("SSE2 instructions enabled\n"); }
|
||||
else
|
||||
{
|
||||
Con_Printf("Unknown Arch specified: %i\n",_M_IX86_FP);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
if (_MSC_VER == 600) { Con_Printf("C Compiler version 6.0\n"); }
|
||||
else if (_MSC_VER == 700) { Con_Printf("C/C++ compiler version 7.0\n"); }
|
||||
else if (_MSC_VER == 800) { Con_Printf("Visual C++, Windows, version 1.0 or Visual C++, 32-bit, version 1.0\n"); }
|
||||
else if (_MSC_VER == 900) { Con_Printf("Visual C++, Windows, version 2.0 or Visual C++, 32-bit, version 2.x\n"); }
|
||||
else if (_MSC_VER == 1000) { Con_Printf("Visual C++, 32-bit, version 4.0\n"); }
|
||||
else if (_MSC_VER == 1020) { Con_Printf("Visual C++, 32-bit, version 4.2\n"); }
|
||||
else if (_MSC_VER == 1100) { Con_Printf("Visual C++, 32-bit, version 5.0\n"); }
|
||||
else if (_MSC_VER == 1200) { Con_Printf("Visual C++, 32-bit, version 6.0\n"); }
|
||||
else if (_MSC_VER == 1300) { Con_Printf("Visual C++, version 7.0\n"); }
|
||||
else if (_MSC_VER == 1310) { Con_Printf("Visual C++ 2003, version 7.1\n"); }
|
||||
else if (_MSC_VER == 1400) { Con_Printf("Visual C++ 2005, version 8.0\n"); }
|
||||
else if (_MSC_VER == 1500) { Con_Printf("Visual C++ 2008, version 9.0\n"); }
|
||||
else if (_MSC_VER == 1600) { Con_Printf("Visual C++ 2010, version 10.0\n"); }
|
||||
else if (_MSC_VER == 1700) { Con_Printf("Visual C++ 2012, version 11.0\n"); }
|
||||
else if (_MSC_VER == 1800) { Con_Printf("Visual C++ 2013, version 12.0\n"); }
|
||||
else if (_MSC_VER == 1900) { Con_Printf("Visual C++ 2015, version 14.0\n"); }
|
||||
else if (_MSC_VER >= 1910 && _MSC_VER < 1920) { Con_Printf("Visual C++ 2017, version 14.1x\n"); }
|
||||
else if (_MSC_VER >= 1920 && _MSC_VER < 1930) { Con_Printf("Visual C++ 2019, version 14.2x\n"); }
|
||||
else
|
||||
{
|
||||
#ifdef _MSC_BUILD
|
||||
Con_Printf("Unknown Microsoft C++ compiler: %i %i %i\n",_MSC_VER, _MSC_FULL_VER, _MSC_BUILD);
|
||||
#else
|
||||
Con_Printf("Unknown Microsoft C++ compiler: %i %i\n",_MSC_VER, _MSC_FULL_VER);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MULTITHREAD
|
||||
#ifdef LOADERTHREAD
|
||||
Con_Printf("multithreading: enabled (loader enabled)\n");
|
||||
Con_Printf("^3multithreading:^7 enabled (loader enabled)\n");
|
||||
#else
|
||||
Con_Printf("multithreading: enabled (no loader)\n");
|
||||
Con_Printf("^3multithreading:^7 enabled (no loader)\n");
|
||||
#endif
|
||||
#else
|
||||
Con_Printf("multithreading: disabled\n");
|
||||
Con_Printf("^3multithreading^7: disabled\n");
|
||||
#endif
|
||||
|
||||
//print out which libraries are disabled
|
||||
|
@ -5886,7 +5891,7 @@ static void COM_Version_f (void)
|
|||
|
||||
Con_Printf("^3Audio Decoders:^7");
|
||||
#ifdef FTE_TARGET_WEB
|
||||
Con_DPrintf(" javascript");
|
||||
Con_Printf(" Browser");
|
||||
#endif
|
||||
#ifndef AVAIL_OGGVORBIS
|
||||
Con_DPrintf(" ^h(disabled: Ogg Vorbis)^7");
|
||||
|
@ -5950,6 +5955,9 @@ static void COM_Version_f (void)
|
|||
#endif
|
||||
#ifdef ENGINE_ROUTING
|
||||
Con_Printf(" routing");
|
||||
#endif
|
||||
#ifdef QCJIT
|
||||
Con_Printf(" qcjit");
|
||||
#endif
|
||||
Con_Printf("\n");
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ typedef struct netadr_s
|
|||
netadrtype_t type;
|
||||
netproto_t prot;
|
||||
|
||||
unsigned short port;
|
||||
unsigned short port; //stored as network-endian.
|
||||
unsigned short connum; //which quake connection/socket the address is talking about. 1-based. 0 is unspecified. this is NOT used for address equivelency.
|
||||
unsigned int scopeid; //ipv6 interface id thing.
|
||||
|
||||
|
|
|
@ -224,10 +224,6 @@ unsigned int Net_PextMask(unsigned int protover, qboolean fornq)
|
|||
mask |= PEXT2_REPLACEMENTDELTAS;
|
||||
if (/*fornq &&*/ pext_predinfo.ival)
|
||||
mask |= PEXT2_PREDINFO;
|
||||
}
|
||||
|
||||
if (pext_infoblobs.ival)
|
||||
mask |= PEXT2_INFOBLOBS;
|
||||
|
||||
if (pext_vrinputs.ival)
|
||||
mask |= PEXT2_VRINPUTS;
|
||||
|
@ -235,12 +231,15 @@ unsigned int Net_PextMask(unsigned int protover, qboolean fornq)
|
|||
if (pext_lerptime.ival)
|
||||
mask |= PEXT2_LERPTIME;
|
||||
|
||||
mask |= PEXT2_NEWSIZEENCODING; //use if we can
|
||||
}
|
||||
|
||||
if (pext_infoblobs.ival)
|
||||
mask |= PEXT2_INFOBLOBS;
|
||||
|
||||
if (MAX_CLIENTS != QWMAX_CLIENTS)
|
||||
mask |= PEXT2_MAXPLAYERS;
|
||||
|
||||
if (mask & PEXT2_REPLACEMENTDELTAS)
|
||||
mask |= PEXT2_NEWSIZEENCODING; //use if we can
|
||||
|
||||
mask |= PEXT2_STUNAWARE;
|
||||
|
||||
if (fornq)
|
||||
|
|
|
@ -68,7 +68,7 @@ typedef struct
|
|||
//attributes
|
||||
#define STUNATTR_MAPPED_ADDRESS 0x0001
|
||||
#define STUNATTR_USERNAME 0x0006
|
||||
#define STUNATTR_MESSAGEINTEGRITIY 0x0008
|
||||
#define STUNATTR_MSGINTEGRITIY_SHA1 0x0008
|
||||
#define STUNATTR_ERROR_CODE 0x0009
|
||||
//#define STUNATTR_CHANNELNUMBER 0x000c //TURN
|
||||
#define STUNATTR_LIFETIME 0x000d //TURN
|
||||
|
@ -81,6 +81,8 @@ typedef struct
|
|||
//#define STUNATTR_EVEN_PORT 0x0018 //TURN
|
||||
#define STUNATTR_REQUESTED_TRANSPORT 0x0019 //TURN
|
||||
#define STUNATTR_DONT_FRAGMENT 0x001a //TURN
|
||||
#define STUNATTR_MSGINTEGRITIY_SHA2_256 0x001C
|
||||
#define STUNATTR_PASSWORD_ALGORITHM 0x001D //yay, screw md5
|
||||
#define STUNATTR_XOR_MAPPED_ADDRESS 0x0020
|
||||
#define STUNATTR_ICE_PRIORITY 0x0024 //ICE
|
||||
#define STUNATTR_ICE_USE_CANDIDATE 0x0025 //ICE
|
||||
|
@ -502,10 +504,13 @@ static qboolean TURN_AddXorAddressAttrib(sizebuf_t *buf, unsigned int attr, neta
|
|||
MSG_WriteByte(buf, ((qbyte*)&to->address)[aofs+i] ^ (buf->data+4)[i]);
|
||||
return true;
|
||||
}
|
||||
void Con_HexDump(qbyte *packet, size_t len, size_t badoffset);
|
||||
static qboolean TURN_AddAuth(sizebuf_t *buf, struct iceserver_s *srv)
|
||||
{ //adds auth info to a stun packet
|
||||
unsigned short len;
|
||||
char integrity[20];
|
||||
char integrity[DIGEST_MAXSIZE];
|
||||
hashfunc_t *hash = &hash_sha1;
|
||||
hashfunc_t *pwdhash = &hash_md5;
|
||||
|
||||
if (!srv->user || !srv->nonce || !srv->realm)
|
||||
return false;
|
||||
|
@ -530,18 +535,38 @@ static qboolean TURN_AddAuth(sizebuf_t *buf, struct iceserver_s *srv)
|
|||
if (len&3)
|
||||
SZ_Write (buf, "\0\0\0\0", 4-(len&3));
|
||||
|
||||
if (pwdhash != &hash_md5)
|
||||
{
|
||||
MSG_WriteShort(buf, BigShort(STUNATTR_PASSWORD_ALGORITHM));
|
||||
len = strlen(srv->nonce);
|
||||
MSG_WriteShort(buf, 4);
|
||||
if (pwdhash == &hash_md5)
|
||||
MSG_WriteShort(buf, 1);
|
||||
else if (pwdhash == &hash_sha2_256)
|
||||
MSG_WriteShort(buf, 2);
|
||||
else
|
||||
return false; //not defined... panic.
|
||||
MSG_WriteShort(buf, 0); //paramlength
|
||||
//no params.
|
||||
}
|
||||
|
||||
//message integrity is a bit annoying
|
||||
buf->data[2] = ((buf->cursize+4+sizeof(integrity)-20)>>8)&0xff; //hashed header length is up to the end of the hmac attribute
|
||||
buf->data[3] = ((buf->cursize+4+sizeof(integrity)-20)>>0)&0xff;
|
||||
buf->data[2] = ((buf->cursize+4+hash->digestsize-20)>>8)&0xff; //hashed header length is up to the end of the hmac attribute
|
||||
buf->data[3] = ((buf->cursize+4+hash->digestsize-20)>>0)&0xff;
|
||||
//but the hash is to the start of the attribute's header
|
||||
{ //long-term credentials do stuff weird.
|
||||
char *tmpkey = va("%s:%s:%s", srv->user, srv->realm, srv->auth);
|
||||
CalcHash(&hash_md5, integrity,16, tmpkey, strlen(tmpkey));
|
||||
len = CalcHash(pwdhash, integrity,sizeof(integrity), tmpkey, strlen(tmpkey));
|
||||
}
|
||||
CalcHMAC(&hash_sha1, integrity, sizeof(integrity), buf->data, buf->cursize, integrity,16);
|
||||
MSG_WriteShort(buf, BigShort(STUNATTR_MESSAGEINTEGRITIY));
|
||||
MSG_WriteShort(buf, BigShort(sizeof(integrity))); //sha1 key length
|
||||
SZ_Write(buf, integrity, sizeof(integrity)); //integrity data
|
||||
len = CalcHMAC(hash, integrity, sizeof(integrity), buf->data, buf->cursize, integrity,len);
|
||||
if (hash == &hash_sha2_256)
|
||||
MSG_WriteShort(buf, BigShort(STUNATTR_MSGINTEGRITIY_SHA2_256));
|
||||
else if (hash == &hash_sha1)
|
||||
MSG_WriteShort(buf, BigShort(STUNATTR_MSGINTEGRITIY_SHA1));
|
||||
else
|
||||
return false; //not defined!
|
||||
MSG_WriteShort(buf, BigShort(len)); //integrity length
|
||||
SZ_Write(buf, integrity, len); //integrity data
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -993,7 +1018,7 @@ static qboolean ICE_SendSpam(struct icestate_s *con)
|
|||
data[3] = ((buf.cursize+4+sizeof(integ)-20)>>0)&0xff;
|
||||
//but the hash is to the start of the attribute's header
|
||||
CalcHMAC(&hash_sha1, integ, sizeof(integ), data, buf.cursize, con->rpwd, strlen(con->rpwd));
|
||||
MSG_WriteShort(&buf, BigShort(STUNATTR_MESSAGEINTEGRITIY)); //MESSAGE-INTEGRITY
|
||||
MSG_WriteShort(&buf, BigShort(STUNATTR_MSGINTEGRITIY_SHA1)); //MESSAGE-INTEGRITY
|
||||
MSG_WriteShort(&buf, BigShort(20)); //sha1 key length
|
||||
SZ_Write(&buf, integ, sizeof(integ)); //integrity data
|
||||
|
||||
|
@ -1041,7 +1066,6 @@ static qboolean ICE_SendSpam(struct icestate_s *con)
|
|||
return false;
|
||||
}
|
||||
|
||||
extern ftenet_generic_connection_t *FTENET_Datagram_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr);
|
||||
#ifdef HAVE_TCP
|
||||
struct turntcp_connection_s
|
||||
{ //this sends packets only to the relay, and accepts them only from there too. all packets must be stun packets (for byte-count framing)
|
||||
|
@ -1243,7 +1267,7 @@ static void ICE_ToStunServer(struct icestate_s *con, struct iceserver_s *srv)
|
|||
memset(&localadr, 0, sizeof(localadr));
|
||||
localadr.type = srv->addr.type;
|
||||
localadr.prot = srv->addr.prot;
|
||||
srv->con = FTENET_Datagram_EstablishConnection(collection, srv->realm, localadr);
|
||||
srv->con = FTENET_Datagram_EstablishConnection(collection, srv->realm, localadr, NULL);
|
||||
}
|
||||
if (!srv->con)
|
||||
{
|
||||
|
@ -2172,7 +2196,7 @@ static qboolean QDECL ICE_Set(struct icestate_s *con, const char *prop, const ch
|
|||
{
|
||||
#ifndef SERVERONLY
|
||||
if (con->proto == ICEP_QWCLIENT)
|
||||
CL_Transfer(&con->qadr); //okay, the client should be using this ice connection now.
|
||||
CL_Transfer(&con->qadr); //okay, the client should be using this ice connection now. FIXME: this should only switch them over if they're still trying to use the aerlier broker.
|
||||
#endif
|
||||
#ifdef HAVE_DTLS
|
||||
if (con->mode == ICEM_WEBRTC)
|
||||
|
@ -2447,7 +2471,8 @@ static char *ICE_CandidateToSDP(struct icecandidate_s *can, char *value, size_t
|
|||
can->info.port,
|
||||
ICE_GetCandidateType(&can->info)
|
||||
);
|
||||
Q_strncatz(value, va(" generation %i", can->info.generation), valuelen);
|
||||
if (can->info.generation)
|
||||
Q_strncatz(value, va(" generation %i", can->info.generation), valuelen); //firefox doesn't like this.
|
||||
if (can->info.type != ICE_HOST)
|
||||
{
|
||||
if (net_ice_relayonly.ival)
|
||||
|
@ -2725,8 +2750,10 @@ static void ICE_PrintSummary(struct icestate_s *con, qboolean islisten)
|
|||
}
|
||||
static void ICE_Debug(struct icestate_s *con)
|
||||
{
|
||||
const char *addrclass;
|
||||
struct icecandidate_s *can;
|
||||
char buf[65536];
|
||||
int i;
|
||||
ICE_Get(con, "state", buf, sizeof(buf));
|
||||
Con_Printf("ICE [%s] (%s):\n", con->friendlyname, buf);
|
||||
if (con->brokerless)
|
||||
|
@ -2750,17 +2777,33 @@ static void ICE_Debug(struct icestate_s *con)
|
|||
Con_Printf("peer:\n"S_COLOR_YELLOW"%s\n", buf);
|
||||
}
|
||||
|
||||
Con_Printf(" servers:\n");
|
||||
for (i = 0; i < con->servers; i++)
|
||||
{
|
||||
const char *status = "?";
|
||||
switch(con->server[i].state)
|
||||
{
|
||||
case TURN_UNINITED: status = "uninited"; break;
|
||||
case TURN_HAVE_NONCE: status = "registering"; break;
|
||||
case TURN_ALLOCATED: status = "allocated"; break;
|
||||
case TURN_TERMINATING: status = "terminating"; break;
|
||||
}
|
||||
NET_AdrToString(buf,sizeof(buf), &con->server[i].addr);
|
||||
Con_Printf(" %s:%s %s realm=%s user=%s auth=%s\n", con->server[i].isstun?"stun":"turn", buf, status, con->server[i].realm, con->server[i].user?con->server[i].user:"<unspecified>", con->server[i].auth?"<hidden>":"<none>");
|
||||
}
|
||||
|
||||
Con_Printf(" local:\n");
|
||||
for (can = con->lc; can; can = can->next)
|
||||
{
|
||||
ICE_CandidateToSDP(can, buf, sizeof(buf));
|
||||
if (con->chosenpeer.type!=NA_INVALID && con->chosenpeer.connum == can->info.network)
|
||||
Con_Printf(S_COLOR_GREEN" %s\n", buf);
|
||||
Con_Printf(S_COLOR_GREEN " %s"S_COLOR_GRAY" <chosen>\n", buf);
|
||||
else if (can->dirty)
|
||||
Con_Printf(S_COLOR_RED" %s\n", buf);
|
||||
Con_Printf(S_COLOR_RED " %s"S_COLOR_GRAY" <not sent>\n", buf);
|
||||
else
|
||||
Con_Printf(S_COLOR_YELLOW" %s\n", buf);
|
||||
}
|
||||
|
||||
Con_Printf(" remote:\n");
|
||||
for (can = con->rc; can; can = can->next)
|
||||
{
|
||||
|
@ -2768,12 +2811,14 @@ static void ICE_Debug(struct icestate_s *con)
|
|||
if (can->reachable)
|
||||
{
|
||||
if (con->chosenpeer.type!=NA_INVALID && NET_CompareAdr(&can->peer,&con->chosenpeer))
|
||||
Con_Printf(S_COLOR_GREEN" %s\n", buf);
|
||||
Con_Printf(S_COLOR_GREEN " %s"S_COLOR_GRAY" <chosen>\n", buf);
|
||||
else
|
||||
Con_Printf(S_COLOR_YELLOW" %s\n", buf);
|
||||
Con_Printf(S_COLOR_YELLOW" %s"S_COLOR_GRAY" <reachable>\n", buf);
|
||||
}
|
||||
else if (NET_ClassifyAddress(&can->peer, &addrclass) < ASCOPE_TURN_REQUIRESCOPE)
|
||||
Con_Printf(S_COLOR_RED" %s"S_COLOR_GRAY" <ignored: %s>\n", buf, addrclass);
|
||||
else
|
||||
Con_Printf(S_COLOR_RED" %s\n", buf);
|
||||
Con_Printf(S_COLOR_RED" %s"S_COLOR_GRAY" <unreachable>\n", buf);
|
||||
}
|
||||
}
|
||||
static void ICE_Show_f(void)
|
||||
|
@ -4157,7 +4202,7 @@ qboolean ICE_WasStun(ftenet_connections_t *col)
|
|||
switch(attrval)
|
||||
{
|
||||
case STUNATTR_USERNAME:
|
||||
case STUNATTR_MESSAGEINTEGRITIY:
|
||||
case STUNATTR_MSGINTEGRITIY_SHA1:
|
||||
break;
|
||||
default:
|
||||
if (attrval & 0x8000)
|
||||
|
@ -4455,7 +4500,7 @@ qboolean ICE_WasStun(ftenet_connections_t *col)
|
|||
err = (((qbyte*)attr)[6]*100) + (((qbyte*)attr)[7]%100);
|
||||
}
|
||||
break;
|
||||
case STUNATTR_MESSAGEINTEGRITIY:
|
||||
case STUNATTR_MSGINTEGRITIY_SHA1:
|
||||
break;
|
||||
}
|
||||
alen = (alen+3)&~3;
|
||||
|
@ -4548,7 +4593,8 @@ qboolean ICE_WasStun(ftenet_connections_t *col)
|
|||
s = &con->server[network];
|
||||
if (s->stunrnd[0] == stun->transactid[0] && s->stunrnd[1] == stun->transactid[1] && s->stunrnd[2] == stun->transactid[2] && NET_CompareAdr(&net_from, &s->addr))
|
||||
break;
|
||||
Con_Printf("Stale transaction id (got %x, expected %x)\n", stun->transactid[0], s->stunrnd[0]);
|
||||
if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"Stale transaction id (got %x, expected %x)\n", stun->transactid[0], s->stunrnd[0]);
|
||||
}
|
||||
}
|
||||
if (!con)
|
||||
|
@ -4575,7 +4621,7 @@ qboolean ICE_WasStun(ftenet_connections_t *col)
|
|||
lifetime = BigLong(*(int*)(attr+1));
|
||||
break;
|
||||
// case STUNATTR_SOFTWARE:
|
||||
case STUNATTR_MESSAGEINTEGRITIY:
|
||||
case STUNATTR_MSGINTEGRITIY_SHA1:
|
||||
// case STUNATTR_FINGERPRINT:
|
||||
break;
|
||||
default:
|
||||
|
@ -4653,13 +4699,13 @@ qboolean ICE_WasStun(ftenet_connections_t *col)
|
|||
s->stunretry = Sys_Milliseconds();
|
||||
|
||||
if (net_ice_debug.ival >= 1)
|
||||
Con_Printf("[%s]: %s: TURN error code %u : %s\n", con->friendlyname, NET_AdrToString(sender, sizeof(sender), &net_from), err, errmsg);
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: %s: TURN error code %u : %s\n", con->friendlyname, NET_AdrToString(sender, sizeof(sender), &net_from), err, errmsg);
|
||||
}
|
||||
else if (err == 403/*forbidden*/) //something bad...
|
||||
{
|
||||
s->state = TURN_UNINITED, s->stunretry = Sys_Milliseconds() + 60*1000;
|
||||
if (net_ice_debug.ival >= 1)
|
||||
Con_Printf("[%s]: %s: TURN error code %u : %s\n", con->friendlyname, NET_AdrToString(sender, sizeof(sender), &net_from), err, errmsg);
|
||||
Con_Printf(CON_ERROR"[%s]: %s: TURN error code %u : %s\n", con->friendlyname, NET_AdrToString(sender, sizeof(sender), &net_from), err, errmsg);
|
||||
}
|
||||
else if (err == 401 && s->state == TURN_UNINITED && s->nonce) //failure when sending auth... give up for a min
|
||||
{ //this happens from initial auth. we need to reply with the real auth request now.
|
||||
|
@ -4778,7 +4824,7 @@ qboolean ICE_WasStun(ftenet_connections_t *col)
|
|||
// Con_Printf("Stun username = \"%s\"\n", username);
|
||||
}
|
||||
break;
|
||||
case STUNATTR_MESSAGEINTEGRITIY:
|
||||
case STUNATTR_MSGINTEGRITIY_SHA1:
|
||||
memcpy(integrity, attr+1, sizeof(integrity));
|
||||
integritypos = (char*)(attr+1);
|
||||
break;
|
||||
|
@ -5020,13 +5066,13 @@ qboolean ICE_WasStun(ftenet_connections_t *col)
|
|||
if (lpwd)
|
||||
{
|
||||
//message integrity is a bit annoying
|
||||
data[2] = ((buf.cursize+4+sizeof(integrity)-20)>>8)&0xff; //hashed header length is up to the end of the hmac attribute
|
||||
data[3] = ((buf.cursize+4+sizeof(integrity)-20)>>0)&0xff;
|
||||
data[2] = ((buf.cursize+4+hash_sha1.digestsize-20)>>8)&0xff; //hashed header length is up to the end of the hmac attribute
|
||||
data[3] = ((buf.cursize+4+hash_sha1.digestsize-20)>>0)&0xff;
|
||||
//but the hash is to the start of the attribute's header
|
||||
CalcHMAC(&hash_sha1, integrity, sizeof(integrity), data, buf.cursize, lpwd, strlen(lpwd));
|
||||
MSG_WriteShort(&buf, BigShort(STUNATTR_MESSAGEINTEGRITIY));
|
||||
MSG_WriteShort(&buf, BigShort(sizeof(integrity))); //sha1 key length
|
||||
SZ_Write(&buf, integrity, sizeof(integrity)); //integrity data
|
||||
MSG_WriteShort(&buf, BigShort(STUNATTR_MSGINTEGRITIY_SHA1));
|
||||
MSG_WriteShort(&buf, BigShort(hash_sha1.digestsize)); //sha1 key length
|
||||
SZ_Write(&buf, integrity, hash_sha1.digestsize); //integrity data
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -5104,11 +5150,56 @@ int ICE_GetPeerCertificate(netadr_t *to, enum certprops_e prop, char *out, size_
|
|||
{
|
||||
#ifdef HAVE_DTLS
|
||||
struct icestate_s *con;
|
||||
int i, c;
|
||||
for (con = icelist; con; con = con->next)
|
||||
{
|
||||
if (NET_CompareAdr(to, &con->qadr))
|
||||
{
|
||||
if (con->dtlsstate && con->dtlsfuncs->GetPeerCertificate)
|
||||
if (prop==QCERT_LOBBYSTATUS)
|
||||
{
|
||||
*out = 0;
|
||||
switch(con->state)
|
||||
{
|
||||
case ICE_INACTIVE:
|
||||
Q_strncpyz(out, "idle", outsize);
|
||||
break;
|
||||
case ICE_FAILED:
|
||||
Q_strncpyz(out, "Failed", outsize);
|
||||
break;
|
||||
case ICE_GATHERING:
|
||||
Q_strncpyz(out, "Gathering", outsize);
|
||||
break;
|
||||
case ICE_CONNECTING:
|
||||
for (i = 0, c = false; i < con->servers; i++)
|
||||
if (!con->server[i].isstun)
|
||||
{
|
||||
if (con->server[i].state == TURN_ALLOCATED)
|
||||
break;
|
||||
c = true;
|
||||
}
|
||||
if (i == con->servers)
|
||||
{
|
||||
if (net_ice_relayonly.ival)
|
||||
Q_strncpyz(out, "Probing ("CON_ERROR"NO TURN SERVER"CON_DEFAULT")", outsize); //can't work, might still get an allocation though.
|
||||
else if (c)
|
||||
Q_strncpyz(out, "Probing ("CON_WARNING"waiting for TURN allocation"CON_DEFAULT")", outsize); //still good for latency. not for privacy though.
|
||||
else
|
||||
Q_strncpyz(out, "Probing ("CON_WARNING"no relay configured"CON_DEFAULT")", outsize); //still good for latency. not for privacy though.
|
||||
}
|
||||
else
|
||||
Q_strncpyz(out, "Probing ("S_COLOR_GREEN"with fallback"CON_DEFAULT")", outsize); //we have a relay for a fallback, all is good, hopefully. except we're still at this stage...
|
||||
break;
|
||||
case ICE_CONNECTED: //past the ICE stage (but maybe not the dtls+sctp layers, these should be less likely to fail, but dtls versions may become an issue)
|
||||
//if (con->dtlsstate && notokay)
|
||||
if (con->sctp && !con->sctp->o.writable)
|
||||
Q_strncpyz(out, "Establishing", outsize); //will also block for the dtls channel of course. its not as easy check the dtls layer.
|
||||
else
|
||||
Q_strncpyz(out, "Established", outsize);
|
||||
break;
|
||||
}
|
||||
return strlen(out);
|
||||
}
|
||||
else if (con->dtlsstate && con->dtlsfuncs->GetPeerCertificate)
|
||||
return con->dtlsfuncs->GetPeerCertificate(con->dtlsstate, prop, out, outsize);
|
||||
else if (prop==QCERT_ISENCRYPTED && con->dtlsstate)
|
||||
return 0;
|
||||
|
@ -5408,6 +5499,25 @@ static void FTENET_ICE_Refresh(ftenet_ice_connection_t *b, int cl, struct icesta
|
|||
FTENET_ICE_SplurgeCmd(b, ICEMSG_CANDIDATE, cl, buf);
|
||||
}
|
||||
}
|
||||
static void Buf_ReadString(const char **data, const char *end, char *out, size_t outsize)
|
||||
{
|
||||
const char *in = *data;
|
||||
char c;
|
||||
outsize--; //count the null early.
|
||||
while (in < end)
|
||||
{
|
||||
c = *in++;
|
||||
if (!c)
|
||||
break;
|
||||
if (outsize)
|
||||
{
|
||||
outsize--;
|
||||
*out++ = c;
|
||||
}
|
||||
}
|
||||
*out = 0;
|
||||
*data = in;
|
||||
}
|
||||
static qboolean FTENET_ICE_GetPacket(ftenet_generic_connection_t *gcon)
|
||||
{
|
||||
json_t *json;
|
||||
|
@ -5598,17 +5708,25 @@ handleerror:
|
|||
break;
|
||||
case ICEMSG_NEWPEER: //relay connection established with a new peer
|
||||
//note that the server ought to wait for an offer from the client before replying with any ice state, but it doesn't really matter for our use-case.
|
||||
{
|
||||
char peer[MAX_QPATH];
|
||||
char relay[MAX_QPATH];
|
||||
char *s;
|
||||
Buf_ReadString(&data, b->in+ofs+len, peer, sizeof(peer));
|
||||
Buf_ReadString(&data, b->in+ofs+len, relay, sizeof(relay));
|
||||
|
||||
if (b->generic.islisten)
|
||||
{
|
||||
// Con_DPrintf("Client connecting: %s\n", data);
|
||||
// Con_DPrintf("Client connecting: %s\n", data);
|
||||
if (cl < 1024 && cl >= b->numclients)
|
||||
{ //looks like a new one... but don't waste memory
|
||||
Z_ReallocElements((void**)&b->clients, &b->numclients, cl+1, sizeof(b->clients[0]));
|
||||
}
|
||||
if (cl >= 0 && cl < b->numclients)
|
||||
{
|
||||
FTENET_ICE_Establish(b, (len>3)?data:NULL, cl, &b->clients[cl].ice);
|
||||
|
||||
FTENET_ICE_Establish(b, *peer?peer:NULL, cl, &b->clients[cl].ice);
|
||||
for (s = relay; (s=COM_Parse(s)); )
|
||||
iceapi.Set(b->clients[cl].ice, "server", com_token);
|
||||
if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: New client spotted...\n", b->clients[cl].ice?b->clients[cl].ice->friendlyname:"?");
|
||||
}
|
||||
|
@ -5617,13 +5735,15 @@ handleerror:
|
|||
}
|
||||
else
|
||||
{
|
||||
// Con_DPrintf("Server found: %s\n", data);
|
||||
FTENET_ICE_Establish(b, (len>3)?data:NULL, cl, &b->ice);
|
||||
//Con_DPrintf("Server found: %s\n", data);
|
||||
FTENET_ICE_Establish(b, *peer?peer:NULL, cl, &b->ice);
|
||||
b->serverid = cl;
|
||||
|
||||
for (s = relay; (s=COM_Parse(s)); )
|
||||
iceapi.Set(b->ice, "server", com_token);
|
||||
if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: Meta channel to game server now open\n", b->ice?b->ice->friendlyname:"?");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ICEMSG_OFFER: //we received an offer from a client
|
||||
json = JSON_Parse(data);
|
||||
|
|
|
@ -1594,8 +1594,8 @@ static const struct urischeme_s urischemes[] =
|
|||
{
|
||||
#ifdef HAVE_PACKET
|
||||
{"udp://", NP_DGRAM, NA_INVALID}, //placeholder for dgram rather than an actual family.
|
||||
{"udp4//", NP_DGRAM, NA_IP},
|
||||
{"udp6//", NP_DGRAM, NA_IPV6},
|
||||
{"udp4://", NP_DGRAM, NA_IP},
|
||||
{"udp6://", NP_DGRAM, NA_IPV6},
|
||||
{"ipx://", NP_DGRAM, NA_IPX},
|
||||
|
||||
//compat with qtv. we don't have any way to exclude specific protocols though.
|
||||
|
@ -1612,40 +1612,40 @@ static const struct urischeme_s urischemes[] =
|
|||
|
||||
#ifdef TCPCONNECT
|
||||
{"tcp://", NP_STREAM, NA_INVALID}, //placeholder for dgram rather than an actual family.
|
||||
{"tcp4//", NP_STREAM, NA_IP},
|
||||
{"tcp6//", NP_STREAM, NA_IPV6},
|
||||
{"tcp4://", NP_STREAM, NA_IP},
|
||||
{"tcp6://", NP_STREAM, NA_IPV6},
|
||||
{"spx://", NP_STREAM, NA_IPX},
|
||||
|
||||
{"ws://", NP_WS, NA_INVALID, true},
|
||||
{"ws://", NP_WS, NA_INVALID, URISCHEME_NEEDSRESOURCE},
|
||||
#ifdef HAVE_SSL
|
||||
{"wss://", NP_WSS, NA_INVALID, true},
|
||||
{"wss://", NP_WSS, NA_INVALID, URISCHEME_NEEDSRESOURCE},
|
||||
{"tls://", NP_TLS, NA_INVALID},
|
||||
#endif
|
||||
#elif defined(HAVE_WEBSOCKCL)
|
||||
{"ws://", NP_WS, NA_WEBSOCKET, true},
|
||||
{"wss://", NP_WSS, NA_WEBSOCKET, true},
|
||||
{"tcp://", NP_WS, NA_WEBSOCKET, true}, //fake it
|
||||
{"tls://", NP_WSS, NA_WEBSOCKET, true}, //fake it
|
||||
{"ws://", NP_WS, NA_WEBSOCKET, URISCHEME_NEEDSRESOURCE},
|
||||
{"wss://", NP_WSS, NA_WEBSOCKET, URISCHEME_NEEDSRESOURCE},
|
||||
{"tcp://", NP_WS, NA_WEBSOCKET, URISCHEME_NEEDSRESOURCE}, //fake it
|
||||
{"tls://", NP_WSS, NA_WEBSOCKET, URISCHEME_NEEDSRESOURCE}, //fake it
|
||||
#endif
|
||||
#ifdef HAVE_DTLS
|
||||
{"dtls://", NP_DTLS, NA_INVALID},
|
||||
#endif
|
||||
|
||||
#if defined(SUPPORT_ICE) || defined(HAVE_WEBSOCKCL)
|
||||
{"ice://", NP_RTC_TCP, NA_INVALID, true},
|
||||
{"rtc://", NP_RTC_TCP, NA_INVALID, true},
|
||||
{"ices://", NP_RTC_TLS, NA_INVALID, true},
|
||||
{"rtcs://", NP_RTC_TLS, NA_INVALID, true},
|
||||
{"ice://", NP_RTC_TCP, NA_INVALID, URISCHEME_NEEDSRESOURCE},
|
||||
{"rtc://", NP_RTC_TCP, NA_INVALID, URISCHEME_NEEDSRESOURCE},
|
||||
{"ices://", NP_RTC_TLS, NA_INVALID, URISCHEME_NEEDSRESOURCE},
|
||||
{"rtcs://", NP_RTC_TLS, NA_INVALID, URISCHEME_NEEDSRESOURCE},
|
||||
#endif
|
||||
|
||||
#ifdef IRCCONNECT
|
||||
{"irc://", NP_IRC, NA_INVALID, true}, //should have been handled explicitly, if supported.
|
||||
{"irc://", NP_IRC, NA_INVALID, URISCHEME_NEEDSRESOURCE}, //should have been handled explicitly, if supported.
|
||||
#endif
|
||||
|
||||
#ifdef UNIXSOCKETS
|
||||
{"udg://", NP_DGRAM, NA_UNIX, true},
|
||||
{"udg://", NP_DGRAM, NA_UNIX, URISCHEME_NEEDSRESOURCE},
|
||||
#ifdef TCPCONNECT
|
||||
{"unix://", NP_STREAM, NA_UNIX, true},
|
||||
{"unix://", NP_STREAM, NA_UNIX, URISCHEME_NEEDSRESOURCE},
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
@ -6033,10 +6033,23 @@ qboolean FTENET_TCP_HTTPResponse(ftenet_tcp_stream_t *st, httparg_t arg[WCATTR_C
|
|||
return true;
|
||||
}
|
||||
|
||||
static int FTENET_TCP_WebRTCIncludeRelay(char *buffer, size_t bufsize, ftenet_tcp_stream_t *list, ftenet_tcp_stream_t *receipient)
|
||||
{
|
||||
int len;
|
||||
*buffer = 0;
|
||||
#ifdef SV_MASTER
|
||||
SVM_SelectRelay(&receipient->remoteaddr, receipient->webrtc.resource, buffer,bufsize);
|
||||
#endif
|
||||
len = strlen(buffer);
|
||||
buffer[len++] = 0; //always add a null to end the list.
|
||||
return len;
|
||||
}
|
||||
|
||||
void FTENET_TCP_WebRTCServerAssigned(ftenet_tcp_stream_t *list, ftenet_tcp_stream_t *client, ftenet_tcp_stream_t *server)
|
||||
{
|
||||
qbyte buffer[256];
|
||||
int trynext = 0;
|
||||
int len = 0;
|
||||
ftenet_tcp_stream_t *o;
|
||||
if (client->webrtc.clientnum < 0)
|
||||
client->webrtc.clientnum = 0;
|
||||
|
@ -6054,23 +6067,37 @@ void FTENET_TCP_WebRTCServerAssigned(ftenet_tcp_stream_t *list, ftenet_tcp_strea
|
|||
|
||||
if (server)
|
||||
{ //and tell them both, if the server is actually up
|
||||
int o = client->remoteaddr.prot;
|
||||
buffer[0] = ICEMSG_NEWPEER;
|
||||
buffer[1] = (client->webrtc.clientnum>>0)&0xff;
|
||||
buffer[2] = (client->webrtc.clientnum>>8)&0xff;
|
||||
// buffer[3] = (client->webrtc.clientnum>>16)&0xff;
|
||||
// buffer[4] = (client->webrtc.clientnum>>24)&0xff;
|
||||
client->remoteaddr.prot = 0;
|
||||
NET_BaseAdrToString(buffer+3, sizeof(buffer)-3, &client->remoteaddr); //let the server know who's trying to connect to them. for ip bans.
|
||||
client->remoteaddr.prot = o;
|
||||
FTENET_TCP_WebSocket_Splurge(server, WS_PACKETTYPE_BINARYFRAME, buffer, 3+strlen(buffer+3));
|
||||
buffer[len++] = ICEMSG_NEWPEER;
|
||||
buffer[len++] = (client->webrtc.clientnum>>0)&0xff;
|
||||
buffer[len++] = (client->webrtc.clientnum>>8)&0xff;
|
||||
// buffer[len++] = (client->webrtc.clientnum>>16)&0xff;
|
||||
// buffer[len++] = (client->webrtc.clientnum>>24)&0xff;
|
||||
|
||||
buffer[0] = ICEMSG_NEWPEER;
|
||||
buffer[1] = 0xff;
|
||||
buffer[2] = 0xff;
|
||||
// buffer[3] = 0xff;
|
||||
// buffer[4] = 0xff;
|
||||
FTENET_TCP_WebSocket_Splurge(client, WS_PACKETTYPE_BINARYFRAME, buffer, 3);
|
||||
//write the client's address, kinda
|
||||
if (client->remoteaddr.type == NA_IP) //anonymise it. hopefully still enough of an address to ban.
|
||||
Q_snprintfz(buffer+len, sizeof(buffer)-len, "%i.%i", client->remoteaddr.address.ip[0], client->remoteaddr.address.ip[1]);
|
||||
else if (client->remoteaddr.type == NA_IPV6) //anonymise it. we don't really know how big an allocation their router got... so include the first 4 bytes and hash the rest to compensate somewhat. most of it'll probably random though. this is messy. the server will be identifying connections more by index.
|
||||
Q_snprintfz(buffer+len, sizeof(buffer)-len, "%04x:%04x-%04x", client->remoteaddr.address.ip6[0]|client->remoteaddr.address.ip6[1], client->remoteaddr.address.ip6[2]|client->remoteaddr.address.ip6[3], 0xffffu&CalcHashInt(&hash_sha1, client->remoteaddr.address.ip6+4, sizeof(client->remoteaddr.address.ip6)-4));
|
||||
else
|
||||
{ //generically shove the client's address into the broker->server packet
|
||||
int o = client->remoteaddr.prot;
|
||||
client->remoteaddr.prot = 0;
|
||||
NET_BaseAdrToString(buffer+len, sizeof(buffer)-len, &client->remoteaddr); //let the server know who's trying to connect to them. for ip bans.
|
||||
client->remoteaddr.prot = o;
|
||||
}
|
||||
len += strlen(buffer+len)+1;
|
||||
len += FTENET_TCP_WebRTCIncludeRelay(buffer+len,sizeof(buffer)-len, list, server);
|
||||
FTENET_TCP_WebSocket_Splurge(server, WS_PACKETTYPE_BINARYFRAME, buffer, len);
|
||||
|
||||
len = 0;
|
||||
buffer[len++] = ICEMSG_NEWPEER;
|
||||
buffer[len++] = 0xff;
|
||||
buffer[len++] = 0xff;
|
||||
// buffer[len++] = 0xff;
|
||||
// buffer[len++] = 0xff;
|
||||
buffer[len++] = 0; //no remote peer name info...
|
||||
len += FTENET_TCP_WebRTCIncludeRelay(buffer+len,sizeof(buffer)-len, list, server);
|
||||
FTENET_TCP_WebSocket_Splurge(client, WS_PACKETTYPE_BINARYFRAME, buffer, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7272,7 +7299,7 @@ restart: //gotos are evil. I am evil. live with it.
|
|||
}
|
||||
}
|
||||
if (!o)
|
||||
Con_DPrintf("Unable to relay\n");
|
||||
Con_DPrintf("Unable to relay p%i to %s\n", st->inbuffer[payoffs+0], (st->clienttype == TCPC_WEBRTC_CLIENT)?"server":"client");
|
||||
}
|
||||
net_message.cursize = 0;
|
||||
}
|
||||
|
@ -8838,7 +8865,50 @@ static void FTENET_WebRTC_Callback(void *ctxp, int ctxi, int/*enum icemsgtype_s*
|
|||
//Con_Printf("To Broker: %i %i %s\n", evtype, ctxi, data);
|
||||
emscriptenfte_ws_send(wsc->brokersock, net_message_buffer, o-net_message_buffer);
|
||||
}
|
||||
static int FTENET_WebRTC_Create(qboolean initiator, ftenet_websocket_connection_t *wsc, int clid)
|
||||
static void FTENET_WebRTC_AddICEServer(char *config, size_t sizeofconfig, qboolean *first, const char *uri)
|
||||
{
|
||||
//we don't do the ?foo stuff properly (RFCs say only ?transport= and only for stun)
|
||||
char *s = strchr(uri, '?'), *next;
|
||||
const char *transport = NULL;
|
||||
const char *user = NULL;
|
||||
const char *auth = NULL;
|
||||
char tmp[256];
|
||||
for (;s;s=next)
|
||||
{
|
||||
*s++ = 0;
|
||||
next = strchr(s, '?');
|
||||
if (next)
|
||||
*next = 0;
|
||||
|
||||
if (!strncmp(s, "transport=", 10))
|
||||
transport = s+10;
|
||||
else if (!strncmp(s, "user=", 5))
|
||||
user = s+5;
|
||||
else if (!strncmp(s, "auth=", 5))
|
||||
auth = s+5;
|
||||
else if (!strncmp(s, "fam=", 4))
|
||||
;
|
||||
}
|
||||
|
||||
if (!strncmp(uri, "turn:", 5) || !strncmp(uri, "turns:", 6))
|
||||
if (!user || !auth)
|
||||
return;
|
||||
|
||||
if (*first)
|
||||
*first = false;
|
||||
else
|
||||
Q_strncatz(config, ",", sizeofconfig);
|
||||
if (transport)
|
||||
Q_strncatz(config, va("\n{\"urls\":[\"%s?transport=%s\"]", COM_QuotedString(uri, tmp,sizeof(tmp), true), transport), sizeofconfig);
|
||||
else
|
||||
Q_strncatz(config, va("\n{\"urls\":[\"%s\"]", COM_QuotedString(uri, tmp,sizeof(tmp), true)), sizeofconfig);
|
||||
if (user)
|
||||
Q_strncatz(config, va(",\"username\":\"%s\"", COM_QuotedString(user, tmp,sizeof(tmp), true)), sizeofconfig);
|
||||
if (auth)
|
||||
Q_strncatz(config, va(",\"credential\":\"%s\"", COM_QuotedString(auth, tmp,sizeof(tmp), true)), sizeofconfig);
|
||||
Q_strncatz(config, "}", sizeofconfig);
|
||||
}
|
||||
static int FTENET_WebRTC_Create(qboolean initiator, ftenet_websocket_connection_t *wsc, int clid, const char *relays)
|
||||
{
|
||||
int fd;
|
||||
char config[4096], tmp[256];
|
||||
|
@ -8889,48 +8959,14 @@ static int FTENET_WebRTC_Create(qboolean initiator, ftenet_websocket_connection_
|
|||
Q_strncatz(config, va("{\"urls\":[\"stun:%s\"]}", COM_QuotedString(com_token, tmp,sizeof(tmp), true)), sizeof(config));
|
||||
}
|
||||
|
||||
//add any user-specified ice servers
|
||||
for(servers = net_ice_servers.string; (servers=COM_Parse(servers)); )
|
||||
{
|
||||
//we don't do the ?foo stuff properly (RFCs say only ?transport= and only for stun)
|
||||
char *s = strchr(com_token, '?'), *next;
|
||||
const char *transport = NULL;
|
||||
const char *user = NULL;
|
||||
const char *auth = NULL;
|
||||
for (;s;s=next)
|
||||
{
|
||||
*s++ = 0;
|
||||
next = strchr(s, '?');
|
||||
if (next)
|
||||
*next = 0;
|
||||
FTENET_WebRTC_AddICEServer(config, sizeof(config), &first, com_token);
|
||||
|
||||
if (!strncmp(s, "transport=", 10))
|
||||
transport = s+10;
|
||||
else if (!strncmp(s, "user=", 5))
|
||||
user = s+5;
|
||||
else if (!strncmp(s, "auth=", 5))
|
||||
auth = s+5;
|
||||
else if (!strncmp(s, "fam=", 4))
|
||||
;
|
||||
}
|
||||
//add any auto-config ones.
|
||||
for(servers = relays; (servers=COM_Parse(servers)); )
|
||||
FTENET_WebRTC_AddICEServer(config, sizeof(config), &first, com_token);
|
||||
|
||||
if (!strncmp(com_token, "turn:", 5) || !strncmp(com_token, "turns:", 6))
|
||||
if (!user || !auth)
|
||||
continue;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
Q_strncatz(config, ",", sizeof(config));
|
||||
if (transport)
|
||||
Q_strncatz(config, va("\n{\"urls\":[\"%s?transport=%s\"]", COM_QuotedString(com_token, tmp,sizeof(tmp), true), transport), sizeof(config));
|
||||
else
|
||||
Q_strncatz(config, va("\n{\"urls\":[\"%s\"]", COM_QuotedString(com_token, tmp,sizeof(tmp), true)), sizeof(config));
|
||||
if (user)
|
||||
Q_strncatz(config, va(",\"username\":\"%s\"", COM_QuotedString(user, tmp,sizeof(tmp), true)), sizeof(config));
|
||||
if (auth)
|
||||
Q_strncatz(config, va(",\"credential\":\"%s\"", COM_QuotedString(auth, tmp,sizeof(tmp), true)), sizeof(config));
|
||||
Q_strncatz(config, "}", sizeof(config));
|
||||
}
|
||||
Q_strncatz(config, va("]"
|
||||
// ",\"bundlePolicy\":\"max-bundle\""
|
||||
",\"iceTransportPolicy\":\"%s\""
|
||||
|
@ -8946,6 +8982,7 @@ static qboolean FTENET_WebRTC_GetPacket(ftenet_generic_connection_t *gcon)
|
|||
{
|
||||
ftenet_websocket_connection_t *wsc = (void*)gcon;
|
||||
size_t i;
|
||||
char id[256];
|
||||
|
||||
if (wsc->heartbeat < realtime)
|
||||
FTENET_WebRTC_Heartbeat(wsc);
|
||||
|
@ -8983,7 +9020,7 @@ static qboolean FTENET_WebRTC_GetPacket(ftenet_generic_connection_t *gcon)
|
|||
{
|
||||
int cmd;
|
||||
short cl;
|
||||
const char *s;
|
||||
const char *s, *relays;
|
||||
char *p;
|
||||
|
||||
MSG_BeginReading(&net_message, msg_nullnetprim);
|
||||
|
@ -9032,6 +9069,9 @@ static qboolean FTENET_WebRTC_GetPacket(ftenet_generic_connection_t *gcon)
|
|||
Con_Printf("Listening on %s\n", wsc->remoteadr.address.websocketurl);
|
||||
break;
|
||||
case ICEMSG_NEWPEER: //connection established with a new peer
|
||||
/*peer*/ MSG_ReadString();
|
||||
relays = MSG_ReadString();
|
||||
|
||||
if (wsc->generic.islisten)
|
||||
{
|
||||
if (cl < 1024 && cl >= wsc->numclients)
|
||||
|
@ -9047,21 +9087,20 @@ static qboolean FTENET_WebRTC_GetPacket(ftenet_generic_connection_t *gcon)
|
|||
}
|
||||
if (cl < wsc->numclients)
|
||||
{
|
||||
char id[256];
|
||||
Q_snprintfz(id, sizeof(id), "/%i_%x", cl+1, rand());
|
||||
if (wsc->clients[cl].datasock != INVALID_SOCKET)
|
||||
emscriptenfte_ws_close(wsc->clients[cl].datasock);
|
||||
memcpy(&wsc->clients[cl].remoteadr, &wsc->remoteadr, sizeof(netadr_t));
|
||||
Q_strncatz(wsc->clients[cl].remoteadr.address.websocketurl, id, sizeof(wsc->clients[cl].remoteadr.address.websocketurl));
|
||||
wsc->clients[cl].remoteadr.port = htons(cl+1);
|
||||
wsc->clients[cl].datasock = FTENET_WebRTC_Create(false, wsc, cl);
|
||||
wsc->clients[cl].datasock = FTENET_WebRTC_Create(false, wsc, cl, relays);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (wsc->datasock != INVALID_SOCKET)
|
||||
emscriptenfte_ws_close(wsc->datasock);
|
||||
wsc->datasock = FTENET_WebRTC_Create(true, wsc, cl);
|
||||
wsc->datasock = FTENET_WebRTC_Create(true, wsc, cl, relays);
|
||||
}
|
||||
break;
|
||||
case ICEMSG_OFFER: //we received an offer from a client
|
||||
|
@ -9636,11 +9675,11 @@ qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, cons
|
|||
case NP_WSS:
|
||||
case NP_TLS:
|
||||
case NP_STREAM:
|
||||
if (!adrstring)
|
||||
if (!adrstring || !*adrstring)
|
||||
adrstring = NET_AdrToString(temp, sizeof(temp), adr); //urgh
|
||||
if (!FTENET_AddToCollection_Ptr(collection, routename, adrstring, adr, peerinfo))
|
||||
return false;
|
||||
Con_Printf("Establishing connection to %s\n", temp);
|
||||
Con_Printf("Establishing connection to \"%s\"\n", adrstring);
|
||||
break;
|
||||
#if defined(SUPPORT_ICE) || defined(FTE_TARGET_WEB)
|
||||
case NP_RTC_TCP:
|
||||
|
|
|
@ -443,6 +443,7 @@ qboolean ICE_WasStun(ftenet_connections_t *col);
|
|||
void QDECL ICE_AddLCandidateConn(ftenet_connections_t *col, netadr_t *addr, int type);
|
||||
void QDECL ICE_AddLCandidateInfo(struct icestate_s *con, netadr_t *adr, int adrno, int type);
|
||||
ftenet_generic_connection_t *FTENET_ICE_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo);
|
||||
extern ftenet_generic_connection_t *FTENET_Datagram_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo);
|
||||
enum icemsgtype_s
|
||||
{ //shared by rtcpeers+broker
|
||||
ICEMSG_PEERLOST=0, //other side dropped connection
|
||||
|
|
|
@ -924,19 +924,19 @@ void QCBUILTIN PF_json_find_object_child(pubprogfuncs_t *prinst, struct globalva
|
|||
|
||||
#ifdef FTE_TARGET_WEB
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
//FIXME: make sure the module is signed/'local'/trusted
|
||||
void QCBUILTIN PF_js_run_script(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
|
||||
{
|
||||
#ifdef FTE_TARGET_WEB
|
||||
const char *jscript = PR_GetStringOfs(prinst, OFS_PARM0);
|
||||
const char *ret;
|
||||
ret = emscripten_run_script_string(jscript);
|
||||
if (ret)
|
||||
G_INT(OFS_RETURN) = PR_TempString(prinst, ret);
|
||||
else
|
||||
#endif
|
||||
G_INT(OFS_RETURN) = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
//model functions
|
||||
|
@ -2002,24 +2002,31 @@ void QCBUILTIN PF_cvar_setf (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo
|
|||
//float(string name, string value) registercvar
|
||||
void QCBUILTIN PF_registercvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
|
||||
{
|
||||
const char *name, *value;
|
||||
int flags = (prinst->callargc>2)?G_FLOAT(OFS_PARM2):0;
|
||||
value = PR_GetStringOfs(prinst, OFS_PARM0);
|
||||
const char *name = PR_GetStringOfs(prinst, OFS_PARM0);
|
||||
const char *value = (prinst->callargc>2)?PR_GetStringOfs(prinst, OFS_PARM1):"";
|
||||
int dpflags = (prinst->callargc>2)?G_FLOAT(OFS_PARM2):0;
|
||||
int realflags = 0;
|
||||
name = PR_GetStringOfs(prinst, OFS_PARM0);
|
||||
|
||||
if (Cvar_FindVar(value))
|
||||
if (dpflags)
|
||||
{ //this is a DP extension, so uses DP's internal cvar flags.
|
||||
//which is stupid when cvar_type reports a different set of flags.
|
||||
//if (dpflags & (1<<0)) dpflags &= ~(1<<0), blocked from realflags |= avialable only to ;
|
||||
if (dpflags & (1<<4)) dpflags &= ~(1<<4), realflags |= CVAR_CHEAT;
|
||||
if (dpflags & (1<<5)) dpflags &= ~(1<<5), realflags |= CVAR_ARCHIVE;
|
||||
if (dpflags & (1<<8)) dpflags &= ~(1<<8), realflags |= CVAR_SERVERINFO;
|
||||
if (dpflags & (1<<9)) dpflags &= ~(1<<9), realflags |= CVAR_USERINFO;
|
||||
if (dpflags & (1<<11)) dpflags &= ~(1<<11), realflags |= CVAR_NOUNSAFEEXPAND;
|
||||
if (dpflags)
|
||||
Con_Printf(CON_WARNING"WARNING: Unknown flags passed to registercvar(\"%s\", \"%s\", %x)\n", name, value, dpflags);
|
||||
}
|
||||
|
||||
if (Cvar_FindVar(name))
|
||||
G_FLOAT(OFS_RETURN) = 0;
|
||||
else
|
||||
{
|
||||
name = value;
|
||||
if (prinst->callargc > 1)
|
||||
value = PR_GetStringOfs(prinst, OFS_PARM1);
|
||||
else
|
||||
value = "";
|
||||
|
||||
flags &= CVAR_ARCHIVE;
|
||||
|
||||
// archive?
|
||||
if (Cvar_Get(name, value, CVAR_USERCREATED|flags, "QC created vars"))
|
||||
if (Cvar_Get(name, value, CVAR_USERCREATED|realflags, "QC created vars"))
|
||||
G_FLOAT(OFS_RETURN) = 1;
|
||||
else
|
||||
G_FLOAT(OFS_RETURN) = 0;
|
||||
|
|
|
@ -225,6 +225,7 @@ enum
|
|||
CVAR_TYPEFLAG_ENGINE =1u<<3, //cvar was created by the engine itself (not user/mod created)
|
||||
CVAR_TYPEFLAG_HASDESCRIPTION=1u<<4, //cvar_description will return something (hopefully) useful
|
||||
CVAR_TYPEFLAG_READONLY =1u<<5, //cvar may not be changed by qc.
|
||||
//any extras added here should be shared with DP.
|
||||
};
|
||||
void QCBUILTIN PF_cvar_type (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
|
||||
void QCBUILTIN PF_uri_escape (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
|
||||
|
@ -584,7 +585,11 @@ void QCBUILTIN PF_json_find_object_child (pubprogfuncs_t *prinst, struct globalv
|
|||
void QCBUILTIN PF_json_get_length (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
|
||||
void QCBUILTIN PF_json_get_child_at_index (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
|
||||
void QCBUILTIN PF_json_get_name (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
|
||||
#ifdef FTE_TARGET_WEB
|
||||
void QCBUILTIN PF_js_run_script (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
|
||||
#else
|
||||
#define PF_js_run_script PF_Ignore
|
||||
#endif
|
||||
void QCBUILTIN PF_base64encode(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
|
||||
void QCBUILTIN PF_base64decode(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
|
||||
|
||||
|
|
|
@ -93,6 +93,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
#define PEXT2_DEPRECATEDORNEW (PEXT2_INFOBLOBS|PEXT2_VRINPUTS|PEXT2_LERPTIME) //extensions that are outdated
|
||||
#define PEXT2_MVDSUPPORT (PEXT2_CLIENTSUPPORT&~PEXT2_DEPRECATED&~PEXT2_STUNAWARE) //pext2 extensions to use when recording mvds.
|
||||
|
||||
#define PEXT2_LONGINDEXES 0 //boosts the maximum player+stat index.
|
||||
|
||||
//EzQuake/Mvdsv extensions. (use ezquake name, to avoid confusion about .mvd format and its protocol differences)
|
||||
#define EZPEXT1_FLOATENTCOORDS 0x00000001 //quirky - doesn't apply to broadcasts, just players+ents. this gives more precision, but will bug out if you try using it to increase map bounds in ways that may not be immediately apparent. iiuc this was added instead of fixing some inconsistent rounding...
|
||||
#define EZPEXT1_SETANGLEREASON 0x00000002 //specifies the reason for an svc_setangles call. the mvdsv implementation will fuck over any mods that writebyte them. we'd need to modify our preparse stuff to work around the issue.
|
||||
|
@ -1265,7 +1267,7 @@ typedef struct entity_state_s
|
|||
qbyte glowcolour;
|
||||
|
||||
qbyte scale; //4.4 precision
|
||||
char fatness;
|
||||
char fatness; //1/16th
|
||||
qbyte hexen2flags;
|
||||
qbyte abslight;
|
||||
|
||||
|
@ -1902,7 +1904,7 @@ typedef struct q1usercmd_s
|
|||
#define RENDER_VIEWMODEL 4
|
||||
#define RENDER_EXTERIORMODEL 8
|
||||
#define RENDER_LOWPRECISION 16 // send as low precision coordinates to save bandwidth
|
||||
#define RENDER_COLORMAPPED 32
|
||||
#define RENDER_COLORMAPPED 32 //networked colormap field is a direct (top<<4)|bottom value rather than a player slot (the |1024 thing d does)
|
||||
//#define RENDER_WORLDOBJECT 64
|
||||
#define RENDER_COMPLEXANIMATION 128
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
//translate is english->lang
|
||||
//untranslate is lang->english for console commands.
|
||||
|
||||
static void FilterPurge(void);
|
||||
static void FilterInit(const char *file);
|
||||
|
||||
int com_language;
|
||||
char sys_language[64] = "";
|
||||
|
@ -23,8 +25,14 @@ static void QDECL TL_LanguageChanged(struct cvar_s *var, char *oldvalue)
|
|||
|
||||
cvar_t language = CVARAFCD("lang", sys_language, "prvm_language", CVAR_USERINFO|CVAR_NORESET/*otherwise gamedir switches will be annoying*/, TL_LanguageChanged, "This cvar contains the language_dialect code of your language, used to find localisation strings.");
|
||||
|
||||
static void Filter_Reload_f(void)
|
||||
{
|
||||
|
||||
// FilterInit(
|
||||
}
|
||||
void TranslateInit(void)
|
||||
{
|
||||
Cmd_AddCommand("com_reloadfilter", Filter_Reload_f);
|
||||
Cvar_Register(&language, "Internationalisation");
|
||||
}
|
||||
|
||||
|
@ -44,6 +52,7 @@ void TL_Shutdown(void)
|
|||
PO_Close(languages[j].po_qex);
|
||||
languages[j].po_qex = NULL;
|
||||
}
|
||||
FilterPurge();
|
||||
}
|
||||
|
||||
static int TL_LoadLanguage(char *lang)
|
||||
|
@ -681,3 +690,140 @@ void TL_Reformat(int language, char *out, size_t outsize, size_t numargs, const
|
|||
}
|
||||
*out = 0;
|
||||
}
|
||||
|
||||
#include <ctype.h>
|
||||
static qbyte *filter[256]; //one list per lead char, simple optimisation instead of some big decision tree.
|
||||
static qbyte *filtermem;
|
||||
static int FilterCompareWords(const void *v1, const void *v2)
|
||||
{
|
||||
const char *s1 = *(const char*const*)v1;
|
||||
const char *s2 = *(const char*const*)v2;
|
||||
return strcmp(s2,s1);
|
||||
}
|
||||
static void FilterPurge(void)
|
||||
{
|
||||
memset(filter, 0, sizeof(filter));
|
||||
free(filtermem);
|
||||
filtermem = NULL;
|
||||
}
|
||||
static void FilterInit(const char *file)
|
||||
{
|
||||
qbyte *tempmem = malloc(strlen(file)+1);
|
||||
qbyte *tempmemstart = tempmem;
|
||||
const char **words;
|
||||
size_t count = 1, i, l;
|
||||
size_t bytes;
|
||||
const char *c;
|
||||
|
||||
FilterPurge();
|
||||
|
||||
for (c = file; *c; c++)
|
||||
if (*c == '\n')
|
||||
count++;
|
||||
|
||||
words = malloc(sizeof(qbyte*)*count);
|
||||
count = 0;
|
||||
for (c = file; *c; )
|
||||
{
|
||||
while (*c == '\n')
|
||||
c++; //don't add 0-byte strings...
|
||||
words[count] = tempmem;
|
||||
for (; *c; c++)
|
||||
{
|
||||
if (*c == ' ')
|
||||
continue; //block even if they omit the spaces.
|
||||
if (*c == '\n')
|
||||
break;
|
||||
*tempmem++ = tolower(*c);
|
||||
}
|
||||
*tempmem++ = 0;
|
||||
count++;
|
||||
}
|
||||
qsort(words, count, sizeof(words[0]), FilterCompareWords); //sort by lead byte... and longest first...
|
||||
i = 0;
|
||||
for (i = 0, bytes = 0; i < count; i++)
|
||||
bytes += strlen(words[i]);
|
||||
bytes += countof(filter);
|
||||
filtermem = malloc(bytes);
|
||||
|
||||
for (l = countof(filter), i = 0; l-- > 0; )
|
||||
{
|
||||
if (i < count && words[i][0] == l)
|
||||
{
|
||||
filter[l] = filtermem;
|
||||
while (i < count && *words[i] == l)
|
||||
{ //second copy... urgh. can forget the first char and replace with a length.
|
||||
*filtermem++ = strlen(words[i]+1);
|
||||
memcpy(filtermem, words[i]+1, filtermem[-1]); //just the text, no null needed. tighly packed.
|
||||
filtermem += filtermem[-1];
|
||||
i++;
|
||||
}
|
||||
*filtermem++ = 0;
|
||||
}
|
||||
else
|
||||
filter[l] = NULL;
|
||||
}
|
||||
free(tempmemstart);
|
||||
free(words);
|
||||
}
|
||||
#define whiteish(c) (c == ',' || c == '.' || c == ' ' || c == '\t' || c == '\r' || c == '\n')
|
||||
char *FilterObsceneString(const qbyte *in, char *outbuf, size_t bufsize)
|
||||
{ //input must be utf-8... if there's any ^ crap in there then strip it first. no bypassing filters with colour codes.
|
||||
char *ret = outbuf;
|
||||
if (strlen(in) >= bufsize)
|
||||
Sys_Error("output buffer too small!");
|
||||
restart:
|
||||
while (*in)
|
||||
{
|
||||
qbyte c = tolower(*in);
|
||||
if (filter[c])
|
||||
{
|
||||
qbyte *m = filter[c];
|
||||
while (*m)
|
||||
{ //for each word starting with this letter...
|
||||
const qbyte *test = in+1;
|
||||
qbyte len = *m;
|
||||
const qbyte *match = m+1;
|
||||
m += 1+len;
|
||||
while (*test)
|
||||
{ //don't let 'foo bar' through when 'foobar' is a bad word.
|
||||
if (whiteish(*test))
|
||||
{
|
||||
test++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tolower(*test) == *match)
|
||||
{
|
||||
test++, match++;
|
||||
if (--len == 0)
|
||||
{ //a match.
|
||||
if (*test && !whiteish(*test))
|
||||
break; //assassinate!
|
||||
while (test > in)
|
||||
{ //censor it.
|
||||
*outbuf = "#*@$"[(outbuf-ret)&3];
|
||||
outbuf++;
|
||||
in++;
|
||||
}
|
||||
goto restart; //double breaks suck
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (*in)
|
||||
{
|
||||
if (whiteish(*in))
|
||||
{
|
||||
*outbuf++ = *in++;
|
||||
break;
|
||||
}
|
||||
*outbuf++ = *in++;
|
||||
}
|
||||
}
|
||||
*outbuf++ = 0; //make sure its null terminated.
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -2349,6 +2349,10 @@ struct font_s *Font_LoadFont(const char *fontfilename, float vheight, float scal
|
|||
f = Z_Malloc(sizeof(*f));
|
||||
f->outline = outline;
|
||||
f->scale = scale;
|
||||
if (height < 1) //doesn't make sense. especially negatives...
|
||||
height = 1;
|
||||
if (height > 128)
|
||||
height = 128; //limit possible damage... we use alloca a bit so don't let the stack get abused too much.
|
||||
f->charheight = height;
|
||||
f->truecharheight = height;
|
||||
f->flags = flags;
|
||||
|
|
|
@ -163,7 +163,7 @@ void R_NetGraph (void)
|
|||
COM_ParseFunString(CON_WHITEMASK, va(" in: %.1f %.0fb\n", pi, bi), line, sizeof(line), false);
|
||||
Draw_ExpandedString(font_console, x, y, line);
|
||||
y += Font_CharVHeight(font_console);
|
||||
COM_ParseFunString(CON_WHITEMASK, va(" out: %.1f %.0fb\n", po, bo), line, sizeof(line), false);
|
||||
COM_ParseFunString(CON_WHITEMASK, va(" out: %.1f %.0fb mtu:%u\n", po, bo, cls.netchan.mtu_cur), line, sizeof(line), false);
|
||||
Draw_ExpandedString(font_console, x, y, line);
|
||||
y += Font_CharVHeight(font_console);
|
||||
}
|
||||
|
|
|
@ -645,7 +645,7 @@ void R_GenDlightBatches(batch_t *batches[])
|
|||
"deferredlight\n"
|
||||
"surfaceparm nodlight\n"
|
||||
"{\n"
|
||||
"program lpp_light\n"
|
||||
"program lpp_light#USE_ARB_SHADOW\n"
|
||||
"blendfunc gl_one gl_one\n"
|
||||
"nodepthtest\n"
|
||||
"map $gbuffer0\n" //depth
|
||||
|
|
|
@ -72,7 +72,6 @@ extern cvar_t r_tessellation;
|
|||
extern cvar_t gl_ati_truform_type;
|
||||
extern cvar_t r_tessellation_level;
|
||||
|
||||
extern cvar_t gl_blendsprites;
|
||||
extern cvar_t r_portaldrawplanes;
|
||||
extern cvar_t r_portalonly;
|
||||
|
||||
|
|
|
@ -3588,7 +3588,7 @@ static qboolean Sh_DrawStencilLight(dlight_t *dl, vec3_t colour, vec3_t axis[3],
|
|||
return true;
|
||||
}
|
||||
#else
|
||||
#define Sh_DrawStencilLight Sh_DrawShadowlessLight
|
||||
#define Sh_DrawStencilLight(dl,rgb,axis,vvis) Sh_DrawShadowlessLight(dl,rgb,axis,vvis,LSHADER_STANDARD)
|
||||
#endif
|
||||
|
||||
qboolean Sh_CullLight(dlight_t *dl, qbyte *vvis)
|
||||
|
|
|
@ -281,7 +281,6 @@ void (APIENTRY *qglPatchParameteriARB)(GLenum pname, GLint value); //core in gl4
|
|||
FTEPFNGLACTIVESTENCILFACEEXTPROC qglActiveStencilFaceEXT;
|
||||
|
||||
|
||||
#define GLchar char
|
||||
#if defined(_DEBUG) && !defined(DEBUG)
|
||||
#define DEBUG
|
||||
#endif
|
||||
|
|
|
@ -2401,7 +2401,7 @@ static BOOL CheckForcePixelFormat(rendererstate_t *info)
|
|||
}
|
||||
}
|
||||
// iAttribute[iAttributes++] = WGL_ALPHA_BITS_ARB; iAttribute[iAttributes++] = 2;
|
||||
iAttribute[iAttributes++] = WGL_DEPTH_BITS_ARB; iAttribute[iAttributes++] = info->depthbits?info->depthbits:16;
|
||||
iAttribute[iAttributes++] = WGL_DEPTH_BITS_ARB; iAttribute[iAttributes++] = info->depthbits?info->depthbits:24;
|
||||
iAttribute[iAttributes++] = WGL_STENCIL_BITS_ARB; iAttribute[iAttributes++] = 8;
|
||||
iAttribute[iAttributes++] = WGL_DOUBLE_BUFFER_ARB; iAttribute[iAttributes++] = GL_TRUE;
|
||||
iAttribute[iAttributes++] = WGL_STEREO_ARB; iAttribute[iAttributes++] = info->stereo;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#else
|
||||
SDL_Surface *sdlsurf;
|
||||
#endif
|
||||
void INS_SetOSK(int osk);
|
||||
|
||||
#include "vr.h"
|
||||
|
||||
|
@ -659,7 +660,7 @@ void GLVID_DeInit (void)
|
|||
vid.activeapp = false;
|
||||
|
||||
IN_DeactivateMouse();
|
||||
|
||||
INS_SetOSK(false);
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2,0,0)
|
||||
SDL_SetWindowGammaRamp(sdlwindow, NULL, NULL, NULL);
|
||||
|
|
|
@ -125,6 +125,10 @@ void ModBrush_LoadGLStuff(void *ctx, void *data, size_t a, size_t b); //data ===
|
|||
|
||||
void GL_InitFogTexture(void);
|
||||
|
||||
#ifndef GL_VERSION_2_0
|
||||
#define GLchar char
|
||||
#endif
|
||||
|
||||
// Function prototypes for the Texture Object Extension routines
|
||||
typedef GLboolean (APIENTRY *ARETEXRESFUNCPTR)(GLsizei, const GLuint *,
|
||||
const GLboolean *);
|
||||
|
|
|
@ -571,7 +571,7 @@ typedef GLboolean (APIENTRYP PFNGLISPROGRAMARBPROC) (GLuint program);
|
|||
#define GL_SAMPLER_2D_RECT_ARB 0x8B63
|
||||
#define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64
|
||||
// dont know if these two should go somewhere better:
|
||||
#if 1//def __APPLE__
|
||||
#ifdef __APPLE__
|
||||
typedef void *GLhandleARB; //Royally Fucked.
|
||||
#else
|
||||
typedef unsigned int GLhandleARB;
|
||||
|
|
|
@ -2637,6 +2637,7 @@ void PR_LocalInfoChanged(char *name, char *oldivalue, char *newvalue)
|
|||
}
|
||||
void PR_PreShutdown(void)
|
||||
{
|
||||
sv.mapchangelocked = true; //don't let the mod fuck over stuff like `disconnect`. its meant to be shutting down, not switching maps.
|
||||
if (svprogfuncs && gfuncs.SV_Shutdown && sv.state)
|
||||
{
|
||||
func_t f = gfuncs.SV_Shutdown;
|
||||
|
@ -7431,7 +7432,7 @@ static void QCBUILTIN PF_checkbuiltin (pubprogfuncs_t *prinst, struct globalvars
|
|||
{ //qc defines the function at least. nothing weird there...
|
||||
if (builtinno > 0 && builtinno < prinst->parms->numglobalbuiltins)
|
||||
{
|
||||
if (!prinst->parms->globalbuiltins[builtinno] || prinst->parms->globalbuiltins[builtinno] == PF_Fixme)
|
||||
if (!prinst->parms->globalbuiltins[builtinno] || prinst->parms->globalbuiltins[builtinno] == PF_Fixme || prinst->parms->globalbuiltins[builtinno] == PF_Ignore)
|
||||
G_FLOAT(OFS_RETURN) = false; //the builtin with that number isn't defined.
|
||||
else
|
||||
{
|
||||
|
@ -11243,7 +11244,7 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
|
|||
{"cos", PF_Fixme, 0, 0, 0, 39, "float(float)"},
|
||||
{"sqrt", PF_Fixme, 0, 0, 0, 40, "float(float)"},
|
||||
{"randomvector", PF_Fixme, 0, 0, 0, 41, "vector()"},
|
||||
{"registercvar", PF_Fixme, 0, 0, 0, 42, D("float(string name, string value, float flags)", "Creates the cvar if it didn't already exist. This presents issues for setting those cvars via startup configs of course, and autocvars are easier but I suppose they don't get any flags (which are ignored anyway, of course).")},
|
||||
{"registercvar", PF_Fixme, 0, 0, 0, 42, D("float(string name, string value, optional float flags)", "Creates the cvar if it didn't already exist. This presents issues for setting those cvars via startup configs of course, and autocvars are easier but I suppose they don't get any flags (which are ignored anyway, of course).")},
|
||||
{"min", PF_Fixme, 0, 0, 0, 43, "float(float,...)"},
|
||||
{"max", PF_Fixme, 0, 0, 0, 44, "float(float,...)"},
|
||||
{"bound", PF_Fixme, 0, 0, 0, 45, "float(float min,float value,float max)"},
|
||||
|
@ -11572,7 +11573,7 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
|
|||
|
||||
{"randomvec", PF_randomvector, 0, 0, 0, 91, D("vector()", "Returns a vector with random values. Each axis is independantly a value between -1 and 1 inclusive.")},
|
||||
{"getlight", PF_sv_getlight, 0, 0, 0, 92, D("DEP_SSQC(\"Broken on dedicated servers, ignores rtlights/etc\") vector(vector org)", "Computes the RGB lighting at the specified position.")},// (DP_QC_GETLIGHT),
|
||||
{"registercvar", PF_registercvar, 0, 0, 0, 93, D("float(string cvarname, string defaultvalue)", "Creates a new cvar on the fly. If it does not already exist, it will be given the specified value. If it does exist, this is a no-op.\nThis builtin has the limitation that it does not apply to configs or commandlines. Such configs will need to use the set or seta command causing this builtin to be a noop.\nIn engines that support it, you will generally find the autocvar feature easier and more efficient to use.")},
|
||||
{"registercvar", PF_registercvar, 0, 0, 0, 93, D("float(string cvarname, string defaultvalue, optional float flags)", "Creates a new cvar on the fly. If it does not already exist, it will be given the specified value. If it does exist, this is a no-op.\nThis builtin has the limitation that it does not apply to configs or commandlines. Such configs will need to use the set or seta command causing this builtin to be a noop.\nIn engines that support it, you will generally find the autocvar feature easier and more efficient to use.")},
|
||||
{"min", PF_min, 0, 0, 0, 94, D("float(float a, float b, ...)", "Returns the lowest value of its arguments.")},// (DP_QC_MINMAXBOUND)
|
||||
{"max", PF_max, 0, 0, 0, 95, D("float(float a, float b, ...)", "Returns the highest value of its arguments.")},// (DP_QC_MINMAXBOUND)
|
||||
{"bound", PF_bound, 0, 0, 0, 96, D("float(float minimum, float val, float maximum)", "Returns val, unless minimum is higher, or maximum is less.")},// (DP_QC_MINMAXBOUND)
|
||||
|
@ -12265,8 +12266,8 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
|
|||
// {"particlethemefree",PF_Fixme, 0, 0, 0, 526, D("void()","Resets the particle theme slot to defaults, and marks it as uninitialised (so themesave might reallocate it)")},
|
||||
// {"particle", PF_Fixme, 0, 0, 0, 527, D("float(vector org, vector vel, optional float theme)","Spawns a particle at the specified position+speed. If theme is specified the other properties come from a theme slot, otherwise they're read from globals.")},
|
||||
// {"delayedparticle", PF_Fixme, 0, 0, 0, 528, D("float(vector org, vector vel, float delay, float collisiondelay, optional float theme)","Basically just extra args for 'particle'.")},
|
||||
{"loadfromdata", PF_loadfromdata, 0, 0, 0, 529, D("void(string s)", "Reads a set of entities from the given string. This string should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead.")},
|
||||
{"loadfromfile", PF_loadfromfile, 0, 0, 0, 530, D("void(string s)", "Reads a set of entities from the named file. This file should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead.")},
|
||||
{"loadfromdata", PF_loadfromdata, 0, 0, 0, 529, D("void(string s)", "Reads a set of entities from the given string. This string should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. No spawn functions will be called.")},
|
||||
{"loadfromfile", PF_loadfromfile, 0, 0, 0, 530, D("void(string s)", "Reads a set of entities from the named file. This file should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. No spawn functions will be called.")},
|
||||
{"setpause", PF_setpause, 0, 0, 0, 531, D("void(float pause)", "SSQC: Sets whether the server should or should not be paused.\n"
|
||||
"CSQC: Only works in singleplayer, suitable for menu auto-pause. To pause in multiplayer use eg localcmd(\"cmd pause\n\") to ask the server side to pause.\n"
|
||||
"Pause state between modules will be ORed, along with engine reasons for auto pausing.")},
|
||||
|
|
|
@ -1401,6 +1401,7 @@ vfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname, const c
|
|||
void SVM_AddBrokerGame(const char *brokerid, const char *info);
|
||||
void SVM_RemoveBrokerGame(const char *brokerid);
|
||||
qboolean SVM_FixupServerAddress(netadr_t *adr, struct dtlspeercred_s *cred);
|
||||
void SVM_SelectRelay(netadr_t *benefitiary, const char *brokerid, char *out, size_t outsize);
|
||||
void FTENET_TCP_ICEResponse(struct ftenet_connections_s *col, int type, const char *cid, const char *sdp);
|
||||
|
||||
|
||||
|
|
|
@ -2885,7 +2885,15 @@ void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, edict_t *
|
|||
vent = ent;
|
||||
|
||||
|
||||
|
||||
if (vent->xv->customizeentityforclient)
|
||||
{
|
||||
globalvars_t *pr_globals = PR_globals(svprogfuncs, PR_CURRENT);
|
||||
pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, vent);
|
||||
pr_global_struct->other = (clent?EDICT_TO_PROG(svprogfuncs, clent):0);
|
||||
PR_ExecuteProgram(svprogfuncs, vent->xv->customizeentityforclient);
|
||||
if(!G_FLOAT(OFS_RETURN))
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
#ifdef NQPROT
|
||||
|
|
|
@ -1013,7 +1013,7 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents,
|
|||
{
|
||||
//.map is commented out because quite frankly, they're a bit annoying when the engine loads the gpled start.map when really you wanted to just play the damn game intead of take it apart.
|
||||
//if you want to load a .map, just use 'map foo.map' instead.
|
||||
char *exts[] = {"%s", "maps/%s", "maps/%s.bsp", "maps/%s.d3dbsp", "maps/%s.cm", "maps/%s.hmp", /*"maps/%s.map",*/ "maps/%s.bsp.gz", "maps/%s.bsp.xz", NULL}, *e;
|
||||
char *exts[] = {"%s", "maps/%s", "maps/%s.bsp", "maps/%s.d3dbsp", "maps/%s.cm", "maps/%s.hmp", "maps/%s.bsp.gz", "maps/%s.bsp.xz", "maps/%s.map", NULL}, *e;
|
||||
int depth, bestdepth = FDEPTH_MISSING;
|
||||
flocation_t loc;
|
||||
time_t filetime;
|
||||
|
@ -1057,7 +1057,6 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents,
|
|||
mod = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strncmp(sv.modelname, "maps/", 5))
|
||||
Q_strncpyz (svs.name, sv.modelname+5, sizeof(svs.name));
|
||||
else
|
||||
|
@ -1583,6 +1582,9 @@ MSV_OpenUserDatabase();
|
|||
else
|
||||
InfoBuf_SetValueForStarKey(&svs.info, "*entfile", "");
|
||||
|
||||
if (usecinematic)
|
||||
file = NULL;
|
||||
else
|
||||
file = Mod_GetEntitiesString(sv.world.worldmodel);
|
||||
if (!file)
|
||||
file = "";
|
||||
|
|
|
@ -2195,7 +2195,7 @@ void SV_ClientProtocolExtensionsChanged(client_t *client)
|
|||
extern cvar_t pext_ezquake_nochunks;
|
||||
extern cvar_t pext_ezquake_verfortrans;
|
||||
s = InfoBuf_ValueForKey(&client->userinfo, "*client");
|
||||
if (!strncmp(s, "ezQuake", 7) || !strncmp(s, "FortressOne", 11))
|
||||
if (!strncmp(s, "ezQuake", 7))
|
||||
{
|
||||
s = COM_Parse(s); //skip name-of-fork
|
||||
COM_Parse(s); //tokenize the version
|
||||
|
@ -5860,7 +5860,11 @@ void SV_InitLocal (void)
|
|||
static cvar_t qws_fullname = CVARF("qws_fullname", FULLENGINENAME, CVAR_NOSET );
|
||||
static cvar_t qws_version = CVARF("qws_version", STRINGIFY(FTE_VER_MAJOR)"."STRINGIFY(FTE_VER_MINOR),CVAR_NOSET );
|
||||
static cvar_t qws_buildnum = CVARF("qws_buildnum", STRINGIFY(SVNREVISION), CVAR_NOSET );
|
||||
#ifdef FTE_TARGET_WEB
|
||||
static cvar_t qws_platform = CVARF("qws_platform", PLATFORM, CVAR_NOSET );
|
||||
#else
|
||||
static cvar_t qws_platform = CVARF("qws_platform", PLATFORM "-" ARCH_CPU_POSTFIX, CVAR_NOSET );
|
||||
#endif
|
||||
static cvar_t qws_builddate = CVARF("qws_builddate",STRINGIFY(SVNDATE), CVAR_NOSET );
|
||||
static cvar_t qws_homepage = CVARF("qws_homepage", ENGINEWEBSITE, CVAR_NOSET );
|
||||
Cvar_Register(&qws_name, "Server Info");
|
||||
|
|
|
@ -31,10 +31,11 @@
|
|||
#define QUAKE3PROTOCOLNAME "Quake3"
|
||||
|
||||
|
||||
#define PREFIX_SECURE(issecure) ((issecure)?"🛡":"⚠️")//shield, vs yellow warning
|
||||
#define PREFIX_NEEDPASS(needpass) (((needpass)&1)?"🔒":"") //padlock, vs no indicator.
|
||||
#define PREFIX_COOP(iscoop) (((iscoop)&1)?"☮":"") //coop: peace sign, deathmatch:no indicator.
|
||||
|
||||
#define PREFIX_SECURE(srv) ((srv)->secure?"🛡":"⚠️")//shield, vs yellow warning
|
||||
#define PREFIX_NEEDPASS(srv) (((srv)->needpass&1)?"🔒":"") //padlock, vs no indicator.
|
||||
#define PREFIX_TYPE(srv) ( ((srv)->type&1)?"☮"/*coop: peace sign, deathmatch:no indicator. */ :\
|
||||
((srv)->type&2)?"📺"/*tv symbol*/ :\
|
||||
"")
|
||||
enum gametypes_e
|
||||
{
|
||||
GT_FFA=0,
|
||||
|
@ -50,9 +51,9 @@ typedef struct svm_server_s {
|
|||
unsigned int bots; //non-human players
|
||||
unsigned int clients; //human players
|
||||
unsigned int maxclients; //limit of bots+clients, but not necessarily spectators.
|
||||
int secure:1;
|
||||
int needpass:1;
|
||||
int coop:1;
|
||||
unsigned int secure:1;
|
||||
unsigned int needpass:1;
|
||||
unsigned int type:4;
|
||||
char hostname[64]; //just for our own listings.
|
||||
char mapname[16]; //just for our own listings.
|
||||
char gamedir[16]; //again...
|
||||
|
@ -649,6 +650,8 @@ static void SVM_GatherServerRule(void *ctx, const char *key, const char *val)
|
|||
char niceval[256];
|
||||
if (rules->lines == countof(rules->line))
|
||||
return; //overflow
|
||||
if (*key == '_')
|
||||
val = "<PRIVATE>"; //was meant to be private... lets show that its there, just not what it is.
|
||||
QuakeCharsToHTML(niceval, sizeof(niceval), val, false);
|
||||
if (!Q_snprintfz(rules->blob+rules->blobofs, sizeof(rules->blob)-rules->blobofs, "<tr><td>%s</td><td>%s</td></tr>\n", key, niceval))
|
||||
{
|
||||
|
@ -685,7 +688,7 @@ void SVM_Generate_ServerinfoEntry(vfsfile_t *f, const char *masteraddr, svm_serv
|
|||
VFS_PRINTF(f, "<tr><td><a href=\"/game/%s%s%s\">%s</a></td><td>%s</td><td>%s%s%s%s</td><td>%s</td><td>%s</td><td>%u/%u</td></tr>\n",
|
||||
server->game?server->game->name:"Unknown", query?"?":"", query?query:"", server->game?server->game->name:"Unknown", //game column
|
||||
url, //address column
|
||||
PREFIX_SECURE(server->secure), PREFIX_NEEDPASS(server->needpass), PREFIX_COOP(server->coop), hostname, //hostname column
|
||||
PREFIX_SECURE(server), PREFIX_NEEDPASS(server), PREFIX_TYPE(server), hostname, //hostname column
|
||||
server->gamedir, server->mapname, server->clients, server->maxclients);
|
||||
VFS_PRINTF(f, "</table>\n");
|
||||
VFS_PRINTF(f, "<br/>\n");
|
||||
|
@ -723,7 +726,7 @@ static vfsfile_t *SVM_Generate_RoomServerinfo(const char **mimetype, const char
|
|||
else
|
||||
{
|
||||
VFS_PRINTF(f, "<table border=1>\n");
|
||||
VFS_PRINTF(f, "<tr><th>Game</th><th>Address</th><th>Hostname</th><th>Mod dir</th><th>Mapname</th><th>Players</th></tr>\n");
|
||||
VFS_PRINTF(f, "<tr><th>Game</th><th>Address</th><th>Hostname</th><th>Gamedir</th><th>Mapname</th><th>Players</th></tr>\n");
|
||||
VFS_PRINTF(f, "<tr><td>?</td><td>%s</td><td>?</td><td>?</td><td>?</td><td>?/?</td></tr>\n", serveraddr);
|
||||
VFS_PRINTF(f, "</table>\n");
|
||||
}
|
||||
|
@ -744,8 +747,12 @@ static vfsfile_t *SVM_Generate_AddrServerinfo(const char **mimetype, const char
|
|||
VFS_PRINTF(f, "%s", master_css);
|
||||
VFS_PRINTF(f, "<h1>Single Server Info</h1>\n");
|
||||
|
||||
#if 1
|
||||
count = NET_StringToAdr_NoDNS(serveraddr, 0, adr)?1:0;
|
||||
#else
|
||||
//FIXME: block dns lookups here?
|
||||
count = NET_StringToAdr2(serveraddr, 0, adr, countof(adr), NULL);
|
||||
#endif
|
||||
while(count-->0)
|
||||
{
|
||||
server = SVM_GetServer(&adr[count]);
|
||||
|
@ -754,12 +761,14 @@ static vfsfile_t *SVM_Generate_AddrServerinfo(const char **mimetype, const char
|
|||
else
|
||||
{
|
||||
VFS_PRINTF(f, "<table border=1>\n");
|
||||
VFS_PRINTF(f, "<tr><th>Game</th><th>Address</th><th>Hostname</th><th>Mod dir</th><th>Mapname</th><th>Players</th></tr>\n");
|
||||
VFS_PRINTF(f, "<tr><th>Game</th><th>Address</th><th>Hostname</th><th>Gamedir</th><th>Mapname</th><th>Players</th></tr>\n");
|
||||
VFS_PRINTF(f, "<tr><td>?</td><td>%s</td><td>?</td><td>?</td><td>?</td><td>?/?</td></tr>\n", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &adr[count]));
|
||||
VFS_PRINTF(f, "</table>\n");
|
||||
}
|
||||
}
|
||||
|
||||
VFS_PRINTF(f, "<br/><a href=\"/\">Other Protocols</a>\n");
|
||||
|
||||
*mimetype = "text/html";
|
||||
return f;
|
||||
}
|
||||
|
@ -820,7 +829,7 @@ vfsfile_t *SVM_Generate_Serverlist(const char **mimetype, const char *masteraddr
|
|||
infourl = url = NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr);
|
||||
preurl = "/server/";
|
||||
}
|
||||
VFS_PRINTF(f, "<tr><td><a href=\"%s%s\">%s</a></td><td>%s%s%s%s</td><td>%s</td><td>%s</td><td>%u", preurl,infourl, url, PREFIX_SECURE(server->secure), PREFIX_NEEDPASS(server->needpass), PREFIX_COOP(server->coop), hostname, server->gamedir, server->mapname, server->clients);
|
||||
VFS_PRINTF(f, "<tr><td><a href=\"%s%s\">%s</a></td><td>%s%s%s%s</td><td>%s</td><td>%s</td><td>%u", preurl,infourl, url, PREFIX_SECURE(server), PREFIX_NEEDPASS(server), PREFIX_TYPE(server), hostname, server->gamedir, server->mapname, server->clients);
|
||||
if (server->bots)
|
||||
VFS_PRINTF(f, "+%ub", server->bots);
|
||||
VFS_PRINTF(f, "/%u", server->maxclients);
|
||||
|
@ -842,11 +851,13 @@ vfsfile_t *SVM_Generate_Serverlist(const char **mimetype, const char *masteraddr
|
|||
VFS_PRINTF(f, ", %u bot%s", (unsigned)bots, bots==1?"":"s");
|
||||
if (specs)
|
||||
VFS_PRINTF(f, ", %u spectator%s", (unsigned)specs, specs==1?"":"s");
|
||||
VFS_PRINTF(f, "\n");
|
||||
VFS_PRINTF(f, "<br/>\n");
|
||||
}
|
||||
else
|
||||
VFS_PRINTF(f, "Protocol '%s' is not known\n", gamename);
|
||||
|
||||
VFS_PRINTF(f, "<br/><a href=\"/\">Other Protocols</a>\n");
|
||||
|
||||
*mimetype = "text/html";
|
||||
return f;
|
||||
}
|
||||
|
@ -902,7 +913,7 @@ vfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname, const c
|
|||
return f;
|
||||
}
|
||||
|
||||
static svm_game_t *SVM_GameFromBrokerID(const char **brokerid)
|
||||
static svm_game_t *SVM_GameFromBrokerID(const char **brokerid, qboolean create)
|
||||
{ //broker id is /GAMENAME/SERVERNAME
|
||||
size_t l;
|
||||
char name[128];
|
||||
|
@ -918,7 +929,7 @@ static svm_game_t *SVM_GameFromBrokerID(const char **brokerid)
|
|||
*brokerid = ++in;
|
||||
else
|
||||
Q_strncpyz(name, "unspecified", sizeof(name));
|
||||
return SVM_FindGame(name, true);
|
||||
return SVM_FindGame(name, create);
|
||||
}
|
||||
static svm_server_t *SVM_FindBrokerHost(const char *brokerid)
|
||||
{
|
||||
|
@ -938,7 +949,7 @@ static svm_server_t *SVM_FindBrokerHost(const char *brokerid)
|
|||
void SVM_RemoveBrokerGame(const char *brokerid)
|
||||
{
|
||||
svm_server_t *s, **link;
|
||||
svm_game_t *game = SVM_GameFromBrokerID(&brokerid);
|
||||
svm_game_t *game = SVM_GameFromBrokerID(&brokerid, false);
|
||||
if (!game)
|
||||
{
|
||||
Con_Printf("SVM_RemoveBrokerGame: failed to find game for brokered server: %s\n", brokerid);
|
||||
|
@ -964,8 +975,9 @@ void SVM_RemoveBrokerGame(const char *brokerid)
|
|||
}
|
||||
void SVM_AddBrokerGame(const char *brokerid, const char *info)
|
||||
{
|
||||
svm_game_t *game = SVM_GameFromBrokerID(&brokerid);
|
||||
svm_game_t *game = SVM_GameFromBrokerID(&brokerid, true);
|
||||
svm_server_t *server = SVM_FindBrokerHost(brokerid);
|
||||
char *s;
|
||||
if (!server)
|
||||
{
|
||||
if (!game)
|
||||
|
@ -993,16 +1005,26 @@ void SVM_AddBrokerGame(const char *brokerid, const char *info)
|
|||
else
|
||||
Con_DPrintf("heartbeat(update - %s): /%s\n", game->name, brokerid);
|
||||
|
||||
server->protover = atoi(Info_ValueForKey(info, "protocol"));
|
||||
server->maxclients = atoi(Info_ValueForKey(info, "maxclients"));
|
||||
s = Info_ValueForKey(info, "sv_maxclients");
|
||||
if (!*s)
|
||||
s = Info_ValueForKey(info, "maxclients");
|
||||
server->maxclients = atoi(s);
|
||||
server->clients = atoi(Info_ValueForKey(info, "clients"));
|
||||
server->secure = !!*Info_ValueForKey(info, "*fp");
|
||||
server->needpass = atoi(Info_ValueForKey(info, "needpass"));
|
||||
server->coop = atoi(Info_ValueForKey(info, "coop"));
|
||||
if (!server->coop)
|
||||
server->type = atoi(Info_ValueForKey(info, "coop"));
|
||||
if (!server->type)
|
||||
{ //deathmatch 0 also means coop 1... servers that report neither are probably annoying DP servers that report nothing useful and should default to DM.
|
||||
const char *v = Info_ValueForKey(info, "deathmatch");
|
||||
server->coop = *v && !atoi(v);
|
||||
server->type = *v && !atoi(v);
|
||||
}
|
||||
server->protover = strtol(Info_ValueForKey(info, "protocol"), &s, 0);
|
||||
for (; *s; s++)
|
||||
{
|
||||
if (*s == 't') //turn
|
||||
server->type |= 2;
|
||||
else if (*s == 'f') //usable for qwfwd
|
||||
server->type |= 4;
|
||||
}
|
||||
Q_strncpyz(server->hostname, Info_ValueForKey(info, "hostname"), sizeof(server->hostname));
|
||||
Q_strncpyz(server->gamedir, Info_ValueForKey(info, "modname"), sizeof(server->gamedir));
|
||||
|
@ -1015,6 +1037,53 @@ void SVM_AddBrokerGame(const char *brokerid, const char *info)
|
|||
|
||||
Q_strncpyz(server->rules, info, sizeof(server->rules));
|
||||
}
|
||||
void SVM_SelectRelay(netadr_t *benefitiary, const char *brokerid, char *out, size_t outsize)
|
||||
{
|
||||
char username[128];
|
||||
char pass[128];
|
||||
char key[128];
|
||||
char adrbuf[64];
|
||||
qbyte dig[DIGEST_MAXSIZE];
|
||||
size_t keysize;
|
||||
svm_server_t *server;
|
||||
svm_game_t *game = SVM_GameFromBrokerID(&brokerid, false);
|
||||
int count;
|
||||
if (!game)
|
||||
return; //nope.
|
||||
|
||||
for (count = 0, server = game->firstserver; server; server = server->next)
|
||||
{
|
||||
if (server->needpass)
|
||||
continue; //nope, not interested.
|
||||
if (server->type & 8) //acting as a turn relay...
|
||||
count++;
|
||||
}
|
||||
if (!count)
|
||||
return; //none for you...
|
||||
|
||||
count = rand()%count; //pick one at random... FIXME: fix closest.
|
||||
|
||||
for (server = game->firstserver; server; server = server->next)
|
||||
{
|
||||
if (server->needpass)
|
||||
continue; //nope, not interested.
|
||||
if (server->type & 8) //acting as a turn relay...
|
||||
{
|
||||
if (count-->0)
|
||||
continue; //we didn't pick this one, keep going.
|
||||
|
||||
//we need a username. this includes a timestamp to ensure it can expire.
|
||||
Q_snprintfz(username,sizeof(username), "%"PRIi64":%s", (quint64_t)time(NULL), brokerid);
|
||||
//we need a password too... its based upon our username and a secret key also known only to the relay.
|
||||
keysize = Base64_DecodeBlock(Info_ValueForKey(server->rules, "_turnkey"),NULL, key,sizeof(key));
|
||||
keysize = CalcHMAC(&hash_sha1, dig,sizeof(dig), username,strlen(username), key,keysize);
|
||||
pass[Base64_EncodeBlock(dig,keysize, pass,sizeof(pass)-1)] = 0;
|
||||
//and spit out the url (with our ?user= and ?auth= bits added.
|
||||
Q_snprintfz(out,outsize, "turn:%s?user=%s?auth=%s", NET_AdrToString(adrbuf,sizeof(adrbuf), &server->adr), username, pass);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static svm_server_t *SVM_Heartbeat(const char *gamename, netadr_t *adr, int numclients, int numbots, int numspecs, double validuntil)
|
||||
{
|
||||
|
@ -1308,6 +1377,8 @@ static void SVM_ProcessUDPPacket(void)
|
|||
{ //dp/q3/etc are annoying, but we can query from an emphemerial socket to check NAT rules.
|
||||
sizebuf_t sb;
|
||||
netadr_t a;
|
||||
char cookie[64];
|
||||
char tmp[64];
|
||||
|
||||
char ourchallenge[256];
|
||||
SVM_GenChallenge(ourchallenge, sizeof(ourchallenge), &net_from);
|
||||
|
@ -1321,12 +1392,23 @@ static void SVM_ProcessUDPPacket(void)
|
|||
if (!SVM_SwitchQuerySocket()) //changes net_from to use a different master-side port so their firewall sees us as someone else
|
||||
a.type = NA_INVALID;
|
||||
|
||||
//send a packet from our alternative port
|
||||
*cookie = 0;
|
||||
s = COM_Parse(s);
|
||||
if (!strcmp(com_token, "FTEMaster"))
|
||||
{
|
||||
while ((s = COM_Parse(s)))
|
||||
{
|
||||
if (!strncmp(com_token, "c=", 2))
|
||||
Q_snprintfz(cookie, sizeof(cookie), " %s a=%s", com_token, NET_AdrToString(tmp,sizeof(tmp), &net_from));
|
||||
}
|
||||
}
|
||||
|
||||
//send a packet from our alternative port, to see if their firewall/NAT is open
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
sb.maxsize = sizeof(net_message_buffer);
|
||||
sb.data = net_message_buffer;
|
||||
MSG_WriteLong(&sb, -1);
|
||||
MSG_WriteString(&sb, va("getinfo %s\n", ourchallenge));
|
||||
MSG_WriteString(&sb, va("getinfo %s %s\n", ourchallenge,cookie));
|
||||
sb.cursize--;
|
||||
NET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);
|
||||
|
||||
|
@ -1342,7 +1424,7 @@ static void SVM_ProcessUDPPacket(void)
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (!strcmp(com_token, "infoResponse"))
|
||||
else if (!strcmp(com_token, "infoResponse") || !strcmp(com_token, "statusResponse"))
|
||||
{
|
||||
char ourchallenge[256];
|
||||
int clients, bots, specs;
|
||||
|
@ -1355,7 +1437,7 @@ static void SVM_ProcessUDPPacket(void)
|
|||
unknownresp = *chal=='?';
|
||||
chal += unknownresp?1:0;
|
||||
SVM_GenChallenge(ourchallenge, sizeof(ourchallenge), &net_from);
|
||||
if (!strcmp(chal, ourchallenge))
|
||||
if (!strcmp(chal, ourchallenge)) //someone's trying to spoof it, to give it the wrong *fp or whatever.
|
||||
{
|
||||
bots = atoi(Info_ValueForKey(s, "bots"));
|
||||
clients = atoi(Info_ValueForKey(s, "clients"));
|
||||
|
@ -1369,21 +1451,39 @@ static void SVM_ProcessUDPPacket(void)
|
|||
srv = SVM_Heartbeat(game, &net_from, clients,bots,specs, svm.time + sv_heartbeattimeout.ival);
|
||||
if (srv)
|
||||
{
|
||||
if (unknownresp)
|
||||
{ //retain _ keys that won't be included in unchallenged responses.
|
||||
char *turnkey = Info_ValueForKey(srv->rules, "_turnkey");
|
||||
Q_strncpyz(srv->rules, s, sizeof(srv->rules));
|
||||
Info_SetValueForKey(srv->rules, "_turnkey", turnkey, sizeof(srv->rules));
|
||||
}
|
||||
else
|
||||
Q_strncpyz(srv->rules, s, sizeof(srv->rules));
|
||||
Info_RemoveKey(srv->rules, "challenge"); //prevent poisoning
|
||||
if (developer.ival)
|
||||
{
|
||||
Con_Printf("Update from %s:\n", NET_AdrToString(ourchallenge,sizeof(ourchallenge), &net_from));
|
||||
Info_Print(s, "\t");
|
||||
if (game)
|
||||
srv->protover = atoi(Info_ValueForKey(s, "protocol"));
|
||||
}
|
||||
srv->maxclients = atoi(Info_ValueForKey(s, "sv_maxclients"));
|
||||
srv->secure = !!*Info_ValueForKey(s, "*fp");
|
||||
srv->needpass = atoi(Info_ValueForKey(s, "needpass"));
|
||||
srv->coop = atoi(Info_ValueForKey(s, "coop"));
|
||||
if (!srv->coop)
|
||||
srv->type = atoi(Info_ValueForKey(s, "coop"));
|
||||
if (!srv->type)
|
||||
{ //deathmatch 0 also means coop 1... servers that report neither are probably annoying DP servers that report nothing useful and should default to DM.
|
||||
const char *v = Info_ValueForKey(s, "deathmatch");
|
||||
srv->coop = *v && !atoi(v);
|
||||
srv->type = *v && !atoi(v);
|
||||
}
|
||||
srv->protover = strtol(Info_ValueForKey(s, "protocol"), &s, 0);
|
||||
for (; *s; s++)
|
||||
{
|
||||
if (*s == 't')
|
||||
srv->type |= 2;
|
||||
else if (*s == 'f')
|
||||
srv->type |= 4;
|
||||
}
|
||||
if (*Info_ValueForKey(srv->rules, "_turnkey"))
|
||||
srv->type |= 8;
|
||||
Q_strncpyz(srv->hostname, Info_ValueForKey(s, "hostname"), sizeof(srv->hostname));
|
||||
Q_strncpyz(srv->gamedir, Info_ValueForKey(s, "modname"), sizeof(srv->gamedir));
|
||||
Q_strncpyz(srv->mapname, Info_ValueForKey(s, "mapname"), sizeof(srv->mapname));
|
||||
|
@ -1412,6 +1512,7 @@ static void SVM_ProcessUDPPacket(void)
|
|||
{ //quakeworld heartbeat
|
||||
int players;
|
||||
sizebuf_t sb;
|
||||
char *nonce;
|
||||
s = MSG_ReadStringLine();
|
||||
//sequence = atoi(s);
|
||||
s = MSG_ReadStringLine();
|
||||
|
@ -1421,6 +1522,7 @@ static void SVM_ProcessUDPPacket(void)
|
|||
|
||||
//placeholder listing...
|
||||
SVM_Heartbeat(NULL, &net_from, players,0,0, svm.time + sv_heartbeattimeout.ival);
|
||||
nonce = MSG_ReadStringLine(); //added a nonce, so the status can contain a private/shared key so the master can know how to generate acceptable passwords for turn proxies.
|
||||
SVM_SwitchQuerySocket();
|
||||
|
||||
//send it a proper query. We'll fill in the other details on response.
|
||||
|
@ -1430,6 +1532,8 @@ static void SVM_ProcessUDPPacket(void)
|
|||
MSG_WriteLong(&sb, -1);
|
||||
MSG_WriteString(&sb, va("status %i\n", 15));
|
||||
sb.cursize--;
|
||||
if (*nonce)
|
||||
MSG_WriteString(&sb, nonce);
|
||||
NET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);
|
||||
}
|
||||
else if (*com_token == M2C_MASTER_REPLY && !com_token[1])
|
||||
|
@ -1555,11 +1659,11 @@ static void SVM_ProcessUDPPacket(void)
|
|||
srv->maxclients = atoi(Info_ValueForKey(s, "maxclients"));
|
||||
srv->secure = !!*Info_ValueForKey(s, "*fp");
|
||||
srv->needpass = atoi(Info_ValueForKey(s, "needpass"));
|
||||
srv->coop = atoi(Info_ValueForKey(s, "coop"));
|
||||
if (!srv->coop)
|
||||
srv->type = atoi(Info_ValueForKey(s, "coop"));
|
||||
if (!srv->type)
|
||||
{ //deathmatch 0 also means coop 1... servers that report neither are probably annoying proxies servers that report nothing useful and should default to DM.
|
||||
const char *v = Info_ValueForKey(s, "deathmatch");
|
||||
srv->coop = *v && !atoi(v);
|
||||
srv->type = *v && !atoi(v);
|
||||
}
|
||||
Q_strncpyz(srv->hostname, Info_ValueForKey(s, "hostname"), sizeof(srv->hostname));
|
||||
Q_strncpyz(srv->gamedir, Info_ValueForKey(s, "*gamedir"), sizeof(srv->gamedir));
|
||||
|
@ -1896,7 +2000,7 @@ void SV_Init (struct quakeparms_s *parms)
|
|||
float SV_Frame (void)
|
||||
{
|
||||
float sleeptime;
|
||||
realtime = Sys_DoubleTime();
|
||||
svm.time = realtime = Sys_DoubleTime();
|
||||
while (1)
|
||||
{
|
||||
const char *cmd = Sys_ConsoleInput ();
|
||||
|
|
|
@ -2007,8 +2007,9 @@ typedef struct {
|
|||
} eval;
|
||||
int statnum;
|
||||
} qcstat_t;
|
||||
qcstat_t qcstats[MAX_CL_STATS];
|
||||
int numqcstats;
|
||||
static qcstat_t qcstats[MAX_CL_STATS];
|
||||
static unsigned int numqcstats;
|
||||
static unsigned int highestqcstat;
|
||||
static void SV_QCStatEval(int type, const char *name, evalc_t *field, eval_t *global, int statnum)
|
||||
{
|
||||
int i;
|
||||
|
@ -2100,6 +2101,7 @@ void SV_QCStatFieldIdx(int type, unsigned int fieldindex, int statnum)
|
|||
void SV_ClearQCStats(void)
|
||||
{
|
||||
numqcstats = 0;
|
||||
highestqcstat = MAX_QW_STATS;
|
||||
}
|
||||
|
||||
extern cvar_t dpcompat_stats;
|
||||
|
@ -2163,8 +2165,9 @@ void SV_UpdateQCStats(edict_t *ent, int *statsi, char const** statss, float *sta
|
|||
}
|
||||
|
||||
/*this function calculates the current stat values for the given client*/
|
||||
void SV_CalcClientStats(client_t *client, int statsi[MAX_CL_STATS], float statsf[MAX_CL_STATS], const char **statss)
|
||||
static unsigned int SV_CalcClientStats(client_t *client, int statsi[MAX_CL_STATS], float statsf[MAX_CL_STATS], const char **statss)
|
||||
{
|
||||
unsigned int m = highestqcstat;
|
||||
edict_t *ent;
|
||||
ent = client->edict;
|
||||
memset (statsi, 0, sizeof(int)*MAX_CL_STATS);
|
||||
|
@ -2289,11 +2292,15 @@ void SV_CalcClientStats(client_t *client, int statsi[MAX_CL_STATS], float statsf
|
|||
statsfi[STAT_MOVEVARS_STEPHEIGHT] = *sv_stepheight.string?sv_stepheight.value:PM_DEFAULTSTEPHEIGHT;
|
||||
statsfi[STAT_MOVEVARS_AIRACCEL_QW] = 1; //we're a quakeworld engine...
|
||||
statsfi[STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION] = 0;
|
||||
|
||||
if (m < 256)
|
||||
m = 256;
|
||||
}
|
||||
#endif
|
||||
|
||||
SV_UpdateQCStats(ent, statsi, statss, statsf);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2309,14 +2316,14 @@ void SV_UpdateClientStats (client_t *client, int pnum, sizebuf_t *msg, client_fr
|
|||
int statsi[MAX_CL_STATS];
|
||||
float statsf[MAX_CL_STATS];
|
||||
const char *statss[MAX_CL_STATS];
|
||||
int i, m;
|
||||
unsigned int i, m;
|
||||
|
||||
/*figure out what the stat values should be*/
|
||||
SV_CalcClientStats(client, statsi, statsf, statss);
|
||||
|
||||
m = MAX_QW_STATS;
|
||||
m = SV_CalcClientStats(client, statsi, statsf, statss);
|
||||
if ((client->fteprotocolextensions & (PEXT_HEXEN2|PEXT_CSQC)) || client->protocol == SCP_DARKPLACES6 || client->protocol == SCP_DARKPLACES7)
|
||||
m = MAX_CL_STATS;
|
||||
m = min(m,256);
|
||||
else
|
||||
m = min(m,MAX_QW_STATS);
|
||||
|
||||
if (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)
|
||||
{
|
||||
|
@ -3759,10 +3766,6 @@ void SV_SendMVDMessage(void)
|
|||
msg.allowoverflow = true;
|
||||
msg.overflowed = false;
|
||||
|
||||
m = MAX_QW_STATS;
|
||||
if (demo.recorder.fteprotocolextensions & (PEXT_HEXEN2|PEXT_CSQC))
|
||||
m = MAX_CL_STATS;
|
||||
|
||||
for (i=0, c = svs.clients ; i<svs.allocated_client_slots && i < 32; i++, c++)
|
||||
{
|
||||
if (c->state != cs_spawned)
|
||||
|
@ -3772,7 +3775,11 @@ void SV_SendMVDMessage(void)
|
|||
continue;
|
||||
|
||||
/*figure out what the stat values should be*/
|
||||
SV_CalcClientStats(c, statsi, statsf, statss);
|
||||
m = SV_CalcClientStats(c, statsi, statsf, statss);
|
||||
if (demo.recorder.fteprotocolextensions & (PEXT_HEXEN2|PEXT_CSQC))
|
||||
m = min(m,MAX_CL_STATS);
|
||||
else
|
||||
m = min(m,MAX_QW_STATS);
|
||||
|
||||
//FIXME we should do something about the packet overhead here. each MVDWrite_Begin is a separate packet!
|
||||
|
||||
|
|
|
@ -188,7 +188,11 @@ qboolean SV_CheckRealIP(client_t *client, qboolean force)
|
|||
if (client->realip_status == -1)
|
||||
return true; //this client timed out.
|
||||
|
||||
if (realtime - client->connection_started > sv_realip_timeout.value)
|
||||
//if they're using some weird protocol just give up right away.
|
||||
if (realtime - client->connection_started > sv_realip_timeout.value ||
|
||||
client->netchan.remote_address.prot != NP_DGRAM || !(
|
||||
(client->netchan.remote_address.type == NA_IP&&*sv_realiphostname_ipv4.string) ||
|
||||
(client->netchan.remote_address.type == NA_IPV6&&sv_realiphostname_ipv6.string)))
|
||||
{
|
||||
if (client->realip_status > 0)
|
||||
SV_PrintToClient(client, PRINT_HIGH, "Couldn't verify your real ip\n");
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
//FIXME: !!permu FOG
|
||||
!!samps shadowmap 2
|
||||
|
||||
#define USE_ARB_SHADOW
|
||||
|
||||
#include "sys/defs.h"
|
||||
#include "sys/pcf.h"
|
||||
|
||||
|
@ -66,7 +64,6 @@ void main ()
|
|||
//fixme: cubemap filters
|
||||
|
||||
float shadows = ShadowmapFilter(s_shadowmap, cubeaxis);
|
||||
lightColour *= atten;
|
||||
|
||||
out_diff = vec4(lightColour * (l_lightcolourscale.x + l_lightcolourscale.y*lightDiffuse*shadows), 1.0);
|
||||
out_spec = vec4(lightColour * l_lightcolourscale.z*spec*shadows, 1.0);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
!!ver 100 150
|
||||
!!permu BUMP //for offsetmapping rather than bumpmapping (real bumps are handled elsewhere)
|
||||
!!cvarf r_glsl_offsetmapping_scale
|
||||
!!samps diffuse specular fullbright lightmap
|
||||
!!samps 2
|
||||
|
||||
//the final defered lighting pass.
|
||||
|
|
|
@ -68,8 +68,8 @@ if (typeof Module['files'] !== "undefined" && Object.keys(Module['files']).lengt
|
|||
xhr.open("GET", ab);
|
||||
xhr.onload = function ()
|
||||
{
|
||||
if (curfile == n)
|
||||
curfile = undefined;
|
||||
if (Module['curfile'] == n)
|
||||
Module['curfile'] = undefined;
|
||||
if (this.status >= 200 && this.status < 300)
|
||||
{
|
||||
let b = FTEH.h[_emscriptenfte_buf_createfromarraybuf(this.response)];
|
||||
|
@ -84,7 +84,7 @@ if (typeof Module['files'] !== "undefined" && Object.keys(Module['files']).lengt
|
|||
{
|
||||
if (typeof Module['curfile'] == "undefined")
|
||||
Module['curfile'] = n; //take it.
|
||||
if (Module['setStatus'] && curfile==n)
|
||||
if (Module['setStatus'] && Module['curfile']==n)
|
||||
Module['setStatus'](n + ' (' + e.loaded + '/' + e.total + ')');
|
||||
};
|
||||
xhr.onerror = function ()
|
||||
|
@ -158,7 +158,7 @@ if (!Module['arguments']) //the html can be explicit about its args if it sets t
|
|||
}
|
||||
}
|
||||
|
||||
if (Module['manifest'] != "")
|
||||
if (Module['manifest'] != undefined)
|
||||
Module['arguments'] = Module['arguments'].concat(['-manifest', Module['manifest']]);
|
||||
|
||||
//registerProtocolHandler needs to be able to pass it through to us... so only allow it if we're parsing args from the url.
|
||||
|
|
|
@ -130,14 +130,14 @@ unsigned char *FS_ReadFile(char *gamedir, char *filename, unsigned int *size)
|
|||
#endif
|
||||
int _cdecl SortFilesByDate(const void *a, const void *b)
|
||||
{
|
||||
if (((availdemo_t*)a)->time < ((availdemo_t*)b)->time)
|
||||
if (((const availdemo_t*)a)->time < ((const availdemo_t*)b)->time)
|
||||
return 1;
|
||||
if (((availdemo_t*)a)->time > ((availdemo_t*)b)->time)
|
||||
if (((const availdemo_t*)a)->time > ((const availdemo_t*)b)->time)
|
||||
return -1;
|
||||
|
||||
if (((availdemo_t*)a)->smalltime < ((availdemo_t*)b)->smalltime)
|
||||
if (((const availdemo_t*)a)->smalltime < ((const availdemo_t*)b)->smalltime)
|
||||
return 1;
|
||||
if (((availdemo_t*)a)->smalltime > ((availdemo_t*)b)->smalltime)
|
||||
if (((const availdemo_t*)a)->smalltime > ((const availdemo_t*)b)->smalltime)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
@ -263,6 +263,8 @@ void Cluster_Run(cluster_t *cluster, qboolean dowait)
|
|||
}
|
||||
}
|
||||
|
||||
TURN_AddFDs(cluster, &socketset, &m);
|
||||
|
||||
for (pend = cluster->pendingproxies; pend; pend = pend->next)
|
||||
{
|
||||
if (pend->sock != INVALID_SOCKET && pend->sock < FD_SETSIZE)
|
||||
|
@ -377,6 +379,8 @@ void Cluster_Run(cluster_t *cluster, qboolean dowait)
|
|||
QTV_Run(old);
|
||||
}
|
||||
|
||||
TURN_CheckFDs(cluster);
|
||||
|
||||
SV_FindProxies(cluster->tcpsocket[0], cluster, NULL); //look for any other proxies wanting to muscle in on the action.
|
||||
SV_FindProxies(cluster->tcpsocket[1], cluster, NULL); //look for any other proxies wanting to muscle in on the action.
|
||||
|
||||
|
@ -515,6 +519,20 @@ int main(int argc, char **argv)
|
|||
strcpy(cluster->hostname, DEFAULT_HOSTNAME);
|
||||
cluster->maxproxies = -1;
|
||||
|
||||
//master protocol setup
|
||||
cluster->protocolname = strdup("FTE-Quake");
|
||||
cluster->protocolver = 3;
|
||||
strlcpy(cluster->master, "master.frag-net.com:27950", sizeof(cluster->master)); //default to eukara's master server.
|
||||
cluster->mastersendtime = cluster->curtime;
|
||||
|
||||
cluster->relayenabled = true; //allow qtv
|
||||
cluster->pingtreeenabled = false; //spammy.
|
||||
cluster->turnenabled = false; //leave turn off by default. we need to know a usable inbound port range, we can't depend on just outgoing ephemerial ones. misconfigured relays will result in failures so don't default this to on.
|
||||
|
||||
#ifdef HAVE_EPOLL
|
||||
cluster->epfd = epoll_create1(0);
|
||||
#endif
|
||||
|
||||
strcpy(cluster->demodir, "qw/demos/");
|
||||
|
||||
Sys_Printf(cluster, "QTV "QTV_VERSION_STRING"\n");
|
||||
|
@ -538,7 +556,7 @@ int main(int argc, char **argv)
|
|||
Net_TCPListen(cluster, 1, SG_UNIX);
|
||||
|
||||
Sys_Printf(cluster, "\n"
|
||||
"Welcome to FTEQTV\n"
|
||||
"Welcome to QTV\n"
|
||||
"Please type\n"
|
||||
"qtv server:port\n"
|
||||
" to connect to a tcp server.\n"
|
||||
|
|
|
@ -1187,7 +1187,7 @@ char *HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend)
|
|||
{
|
||||
//if (!strcmp(wsprot, "quake")) //webquake. we don't support this! (no OOB and missing header flags and some screwy sequence numbers)
|
||||
if (!strcmp(wsprot, "fteqw") || //as a client
|
||||
(!strcmp(wsprot, "faketcp") && !urilen)) //as a qtv proxy (eztv style, but websocked). we are NOT proxying tcp. require a qtv handshake over the resulting websocket connection.
|
||||
(!strcmp(wsprot, "faketcp") && urilen==1&&!strncmp(uri,"/",1))) //as a qtv proxy (eztv style, but websocked). we are NOT proxying tcp. require a qtv handshake over the resulting websocket connection.
|
||||
break; //break out on the first one we know. this is the recommended way...
|
||||
}
|
||||
|
||||
|
|
30
fteqtv/msg.c
30
fteqtv/msg.c
|
@ -25,6 +25,14 @@ unsigned short ReadShort(netmsg_t *b)
|
|||
|
||||
return b1 | (b2<<8);
|
||||
}
|
||||
unsigned short ReadBigShort(netmsg_t *b)
|
||||
{
|
||||
int b1, b2;
|
||||
b1 = ReadByte(b);
|
||||
b2 = ReadByte(b);
|
||||
|
||||
return (b1<<8) | b2;
|
||||
}
|
||||
unsigned int ReadLong(netmsg_t *b)
|
||||
{
|
||||
int s1, s2;
|
||||
|
@ -33,6 +41,14 @@ unsigned int ReadLong(netmsg_t *b)
|
|||
|
||||
return s1 | (s2<<16);
|
||||
}
|
||||
unsigned int ReadBigLong(netmsg_t *b)
|
||||
{
|
||||
unsigned int s1, s2;
|
||||
s1 = ReadBigShort(b);
|
||||
s2 = ReadBigShort(b);
|
||||
|
||||
return (s1<<16) | s2;
|
||||
}
|
||||
|
||||
unsigned int BigLong(unsigned int val)
|
||||
{
|
||||
|
@ -117,6 +133,11 @@ void WriteShort(netmsg_t *b, unsigned short l)
|
|||
WriteByte(b, (l&0x00ff)>>0);
|
||||
WriteByte(b, (l&0xff00)>>8);
|
||||
}
|
||||
void WriteBigShort(netmsg_t *b, unsigned short l)
|
||||
{
|
||||
WriteByte(b, (l&0xff00)>>8);
|
||||
WriteByte(b, (l&0x00ff)>>0);
|
||||
}
|
||||
void WriteLong(netmsg_t *b, unsigned int l)
|
||||
{
|
||||
WriteByte(b, (l&0x000000ff)>>0);
|
||||
|
@ -124,6 +145,13 @@ void WriteLong(netmsg_t *b, unsigned int l)
|
|||
WriteByte(b, (l&0x00ff0000)>>16);
|
||||
WriteByte(b, (l&0xff000000)>>24);
|
||||
}
|
||||
void WriteBigLong(netmsg_t *b, unsigned int l)
|
||||
{
|
||||
WriteByte(b, (l&0xff000000)>>24);
|
||||
WriteByte(b, (l&0x00ff0000)>>16);
|
||||
WriteByte(b, (l&0x0000ff00)>>8);
|
||||
WriteByte(b, (l&0x000000ff)>>0);
|
||||
}
|
||||
void WriteFloat(netmsg_t *b, float f)
|
||||
{
|
||||
union {
|
||||
|
@ -168,7 +196,7 @@ void WriteData(netmsg_t *b, const void *data, int length)
|
|||
return;
|
||||
buf = (unsigned char*)b->data+b->cursize;
|
||||
for (i = 0; i < length; i++)
|
||||
*buf++ = ((unsigned char*)data)[i];
|
||||
*buf++ = ((const unsigned char*)data)[i];
|
||||
b->cursize+=length;
|
||||
}
|
||||
void WriteCoordf(netmsg_t *b, unsigned int pext, float fl)
|
||||
|
|
|
@ -126,6 +126,7 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma
|
|||
|
||||
tv->pext1 = 0;
|
||||
tv->pext2 = 0;
|
||||
tv->pexte = 0;
|
||||
|
||||
//when it comes to QTV, the proxy 'blindly' forwards the data after parsing the header, so we need to support EVERYTHING the original server might.
|
||||
//and if we don't, then we might have troubles.
|
||||
|
@ -209,6 +210,12 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma
|
|||
if (protocol & ~supported)
|
||||
Sys_Printf(tv->cluster, "ParseMessage: PROTOCOL_VERSION_FTE2 (%x) not supported\n", protocol & ~supported);
|
||||
continue;
|
||||
case PROTOCOL_VERSION_EZQUAKE1:
|
||||
tv->pexte = protocol = ReadLong(m);
|
||||
supported = PEXTE_HIDDENMESSAGES;
|
||||
if (protocol & ~supported)
|
||||
Sys_Printf(tv->cluster, "ParseMessage: Unsupported MVD1 protocol flags %#x\n", protocol);
|
||||
continue;
|
||||
case PROTOCOL_VERSION_HUFFMAN:
|
||||
Sys_Printf(tv->cluster, "ParseMessage: PROTOCOL_VERSION_HUFFMAN not supported\n");
|
||||
ParseError(m);
|
||||
|
@ -240,7 +247,7 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma
|
|||
ParseError(m);
|
||||
return;
|
||||
default:
|
||||
Sys_Printf(tv->cluster, "ParseMessage: Unknown protocol version %x\n", protocol);
|
||||
Sys_Printf(tv->cluster, "ParseMessage: Unknown protocol version %#x\n", protocol);
|
||||
ParseError(m);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -211,6 +211,7 @@ enum {
|
|||
|
||||
#define PROTOCOL_VERSION_FTE (('F'<<0) + ('T'<<8) + ('E'<<16) + ('X' << 24)) //fte extensions.
|
||||
#define PROTOCOL_VERSION_FTE2 (('F'<<0) + ('T'<<8) + ('E'<<16) + ('2' << 24)) //fte extensions.
|
||||
#define PROTOCOL_VERSION_EZQUAKE1 (('M'<<0) + ('V'<<8) + ('D'<<16) + ('1' << 24)) //ezquake/mvdsv extensions
|
||||
#define PROTOCOL_VERSION_HUFFMAN (('H'<<0) + ('U'<<8) + ('F'<<16) + ('F' << 24)) //packet compression
|
||||
#define PROTOCOL_VERSION_VARLENGTH (('v'<<0) + ('l'<<8) + ('e'<<16) + ('n' << 24)) //variable length handshake
|
||||
#define PROTOCOL_VERSION_FRAGMENT (('F'<<0) + ('R'<<8) + ('A'<<16) + ('G' << 24)) //supports fragmentation/packets larger than 1450
|
||||
|
@ -260,6 +261,7 @@ enum {
|
|||
#define PEXT2_INFOBLOBS 0x00000080 //serverinfo+userinfo lengths can be MUCH higher (protocol is unbounded, but expect low sanity limits on userinfo), and contain nulls etc.
|
||||
//#define PEXT2_PK3DOWNLOADS 0x10000000 //retrieve a list of pk3s/pk3s/paks for downloading (with optional URL and crcs)
|
||||
|
||||
#define PEXTE_HIDDENMESSAGES 0x20 //random demo metadata...
|
||||
|
||||
//flags on entities
|
||||
#define U_ORIGIN1 (1<<9)
|
||||
|
|
50
fteqtv/qtv.h
50
fteqtv/qtv.h
|
@ -162,6 +162,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <stdarg.h>
|
||||
|
@ -173,6 +174,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
|
||||
#define ioctlsocket ioctl
|
||||
#define closesocket close
|
||||
|
||||
#if defined(__linux__) && !defined(ANDROID)
|
||||
// #define HAVE_EPOLL
|
||||
#endif
|
||||
#ifdef HAVE_EPOLL
|
||||
#include <sys/epoll.h>
|
||||
#endif
|
||||
#elif (defined(__MORPHOS__) && !defined(ixemul))
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
@ -180,6 +188,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
@ -247,14 +256,15 @@ typedef struct
|
|||
void (*process) (void *context, const void *data, size_t datasize);
|
||||
void (*terminate) (unsigned char *digest, void *context);
|
||||
} hashfunc_t;
|
||||
extern hashfunc_t hash_md5;
|
||||
extern hashfunc_t hash_sha1;
|
||||
/*extern hashfunc_t hash_sha2_224;
|
||||
extern hashfunc_t hash_sha2_256;
|
||||
extern hashfunc_t hash_sha2_384;
|
||||
extern hashfunc_t hash_sha2_512;*/
|
||||
#define HMAC HMAC_quake //stop conflicts...
|
||||
size_t CalcHash(hashfunc_t *hash, unsigned char *digest, size_t maxdigestsize, const unsigned char *string, size_t stringlen);
|
||||
size_t HMAC(hashfunc_t *hashfunc, unsigned char *digest, size_t maxdigestsize, const unsigned char *data, size_t datalen, const unsigned char *key, size_t keylen);
|
||||
unsigned int CalcHashInt(const hashfunc_t *hash, const void *data, size_t datasize);
|
||||
size_t CalcHMAC(hashfunc_t *hashfunc, unsigned char *digest, size_t maxdigestsize, const unsigned char *data, size_t datalen, const unsigned char *key, size_t keylen);
|
||||
|
||||
|
||||
#ifdef LIBQTV
|
||||
|
@ -667,6 +677,7 @@ struct sv_s { //details about a server connection (also known as stream)
|
|||
qboolean usequakeworldprotocols;
|
||||
unsigned int pext1;
|
||||
unsigned int pext2;
|
||||
unsigned int pexte;
|
||||
int challenge;
|
||||
unsigned short qport;
|
||||
int isconnected;
|
||||
|
@ -786,6 +797,8 @@ enum
|
|||
SG_UNIX,
|
||||
SOCKETGROUPS
|
||||
};
|
||||
|
||||
typedef struct turnclient_s turnclient_t;
|
||||
struct cluster_s {
|
||||
SOCKET qwdsocket[SOCKETGROUPS]; //udp + quakeworld protocols
|
||||
SOCKET tcpsocket[SOCKETGROUPS]; //tcp listening socket (for mvd and listings and stuff)
|
||||
|
@ -798,6 +811,26 @@ struct cluster_s {
|
|||
unsigned int mastersequence;
|
||||
unsigned int curtime;
|
||||
|
||||
#ifdef HAVE_EPOLL
|
||||
int epfd;
|
||||
#endif
|
||||
unsigned int numrelays;
|
||||
turnclient_t *turns;
|
||||
char chalkey[64]; //to identify the master properly. probably kinda pointless. base64 encoded.
|
||||
unsigned char turnkey[32]; //raw key shared with broker to prove TURN identity was given by broker. NOTE: we are not verifying each, so we depend on clockskew to prevent any longterm abuse. there's no accounts anywhere though so anyone can get a key if they ask properly.
|
||||
qboolean turnenabled;
|
||||
unsigned short turn_minport, turn_maxport; //set to 0 to let the OS decide.
|
||||
char *protocolname;
|
||||
int protocolver;
|
||||
unsigned char turn_ipv4[4];
|
||||
unsigned char turn_ipv6[16];
|
||||
unsigned int numpeers;
|
||||
struct relaypeer_s *relaypeer;
|
||||
unsigned int relay_lastping;
|
||||
unsigned int relay_lastquery;
|
||||
qboolean relayenabled;
|
||||
qboolean pingtreeenabled;
|
||||
|
||||
viewer_t *viewers;
|
||||
int numviewers;
|
||||
sv_t *servers;
|
||||
|
@ -875,7 +908,9 @@ enum {
|
|||
|
||||
unsigned char ReadByte(netmsg_t *b);
|
||||
unsigned short ReadShort(netmsg_t *b);
|
||||
unsigned short ReadBigShort(netmsg_t *b);
|
||||
unsigned int ReadLong(netmsg_t *b);
|
||||
unsigned int ReadBigLong(netmsg_t *b);
|
||||
float ReadFloat(netmsg_t *b);
|
||||
void ReadString(netmsg_t *b, char *string, int maxlen);
|
||||
float ReadCoord(netmsg_t *b, unsigned int pext);
|
||||
|
@ -905,7 +940,9 @@ float ReadFloat(netmsg_t *b);
|
|||
void ReadString(netmsg_t *b, char *string, int maxlen);
|
||||
void WriteByte(netmsg_t *b, unsigned char c);
|
||||
void WriteShort(netmsg_t *b, unsigned short l);
|
||||
void WriteBigShort(netmsg_t *b, unsigned short l);
|
||||
void WriteLong(netmsg_t *b, unsigned int l);
|
||||
void WriteBigLong(netmsg_t *b, unsigned int l);
|
||||
void WriteFloat(netmsg_t *b, float f);
|
||||
void WriteCoord(netmsg_t *b, float c, unsigned int pext);
|
||||
void WriteAngle(netmsg_t *b, float a, unsigned int pext);
|
||||
|
@ -1018,6 +1055,15 @@ void tobase64(unsigned char *out, int outlen, unsigned char *in, int inlen);
|
|||
void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum);
|
||||
void Menu_Draw(cluster_t *cluster, viewer_t *viewer);
|
||||
|
||||
//relay.c
|
||||
void TURN_CheckFDs(cluster_t *cluster);
|
||||
void TURN_AddFDs(cluster_t *cluster, fd_set *set, int *m);
|
||||
qboolean TURN_IsRequest(cluster_t *cluster, netmsg_t *m, netadr_t *from); //handles both TURN/STUN packets, and relays inbound qwfwd connections too.
|
||||
void Fwd_NewQWFwd(cluster_t *cluster, netadr_t *from, char *targ); //creates a new qwfwd context.
|
||||
void TURN_RelayStatus(cmdctxt_t *ctx);
|
||||
void Fwd_PingStatus(cluster_t *cluster, netadr_t *from, qboolean ext);
|
||||
void Fwd_ParseServerList(cluster_t *cluster, netmsg_t *m, int af);
|
||||
void Fwd_PingResponse(cluster_t *cluster, netadr_t *from);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
268
fteqtv/qw.c
268
fteqtv/qw.c
|
@ -20,8 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
|
||||
#include "qtv.h"
|
||||
#include <string.h>
|
||||
|
||||
#include "bsd_string.h"
|
||||
#include <time.h>
|
||||
|
||||
static const filename_t ConnectionlessModelList[] = {{""}, {"maps/start.bsp"}, {"progs/player.mdl"}, {""}};
|
||||
static const filename_t ConnectionlessSoundList[] = {{""}, {""}};
|
||||
|
@ -183,6 +182,11 @@ void BuildServerData(sv_t *tv, netmsg_t *msg, int servercount, viewer_t *viewer)
|
|||
WriteLong(msg, PROTOCOL_VERSION_FTE2);
|
||||
WriteLong(msg, tv->pext2);
|
||||
}
|
||||
if (tv->pexte)
|
||||
{
|
||||
WriteLong(msg, PROTOCOL_VERSION_EZQUAKE1);
|
||||
WriteLong(msg, tv->pexte);
|
||||
}
|
||||
}
|
||||
WriteLong(msg, PROTOCOL_VERSION);
|
||||
WriteLong(msg, servercount);
|
||||
|
@ -710,13 +714,40 @@ void QW_SetViewersServer(cluster_t *cluster, viewer_t *viewer, sv_t *sv)
|
|||
}
|
||||
|
||||
//fixme: will these want to have state?..
|
||||
int NewChallenge(netadr_t *addr)
|
||||
int NewChallenge(cluster_t *cluster, netadr_t *addr)
|
||||
{
|
||||
return 4;
|
||||
unsigned int r = 0, l;
|
||||
unsigned char *digest;
|
||||
void *ctx;
|
||||
hashfunc_t *func = &hash_sha1;
|
||||
static time_t t;
|
||||
|
||||
//reminder: Challenges exist so clients can't spoof their source address and waste our ram without us being able to ban them without banning everyone.
|
||||
size_t sz = 0;
|
||||
if (((struct sockaddr*)addr->sockaddr)->sa_family == AF_INET)
|
||||
sz = sizeof(struct sockaddr_in);
|
||||
else if (((struct sockaddr*)addr->sockaddr)->sa_family == AF_INET6)
|
||||
sz = sizeof(struct sockaddr_in6);
|
||||
//else error
|
||||
|
||||
ctx = alloca(func->contextsize);
|
||||
func->init(ctx);
|
||||
if (!t) //must be constant, so only do this if its still 0.
|
||||
t = time(NULL);
|
||||
func->process(ctx, addr, sz); //hash their address primarily.
|
||||
func->process(ctx, cluster->turnkey, sizeof(cluster->turnkey)); //might not be set...
|
||||
func->process(ctx, &t, sizeof(t)); //extra privacy, sizeof doesn't matter as its only our process that cares
|
||||
//func->process(ctx, cluster, sizeof(cluster)); //a random pointer too, because zomgwtf
|
||||
|
||||
digest = alloca(func->digestsize);
|
||||
func->terminate(digest, ctx);
|
||||
for (l = 0; l < func->digestsize; l++)
|
||||
r ^= digest[l]<<((l%sizeof(r))*8);
|
||||
return r;
|
||||
}
|
||||
qboolean ChallengePasses(netadr_t *addr, int challenge)
|
||||
qboolean ChallengePasses(cluster_t *cluster, netadr_t *addr, int challenge)
|
||||
{
|
||||
if (challenge == 4)
|
||||
if (challenge == NewChallenge(cluster, addr))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -936,7 +967,8 @@ void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage)
|
|||
|
||||
char qport[32];
|
||||
char challenge[32];
|
||||
char infostring[256];
|
||||
char infostring[1024];
|
||||
char prx[256];
|
||||
int i;
|
||||
|
||||
connectmessage+=11;
|
||||
|
@ -945,12 +977,19 @@ void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage)
|
|||
connectmessage = COM_ParseToken(connectmessage, challenge, sizeof(challenge), "");
|
||||
connectmessage = COM_ParseToken(connectmessage, infostring, sizeof(infostring), "");
|
||||
|
||||
if (!ChallengePasses(addr, atoi(challenge)))
|
||||
if (!ChallengePasses(cluster, addr, atoi(challenge)))
|
||||
{
|
||||
Netchan_OutOfBandPrint(cluster, *addr, "n" "Bad challenge");
|
||||
return;
|
||||
}
|
||||
|
||||
Info_ValueForKey(infostring, "prx", prx,sizeof(prx));
|
||||
if (*prx)
|
||||
{
|
||||
Fwd_NewQWFwd(cluster, addr, prx);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
viewer = malloc(sizeof(viewer_t));
|
||||
if (!viewer)
|
||||
|
@ -1169,6 +1208,109 @@ void QTV_Status(cluster_t *cluster, netadr_t *from)
|
|||
WriteByte(&msg, 0);
|
||||
NET_SendPacket(cluster, NET_ChooseSocket(cluster->qwdsocket, from, *from), msg.cursize, msg.data, *from);
|
||||
}
|
||||
static void QTV_GetInfo(cluster_t *cluster, netadr_t *from, char *args)
|
||||
{
|
||||
//ftemaster support
|
||||
char challenge[256], tmp[64];
|
||||
char protocolname[MAX_QPATH];
|
||||
char buffer[8192];
|
||||
netmsg_t msg;
|
||||
qboolean authed = false;
|
||||
InitNetMsg(&msg, buffer, sizeof(buffer));
|
||||
|
||||
args = COM_ParseToken(args, challenge, sizeof(challenge), "");
|
||||
while((args = COM_ParseToken(args, tmp, sizeof(tmp), "")))
|
||||
{
|
||||
if (!strncmp(tmp, "c=",2) && !strcmp(tmp+2, cluster->chalkey))
|
||||
authed = true; //they're able to read our outgoing packets. assume not intercepted (at least blocks spoofed packets). should really use (d)tls. this is more to protect our resources than anything else though, so doesn't need to be strong.
|
||||
else if (!strncmp(tmp, "a=",2) && authed)
|
||||
{
|
||||
netadr_t adr;
|
||||
if (NET_StringToAddr(tmp+2, &adr, 0))
|
||||
{ //master told us our IP. we can use that to report to turn clients
|
||||
if (((struct sockaddr*)&adr.sockaddr)->sa_family == AF_INET)
|
||||
memcpy(cluster->turn_ipv4, &((struct sockaddr_in*)&adr.sockaddr)->sin_addr, 4);
|
||||
else if (((struct sockaddr*)&adr.sockaddr)->sa_family == AF_INET6)
|
||||
memcpy(cluster->turn_ipv6, &((struct sockaddr_in6*)&adr.sockaddr)->sin6_addr, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
COM_ParseToken(cluster->protocolname?cluster->protocolname:"FTE-Quake", protocolname, sizeof(protocolname), ""); //we can only report one, so report the first.
|
||||
|
||||
//response packet header
|
||||
WriteLong(&msg, ~0u);
|
||||
// if (fullstatus)
|
||||
// WriteString2(&msg, "statusResponse\n");
|
||||
// else
|
||||
WriteString2(&msg, "infoResponse\n");
|
||||
|
||||
//first line contains the serverinfo, or some form of it
|
||||
WriteString2(&msg, "\\*QTV\\"); WriteString2(&msg, QTV_VERSION_STRING);
|
||||
// WriteString2(&msg, "\\*fp\\"); WriteString2(&msg, hash(cert));
|
||||
if (authed)
|
||||
{ //only reported to the master server to generate time-based auth tokens.
|
||||
tobase64(tmp,sizeof(tmp), cluster->turnkey, sizeof(cluster->turnkey));
|
||||
WriteString2(&msg, "\\_turnkey\\"); WriteString2(&msg, tmp);
|
||||
}
|
||||
WriteString2(&msg, "\\challenge\\"); WriteString2(&msg, challenge);
|
||||
WriteString2(&msg, "\\gamename\\"); WriteString2(&msg, protocolname);
|
||||
snprintf(tmp, sizeof(tmp), "%i%s", cluster->protocolname?cluster->protocolver:3, "t"); //'w':quakeworld, 'n'/'d':netquake, 'x':qe, 't':qtv, 'r':turnrelay, 'f':fwd
|
||||
WriteString2(&msg, "\\protocol\\"); WriteString2(&msg, tmp);
|
||||
WriteString2(&msg, "\\clients\\"); WriteString2(&msg, "0");
|
||||
WriteString2(&msg, "\\sv_maxclients\\"); WriteString2(&msg, "0");
|
||||
WriteString2(&msg, "\\modname\\"); WriteString2(&msg, "QTV");
|
||||
WriteString2(&msg, "\\mapname\\"); WriteString2(&msg, "QTV");
|
||||
WriteString2(&msg, "\\hostname\\"); WriteString2(&msg, cluster->hostname);
|
||||
snprintf(tmp, sizeof(tmp), "%i", cluster->tcplistenportnum);
|
||||
WriteString2(&msg, "\\sv_port_tcp\\"); WriteString2(&msg, tmp);
|
||||
|
||||
/*if (fullstatus)
|
||||
{
|
||||
client_t *cl;
|
||||
char *start = resp;
|
||||
|
||||
if (resp != response+sizeof(response))
|
||||
{
|
||||
resp[-1] = '\n'; //replace the null terminator that we already wrote
|
||||
|
||||
//on the following lines we have an entry for each client
|
||||
for (i=0 ; i<svs.allocated_client_slots ; i++)
|
||||
{
|
||||
cl = &svs.clients[i];
|
||||
if ((cl->state == cs_connected || cl->state == cs_spawned || cl->name[0]) && !cl->spectator)
|
||||
{
|
||||
Q_strncpyz(resp, va(
|
||||
"%d %d \"%s\" \"%s\"\n"
|
||||
,
|
||||
cl->old_frags,
|
||||
SV_CalcPing(cl, false),
|
||||
cl->team,
|
||||
cl->name
|
||||
), sizeof(response) - (resp-response));
|
||||
resp += strlen(resp);
|
||||
}
|
||||
}
|
||||
|
||||
*resp++ = 0; //this might not be a null
|
||||
if (resp == response+sizeof(response))
|
||||
{
|
||||
//we're at the end of the buffer, it's full. bummer
|
||||
//replace 12 bytes with infoResponse
|
||||
memcpy(response+4, "infoResponse", 12);
|
||||
//move down by len(statusResponse)-len(infoResponse) bytes
|
||||
memmove(response+4+12, response+4+14, resp-response-(4+14));
|
||||
start -= 14-12; //fix this pointer
|
||||
|
||||
resp = start;
|
||||
resp[-1] = 0; //reset the \n
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
WriteByte(&msg, 0);
|
||||
|
||||
NET_SendPacket(cluster, NET_ChooseSocket(cluster->qwdsocket, from, *from), msg.cursize, msg.data, *from);
|
||||
}
|
||||
|
||||
void QTV_StatusResponse(cluster_t *cluster, char *msg, netadr_t *from)
|
||||
{
|
||||
|
@ -1284,10 +1426,21 @@ void ConnectionlessPacket(cluster_t *cluster, netadr_t *from, netmsg_t *m)
|
|||
QTV_Status(cluster, from);
|
||||
return;
|
||||
}
|
||||
if (!strncmp(buffer, "getinfo", 7))
|
||||
{
|
||||
QTV_GetInfo(cluster, from, buffer+7);
|
||||
return;
|
||||
}
|
||||
if (!strncmp(buffer, "getchallenge", 12))
|
||||
{
|
||||
i = NewChallenge(from);
|
||||
i = NewChallenge(cluster, from);
|
||||
if (!cluster->relayenabled)
|
||||
Netchan_OutOfBandPrint(cluster, *from, "c%i", i);
|
||||
else
|
||||
{ //special response to say we don't support dtls, but can proxy it, so use dtlsconnect without needing to send any private info until the final target is determined.
|
||||
snprintf(buffer, sizeof(buffer), "c%i%cDTLS\xff\xff\xff\xff", i, 0); //PROTOCOL_VERSION_DTLSUPGRADE
|
||||
Netchan_OutOfBand(cluster, *from, strlen(buffer)+9, buffer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!strncmp(buffer, "connect 28 ", 11))
|
||||
|
@ -1298,6 +1451,57 @@ void ConnectionlessPacket(cluster_t *cluster, netadr_t *from, netmsg_t *m)
|
|||
NewQWClient(cluster, from, buffer);
|
||||
return;
|
||||
}
|
||||
if (!strncmp(buffer, "getserversExtResponse", 21) && cluster->pingtreeenabled)
|
||||
{ //q3-style serverlist response
|
||||
m->readpos = 4+21;
|
||||
Fwd_ParseServerList(cluster, m, -1);
|
||||
return;
|
||||
}
|
||||
if (!strncmp(buffer, "d\n", 2) && cluster->pingtreeenabled)
|
||||
{ //legacy qw serverlist response
|
||||
m->readpos = 4+2;
|
||||
Fwd_ParseServerList(cluster, m, AF_INET);
|
||||
return;
|
||||
}
|
||||
if (!strcmp(buffer, "l") && cluster->pingtreeenabled)
|
||||
{ //qw ping response
|
||||
Fwd_PingResponse(cluster, from);
|
||||
return;
|
||||
}
|
||||
if (!strncmp(buffer, "pingstatus", 10) && cluster->pingtreeenabled)
|
||||
{
|
||||
int ext = false;
|
||||
char arg[64];
|
||||
if (buffer[10] == ' ')
|
||||
{
|
||||
char *s = buffer + 11;
|
||||
while (*s)
|
||||
{
|
||||
s = COM_ParseToken(s, arg,sizeof(arg), ""); //
|
||||
if (!strcmp(arg, "ext"))
|
||||
ext = true;
|
||||
}
|
||||
}
|
||||
Fwd_PingStatus(cluster, from, ext);
|
||||
return;
|
||||
}
|
||||
if (!strncmp(buffer, "dtlsconnect ", 12) && cluster->relayenabled)
|
||||
{ //dtlsconnect challenge [finalip@middleip@targetip]
|
||||
char challenge[64];
|
||||
char *s = COM_ParseToken(buffer+12, challenge,sizeof(challenge), ""); //
|
||||
if (ChallengePasses(cluster, from, atoi(challenge)))
|
||||
{
|
||||
while(*s == ' ')
|
||||
s++;
|
||||
Fwd_NewQWFwd(cluster, from, s); //will send a challenge to the target.
|
||||
//the relay code will pass the response to the client triggering a new dtlsconnect.
|
||||
//eventually punching all the way through to the target which will respond with a dtlsopened.
|
||||
//the client will then be free to send its dtls handshakes, with the server's certificate matched against the fingerprint reported by the master.
|
||||
//this should ensure there's no tampering.
|
||||
//note that we cannot read any disconnect hints when they're encrypted, so we'll be depending on timeouts (which also avoids malicious disconnect spoofs, yay?)
|
||||
}
|
||||
return;
|
||||
}
|
||||
// if (buffer[0] == 'l' && (!buffer[1] || buffer[1] == '\n'))
|
||||
// {
|
||||
// Sys_Printf(cluster, "Ack from %s\n", );
|
||||
|
@ -4277,6 +4481,10 @@ void QW_ProcessUDPPacket(cluster_t *cluster, netmsg_t *m, netadr_t from)
|
|||
|
||||
if (*(int*)m->data == -1)
|
||||
{ //connectionless message
|
||||
if (TURN_IsRequest(cluster, m, &from))
|
||||
return;
|
||||
m->readpos = 0;
|
||||
|
||||
ConnectionlessPacket(cluster, &from, m);
|
||||
return;
|
||||
}
|
||||
|
@ -4297,7 +4505,7 @@ void QW_ProcessUDPPacket(cluster_t *cluster, netmsg_t *m, netadr_t from)
|
|||
{
|
||||
if (v->netchan.isnqprotocol)
|
||||
{
|
||||
if (Net_CompareAddress(&v->netchan.remote_address, &from, 0, 0))
|
||||
if (Net_CompareAddress(&v->netchan.remote_address, &from, 0, 1))
|
||||
{
|
||||
if (NQNetchan_Process(cluster, &v->netchan, m))
|
||||
{
|
||||
|
@ -4314,6 +4522,7 @@ void QW_ProcessUDPPacket(cluster_t *cluster, netmsg_t *m, netadr_t from)
|
|||
QTV_Run(v->server);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -4355,15 +4564,20 @@ void QW_ProcessUDPPacket(cluster_t *cluster, netmsg_t *m, netadr_t from)
|
|||
QTV_Run(v->server);
|
||||
}
|
||||
}
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!v && cluster->allownqclients)
|
||||
m->readpos = 0;
|
||||
|
||||
if (TURN_IsRequest(cluster, m, &from))
|
||||
return;
|
||||
m->readpos = 0;
|
||||
|
||||
if (cluster->allownqclients)
|
||||
{
|
||||
unsigned int ctrl;
|
||||
//NQ connectionless packet?
|
||||
m->readpos = 0;
|
||||
ctrl = ReadLong(m);
|
||||
ctrl = SwapLong(ctrl);
|
||||
if (ctrl & NETFLAG_CTL)
|
||||
|
@ -4410,7 +4624,7 @@ void QW_ProcessUDPPacket(cluster_t *cluster, netmsg_t *m, netadr_t from)
|
|||
{
|
||||
if (v->netchan.isnqprotocol)
|
||||
{
|
||||
if (Net_CompareAddress(&v->netchan.remote_address, &from, 0, 0))
|
||||
if (Net_CompareAddress(&v->netchan.remote_address, &from, 0, 1))
|
||||
{
|
||||
Sys_Printf(cluster, "Dup connect from %s\n", v->name);
|
||||
v->drop = true;
|
||||
|
@ -4446,7 +4660,11 @@ void QW_TCPConnection(cluster_t *cluster, oproxy_t *sock, char *initialstreamnam
|
|||
free(initialstreamname);
|
||||
}
|
||||
else
|
||||
{
|
||||
{ //okay, we're adding this as a client
|
||||
//try and disable nagle, we don't really want to be wasting time not sending anything.
|
||||
int _true = 1;
|
||||
setsockopt(sock->sock, IPPROTO_TCP, TCP_NODELAY, (char *)&_true, sizeof(_true));
|
||||
|
||||
tc->sock = sock->sock;
|
||||
tc->websocket = sock->websocket; //copy it over
|
||||
|
||||
|
@ -4487,6 +4705,11 @@ void QW_UpdateUDPStuff(cluster_t *cluster)
|
|||
{
|
||||
if (NET_StringToAddr(cluster->master, &from, 27000))
|
||||
{
|
||||
if (cluster->turnenabled)
|
||||
sprintf(buffer, "\377\377\377\377""heartbeat FTEMaster c=%s\n", cluster->chalkey); //fill buffer with a heartbeat
|
||||
else if (cluster->protocolname)
|
||||
sprintf(buffer, "\377\377\377\377""heartbeat Darkplaces\n"); //older, broader compatibility.
|
||||
else
|
||||
sprintf(buffer, "a\n%i\n0\n", cluster->mastersequence++); //fill buffer with a heartbeat
|
||||
//why is there no \xff\xff\xff\xff ?..
|
||||
NET_SendPacket(cluster, NET_ChooseSocket(cluster->qwdsocket, &from, from), strlen(buffer), buffer, from);
|
||||
|
@ -4510,8 +4733,8 @@ void QW_UpdateUDPStuff(cluster_t *cluster)
|
|||
break;
|
||||
continue;
|
||||
}
|
||||
from.tcpcon = NULL;
|
||||
read = recvfrom(cluster->qwdsocket[socketno], buffer, sizeof(buffer), 0, (struct sockaddr*)&from.sockaddr, (unsigned*)&fromsize);
|
||||
memset(&from, 0, sizeof(from));
|
||||
read = recvfrom(cluster->qwdsocket[socketno], buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&from.sockaddr, (unsigned*)&fromsize);
|
||||
|
||||
if (read < 0) //it's bad.
|
||||
{
|
||||
|
@ -4523,6 +4746,16 @@ void QW_UpdateUDPStuff(cluster_t *cluster)
|
|||
|
||||
if (read <= 5) //otherwise it's a runt or bad.
|
||||
{
|
||||
if (read == 1 && *buffer == 'l')
|
||||
{ //ffs. easier to just fix it up here.
|
||||
buffer[0] =
|
||||
buffer[1] =
|
||||
buffer[2] =
|
||||
buffer[3] = 0xff;
|
||||
buffer[4] = 'l';
|
||||
read = 5;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -4530,6 +4763,7 @@ void QW_UpdateUDPStuff(cluster_t *cluster)
|
|||
m.data = buffer;
|
||||
m.readpos = 0;
|
||||
|
||||
buffer[m.cursize] = 0; //make sure its null terminated.
|
||||
QW_ProcessUDPPacket(cluster, &m, from);
|
||||
}
|
||||
|
||||
|
|
122
fteqtv/rcon.c
122
fteqtv/rcon.c
|
@ -410,6 +410,7 @@ void Cmd_Master(cmdctxt_t *ctx)
|
|||
void Cmd_UDPPort(cmdctxt_t *ctx)
|
||||
{
|
||||
int newp = atoi(Cmd_Argv(ctx, 1));
|
||||
ctx->cluster->qwlistenportnum = newp;
|
||||
NET_InitUDPSocket(ctx->cluster, newp, SG_IPV6);
|
||||
NET_InitUDPSocket(ctx->cluster, newp, SG_IPV4);
|
||||
}
|
||||
|
@ -600,9 +601,13 @@ void Cmd_Say(cmdctxt_t *ctx)
|
|||
void Cmd_Status(cmdctxt_t *ctx)
|
||||
{
|
||||
Cmd_Printf(ctx, "QTV Status:\n");
|
||||
Cmd_Printf(ctx, " %i sources\n", ctx->cluster->numservers);
|
||||
Cmd_Printf(ctx, " %i viewers\n", ctx->cluster->numviewers);
|
||||
Cmd_Printf(ctx, " %i proxies\n", ctx->cluster->numproxies);
|
||||
Cmd_Printf(ctx, " %i sources%s\n", ctx->cluster->numservers, ctx->cluster->nouserconnects?" (admin only)":" (user allowed)");
|
||||
Cmd_Printf(ctx, " %i udp clients %s\n", ctx->cluster->numviewers, ctx->cluster->allownqclients?" (qw+nq)":" (qw only)");
|
||||
if (ctx->cluster->maxproxies)
|
||||
Cmd_Printf(ctx, " %i tcp clients (of %i)\n", ctx->cluster->numproxies, ctx->cluster->maxproxies);
|
||||
else
|
||||
Cmd_Printf(ctx, " %i tcp clients\n", ctx->cluster->numproxies);
|
||||
TURN_RelayStatus(ctx);
|
||||
|
||||
Cmd_Printf(ctx, "Common Options:\n");
|
||||
Cmd_Printf(ctx, " Hostname %s\n", ctx->cluster->hostname);
|
||||
|
@ -610,7 +615,7 @@ void Cmd_Status(cmdctxt_t *ctx)
|
|||
if (ctx->cluster->chokeonnotupdated)
|
||||
Cmd_Printf(ctx, " Choke\n");
|
||||
if (ctx->cluster->lateforward)
|
||||
Cmd_Printf(ctx, " Late forwarding\n");
|
||||
Cmd_Printf(ctx, " Late forwarding (delayed streams)\n");
|
||||
if (!ctx->cluster->notalking)
|
||||
Cmd_Printf(ctx, " Talking allowed\n");
|
||||
if (ctx->cluster->nobsp)
|
||||
|
@ -621,7 +626,6 @@ void Cmd_Status(cmdctxt_t *ctx)
|
|||
Cmd_Printf(ctx, " tcp port %i\n", ctx->cluster->tcplistenportnum);
|
||||
if (ctx->cluster->qwdsocket[SG_IPV4] != INVALID_SOCKET || ctx->cluster->qwdsocket[SG_IPV6] != INVALID_SOCKET)
|
||||
Cmd_Printf(ctx, " udp port %i\n", ctx->cluster->qwlistenportnum);
|
||||
Cmd_Printf(ctx, " user connections are %sallowed\n", ctx->cluster->nouserconnects?"NOT ":"");
|
||||
Cmd_Printf(ctx, "\n");
|
||||
|
||||
|
||||
|
@ -1227,7 +1231,110 @@ void Cmd_Watch(cmdctxt_t *ctx)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <fcntl.h>
|
||||
qboolean Sys_RandomBytes(unsigned char *out, int len)
|
||||
{
|
||||
qboolean res;
|
||||
int fd = open("/dev/urandom", 0);
|
||||
res = (read(fd, out, len) == len);
|
||||
close(fd);
|
||||
return res;
|
||||
}
|
||||
#else
|
||||
qboolean Sys_RandomBytes(unsigned char *out, int len)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void Cmd_Turn(cmdctxt_t *ctx)
|
||||
{
|
||||
if (Cmd_Argc(ctx) < 2)
|
||||
{
|
||||
if (ctx->cluster->turnenabled && ctx->cluster->turn_minport)
|
||||
Cmd_Printf(ctx, "turn is enabled, using ports %i-%i\n", ctx->cluster->turn_minport, ctx->cluster->turn_maxport);
|
||||
else if (ctx->cluster->turnenabled)
|
||||
Cmd_Printf(ctx, "turn is enabled, using ephemerial ports\n");
|
||||
else
|
||||
Cmd_Printf(ctx, "turn is disabled\n");
|
||||
return;
|
||||
}
|
||||
if (!Cmd_IsLocal(ctx))
|
||||
{
|
||||
Cmd_Printf(ctx, "turn support may not be configured remotely\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Cmd_Argc(ctx) >= 3)
|
||||
{ //two args - assume a two number range, so turn it on.
|
||||
ctx->cluster->turnenabled = true;
|
||||
ctx->cluster->turn_minport = atoi(Cmd_Argv(ctx, 1));
|
||||
ctx->cluster->turn_maxport = atoi(Cmd_Argv(ctx, 2));
|
||||
}
|
||||
else if ( atoi(Cmd_Argv(ctx, 1))) //a boolean. turn it back on..
|
||||
ctx->cluster->turnenabled = true; //switch it back on with whatever port range it previously had. probably 0-0 for ephemerial. probably bad for the relay's firewalls...
|
||||
else
|
||||
ctx->cluster->turnenabled = false; //and off.
|
||||
|
||||
if (!*ctx->cluster->chalkey && ctx->cluster->turnenabled)
|
||||
{
|
||||
unsigned char chalkey[12];
|
||||
if (!Sys_RandomBytes(chalkey, sizeof(chalkey)) ||
|
||||
!Sys_RandomBytes(ctx->cluster->turnkey, sizeof(ctx->cluster->turnkey)))
|
||||
{
|
||||
Cmd_Printf(ctx, "no random generator\n");
|
||||
ctx->cluster->turnenabled = false;
|
||||
return;
|
||||
}
|
||||
tobase64(ctx->cluster->chalkey,sizeof(ctx->cluster->chalkey), chalkey, sizeof(chalkey));
|
||||
}
|
||||
|
||||
if (ctx->cluster->turnenabled && ctx->cluster->turn_minport)
|
||||
Cmd_Printf(ctx, "turn keys updated, using ports %i-%i\n", ctx->cluster->turn_minport, ctx->cluster->turn_maxport);
|
||||
else if (ctx->cluster->turnenabled)
|
||||
Cmd_Printf(ctx, "turn keys updated, using ephemerial ports\n");
|
||||
else
|
||||
Cmd_Printf(ctx, "turn disabled\n");
|
||||
}
|
||||
static void Cmd_Relay(cmdctxt_t *ctx)
|
||||
{
|
||||
if (Cmd_Argc(ctx) >= 2)
|
||||
{
|
||||
if (Cmd_IsLocal(ctx))
|
||||
{
|
||||
Cmd_Printf(ctx, "relay support may not be configured remotely\n");
|
||||
return;
|
||||
}
|
||||
switch(atoi(Cmd_Argv(ctx, 1)))
|
||||
{
|
||||
case 0:
|
||||
ctx->cluster->relayenabled = ctx->cluster->pingtreeenabled = false;
|
||||
Cmd_Printf(ctx, "turn disabled\n");
|
||||
break;
|
||||
case 1:
|
||||
ctx->cluster->relayenabled = ctx->cluster->pingtreeenabled = true;
|
||||
break;
|
||||
default:
|
||||
ctx->cluster->relayenabled = true;
|
||||
ctx->cluster->pingtreeenabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->cluster->relayenabled && ctx->cluster->pingtreeenabled)
|
||||
Cmd_Printf(ctx, "relay is enabled (with pinging)\n");
|
||||
else if (ctx->cluster->relayenabled)
|
||||
Cmd_Printf(ctx, "relay is enabled, WITHOUT pinging\n");
|
||||
else
|
||||
Cmd_Printf(ctx, "relay is disabled\n");
|
||||
}
|
||||
static void Cmd_ProtocolName(cmdctxt_t *ctx)
|
||||
{
|
||||
free(ctx->cluster->protocolname);
|
||||
ctx->cluster->protocolname = strdup(Cmd_Argv(ctx, 1));
|
||||
ctx->cluster->protocolver = atoi(Cmd_Argv(ctx, 2));
|
||||
}
|
||||
|
||||
typedef struct rconcommands_s {
|
||||
char *name;
|
||||
|
@ -1301,6 +1408,11 @@ const rconcommands_t rconcommands[] =
|
|||
{"initialdelay",0, 1, Cmd_InitialDelay, "Specifies the duration for which new connections will be buffered. Large values prevents players from spectating their enemies as a cheap wallhack."},
|
||||
{"slowdelay", 0, 1, Cmd_SlowDelay, "If a server is not sending enough data, the proxy will delay parsing for this long."},
|
||||
|
||||
{"turn", 0, 1, Cmd_Turn, "Controls whether we accept turn requests."},
|
||||
{"relay", 0, 1, Cmd_Relay, "Controls whether we accept qwfwd-style relay requests."},
|
||||
{"qwfwd", 0, 1, Cmd_Relay},
|
||||
{"protocolname",0, 1, Cmd_ProtocolName, "Protocol Name:Version used to register with master."},
|
||||
|
||||
|
||||
{"halt", 1, 0, Cmd_Halt, "disables a stream, preventing it from reconnecting until someone tries watching it anew. Boots current spectators"},
|
||||
{"disable", 1, 0, Cmd_Halt},
|
||||
|
|
1373
fteqtv/relay.c
Normal file
1373
fteqtv/relay.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -109,8 +109,8 @@ qboolean NET_StringToAddr (char *s, netadr_t *sadr, int defaultport)
|
|||
}
|
||||
else
|
||||
#endif
|
||||
#if 0//def IPPROTO_IPV6
|
||||
if (getaddrinfo)
|
||||
#ifndef _WIN32
|
||||
if (1)
|
||||
{//ipv6 method (can return ipv4 addresses too)
|
||||
struct addrinfo *addrinfo, *pos;
|
||||
struct addrinfo udp6hint;
|
||||
|
@ -124,6 +124,23 @@ qboolean NET_StringToAddr (char *s, netadr_t *sadr, int defaultport)
|
|||
udp6hint.ai_socktype = SOCK_DGRAM;
|
||||
udp6hint.ai_protocol = IPPROTO_UDP;
|
||||
|
||||
if (*s == '[')
|
||||
{
|
||||
s++;
|
||||
colon = strchr(s, ']');
|
||||
if (!colon || colon-s >= sizeof(copy))
|
||||
return false; //too long to handle.
|
||||
memcpy(copy, s, colon-s);
|
||||
copy[colon-s] = 0;
|
||||
colon++;
|
||||
if (*colon == ':')
|
||||
port = colon;
|
||||
else
|
||||
port = NULL;
|
||||
s = copy;
|
||||
}
|
||||
else
|
||||
{
|
||||
port = s + strlen(s);
|
||||
while(port >= s)
|
||||
{
|
||||
|
@ -131,36 +148,38 @@ qboolean NET_StringToAddr (char *s, netadr_t *sadr, int defaultport)
|
|||
break;
|
||||
port--;
|
||||
}
|
||||
}
|
||||
|
||||
if (port == s)
|
||||
port = NULL;
|
||||
if (port)
|
||||
{
|
||||
len = port - s;
|
||||
if (len > sizeof(dupbase))
|
||||
len = sizeof(dupbase);
|
||||
strlcpy(dupbase, s, len);
|
||||
if (len > sizeof(dupbase)-1)
|
||||
len = sizeof(dupbase)-1;
|
||||
memcpy(dupbase, s, len);
|
||||
dupbase[len] = 0;
|
||||
error = getaddrinfo(dupbase, port+1, &udp6hint, &addrinfo);
|
||||
}
|
||||
else
|
||||
error = EAI_NONAME;
|
||||
error = EAI_NONAME, addrinfo=NULL;
|
||||
if (error) //failed, try string with no port.
|
||||
error = getaddrinfo(s, NULL, &udp6hint, &addrinfo); //remember, this func will return any address family that could be using the udp protocol... (ip4 or ip6)
|
||||
if (error)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
((struct sockaddr*)sadr)->sa_family = 0;
|
||||
((struct sockaddr*)sadr->sockaddr)->sa_family = 0;
|
||||
for (pos = addrinfo; pos; pos = pos->ai_next)
|
||||
{
|
||||
switch(pos->ai_family)
|
||||
{
|
||||
case AF_INET6:
|
||||
if (((struct sockaddr_in *)sadr)->sin_family == AF_INET6)
|
||||
if (((struct sockaddr_in *)sadr->sockaddr)->sin_family == AF_INET6)
|
||||
break; //first one should be best...
|
||||
//fallthrough
|
||||
case AF_INET:
|
||||
memcpy(sadr, addrinfo->ai_addr, addrinfo->ai_addrlen);
|
||||
memcpy(sadr->sockaddr, addrinfo->ai_addr, addrinfo->ai_addrlen);
|
||||
if (pos->ai_family == AF_INET)
|
||||
goto dblbreak; //don't try finding any more, this is quake, they probably prefer ip4...
|
||||
break;
|
||||
|
@ -168,7 +187,7 @@ qboolean NET_StringToAddr (char *s, netadr_t *sadr, int defaultport)
|
|||
}
|
||||
dblbreak:
|
||||
pfreeaddrinfo (addrinfo);
|
||||
if (!((struct sockaddr*)sadr)->sa_family) //none suitablefound
|
||||
if (!((struct sockaddr*)sadr->sockaddr)->sa_family) //none suitablefound
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
@ -209,7 +228,37 @@ qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2)
|
|||
{
|
||||
struct sockaddr *g1=(void*)s1->sockaddr, *g2=(void*)s2->sockaddr;
|
||||
if (g1->sa_family != g2->sa_family)
|
||||
{ //urgh...
|
||||
if (g1->sa_family == AF_INET6 && g2->sa_family == AF_INET && (
|
||||
((unsigned int*)&((struct sockaddr_in6 *)g1)->sin6_addr)[0] == 0 &&
|
||||
((unsigned int*)&((struct sockaddr_in6 *)g1)->sin6_addr)[1] == 0 &&
|
||||
((unsigned short*)&((struct sockaddr_in6 *)g1)->sin6_addr)[4] == 0 &&
|
||||
((unsigned short*)&((struct sockaddr_in6 *)g1)->sin6_addr)[5] == 0xffff))
|
||||
{
|
||||
struct sockaddr_in6 *i1=(void*)s1->sockaddr;
|
||||
struct sockaddr_in *i2=(void*)s2->sockaddr;
|
||||
if (((unsigned int*)&i1->sin6_addr)[3] != *(unsigned int*)&i2->sin_addr)
|
||||
return false;
|
||||
if (i1->sin6_port != i2->sin_port && qp1 != qp2) //allow qports to match instead of ports, if required.
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
if (g1->sa_family == AF_INET && g2->sa_family == AF_INET6 && (
|
||||
((unsigned int*)&((struct sockaddr_in6 *)g2)->sin6_addr)[0] == 0 &&
|
||||
((unsigned int*)&((struct sockaddr_in6 *)g2)->sin6_addr)[1] == 0 &&
|
||||
((unsigned short*)&((struct sockaddr_in6 *)g2)->sin6_addr)[4] == 0 &&
|
||||
((unsigned short*)&((struct sockaddr_in6 *)g2)->sin6_addr)[5] == 0xffff))
|
||||
{
|
||||
struct sockaddr_in6 *i1=(void*)s2->sockaddr;
|
||||
struct sockaddr_in *i2=(void*)s1->sockaddr;
|
||||
if (((unsigned int*)&i1->sin6_addr)[3] != *(unsigned int*)&i2->sin_addr)
|
||||
return false;
|
||||
if (i1->sin6_port != i2->sin_port && qp1 != qp2) //allow qports to match instead of ports, if required.
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
switch(g1->sa_family)
|
||||
{
|
||||
default:
|
||||
|
@ -2410,6 +2459,10 @@ void QTV_Run(sv_t *qtv)
|
|||
switch(qtv->buffer[1]&dem_mask)
|
||||
{
|
||||
case dem_multiple:
|
||||
if ((qtv->pexte&PEXTE_HIDDENMESSAGES) &&
|
||||
0 == (buffer[lengthofs-4]<<0) + (buffer[lengthofs-3]<<8) + (buffer[lengthofs-2]<<16) + (buffer[lengthofs-1]<<24))
|
||||
; //fucked hidden message crap. don't trip up on it.
|
||||
else
|
||||
ParseMessage(qtv, buffer+lengthofs+4, length, qtv->buffer[1]&dem_mask, (buffer[lengthofs-4]<<0) + (buffer[lengthofs-3]<<8) + (buffer[lengthofs-2]<<16) + (buffer[lengthofs-1]<<24));
|
||||
break;
|
||||
case dem_single:
|
||||
|
|
|
@ -1737,6 +1737,18 @@ static void VARGS Cef_Key (void *ctx, int code, int unicode, int event)
|
|||
return;
|
||||
}
|
||||
|
||||
if (code == K_TOUCH)
|
||||
{ //FIXME
|
||||
cef_release(host);
|
||||
return;
|
||||
}
|
||||
if (code == K_TOUCHSLIDE || code == K_TOUCHTAP || code == K_TOUCHLONG)
|
||||
{
|
||||
cef_release(host);
|
||||
return; //has to do its own
|
||||
}
|
||||
|
||||
|
||||
//handle mouse wheels
|
||||
if (code == K_MWHEELUP || code == K_MWHEELDOWN)
|
||||
{
|
||||
|
|
|
@ -978,7 +978,7 @@ void CLQ3_SendAuthPacket(struct ftenet_connections_s *socket, netadr_t *gameserv
|
|||
|
||||
//send the auth packet
|
||||
//this should be the right code, but it doesn't work.
|
||||
if (gameserver->type == NA_IP)
|
||||
if (gameserver->type == NA_IP && gameserver->prot == NP_DGRAM)
|
||||
{
|
||||
char *key = cvarfuncs->GetNVFDG("cl_cdkey", "", CVAR_ARCHIVE, "Quake3 auth", "Q3 Compat")->string;
|
||||
netadr_t authaddr;
|
||||
|
|
Loading…
Reference in a new issue