forked from fte/fteqw
1
0
Fork 0

megacommit.

adds qtv relay support.
lots of other misc tweaks.
This commit is contained in:
Shpoike 2024-07-14 19:56:31 +01:00
parent effee0e45a
commit ff1a2299f4
69 changed files with 3621 additions and 748 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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.")},

View File

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

View File

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

View File

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

View 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");

View File

@ -31,10 +31,11 @@
#define QUAKE3PROTOCOLNAME "Quake3"
#define PREFIX_SECURE(issecure) ((issecure)?"&#x1f6e1;":"&#x26A0;&#xFE0F;")//shield, vs yellow warning
#define PREFIX_NEEDPASS(needpass) (((needpass)&1)?"&#x1F512;":"") //padlock, vs no indicator.
#define PREFIX_COOP(iscoop) (((iscoop)&1)?"&#x262E;":"") //coop: peace sign, deathmatch:no indicator.
#define PREFIX_SECURE(srv) ((srv)->secure?"&#x1f6e1;":"&#x26A0;&#xFE0F;")//shield, vs yellow warning
#define PREFIX_NEEDPASS(srv) (((srv)->needpass&1)?"&#x1F512;":"") //padlock, vs no indicator.
#define PREFIX_TYPE(srv) ( ((srv)->type&1)?"&#x262E;"/*coop: peace sign, deathmatch:no indicator. */ :\
((srv)->type&2)?"&#x1f4fa;"/*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 ();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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