fixed eztv md4 incompatibility.

reimplemented qtvreverse command.
fixed some stuffcmds being handled by the wrong splitscreen seats (was noticable in TF).
rework smartjump to try to be more predictable...
rework relighting to try to be more robust (and more self-contained).
allow the csqc to actually use VF_PROJECTIONOFFSET.
jump now moves upwards instead of trying to lock on to a nearby player when spectating.
assume 32 fullbright pixels when running with a palette.lmp yet no colormap.lmp (happens with some total conversions).
tweaked scoreboard for fainter backgrounds.
rearranged autoid, to be smaller etc.
hacked around dodgy conchars.lmp - don't treat 128*128 qpics as qpics to work around workarounds for buggy wad tools (with a warning).
fixed missing fullbrights on h2holey models.
avoided warning about mod_h2holey_bugged on dedicated servers.
added net_ice_exchangeprivateips, for people worried about exposing lan IPs when using ICE.
sv_public 2: implemented client support for our webrtc broker in order to use our own ICE implementation without needing to faff around with irc accounts or plugins etc.
TODO: ensure at least one ephemerial udp port when using ice or come up with some better sv_port handling
fixed multiple tls bugs (one could cause server problems). change net_enable_tls to disabled by default anyway (reenable for the server to be able to respond to https/wss/tls schemes again).
don't colourmap when there appears to be a highres diffusemap on q1 models.
imgtool now understands exporting from qpics in wads, as well as just mips.
implemented speed-o-meter in ezhud.
added removeinstant builtin to avoid the half-second rule.


git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5614 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2020-02-11 18:06:10 +00:00
parent 75593b6578
commit 9033f7b237
83 changed files with 3585 additions and 1445 deletions

View file

@ -829,6 +829,7 @@ ELSE()
ADD_EXECUTABLE(ftemaster
${FTESV_ARCH_FILES}
engine/server/sv_master.c
engine/common/net_ice.c #for the stun responses.
engine/common/net_wins.c
engine/common/cvar.c
engine/common/cmd.c

View file

@ -2328,10 +2328,13 @@ $(RELEASE_DIR)/imgtool$(BITS)$(EXEPOSTFIX): $(IMGTOOL_OBJECTS)
imgtool-rel: $(RELEASE_DIR)/imgtool$(BITS)$(EXEPOSTFIX)
imgtool: imgtool-rel
MASTER_OBJECTS=server/sv_sys_unix.c common/sys_linux_threads.c common/net_ssl_gnutls.c server/sv_master.c common/net_wins.c common/cvar.c common/cmd.c common/sha1.c http/httpclient.c common/log.c common/fs.c common/fs_stdio.c common/common.c common/translate.c common/zone.c qclib/hash.c
MASTER_OBJECTS=server/sv_sys_unix.c common/sys_linux_threads.c common/net_ssl_gnutls.c server/sv_master.c common/net_wins.c common/net_ice.c common/cvar.c common/cmd.c common/sha1.c http/httpclient.c common/log.c common/fs.c common/fs_stdio.c common/common.c common/translate.c common/zone.c qclib/hash.c
$(RELEASE_DIR)/ftemaster$(BITS)$(EXEPOSTFIX): $(MASTER_OBJECTS)
$(CC) -o $@ $(MASTER_OBJECTS) -Icommon -Iclient -Iqclib -Igl -Iserver -DMASTERONLY -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp -lm -ldl
$(CC) -o $@ $(MASTER_OBJECTS) -flto=jobserver -fvisibility=hidden -Icommon -Iclient -Iqclib -Igl -Iserver -DMASTERONLY -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp -lm -ldl -lz $(RELEASE_CFLAGS) $(RELEASE_LDFLAGS)
$(DEBUG_DIR)/ftemaster$(BITS)$(EXEPOSTFIX): $(MASTER_OBJECTS)
$(CC) -o $@ $(MASTER_OBJECTS) -Icommon -Iclient -Iqclib -Igl -Iserver -DMASTERONLY -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp -lm -ldl -lz $(DEBUG_CFLAGS) $(DEBUG_LDFLAGS)
master-rel: $(RELEASE_DIR)/ftemaster$(BITS)$(EXEPOSTFIX)
master-dbg: $(DEBUG_DIR)/ftemaster$(BITS)$(EXEPOSTFIX)
master: master-rel
QTV_OBJECTS= \

View file

@ -951,7 +951,7 @@ void Cam_SetModAutoTrack(int userid)
Con_Printf("//at: invalid userid\n");
}
void Cam_TrackCrosshairedPlayer(playerview_t *pv)
/*static void Cam_TrackCrosshairedPlayer(playerview_t *pv)
{
inframe_t *frame;
player_state_t *player;
@ -978,11 +978,11 @@ void Cam_TrackCrosshairedPlayer(playerview_t *pv)
}
}
// Con_Printf("Track %i? %f\n", best, bestdot);
if (best != -1) //did we actually get someone?
if (best > .707) //did we actually get someone?
{
Cam_Lock(pv, best);
}
}
}*/
void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd)
{
@ -1091,8 +1091,8 @@ void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd)
pv->cam_oldbuttons &= ~BUTTON_ATTACK;
if (pv->cam_state == CAM_FREECAM && autotrackmode == TM_USER)
{
if ((cmd->buttons & BUTTON_JUMP) && !(pv->cam_oldbuttons & BUTTON_JUMP))
Cam_TrackCrosshairedPlayer(pv);
// if ((cmd->buttons & BUTTON_JUMP) && !(pv->cam_oldbuttons & BUTTON_JUMP))
// Cam_TrackCrosshairedPlayer(pv);
pv->cam_oldbuttons = (pv->cam_oldbuttons&~BUTTON_JUMP) | (cmd->buttons & BUTTON_JUMP);
return;
}

View file

@ -2632,7 +2632,8 @@ void CL_QTVPoll (void)
qboolean init_numviewers = false;
qboolean iseztv = false;
char srchost[256];
char auth[64];
char challenge[128];
if (!qtvrequest)
return;
@ -2706,6 +2707,8 @@ void CL_QTVPoll (void)
}
s[1] = '\0'; //make sure its null terminated before the data payload
s = qtvrequestbuffer;
*auth = *challenge = 0;
for (e = s; *e; )
{
if (*e == '\r')
@ -2735,6 +2738,14 @@ void CL_QTVPoll (void)
{ //printable error
Con_Printf("Demo%s is available\n", colon);
}
else if (!strcmp(s, "AUTH"))
{
while (*colon && *(unsigned char*)colon <= ' ')
colon++;
Q_strncpyz(auth, colon, sizeof(auth));
}
else if (!strcmp(s, "CHALLENGE"))
Q_strncpyz(challenge, colon, sizeof(challenge));
//generic sourcelist responce
else if (!strcmp(s, "ASOURCE"))
@ -2830,6 +2841,14 @@ void CL_QTVPoll (void)
return;
}
if (!strcmp(auth, "NONE"))
;
// else if (!strcmp(auth, "PLAIN"))
// else if (!strcmp(auth, "MD4"))
// else if (!strcmp(auth, "SHA1"))
else if (*auth)
Con_Printf("Server requires unsupported auth method: %s\n", auth);
SCR_SetLoadingStage(LS_NONE);
VFS_CLOSE(qtvrequest);
qtvrequest = NULL;
@ -3013,6 +3032,7 @@ void CL_ParseQTVDescriptor(vfsfile_t *f, const char *name)
VFS_CLOSE(f);
}
#include "netinc.h"
void CL_QTVPlay_f (void)
{
qboolean raw=0;
@ -3021,11 +3041,11 @@ void CL_QTVPlay_f (void)
char *host;
char msg[4096];
int msglen=0;
// char *password;
char *password;
if (Cmd_Argc() < 2)
{
Con_Printf("Usage: qtvplay [stream@]hostname[:port] [password]\n");
Con_Printf("Usage: qtvplay [stream@][tls://]hostname[:port] [password]\n");
return;
}
@ -3047,8 +3067,23 @@ void CL_QTVPlay_f (void)
connrequest = strchrrev(connrequest, '@');
if (connrequest)
host = connrequest+1;
Q_strncpyz(qtvhostname, host, sizeof(qtvhostname));
newf = FS_OpenTCP(qtvhostname, 27599);
#ifdef HAVE_SSL
if (!strncmp(host, "tls://", 6))
{
char *colon;
Q_strncpyz(qtvhostname, host+6, sizeof(qtvhostname));
colon = strchr(qtvhostname, ':');
newf = FS_OpenTCP(qtvhostname, 27599);
if (colon) *colon = 0;
newf = FS_OpenSSL(qtvhostname, newf, false);
if (colon) *colon = ':';
}
else
#endif
{
Q_strncpyz(qtvhostname, host, sizeof(qtvhostname));
newf = FS_OpenTCP(qtvhostname, 27599);
}
if (!newf)
{
@ -3063,7 +3098,7 @@ void CL_QTVPlay_f (void)
else
host = NULL;
// password = Cmd_Argv(2);
password = Cmd_Argv(2);
if (qtvcl_forceversion1.ival)
{
@ -3079,6 +3114,33 @@ void CL_QTVPlay_f (void)
}
msglen += strlen(msg+msglen);
if (password)
{
#if 0
//just send it directly, we can't handle the tripple handshake for the challenge info
Q_snprintfz(msg+msglen, sizeof(msg)-msglen,
"AUTH: PLAIN\n"
"PASSWORD: %s\n"
, password);
#else
//report supported auth methods to the server. it'll pick one and send us a challenge.
Q_snprintfz(msg+msglen, sizeof(msg)-msglen,
// "AUTH: SHA1\n"
// "AUTH: MD4\n"
// "AUTH: CCITT\n"
"AUTH: PLAIN\n");
#endif
msglen += strlen(msg+msglen);
}
else
{
//include supported auth methods, so server can pick one (and give suitable challenge in its response)
Q_snprintfz(msg+msglen, sizeof(msg)-msglen,
"AUTH: NONE\n"
"");
msglen += strlen(msg+msglen);
}
if (raw)
{
Q_snprintfz(msg+msglen, sizeof(msg)-msglen,

View file

@ -4646,17 +4646,17 @@ void CLQW_ParsePlayerinfo (void)
if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV)
{
player_state_t *prevstate, dummy;
player_state_t dummy;
if (!cl.parsecount || info->prevcount > cl.parsecount || cl.parsecount - info->prevcount >= UPDATE_BACKUP - 1)
{
memset(&dummy, 0, sizeof(dummy));
prevstate = &dummy;
oldstate = &dummy;
}
else
{
prevstate = &cl.inframes[info->prevcount & UPDATE_MASK].playerstate[num];
oldstate = &cl.inframes[info->prevcount & UPDATE_MASK].playerstate[num];
}
memcpy(state, prevstate, sizeof(player_state_t));
memcpy(state, oldstate, sizeof(player_state_t));
info->prevcount = cl.parsecount;
#ifdef QUAKESTATS
@ -4689,7 +4689,7 @@ void CLQW_ParsePlayerinfo (void)
}
}
VectorSubtract(state->origin, prevstate->origin, dist);
VectorSubtract(state->origin, oldstate->origin, dist);
VectorScale(dist, 1/(cl.inframes[parsecountmod].packet_entities.servertime - cl.inframes[oldparsecountmod].packet_entities.servertime), state->velocity);
VectorCopy (state->origin, state->predorigin);

View file

@ -64,10 +64,8 @@ int CL_TargettedSplit(qboolean nowrap)
int mod;
//explicitly targetted at some seat number from the server
if (Cmd_ExecLevel > RESTRICT_SERVER)
return Cmd_ExecLevel - RESTRICT_SERVER-1;
if (Cmd_ExecLevel == RESTRICT_SERVER)
return 0;
if (Cmd_ExecLevel >= RESTRICT_SERVER)
return Cmd_ExecLevel - RESTRICT_SERVER;
//locally executed command.
if (nowrap)
@ -531,31 +529,32 @@ static void IN_UseDown (void) {KeyDown(&in_use, NULL);}
static void IN_UseUp (void) {KeyUp(&in_use);}
static void IN_JumpDown (void)
{
qboolean condition;
qboolean up;
int pnum = CL_TargettedSplit(false);
playerview_t *pv = &cl.playerview[pnum];
condition = (cls.state == ca_active && cl_smartjump.ival && !prox_inmenu.ival);
up = (cls.state == ca_active && cl_smartjump.ival && !prox_inmenu.ival);
if (!up)
up = false;
#ifdef Q2CLIENT
if (condition && cls.protocol == CP_QUAKE2)
KeyDown(&in_up, &in_down);
else
else if (cls.protocol == CP_QUAKE2)
up = true; //always smartjump in q2.
#endif
else if (pv->spectator && pv->cam_state != CAM_FREECAM)
up = false; //if we're tracking, don't confuse stuff.
#ifdef QUAKESTATS
if (condition && cl.playerview[pnum].stats[STAT_HEALTH] > 0 && !cls.demoplayback && !pv->spectator &&
(cls.protocol==CP_NETQUAKE || cl.inframes[cl.validsequence&UPDATE_MASK].playerstate[pv->playernum].messagenum == cl.validsequence)
&& cl.playerview[pnum].waterlevel >= 2 && (!cl.teamfortress || !(in_forward.state[pnum] & 1))
)
KeyDown(&in_up, &in_down);
else
else if (!pv->spectator && pv->stats[STAT_HEALTH] <= 0)
up = false; //don't ever 'swim' when dead.
else if (pv->pmovetype == PM_FLY || pv->pmovetype == PM_6DOF || pv->pmovetype == PM_SPECTATOR || pv->pmovetype == PM_OLD_SPECTATOR)
up = true; //fling/spectating
else if ((pv->pmovetype == PM_NORMAL || pv->pmovetype == PM_WALLWALK) && pv->waterlevel >= 2 && (!cl.teamfortress || !(in_forward.state[pnum] & 1)))
up = true; //swimming. TF only (silently) smartjumps when NOT moving.
#endif
if (condition && pv->spectator && pv->cam_state == CAM_FREECAM)
KeyDown(&in_up, &in_down);
else
KeyDown(&in_jump, &in_down);
up = false;
KeyDown((up?&in_up:&in_jump), &in_down);
}
static void IN_JumpUp (void)
{

View file

@ -102,7 +102,7 @@ cvar_t m_forward = CVARF("m_forward","1", CVAR_ARCHIVE);
cvar_t m_side = CVARF("m_side","0.8", CVAR_ARCHIVE);
cvar_t cl_lerp_maxinterval = CVARD("cl_lerp_maxinterval", "0.3", "Maximum interval between keyframes, in seconds. Larger values can result in entities drifting very slowly when they move sporadically.");
cvar_t cl_lerp_players = CVARD("cl_lerp_players", "1", "Set this to make other players smoother, though it may increase effective latency. Affects only QuakeWorld.");
cvar_t cl_lerp_players = CVARD("cl_lerp_players", "0", "Set this to make other players smoother, though it may increase effective latency. Affects only QuakeWorld.");
cvar_t cl_predict_players = CVARD("cl_predict_players", "1", "Clear this cvar to see ents exactly how they are on the server.");
cvar_t cl_predict_players_frac = CVARD("cl_predict_players_frac", "0.9", "How much of other players to predict. Values less than 1 will help minimize overruns.");
cvar_t cl_predict_players_latency = CVARD("cl_predict_players_latency", "1.0", "Push the player back according to your latency, to give a smooth consistent simulation of the server.");
@ -206,6 +206,7 @@ cvar_t host_mapname = CVARAF("mapname", "",
#define RULESETADVICE " You should not normally change this cvar from its permissive default, instead impose limits on yourself only through the 'ruleset' cvar."
cvar_t ruleset_allow_playercount = CVARD("ruleset_allow_playercount", "1", "Specifies whether teamplay triggers that count nearby players are allowed in the current ruleset."RULESETADVICE);
cvar_t ruleset_allow_frj = CVARD("ruleset_allow_frj", "1", "Specifies whether Forward-Rocket-Jump scripts are allowed in the current ruleset. If 0, limits on yaw speed will be imposed so they cannot be scripted."RULESETADVICE);
//FIXME: rename ruleset_allow_frj to allow_scripts to match ezquake - 0: block multiple commands in binds, 1: cap angle speed changes, 2: vanilla quake
cvar_t ruleset_allow_semicheats = CVARD("ruleset_allow_semicheats", "1", "If 0, this blocks a range of cvars that are marked as semi-cheats. Such cvars will be locked to their empty/0 value."RULESETADVICE);
cvar_t ruleset_allow_packet = CVARD("ruleset_allow_packet", "1", "If 0, network packets sent via the 'packet' command will be blocked. This makes scripting timers a little harder."RULESETADVICE);
cvar_t ruleset_allow_particle_lightning = CVARD("ruleset_allow_particle_lightning", "1", "A setting of 0 blocks using the particle system to replace lightning gun trails. This prevents making the trails thinner thus preventing them from obscuring your view of your enemies."RULESETADVICE);
@ -264,9 +265,9 @@ unsigned int cl_maxstris;
static struct
{
qboolean trying;
qboolean istransfer; //ignore the user's desired server (don't change connect.adr).
netadr_t adr; //address that we're trying to transfer to. FIXME: support multiple resolved addresses, eg both ::1 AND 127.0.0.1
qboolean trying;
qboolean istransfer; //ignore the user's desired server (don't change connect.adr).
netadr_t adr; //address that we're trying to transfer to. FIXME: support multiple resolved addresses, eg both ::1 AND 127.0.0.1
#ifdef HAVE_DTLS
enum
{
@ -276,19 +277,20 @@ static struct
DTLS_ACTIVE
} dtlsupgrade;
#endif
int mtu;
unsigned int compresscrc;
int protocol; //nq/qw/q2/q3. guessed based upon server replies
int subprotocol; //the monkeys are trying to eat me.
unsigned int fteext1;
unsigned int fteext2;
unsigned int ezext1;
int qport;
int challenge; //tracked as part of guesswork based upon what replies we get.
double time; //for connection retransmits
int defaultport;
int tries; //increased each try, every fourth trys nq connect packets.
unsigned char guid[64];
int mtu;
unsigned int compresscrc;
int protocol; //nq/qw/q2/q3. guessed based upon server replies
int subprotocol; //the monkeys are trying to eat me.
unsigned int fteext1;
unsigned int fteext2;
unsigned int ezext1;
int qport;
int challenge; //tracked as part of guesswork based upon what replies we get.
double time; //for connection retransmits
int defaultport;
int tries; //increased each try, every fourth trys nq connect packets.
unsigned char guid[64];
// qbyte fingerprint[5*4]; //sha1 hash of accepted dtls certs
} connectinfo;
quakeparms_t host_parms;
@ -1033,22 +1035,6 @@ void CL_CheckForResend (void)
if (startuppending || r_blockvidrestart)
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.
/*
#ifdef NQPROT
if (connect_type)
{
if (!connect_time || !(realtime - connect_time < 5.0))
{
connect_time = realtime;
NQ_BeginConnect(cls.servername);
NQ_ContinueConnect(cls.servername);
}
else
NQ_ContinueConnect(cls.servername);
return;
}
#endif
*/
if (connectinfo.time && realtime - connectinfo.time < 5.0)
return;
@ -1228,6 +1214,7 @@ void CL_BeginServerConnect(const char *host, int port, qboolean noproxy)
connectinfo.trying = true;
connectinfo.defaultport = port;
connectinfo.protocol = CP_UNKNOWN;
SCR_SetLoadingStage(LS_CONNECTION);
CL_CheckForResend();
}
@ -1276,11 +1263,9 @@ void CL_Transfer_f(void)
memset(&connectinfo, 0, sizeof(connectinfo));
if (NET_StringToAdr(server, 0, &connectinfo.adr))
{
if (cls.state)
{
connectinfo.istransfer = true;
Q_strncpyz(connectinfo.guid, oldguid, sizeof(oldguid)); //retain the same guid on transfers
}
connectinfo.istransfer = true;
Q_strncpyz(connectinfo.guid, oldguid, sizeof(oldguid)); //retain the same guid on transfers
Cvar_Set(&cl_disconnectreason, "Transferring....");
connectinfo.trying = true;
connectinfo.defaultport = cl_defaultport.value;
@ -2161,7 +2146,7 @@ void CL_Color_f (void)
if (top == 0)
*num = '\0';
if (Cmd_ExecLevel>RESTRICT_SERVER) //colour command came from server for a split client
Cbuf_AddText(va("p%i cmd setinfo topcolor \"%s\"\n", Cmd_ExecLevel-RESTRICT_SERVER-1, num), Cmd_ExecLevel);
Cbuf_AddText(va("p%i cmd setinfo topcolor \"%s\"\n", pnum+1, num), Cmd_ExecLevel);
// else if (server_owns_colour)
// Cvar_LockFromServer(&topcolor, num);
else
@ -2170,7 +2155,7 @@ void CL_Color_f (void)
if (bottom == 0)
*num = '\0';
if (Cmd_ExecLevel>RESTRICT_SERVER) //colour command came from server for a split client
Cbuf_AddText(va("p%i cmd setinfo bottomcolor \"%s\"\n", Cmd_ExecLevel-RESTRICT_SERVER-1, num), Cmd_ExecLevel);
Cbuf_AddText(va("p%i cmd setinfo bottomcolor \"%s\"\n", pnum+1, num), Cmd_ExecLevel);
else if (server_owns_colour)
Cvar_LockFromServer(&bottomcolor, num);
else
@ -2535,6 +2520,8 @@ void CL_SetInfoBlob (int pnum, const char *key, const char *value, size_t values
return;
}
}
else if (pnum < 0 || pnum >= MAX_SPLITS)
return;
InfoBuf_SetStarBlobKey(&cls.userinfo[pnum], key, value, valuesize);
}
@ -2960,6 +2947,7 @@ void CL_Reconnect_f (void)
}
CL_Disconnect(NULL);
connectinfo.tries = 0; //re-ensure routes.
CL_BeginServerReconnect();
}
@ -3142,6 +3130,13 @@ void CL_ConnectionlessPacket (void)
Con_TPrintf ("challenge\n");
if (!NET_CompareAdr(&connectinfo.adr, &net_from))
{
if (connectinfo.adr.prot != NP_RTC_TCP && connectinfo.adr.prot != NP_RTC_TLS)
Con_Printf("Challenge from wrong server, ignoring\n");
return;
}
if (!strcmp(com_token, "hallengeResponse"))
{
/*Quake3*/
@ -3589,6 +3584,7 @@ client_connect: //fixme: make function
cls.netchan.qportsize = 1;
}
cls.netchan.pext_fragmentation = connectinfo.mtu?true:false;
cls.netchan.pext_stunaware = !!(connectinfo.fteext2&PEXT2_STUNAWARE);
if (connectinfo.mtu >= 64)
{
cls.netchan.mtu = connectinfo.mtu;
@ -3907,6 +3903,10 @@ void CL_ReadPackets (void)
continue;
}
if (cls.netchan.pext_stunaware) //should be safe to do this here.
if (NET_WasSpecialPacket(cls.sockets))
continue;
switch(cls.protocol)
{
case CP_NETQUAKE:
@ -4450,6 +4450,7 @@ void CL_Status_f(void)
char adr[128];
float pi, po, bi, bo;
NET_PrintAddresses(cls.sockets);
NET_PrintConnectionsStatus(cls.sockets);
if (NET_GetRates(cls.sockets, &pi, &po, &bi, &bo))
Con_Printf("packets,bytes/sec: in: %g %g out: %g %g\n", pi, bi, po, bo); //not relevent as a limit.
@ -5954,7 +5955,9 @@ double Host_Frame (double time)
return; // framerate is too high
*/
Mod_Think(); //think even on idle (which means small walls and a fast cpu can get more surfaces done.
#ifdef RUNTIMELIGHTING
RelightThink(); //think even on idle (which means small walls and a fast cpu can get more surfaces done.
#endif
#ifndef CLIENTONLY
if (sv.state && cls.state != ca_active)

View file

@ -35,14 +35,15 @@ enum masterprotocol_e
#define SS_PROXY (1<<7u)
#define PING_DEAD 0xffff //default ping value to denote servers that are not responding.
#define PING_MAX 0xfffe //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.
#define PING_MAX 0xfffd //highest 'valid' ping value.
//despite not supporting nq or q2, we still load them. We just filter them. This is to make sure we properly write the listing files.
enum mastertype_e
{
MT_BAD, //this would be an error
MT_MASTERHTTPJSON,
// MT_MASTERHTTPJSON,
MT_MASTERHTTP,
MT_MASTERUDP,
MT_BCAST,
@ -127,6 +128,7 @@ typedef struct serverinfo_s
{
char name[64]; //hostname.
netadr_t adr;
char brokerid[64]; //'rtc[s]://adr//brokerid'
short special; //flags
short protocol;
@ -191,6 +193,7 @@ extern struct selectedserver_s
{
qboolean inuse;
netadr_t adr;
char brokerid[64];
float refreshtime;
int lastplayer;
char lastrule[64];
@ -224,8 +227,7 @@ qboolean CL_QueryServers(void);
int Master_CheckPollSockets(void);
void MasterInfo_Shutdown(void);
void MasterInfo_WriteServers(void);
void MasterInfo_Request(master_t *mast);
serverinfo_t *Master_InfoForServer (netadr_t *addr);
serverinfo_t *Master_InfoForServer (netadr_t *addr, const char *brokerid);
serverinfo_t *Master_InfoForNum (int num);
unsigned int Master_TotalCount(void);
unsigned int Master_NumPolled(void); //progress indicator
@ -234,6 +236,7 @@ void Master_SetupSockets(void);
void MasterInfo_Refresh(qboolean doreset);
void Master_QueryServer(serverinfo_t *server);
void MasterInfo_WriteServers(void);
char *Master_ServerToString (char *s, int len, serverinfo_t *a); //like NET_AdrToString, but handles more complex addresses.
hostcachekey_t Master_KeyForName(const char *keyname);
float Master_ReadKeyFloat(serverinfo_t *server, hostcachekey_t keynum);

View file

@ -6238,7 +6238,7 @@ static void CL_ParsePrint(char *msg, int level)
}
else
strcat(printtext, msg); //safe due to size on if.
while((msg = strchr(printtext, '\n')))
while((msg = strchr(printtext, '\n')) || (msg = strchr(printtext, '\r')))
{
n = msg[1];
msg[1] = 0;
@ -6399,6 +6399,7 @@ static void CL_ParseTeamInfo(void)
static char stufftext[4096];
static void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds from network segregation.
{
int cbuflevel;
#ifdef NQPROT
if (!*stufftext && *msg == 1 && !cls.allow_csqc)
{
@ -6411,7 +6412,8 @@ static void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds
while((msg = strchr(stufftext, '\n')))
{
*msg = '\0';
Con_DLPrintf((cls.state==ca_active)?1:2, "stufftext: %s\n", stufftext);
cbuflevel = RESTRICT_SERVERSEAT(destsplit);
Con_DLPrintf((cls.state==ca_active)?1:2, "stufftext%i: %s\n", destsplit, stufftext);
if (!strncmp(stufftext, "fullserverinfo ", 15) || !strncmp(stufftext, "//fullserverinfo ", 17))
{
Cmd_TokenizeString(stufftext+2, false, false);
@ -6532,9 +6534,9 @@ static void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds
Q_strncatz(newdemo, Cmd_Argv(1), sizeof(newdemo));
}
Cbuf_AddText ("playdemo ", RESTRICT_SERVER+destsplit);
Cbuf_AddText (COM_QuotedString(newdemo, temp, sizeof(temp), false), RESTRICT_SERVER+destsplit);
Cbuf_AddText ("\n", RESTRICT_SERVER+destsplit);
Cbuf_AddText ("playdemo ", cbuflevel);
Cbuf_AddText (COM_QuotedString(newdemo, temp, sizeof(temp), false), cbuflevel);
Cbuf_AddText ("\n", cbuflevel);
}
#ifdef CSQC_DAT
else if (CSQC_StuffCmd(destsplit, stufftext, msg))
@ -6546,20 +6548,20 @@ static void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds
COM_Parse(stufftext + 11);
if (Cmd_Exists(com_token))
{
Cbuf_AddText ("cmd cmdsupported ", RESTRICT_SERVER+destsplit);
Cbuf_AddText (com_token, RESTRICT_SERVER+destsplit);
Cbuf_AddText ("\n", RESTRICT_SERVER+destsplit);
Cbuf_AddText ("cmd cmdsupported ", cbuflevel);
Cbuf_AddText (com_token, cbuflevel);
Cbuf_AddText ("\n", cbuflevel);
}
}
else if (!strncmp(stufftext, "//exectrigger ", 14)) //so that mods can add whatever 'alias grabbedarmour' or whatever triggers that users might want to script responses for, without errors about unknown commands
{
COM_Parse(stufftext + 14);
if (Cmd_AliasExist(com_token, RESTRICT_SERVER))
Cmd_ExecuteString(com_token, RESTRICT_SERVER); //do this NOW so that it's done before any models or anything are loaded
if (Cmd_AliasExist(com_token, cbuflevel))
Cmd_ExecuteString(com_token, cbuflevel); //do this NOW so that it's done before any models or anything are loaded
}
else if (!strncmp(stufftext, "//set ", 6)) //equivelent to regular set, except non-spammy if it doesn't exist, and happens instantly without extra latency.
{
Cmd_ExecuteString(stufftext+2, RESTRICT_SERVER+destsplit); //do this NOW so that it's done before any models or anything are loaded
Cmd_ExecuteString(stufftext+2, cbuflevel); //do this NOW so that it's done before any models or anything are loaded
}
else if (!strncmp(stufftext, "//at ", 5)) //ktx autotrack hints
{
@ -6635,9 +6637,9 @@ static void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds
else
{
if (!strncmp(stufftext, "cmd ", 4))
Cbuf_AddText (va("p%i ", destsplit+1), RESTRICT_SERVER+destsplit); //without this, in_forceseat can break directed cmds.
Cbuf_AddText (stufftext, RESTRICT_SERVER+destsplit);
Cbuf_AddText ("\n", RESTRICT_SERVER+destsplit);
Cbuf_AddText (va("p%i ", destsplit+1), cbuflevel); //without this, in_forceseat can break directed cmds.
Cbuf_AddText (stufftext, cbuflevel);
Cbuf_AddText ("\n", cbuflevel);
}
msg++;

View file

@ -646,6 +646,11 @@ static float QDECL Plug_GetTrackerOwnFrags(int seat, char *outptr, size_t outlen
else
return Stats_GetLastOwnFrag(seat, outptr, outlen);
}
static void QDECL Plug_GetPredInfo(int seat, vec3_t outvel)
{
if ((unsigned)seat < MAX_SPLITS)
VectorCopy(cl.playerview[seat].simvel, outvel);
}
#endif
static void QDECL Plug_GetLocationName(const float *locpoint, char *outbuffer, size_t bufferlen)

View file

@ -486,17 +486,8 @@ void CL_CalcCrouch (playerview_t *pv)
return;
}
/*fixme: this helps lifts with cl_nopred, but seems to harm ramps slightly, might just be my imagination. I guess we need to check last frame too.
//check if we moved in the x/y axis. if we didn't then we're on a vertically moving platform and shouldn't be crouching.
VectorMA(pv->oldorigin, pv->oldz-orgz, pv->gravitydir, pv->oldorigin);
VectorSubtract(pv->simorg, pv->oldorigin, delta);
if (Length(delta)<0.001)
pv->oldz = orgz;
*/
VectorCopy (pv->simorg, pv->oldorigin);
if (pv->onground && orgz - pv->oldz)
{
if (pv->oldz > orgz)

View file

@ -297,6 +297,7 @@ struct
{
unsigned int startms;
netadr_t adr;
char brokername[64];
} ui_pings[MAX_PINGREQUESTS];
#define UITAGNUM 2452
@ -879,11 +880,14 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
if (ui_pings[i].adr.type == NA_INVALID)
{
serverinfo_t *info;
const char *p = NULL;
COM_Parse(cmdtext + 5);
ui_pings[i].startms = Sys_Milliseconds();
if (NET_StringToAdr(com_token, 0, &ui_pings[i].adr))
if (NET_StringToAdr2(com_token, 0, &ui_pings[i].adr, 1, &p))
{
info = Master_InfoForServer(&ui_pings[i].adr);
if (p && *p=='/')
p++;
info = Master_InfoForServer(&ui_pings[i].adr, p);
if (info)
{
info->special |= SS_KEEPINFO;
@ -891,6 +895,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
Master_QueryServer(info);
}
}
Q_strncpyz(ui_pings[i].brokername, p?p:"", sizeof(ui_pings[i].brokername));
break;
}
}
@ -1159,13 +1164,16 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
char *buf = VM_POINTER(arg[1]);
size_t bufsize = VM_LONG(arg[2]);
int *ping = VM_POINTER(arg[3]);
serverinfo_t *info = Master_InfoForServer(&ui_pings[i].adr);
NET_AdrToString(buf, bufsize, &ui_pings[i].adr);
serverinfo_t *info = Master_InfoForServer(&ui_pings[i].adr, ui_pings[i].brokername);
if (info)
Master_ServerToString(buf, bufsize, info);
else
NET_AdrToString(buf, bufsize, &ui_pings[i].adr);
if (info && (info->status & SRVSTATUS_ALIVE) && info->moreinfo)
if (info && /*(info->status & SRVSTATUS_ALIVE) &&*/ info->moreinfo)
{
VM_LONG(ret) = true;
*ping = info->ping;
*ping = (info->ping == PING_UNKNOWN)?1:info->ping;
break;
}
i = Sys_Milliseconds()-ui_pings[i].startms;
@ -1188,8 +1196,8 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
char *buf = VM_POINTER(arg[1]);
size_t bufsize = VM_LONG(arg[2]);
char *adr;
serverinfo_t *info = Master_InfoForServer(&ui_pings[i].adr);
if (info && (info->status & SRVSTATUS_ALIVE) && info->moreinfo)
serverinfo_t *info = Master_InfoForServer(&ui_pings[i].adr, ui_pings[i].brokername);
if (info && /*(info->status & SRVSTATUS_ALIVE) &&*/ info->moreinfo)
{
adr = info->moreinfo->info;
if (!adr)
@ -1269,7 +1277,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
serverinfo_t *info = Master_InfoForNum(VM_LONG(arg[1]));
if (info)
{
adr = NET_AdrToString(adrbuf, sizeof(adrbuf), &info->adr);
adr = Master_ServerToString(adrbuf, sizeof(adrbuf), info);
if (strlen(adr) < VM_LONG(arg[3]))
{
strcpy(buf, adr);

View file

@ -358,10 +358,6 @@ typedef struct
int colourkey; //compacted version of the colours, for comparison against caches
} lightstyle_t;
#define MAX_EFRAGS 512
#define MAX_DEMOS 8
#define MAX_DEMONAME 16
@ -531,7 +527,7 @@ typedef struct
float latency; // rolling average
qboolean allow_anyparticles;
qboolean allow_skyboxes;
qboolean allow_skyboxes; //skyboxes/domes do not need to be depth-masked when set. FIXME: we treat this as an optimisation hint, but some hl/q2/q3 maps require strict do-not-mask rules to look right.
qboolean allow_watervis; //fixme: not checked any more
float allow_fbskins; //fraction of allowance
qboolean allow_cheats;
@ -865,6 +861,7 @@ typedef struct
float maxpitch;
qboolean paused; // send over by server
qboolean implicitpause; //for csqc. only a hint, respected only in singleplayer.
enum
{
@ -1528,7 +1525,6 @@ void Cam_Lock(playerview_t *pv, int playernum); //attempt to lock on to the give
void Cam_NowLocked(playerview_t *pv); //player was located, track them now
void Cam_SelfTrack(playerview_t *pv);
void Cam_Track(playerview_t *pv, usercmd_t *cmd);
void Cam_TrackCrosshairedPlayer(playerview_t *pv);
void Cam_SetModAutoTrack(int userid);
void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd);
void Cam_Reset(void);

View file

@ -2458,7 +2458,7 @@ void CLQ2_CalcViewValues (int seat)
for (i=0 ; i<3 ; i++)
{
pv->simorg[i] = pv->predicted_origin[i] + ops->viewoffset[i]
+ cl.lerpfrac * (ps->viewoffset[i] - ops->viewoffset[i])
+ lerp * (ps->viewoffset[i] - ops->viewoffset[i])
- backlerp * pv->prediction_error[i];
}

View file

@ -1887,8 +1887,8 @@ static int Con_DrawProgress(int left, int right, int y)
{
conchar_t dlbar[1024], *chr;
unsigned char progresspercenttext[128];
char *progresstext = NULL;
char *txt;
const char *progresstext = NULL;
const char *txt;
int x, tw;
int i;
int barwidth, barleft;
@ -1937,14 +1937,9 @@ static int Con_DrawProgress(int left, int right, int y)
}
}
#ifdef RUNTIMELIGHTING
else if (lightmodel)
else if ((progresstext=RelightGetProgress(&progresspercent)))
{
if (relitsurface < lightmodel->numsurfaces)
{
progresstext = "light";
progresspercent = (relitsurface*100.0f) / lightmodel->numsurfaces;
sprintf(progresspercenttext, " %02d%%", (int)progresspercent);
}
sprintf(progresspercenttext, " %02d%%", (int)progresspercent);
}
#endif

View file

@ -7320,10 +7320,12 @@ void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags)
switch(mips->encoding)
{
case TF_TRANS8:
case TF_H2_TRANS8_0:
case PTI_P8:
if (sh_config.can_mipcap)
return; //if we can cap mips, do that. it'll save lots of expensive lookups and uglyness.
for (mip = mips->mipcount; mip < 32; mip++)
for (mip = mips->mipcount; mip < countof(mips->mip); mip++)
{
mips->mip[mip].width = mips->mip[mip-1].width >> 1;
mips->mip[mip].height = mips->mip[mip-1].height >> 1;
@ -7346,7 +7348,7 @@ void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags)
case PTI_R8_SNORM:
case PTI_L8:
case PTI_L8_SRGB:
for (mip = mips->mipcount; mip < 32; mip++)
for (mip = mips->mipcount; mip < countof(mips->mip); mip++)
{
mips->mip[mip].width = mips->mip[mip-1].width >> 1;
mips->mip[mip].height = mips->mip[mip-1].height >> 1;
@ -7369,7 +7371,7 @@ void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags)
case PTI_RG8_SNORM:
case PTI_L8A8:
case PTI_L8A8_SRGB:
for (mip = mips->mipcount; mip < 32; mip++)
for (mip = mips->mipcount; mip < countof(mips->mip); mip++)
{
mips->mip[mip].width = mips->mip[mip-1].width >> 1;
mips->mip[mip].height = mips->mip[mip-1].height >> 1;
@ -7389,7 +7391,7 @@ void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags)
}
return;
case PTI_RGBA32F:
for (mip = mips->mipcount; mip < 32; mip++)
for (mip = mips->mipcount; mip < countof(mips->mip); mip++)
{
mips->mip[mip].width = mips->mip[mip-1].width >> 1;
mips->mip[mip].height = mips->mip[mip-1].height >> 1;
@ -7409,7 +7411,7 @@ void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags)
}
break;
case PTI_RGBA16F:
for (mip = mips->mipcount; mip < 32; mip++)
for (mip = mips->mipcount; mip < countof(mips->mip); mip++)
{
mips->mip[mip].width = mips->mip[mip-1].width >> 1;
mips->mip[mip].height = mips->mip[mip-1].height >> 1;
@ -7429,7 +7431,7 @@ void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags)
}
break;
case PTI_RGBA16:
for (mip = mips->mipcount; mip < 32; mip++)
for (mip = mips->mipcount; mip < countof(mips->mip); mip++)
{
mips->mip[mip].width = mips->mip[mip-1].width >> 1;
mips->mip[mip].height = mips->mip[mip-1].height >> 1;
@ -7452,7 +7454,7 @@ void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags)
case PTI_BGR8_SRGB:
case PTI_RGB8:
case PTI_BGR8:
for (mip = mips->mipcount; mip < 32; mip++)
for (mip = mips->mipcount; mip < countof(mips->mip); mip++)
{
mips->mip[mip].width = mips->mip[mip-1].width >> 1;
mips->mip[mip].height = mips->mip[mip-1].height >> 1;
@ -7479,7 +7481,7 @@ void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags)
case PTI_RGBX8:
case PTI_BGRA8:
case PTI_BGRX8:
for (mip = mips->mipcount; mip < 32; mip++)
for (mip = mips->mipcount; mip < countof(mips->mip); mip++)
{
mips->mip[mip].width = mips->mip[mip-1].width >> 1;
mips->mip[mip].height = mips->mip[mip-1].height >> 1;
@ -7963,15 +7965,16 @@ static void Image_Tr_NoTransform(struct pendingtextureinfo *mips, int dummy)
{
}
static void Image_Tr_PalettedtoRGBX8(struct pendingtextureinfo *mips, int dummy)
static void Image_Tr_PalettedtoRGBX8(struct pendingtextureinfo *mips, int alphapix)
{
unsigned int mip;
for (mip = 0; mip < mips->mipcount; mip++)
{
qbyte *in = mips->mip[mip].data;
unsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;
qbyte *out = BZ_Malloc(sizeof(*out)*4*p);
void *newdata = out;
qbyte *out;
size_t datasize = sizeof(*out)*4*p;
void *newdata = out = BZ_Malloc(datasize);
while(p-->0)
{
@ -7979,13 +7982,13 @@ static void Image_Tr_PalettedtoRGBX8(struct pendingtextureinfo *mips, int dummy)
*out++ = host_basepal[l*3+0];
*out++ = host_basepal[l*3+1];
*out++ = host_basepal[l*3+2];
*out++ = (l==255)?255:0;
*out++ = (l==alphapix)?0:255;
}
if (mips->mip[mip].needfree)
BZ_Free(mips->mip[mip].data);
mips->mip[mip].needfree = true;
mips->mip[mip].data = newdata;
mips->mip[mip].datasize = sizeof(*out)*4*p;
mips->mip[mip].datasize = datasize;
}
}
@ -10711,7 +10714,24 @@ const char *Image_FormatName(uploadfmt_t fmt)
#ifdef FTE_TARGET_WEB
case PTI_WHOLEFILE: return "Whole File";
#endif
case PTI_EMULATED:
case TF_INVALID: return "INVALID";
case TF_BGR24_FLIP: return "BGR24_FLIP";
case TF_MIP4_P8: return "MIP4_P8";
case TF_MIP4_SOLID8: return "MIP4_SOLID8";
case TF_MIP4_8PAL24: return "MIP4_8PAL24";
case TF_MIP4_8PAL24_T255: return "MIP4_8PAL24_T255";
case TF_SOLID8: return "SOLID8";
case TF_TRANS8: return "TRANS8_255";
case TF_TRANS8_FULLBRIGHT: return "TRANS8_FULLBRIGHT";
case TF_HEIGHT8: return "HEIGHT8";
case TF_HEIGHT8PAL: return "HEIGHT8PAL";
case TF_H2_T7G1: return "H2_T7G1";
case TF_H2_TRANS8_0: return "TRANS8_0";
case TF_H2_T4A4: return "H2_T4A4";
case TF_8PAL24: return "8PAL24";
case TF_8PAL32: return "8PAL32";
case PTI_LLLX8: return "LLLX8";
case PTI_LLLA8: return "LLLA8";
case PTI_MAX:
break;
}
@ -11223,7 +11243,9 @@ static struct
{PTI_RG8, PTI_RGBX8, Image_Tr_RG8ToRGXX8},
{PTI_RGBX8, PTI_P8, Image_Tr_RGBX8toPaletted},
{PTI_P8, PTI_RGBX8, Image_Tr_PalettedtoRGBX8},
{PTI_P8, PTI_RGBX8, Image_Tr_PalettedtoRGBX8, -1},
{TF_H2_TRANS8_0,PTI_RGBA8, Image_Tr_PalettedtoRGBX8, 0},
{TF_TRANS8, PTI_RGBA8, Image_Tr_PalettedtoRGBX8, 255},
};
void Image_ChangeFormat(struct pendingtextureinfo *mips, qboolean *allowedformats, uploadfmt_t origfmt, const char *imagename)
{

View file

@ -1209,7 +1209,7 @@ void Key_ConsoleRelease(console_t *con, int key, unsigned int unicode)
if ((key == K_MOUSE1 && con->buttonsdown == CB_SCROLL) || (key == K_MOUSE2 && con->buttonsdown == CB_SCROLL_R))
{
con->buttonsdown = CB_NONE;
if (abs(con->mousedown[0] - con->mousecursor[0]) < 5 && abs(con->mousedown[1] - con->mousecursor[1]) < 5)
if (fabs(con->mousedown[0] - con->mousecursor[0]) < 5 && fabs(con->mousedown[1] - con->mousecursor[1]) < 5)
{
buffer = Con_CopyConsole(con, false, false, false);
Con_Footerf(con, false, "");
@ -2652,7 +2652,7 @@ void Key_Bind_f (void)
if (bindcmdlevel[b][modifier] != level)
{
if (Cmd_ExecLevel > RESTRICT_SERVER)
Q_snprintfz(leveldesc, sizeof(leveldesc), ", for seat %i", Cmd_ExecLevel - RESTRICT_SERVER-1);
Q_snprintfz(leveldesc, sizeof(leveldesc), ", for seat %i", Cmd_ExecLevel - RESTRICT_SERVER);
else if (Cmd_ExecLevel == RESTRICT_SERVER)
Q_snprintfz(leveldesc, sizeof(leveldesc), ", bound by server");
else if (bindcmdlevel[b][modifier]>=RESTRICT_INSECURE)

View file

@ -281,7 +281,7 @@ static void SL_ServerDraw (int x, int y, menucustom_t *ths, emenu_t *menu)
}
else if (thisone == info->scrollpos + (int)(mousecursor_y-info->servers_top)/8 && mousecursor_x < x)
R2D_ImageColours(SRGBA((sin(realtime*4.4)*0.25)+0.5, (sin(realtime*4.4)*0.25)+0.5, 0.08, sb_alpha.value));
else if (selectedserver.inuse && NET_CompareAdr(&si->adr, &selectedserver.adr))
else if (selectedserver.inuse && NET_CompareAdr(&si->adr, &selectedserver.adr) && !strcmp(si->brokerid, selectedserver.brokerid))
R2D_ImageColours(SRGBA(((sin(realtime*4.4)*0.25)+0.5) * 0.5, ((sin(realtime*4.4)*0.25)+0.5)*0.5, 0.08*0.5, sb_alpha.value));
else
{
@ -299,8 +299,8 @@ static void SL_ServerDraw (int x, int y, menucustom_t *ths, emenu_t *menu)
if (sb_showplayers.value) {Draw_FunStringWidth((x-5*8), y, va("%2i/%2i", si->numhumans, si->maxplayers), 5*8, false, false); x-=6*8;}
if (sb_showmap.value) {Draw_FunStringWidth((x-8*8), y, si->map, 8*8, false, false); x-=9*8;}
if (sb_showgamedir.value) {Draw_FunStringWidth((x-8*8), y, si->gamedir, 8*8, false, false); x-=9*8;}
if (sb_showping.value) {Draw_FunStringWidth((x-3*8), y, va("%i", si->ping), 3*8, false, false); x-=4*8;}
if (sb_showaddress.value) {Draw_FunStringWidth((x-21*8), y, NET_AdrToString(adr, sizeof(adr), &si->adr), 21*8, false, false); x-=22*8;}
if (sb_showping.value) {Draw_FunStringWidth((x-3*8), y, *si->brokerid?"---":va("%i", si->ping), 3*8, false, false); x-=4*8;}
if (sb_showaddress.value) {Draw_FunStringWidth((x-21*8), y, Master_ServerToString(adr, sizeof(adr), si), 21*8, false, false); x-=22*8;}
Draw_FunStringWidth(0, y, si->name, x, false, false);
}
}
@ -431,7 +431,7 @@ static void SL_PostDraw (emenu_t *menu)
if (serverpreview)
{
serverinfo_t *server = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr):NULL;
serverinfo_t *server = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL;
int h = 0;
int w = 240;
if (server && selectedserver.refreshtime < realtime)
@ -520,7 +520,7 @@ static void SL_PostDraw (emenu_t *menu)
y += 8;
Draw_FunStringWidth (x, y, Info_ValueForKey(server->moreinfo->info, "status"), w, 2, false);
y += 8;
Draw_FunStringWidth (x, y, NET_AdrToString(buf, sizeof(buf), &server->adr), w, 2, false);
Draw_FunStringWidth (x, y, Master_ServerToString(buf, sizeof(buf), server), w, 2, false);
y += 8;
Draw_FunStringWidth (x, y, "^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f", w, 2, false);
@ -532,7 +532,7 @@ static void SL_PostDraw (emenu_t *menu)
for (prox = server; prox; prox = prox->prevpeer)
{
Draw_FunStringWidth (x, y, va("%i", prox->cost), 32-8, true, false);
Draw_FunStringWidth (x + 32, y, NET_AdrToString(buf, sizeof(buf), &prox->adr), w/2 - 8 - 32, true, false);
Draw_FunStringWidth (x + 32, y, Master_ServerToString(buf, sizeof(buf), prox), w/2 - 8 - 32, true, false);
Draw_FunStringWidth (x + w/2, y, prox->name, w/2, false, false);
y += 8;
}
@ -641,7 +641,7 @@ static void SL_PostDraw (emenu_t *menu)
Draw_FunStringWidth(vid.width/2 - 100, vid.height/2 + 0, "Please wait", 200, 2, false);
}
if ((server->special & SS_PROTOCOLMASK) == SS_QUAKEWORLD)
if (server && (server->special & SS_PROTOCOLMASK) == SS_QUAKEWORLD)
{
int lx = vid.width/2 - w/2;
int y = vid.height/2 - h/2 - 4 + h;
@ -732,7 +732,7 @@ static qboolean SL_Key (int key, emenu_t *menu)
if (serverpreview)
{
char buf[64];
serverinfo_t *server = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr):NULL;
serverinfo_t *server = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL;
extern qboolean keydown[];
qboolean ctrldown = keydown[K_LCTRL] || keydown[K_RCTRL];
@ -772,7 +772,7 @@ static qboolean SL_Key (int key, emenu_t *menu)
if (--serverpreview < 1)
serverpreview = 4;
if (serverpreview == 4)
if (serverpreview == 4 && server)
Master_FindRoute(server->adr);
return true;
}
@ -781,13 +781,14 @@ static qboolean SL_Key (int key, emenu_t *menu)
if (++serverpreview > 4)
serverpreview = 1;
if (serverpreview == 4)
if (serverpreview == 4 && server)
Master_FindRoute(server->adr);
return true;
}
else if (key == 'b' && serverpreview != 4)
{
Master_FindRoute(server->adr);
if (server)
Master_FindRoute(server->adr);
serverpreview = 4;
}
else if (key == 'b' || key == 'o' || key == 'j' || key == K_ENTER || key == K_KP_ENTER || key == K_GP_START) //join
@ -810,13 +811,13 @@ dojoin:
Cbuf_AddText("connect ", RESTRICT_LOCAL);
//output the server's address
Cbuf_AddText(va("%s", NET_AdrToString(buf, sizeof(buf), &server->adr)), RESTRICT_LOCAL);
Cbuf_AddText(va("%s", Master_ServerToString(buf, sizeof(buf), server)), RESTRICT_LOCAL);
if (serverpreview == 4 || key == 'b')
{ //and postfix it with routing info if we're going for a proxied route.
if (serverpreview != 4)
Master_FindRoute(server->adr);
for (server = server->prevpeer; server; server = server->prevpeer)
Cbuf_AddText(va("@%s", NET_AdrToString(buf, sizeof(buf), &server->adr)), RESTRICT_LOCAL);
Cbuf_AddText(va("@%s", Master_ServerToString(buf, sizeof(buf), server)), RESTRICT_LOCAL);
}
Cbuf_AddText("\n", RESTRICT_LOCAL);
@ -826,7 +827,7 @@ dojoin:
}
else if (server && key == 'c' && ctrldown) //copy to clip
{
Sys_SaveClipboard(CBT_CLIPBOARD, NET_AdrToString(buf, sizeof(buf), &server->adr));
Sys_SaveClipboard(CBT_CLIPBOARD, Master_ServerToString(buf, sizeof(buf), server));
return true;
}
else if (server && (key == 'v' || key == 'c')) //say to current server
@ -840,11 +841,11 @@ dojoin:
while((s = strchr(safename, '\n')))
*s = ' ';
if (key == 'c')
Sys_SaveClipboard(CBT_CLIPBOARD, va("%s - %s\n", server->name, NET_AdrToString(buf, sizeof(buf), &server->adr)));
Sys_SaveClipboard(CBT_CLIPBOARD, va("%s - %s\n", server->name, Master_ServerToString(buf, sizeof(buf), server)));
else if (ctrldown)
Cbuf_AddText(va("say_team %s - %s\n", server->name, NET_AdrToString(buf, sizeof(buf), &server->adr)), RESTRICT_LOCAL);
Cbuf_AddText(va("say_team %s - %s\n", server->name, Master_ServerToString(buf, sizeof(buf), server)), RESTRICT_LOCAL);
else
Cbuf_AddText(va("say %s - %s\n", server->name, NET_AdrToString(buf, sizeof(buf), &server->adr)), RESTRICT_LOCAL);
Cbuf_AddText(va("say %s - %s\n", server->name, Master_ServerToString(buf, sizeof(buf), server)), RESTRICT_LOCAL);
return true;
}
//eat (nearly) all keys
@ -1303,9 +1304,9 @@ static void M_QuickConnect_PreDraw(emenu_t *menu)
Con_Printf("Quick connect found %s (gamedir %s, players %i/%i/%i, ping %ims)\n", best->name, best->gamedir, best->numhumans, best->players, best->maxplayers, best->ping);
if ((best->special & SS_PROTOCOLMASK) == SS_NETQUAKE)
Cbuf_AddText(va("nqconnect %s\n", NET_AdrToString(adr, sizeof(adr), &best->adr)), RESTRICT_LOCAL);
Cbuf_AddText(va("nqconnect %s\n", Master_ServerToString(adr, sizeof(adr), best)), RESTRICT_LOCAL);
else
Cbuf_AddText(va("join %s\n", NET_AdrToString(adr, sizeof(adr), &best->adr)), RESTRICT_LOCAL);
Cbuf_AddText(va("join %s\n", Master_ServerToString(adr, sizeof(adr), best)), RESTRICT_LOCAL);
M_ToggleMenu_f();
return;

View file

@ -1854,7 +1854,7 @@ static cin_t *Media_WinAvi_TryLoad(char *name)
if(qAVIStreamReadFormat(cin->avi.pavisound, qAVIStreamStart(cin->avi.pavisound), pChunk, &lSize))
{
// error
Con_Printf("Failiure reading sound info\n");
Con_Printf("Failure reading sound info\n");
}
cin->avi.pWaveFormat = (LPWAVEFORMATEX)pChunk;
}

View file

@ -533,6 +533,7 @@ void M_Menu_GameOptions_f (void)
typedef struct {
menuedit_t *hostnameedit;
menucombo_t *publicgame;
menucombo_t *deathmatch;
menucombo_t *numplayers;
menucombo_t *teamplay;
@ -582,6 +583,7 @@ qboolean MultiBeginGame (union menuoption_s *option,struct emenu_s *menu, int ke
Cbuf_AddText(va("skill %i\n", info->skill->selectedoption), RESTRICT_LOCAL);
Cbuf_AddText(va("timelimit %i\n", info->timelimit->selectedoption*5), RESTRICT_LOCAL);
Cbuf_AddText(va("fraglimit %i\n", info->fraglimit->selectedoption*10), RESTRICT_LOCAL);
Cbuf_AddText(va("sv_public %i\n", info->publicgame->selectedoption-1), RESTRICT_LOCAL);
Cbuf_AddText(va("map \"%s\"\n", info->mapnameedit->text), RESTRICT_LOCAL);
if (info->rundedicated->value)
@ -647,6 +649,14 @@ void M_Menu_GameOptions_f (void)
"100 frags",
NULL
};
static const char *publicoptions[] = {
"Disabled",
"Private/LAN",
"Public (Manual)",
"Public (Holepunch)",
NULL
};
extern cvar_t sv_public;
newmultimenu_t *info;
emenu_t *menu;
int y = 40;
@ -677,7 +687,10 @@ void M_Menu_GameOptions_f (void)
menu->selecteditem = (menuoption_t*)
MC_AddCommand (menu, 64, 160, y, "Start game", MultiBeginGame);y+=16;
info->hostnameedit = MC_AddEdit (menu, 64, 160, y, "Hostname", name.string);y+=16;
y+=4;
info->hostnameedit = MC_AddEdit (menu, 64, 160, y, "Hostname", name.string);y+=16;
info->publicgame = MC_AddCombo (menu, 64, 160, y, "Public", publicoptions, bound(0, sv_public.ival+1, 4));y+=8;
y+=4;
for (players = 0; players < sizeof(numplayeroptions)/ sizeof(numplayeroptions[0]); players++)
{

View file

@ -911,6 +911,7 @@ const char *presetexec[] =
"seta cl_nolerp 1;"
"seta r_lerpmuzzlehack 0;"
"seta v_gunkick 0;"
"seta r_shadows 0;"
"seta v_viewmodel_quake 0;"
"seta cl_rollangle 0;"
"seta cl_bob 0;"

View file

@ -188,9 +188,6 @@ extern void Mod_TouchModel (const char *name);
extern const char *Mod_FixName (const char *modname, const char *worldname); //remaps the name appropriately
const char *Mod_ParseWorldspawnKey (struct model_s *mod, const char *key, char *buffer, size_t sizeofbuffer);
extern long relitsurface;
extern struct model_s *lightmodel;
extern void Mod_Think (void);
extern qboolean Mod_GetModelEvent (struct model_s *model, int animation, int eventidx, float *timestamp, int *eventcode, char **eventdata);
extern int Mod_SkinNumForName (struct model_s *model, int surfaceidx, const char *name);
extern int Mod_FrameNumForName (struct model_s *model, int surfaceidx, const char *name);
@ -295,21 +292,21 @@ struct pendingtextureinfo
{
enum
{
//formats are all w*h*(d||l)
PTI_2D, //w*h*1
PTI_3D, //w*h*d - only format which actually changes mip depths
PTI_CUBE, //w*h*6
PTI_2D_ARRAY, //w*h*layers
PTI_CUBE_ARRAY, //w*h*(layers*6)
//formats are all w*h*d (where depth has limitations according to type)
PTI_2D, //w*h*1 - depth MUST be 1
PTI_3D, //w*h*d - we can't generate 3d mips
PTI_CUBE, //w*h*6 - depth MUST be 6 (faces must be tightly packed)
PTI_2D_ARRAY, //w*h*layers - depth is =layers
PTI_CUBE_ARRAY, //w*h*(layers*6) - depth is =(layers*6).
} type;
uploadfmt_t encoding; //0
void *extrafree;
uploadfmt_t encoding; //PTI_* formats
void *extrafree; //avoids some memcpys
int mipcount;
struct
{
void *data;
size_t datasize;
size_t datasize; //ceil(width/blockwidth)*ceil(height/blockheight)*ceil(depth/blockdepth)*blocksize - except that blockdepth is always considered 1 for now.
int width;
int height;
int depth;

View file

@ -102,6 +102,10 @@ typedef struct {
netadr_t adr[MAX_MASTER_ADDRESSES];
} net_masterlist_t;
static net_masterlist_t net_masterlist[] = {
#if 0 //for debugging
{MP_DPMASTER, CVARFC("net_masterextra1", "localhost:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: Eukara
#else
#ifndef QUAKETC
//user-specified master lists.
{MP_QUAKEWORLD, CVARC("net_qwmaster1", "", Net_Masterlist_Callback)},
@ -115,7 +119,7 @@ static net_masterlist_t net_masterlist[] = {
#endif
//dpmaster is the generic non-quake-specific master protocol that we use for custom stand-alone mods.
{MP_DPMASTER, CVARAFC("net_master1", "", "sv_master1", 0, Net_Masterlist_Callback)},
{MP_DPMASTER, CVARAFC("net_master1", "localhost", "sv_master1", 0, Net_Masterlist_Callback)},
{MP_DPMASTER, CVARAFC("net_master2", "", "sv_master2", 0, Net_Masterlist_Callback)},
{MP_DPMASTER, CVARAFC("net_master3", "", "sv_master3", 0, Net_Masterlist_Callback)},
{MP_DPMASTER, CVARAFC("net_master4", "", "sv_master4", 0, Net_Masterlist_Callback)},
@ -163,9 +167,10 @@ static net_masterlist_t net_masterlist[] = {
// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "master.teamdamage.com:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "master.teamdamage.com"},
//Total conversions will need to define their own in defaults.cfg or whatever.
{MP_DPMASTER, CVARFC("net_masterextra1", "ghdigital.com:27950 207.55.114.154:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //207.55.114.154 (was 69.59.212.88 (admin: LordHavoc)
{MP_DPMASTER, CVARFC("net_masterextra2", "dpmaster.deathmask.net:27950 107.161.23.68:27950 [2604:180::4ac:98c1]:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //107.161.23.68 (admin: Willis)
{MP_DPMASTER, CVARFC("net_masterextra3", "dpmaster.tchr.no:27950 92.62.40.73:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //92.62.40.73 (admin: tChr)
{MP_DPMASTER, CVARFC("net_masterextra1", "frag-net.com:27950 198.58.111.37:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: Eukara
// {MP_DPMASTER, CVARFC("net_masterextra1", ""/*"ghdigital.com:27950 207.55.114.154:27950"*/, CVAR_NOSAVE, Net_Masterlist_Callback)}, //(was 69.59.212.88) admin: LordHavoc
{MP_DPMASTER, CVARFC("net_masterextra2", "dpmaster.deathmask.net:27950 107.161.23.68:27950 [2604:180::4ac:98c1]:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: Willis
{MP_DPMASTER, CVARFC("net_masterextra3", "dpmaster.tchr.no:27950 92.62.40.73:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: tChr
#else
{MP_DPMASTER, CVARFC("net_masterextra1", "", CVAR_NOSAVE, Net_Masterlist_Callback)},
{MP_DPMASTER, CVARFC("net_masterextra2", "", CVAR_NOSAVE, Net_Masterlist_Callback)},
@ -195,6 +200,7 @@ static net_masterlist_t net_masterlist[] = {
{MP_QUAKE3, CVARFC("net_q3masterextra6", "dpmaster.deathmask.net:27950", CVAR_NOSAVE, Net_Masterlist_Callback), "US: DeathMask.net"},
{MP_QUAKE3, CVARFC("net_q3masterextra8", "master3.idsoftware.com:27950", CVAR_NOSAVE, Net_Masterlist_Callback), "US: id Software Quake III Master"},
#endif
#endif
#endif
{MP_UNSPECIFIED, CVAR(NULL, NULL)}
@ -477,7 +483,7 @@ void SV_Master_Worker_Resolve(void *ctx, void *data, size_t a, size_t b)
{
str = COM_ParseOut(str, token, sizeof(token));
if (*token)
found += NET_StringToAdr2(token, 0, &work->na[found], MAX_MASTER_ADDRESSES-found);
found += NET_StringToAdr2(token, 0, &work->na[found], MAX_MASTER_ADDRESSES-found, NULL);
if (first && found)
break; //if we found one by name, don't try any fallback ip addresses.
first = false;
@ -501,6 +507,10 @@ void SV_Master_Heartbeat (void)
if (sv_public.ival<=0 || SSV_IsSubServer())
return;
#ifdef SUPPORT_ICE
if (sv_public.ival == 2)
return; //using our broker service. we're configured as behind a nat so these addresses won't work anyway.
#endif
if (realtime-interval - svs.last_heartbeat < interval)
return; // not time to send yet
@ -776,7 +786,7 @@ void Master_SetupSockets(void)
pollsocketsList[i] = INVALID_SOCKET;
}
void Master_HideServer(serverinfo_t *server)
static void Master_HideServer(serverinfo_t *server)
{
int i, j;
for (i = 0; i < numvisibleservers;)
@ -793,7 +803,7 @@ void Master_HideServer(serverinfo_t *server)
server->status &= ~SRVSTATUS_DISPLAYED;
}
void Master_InsertAt(serverinfo_t *server, int pos)
static void Master_InsertAt(serverinfo_t *server, int pos)
{
int i;
if (numvisibleservers >= maxvisibleservers)
@ -811,7 +821,7 @@ void Master_InsertAt(serverinfo_t *server, int pos)
server->status |= SRVSTATUS_DISPLAYED;
}
qboolean Master_CompareInteger(int a, int b, slist_test_t rule)
static qboolean Master_CompareInteger(int a, int b, slist_test_t rule)
{
switch(rule)
{
@ -836,7 +846,7 @@ qboolean Master_CompareInteger(int a, int b, slist_test_t rule)
}
return false;
}
qboolean Master_CompareString(const char *a, const char *b, slist_test_t rule)
static qboolean Master_CompareString(const char *a, const char *b, slist_test_t rule)
{
switch(rule)
{
@ -864,7 +874,22 @@ qboolean Master_CompareString(const char *a, const char *b, slist_test_t rule)
return false;
}
qboolean Master_ServerIsGreater(serverinfo_t *a, serverinfo_t *b)
char *Master_ServerToString (char *s, int len, serverinfo_t *a)
{
if (*a->brokerid)
{
if (a->adr.type==NA_INVALID)
*s = 0; //default broker... skip it for brevity.
else
NET_AdrToString(s, len, &a->adr);
Q_strncatz(s, "/", len);
Q_strncatz(s, a->brokerid, len);
return s;
}
return NET_AdrToString(s, len, &a->adr);
}
static qboolean Master_ServerIsGreater(serverinfo_t *a, serverinfo_t *b)
{
if (sort_categories)
if (a->qccategory != b->qccategory)
@ -1287,7 +1312,7 @@ char *Master_ReadKeyString(serverinfo_t *server, hostcachekey_t keynum)
case SLKEY_NAME:
return server->name;
case SLKEY_ADDRESS:
return NET_AdrToString(adr, sizeof(adr), &server->adr);
return Master_ServerToString(adr, sizeof(adr), server);
case SLKEY_GAMEDIR:
return server->gamedir;
@ -1397,7 +1422,7 @@ serverinfo_t *Master_FindRoute(netadr_t target)
{
serverinfo_t *info, *targ, *prox;
extern cvar_t cl_proxyaddr;
targ = Master_InfoForServer(&target);
targ = Master_InfoForServer(&target, NULL);
if (!targ) //you wot?
return NULL;
@ -1412,7 +1437,7 @@ serverinfo_t *Master_FindRoute(netadr_t target)
*chain = 0;
if (NET_StringToAdr(cl_proxyaddr.string, 0, &pa))
prox = Master_InfoForServer(&pa);
prox = Master_InfoForServer(&pa, NULL);
else
prox = NULL;
if (chain)
@ -1479,9 +1504,9 @@ int Master_FindBestRoute(char *server, char *out, size_t outsize, int *directcos
*directcost = route->ping;
*chainedcost = route->cost;
Q_strncatz(out, NET_AdrToString(buf, sizeof(buf), &route->adr), outsize);
Q_strncatz(out, Master_ServerToString(buf, sizeof(buf), route), outsize);
for (ret = 0, route = route->prevpeer; route; route = route->prevpeer, ret++)
Q_strncatz(out, va("@%s", NET_AdrToString(buf, sizeof(buf), &route->adr)), outsize);
Q_strncatz(out, va("@%s", Master_ServerToString(buf, sizeof(buf), route)), outsize);
return ret;
}
@ -1581,7 +1606,7 @@ void CLMaster_AddMaster_Worker_Resolve(void *ctx, void *data, size_t a, size_t b
{
str = COM_ParseOut(str, token, sizeof(token));
if (*token)
found += NET_StringToAdr2(token, 0, &adrs[found], MAX_MASTER_ADDRESSES-found);
found += NET_StringToAdr2(token, 0, &adrs[found], MAX_MASTER_ADDRESSES-found, NULL);
//we don't do this logic because windows doesn't look up ipv6 names if it only has teredo
//this means an ipv4+teredo client cannot see ivp6-only servers. and that sucks.
// if (first && found)
@ -1787,8 +1812,6 @@ qboolean Master_LoadMasterList (char *filename, qboolean withcomment, int defaul
servertype = MT_MASTERUDP;
else if (!strcmp(sep, "masterhttp"))
servertype = MT_MASTERHTTP;
else if (!strcmp(sep, "masterhttpjson"))
servertype = MT_MASTERHTTPJSON;
else if (!strcmp(sep, "bcast"))
servertype = MT_BCAST;
@ -1813,11 +1836,6 @@ qboolean Master_LoadMasterList (char *filename, qboolean withcomment, int defaul
//legacy compat
#ifdef NQPROT
else if (!strcmp(com_token, "httpjson"))
{
servertype = MT_MASTERHTTPJSON;
protocoltype = MP_NETQUAKE;
}
else if (!strcmp(com_token, "httpnq"))
{
servertype = MT_MASTERHTTP;
@ -1861,7 +1879,6 @@ qboolean Master_LoadMasterList (char *filename, qboolean withcomment, int defaul
switch (servertype)
{
case MT_MASTERHTTPJSON:
case MT_MASTERHTTP:
Master_AddMasterHTTP(entry, servertype, protocoltype, name);
break;
@ -2150,9 +2167,9 @@ int Master_CheckPollSockets(void)
if (ccrep == CCREP_PLAYER_INFO)
{
serverinfo_t *selserver = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr):NULL;
serverinfo_t *info = Master_InfoForServer(&net_from);
info = Master_InfoForServer(&net_from);
serverinfo_t *selserver = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL;
serverinfo_t *info = Master_InfoForServer(&net_from, NULL);
info = Master_InfoForServer(&net_from, NULL);
if (selserver == info)
{
char playeraddrbuf[256];
@ -2195,10 +2212,10 @@ int Master_CheckPollSockets(void)
}
else if (ccrep == CCREP_RULE_INFO)
{
serverinfo_t *selserver = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr):NULL;
serverinfo_t *info = Master_InfoForServer(&net_from);
serverinfo_t *selserver = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL;
serverinfo_t *info = Master_InfoForServer(&net_from, NULL);
char *s, *old;
info = Master_InfoForServer(&net_from);
info = Master_InfoForServer(&net_from, NULL);
if (selserver == info)
{
s = MSG_ReadString();
@ -2267,9 +2284,9 @@ void SListOptionChanged(serverinfo_t *newserver)
{
for (oldserver = firstserver; oldserver; oldserver=oldserver->next)
{
if (NET_CompareAdr(&selectedserver.adr, &oldserver->adr))//*(int*)selectedserver.ipaddress == *(int*)server->ipaddress && selectedserver.port == server->port)
if (NET_CompareAdr(&selectedserver.adr, &oldserver->adr) && !strcmp(selectedserver.brokerid, oldserver->brokerid))
{
if (oldserver->moreinfo)
if (oldserver->moreinfo && oldserver->ping!=PING_UNKNOWN)
{
Z_Free(oldserver->moreinfo);
oldserver->moreinfo = NULL;
@ -2283,6 +2300,7 @@ void SListOptionChanged(serverinfo_t *newserver)
return;
selectedserver.adr = newserver->adr;
strcpy(selectedserver.brokerid, newserver->brokerid);
if (newserver->moreinfo) //we cached it.
{
@ -2327,22 +2345,78 @@ void SListOptionChanged(serverinfo_t *newserver)
}
#ifdef WEBCLIENT
void MasterInfo_ProcessHTTP(struct dl_download *dl)
static void MasterInfo_ProcessHTTPInfo(serverinfo_t *srv, const char *info)
{
char adrbuf[MAX_ADR_SIZE];
if (info && (!(srv->status & SRVSTATUS_ALIVE) || srv->ping == PING_UNKNOWN))
{
if (srv->adr.prot == NP_RTC_TCP || srv->adr.prot == NP_RTC_TCP)
{
srv->sends = 0; //no point pinging it, it won't work.
srv->ping = PING_UNKNOWN;
srv->status |= SRVSTATUS_ALIVE; //or at least wouldn't have been reported this time around.
}
else
srv->sends = 1; //no point pinging it, it won't work.
Q_strncpyz(srv->name, Info_ValueForKey(info, "hostname"), sizeof(srv->name));
Q_strncpyz(srv->gamedir, Info_ValueForKey(info, "modname"), sizeof(srv->gamedir));
Q_strncpyz(srv->map, Info_ValueForKey(info, "mapname"), sizeof(srv->map));
srv->players = atoi(Info_ValueForKey(info, "clients"));
srv->maxplayers = atoi(Info_ValueForKey(info, "maxclients"));
srv->numbots = 0;
srv->numhumans = srv->players - srv->numbots;
srv->freeslots = srv->maxplayers - srv->players;
if (!srv->moreinfo)// && ((slist_cacheinfo.value == 2 || (NET_CompareAdr(&srv->adr, &selectedserver.adr)&&!strcmp(srv->brokerid,selectedserver.brokerid))) || (srv->special & SS_KEEPINFO)))
srv->moreinfo = Z_Malloc(sizeof(serverdetailedinfo_t));
if (srv->moreinfo)
Q_strncpyz(srv->moreinfo->info, info, sizeof(srv->moreinfo->info));
}
if (!*srv->name)
Q_snprintfz(srv->name, sizeof(srv->name), "%s h", Master_ServerToString(adrbuf, sizeof(adrbuf), srv));
}
static void MasterInfo_ProcessHTTP(struct dl_download *dl)
{
master_t *mast = dl->user_ctx;
vfsfile_t *file = dl->file;
int protocoltype = mast->protocoltype;
int protocoltype;
netadr_t adr;
char *s;
char *el;
serverinfo_t *info;
char adrbuf[MAX_ADR_SIZE];
char linebuffer[2048];
mast->dl = NULL;
char *brokerid;
char *infostring;
netadr_t brokeradr;
if (mast)
{
brokeradr = mast->adr;
mast->dl = NULL;
protocoltype = mast->protocoltype;
}
else
{
NET_StringToAdr("/", PORT_ICEBROKER, &brokeradr);
protocoltype = MP_QUAKE3;
}
if (!file)
return;
brokeradr.type = NA_INVALID; //should be the default broker...
brokeradr.prot = NP_RTC_TLS;
for (info = firstserver; info; info = info->next)
{
if (NET_CompareAdr(&info->adr, &brokeradr))
{
info->ping = PING_DEAD;
info->status &= ~SRVSTATUS_ALIVE;
}
}
while(VFS_GETS(file, linebuffer, sizeof(linebuffer)))
{
s = linebuffer;
@ -2356,18 +2430,41 @@ void MasterInfo_ProcessHTTP(struct dl_download *dl)
if (*s == '#') //hash is a comment, apparently.
continue;
if (!NET_StringToAdr(s, 80, &adr))
continue;
for (infostring = s; *infostring && *infostring != ' '; )
infostring++;
if (*infostring == ' ')
*infostring++ = 0;
else
infostring = NULL;
if (!strncmp(s, "ice:///", 7) || !strncmp(s, "ices:///", 8) || !strncmp(s, "rtc:///", 7) || !strncmp(s, "rtcs:///", 8))
{
brokerid = s+7;
while (*brokerid == '/')
brokerid++;
adr = brokeradr;
if (!*brokerid)
continue; //invalid...
}
else
{
brokerid = "";
if (!NET_StringToAdr(s, 80, &adr))
continue;
}
if ((info = Master_InfoForServer(&adr))) //remove if the server already exists.
if ((info = Master_InfoForServer(&adr, brokerid))) //remove if the server already exists.
{
info->sends = 1; //reset.
MasterInfo_ProcessHTTPInfo(info, infostring);
Master_ResortServer(info);
}
else
{
info = Z_Malloc(sizeof(serverinfo_t));
info->adr = adr;
info->sends = 1;
Q_strncpyz(info->brokerid, brokerid, sizeof(info->brokerid));
info->special = 0;
if (protocoltype == MP_QUAKEWORLD)
@ -2390,7 +2487,7 @@ void MasterInfo_ProcessHTTP(struct dl_download *dl)
info->refreshtime = 0;
info->ping = PING_DEAD;
snprintf(info->name, sizeof(info->name), "%s h", NET_AdrToString(adrbuf, sizeof(adrbuf), &info->adr));
MasterInfo_ProcessHTTPInfo(info, infostring);
info->next = firstserver;
firstserver = info;
@ -2400,141 +2497,10 @@ void MasterInfo_ProcessHTTP(struct dl_download *dl)
info->status |= SRVSTATUS_GLOBAL;
}
}
char *jsonnode(int level, char *node)
{
netadr_t adr = {NA_INVALID};
char servername[256] = {0};
char key[256];
int flags = SS_NETQUAKE; //assumption
int port = 0;
int cp = 0, mp = 0;
if (*node != '{')
return node;
do
{
node++;
node = COM_ParseToken(node, ",:{}[]");
if (*node != ':')
continue;
node++;
if (*node == '[')
{
do
{
node++;
node = jsonnode(level+1, node);
if (!node)
return NULL;
if (*node == ']')
{
break;
}
} while(*node == ',');
if (*node != ']')
return NULL;
node++;
}
else
{
Q_strncpyz(key, com_token, sizeof(key));
node = COM_ParseToken(node, ",:{}[]");
if (level == 1)
{
if (!strcmp(key, "IPAddress"))
{
if (!NET_StringToAdr(com_token, 0, &adr))
adr.type = NA_INVALID;
}
if (!strcmp(key, "Port"))
port = atoi(com_token);
if (!strcmp(key, "DNS"))
Q_strncpyz(servername, com_token, sizeof(servername));
if (!strcmp(key, "CurrentPlayerCount"))
cp = atoi(com_token);
if (!strcmp(key, "MaxPlayers"))
mp = atoi(com_token);
if (!strcmp(key, "Game"))
{
flags &= ~SS_PROTOCOLMASK;
if (!strcmp(com_token, "NetQuake"))
flags |= SS_NETQUAKE;
if (!strcmp(com_token, "QuakeWorld"))
flags |= SS_QUAKEWORLD;
if (!strcmp(com_token, "Quake2"))
flags |= SS_QUAKE2;
if (!strcmp(com_token, "Quake3"))
flags |= SS_QUAKE3;
}
}
}
} while(*node == ',');
if (*node == '}')
node++;
if (adr.type != NA_INVALID)
{
serverinfo_t *info;
if (port)
adr.port = htons(port);
if ((info = Master_InfoForServer(&adr))) //remove if the server already exists.
{
if (!info->special)
info->special = flags;
info->sends = 1; //reset.
}
else
{
info = Z_Malloc(sizeof(serverinfo_t));
info->ping = PING_DEAD;
info->adr = adr;
info->sends = 1;
info->special = flags;
info->refreshtime = 0;
info->players = cp;
info->maxplayers = mp;
snprintf(info->name, sizeof(info->name), "%s j", *servername?servername:NET_AdrToString(servername, sizeof(servername), &info->adr));
info->next = firstserver;
firstserver = info;
Master_ResortServer(info);
}
info->status |= SRVSTATUS_GLOBAL;
}
return node;
}
void MasterInfo_ProcessHTTPJSON(struct dl_download *dl)
{
int len;
char *buf;
master_t *mast = dl->user_ctx;
mast->dl = NULL;
if (dl->file)
{
len = VFS_GETLEN(dl->file);
buf = malloc(len + 1);
VFS_READ(dl->file, buf, len);
buf[len] = 0;
jsonnode(0, buf);
free(buf);
}
else
{
Con_Printf("Unable to query master at \"%s\"\n", dl->url);
}
}
#endif
//don't try sending to servers we don't support
void MasterInfo_Request(master_t *mast)
static void MasterInfo_Request(master_t *mast)
{
if (!mast)
return;
@ -2546,17 +2512,6 @@ void MasterInfo_Request(master_t *mast)
switch(mast->mastertype)
{
#ifdef WEBCLIENT
case MT_MASTERHTTPJSON:
if (!mast->dl)
{
mast->dl = HTTP_CL_Get(mast->address, NULL, MasterInfo_ProcessHTTPJSON);
if (mast->dl)
{
mast->dl->user_ctx = mast;
mast->dl->isquery = true;
}
}
break;
case MT_MASTERHTTP:
if (!mast->dl)
{
@ -2683,19 +2638,19 @@ void MasterInfo_WriteServers(void)
switch(server->special & SS_PROTOCOLMASK)
{
case SS_QUAKE3:
VFS_PUTS(qws, va("%s\t%s\t%s\n", NET_AdrToString(adr, sizeof(adr), &server->adr), "favorite:q3", server->name));
VFS_PUTS(qws, va("%s\t%s\t%s\n", Master_ServerToString(adr, sizeof(adr), server), "favorite:q3", server->name));
break;
case SS_QUAKE2:
VFS_PUTS(qws, va("%s\t%s\t%s\n", NET_AdrToString(adr, sizeof(adr), &server->adr), "favorite:q2", server->name));
VFS_PUTS(qws, va("%s\t%s\t%s\n", Master_ServerToString(adr, sizeof(adr), server), "favorite:q2", server->name));
break;
case SS_NETQUAKE:
VFS_PUTS(qws, va("%s\t%s\t%s\n", NET_AdrToString(adr, sizeof(adr), &server->adr), "favorite:nq", server->name));
VFS_PUTS(qws, va("%s\t%s\t%s\n", Master_ServerToString(adr, sizeof(adr), server), "favorite:nq", server->name));
break;
// case SS_DARKPLACES:
// VFS_PUTS(qws, va("%s\t%s\t%s\n", NET_AdrToString(adr, sizeof(adr), &server->adr), "favorite:dp", server->name));
// VFS_PUTS(qws, va("%s\t%s\t%s\n", Master_ServerToString(adr, sizeof(adr), server), "favorite:dp", server->name));
// break;
case SS_QUAKEWORLD:
VFS_PUTS(qws, va("%s\t%s\t%s\n", NET_AdrToString(adr, sizeof(adr), &server->adr), "favorite:qw", server->name));
VFS_PUTS(qws, va("%s\t%s\t%s\n", Master_ServerToString(adr, sizeof(adr), server), "favorite:qw", server->name));
break;
}
}
@ -2753,6 +2708,21 @@ void MasterInfo_Refresh(qboolean doreset)
Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_Q3SERVER), MT_BCAST, MP_QUAKE3, "Nearby Quake3 UDP servers.");
#endif
if (!fs_manifest->rtcbroker || !*fs_manifest->rtcbroker)
; //nope, sorry, not configured.
else
{
char *url;
COM_Parse(com_protocolname.string);
if (!strncmp(fs_manifest->rtcbroker, "tls://", 6))
url = va("https://%s/raw/%s", fs_manifest->rtcbroker+6, com_token);
else if (!strncmp(fs_manifest->rtcbroker, "tcp://", 6))
url = va("http://%s/raw/%s", fs_manifest->rtcbroker+6, com_token);
else
url = va("http://%s/raw/%s", fs_manifest->rtcbroker, com_token);
Master_AddMasterHTTP(url, MT_MASTERHTTP, MP_QUAKEWORLD, "Public Servers Potentially Behind A NAT.");
}
for (i = 0; net_masterlist[i].cv.name; i++)
{
Master_AddMaster(net_masterlist[i].cv.string, MT_MASTERUDP, net_masterlist[i].protocol, net_masterlist[i].comment);
@ -2772,6 +2742,8 @@ void Master_QueryServer(serverinfo_t *server)
{
char data[2048];
server->sends--;
if (*server->brokerid)
return; //don't even try. we have no direct route.
server->refreshtime = Sys_DoubleTime();
switch(server->special & SS_PROTOCOLMASK)
@ -2972,13 +2944,15 @@ unsigned int Master_NumAlive(void)
}
//true if server is on a different master's list.
serverinfo_t *Master_InfoForServer (netadr_t *addr)
serverinfo_t *Master_InfoForServer (netadr_t *addr, const char *brokerid)
{
serverinfo_t *info;
if (!brokerid)
brokerid="";
for (info = firstserver; info; info = info->next)
{
if (NET_CompareAdr(&info->adr, addr))
if (!strcmp(info->brokerid, brokerid) && NET_CompareAdr(&info->adr, addr))
return info;
}
return NULL;
@ -3056,7 +3030,7 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor
serverinfo_t *info;
char adr[MAX_ADR_SIZE];
info = Master_InfoForServer(&net_from);
info = Master_InfoForServer(&net_from, NULL);
if (!info) //not found...
{
@ -3069,7 +3043,7 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor
info->adr = net_from;
snprintf(info->name, sizeof(info->name), "%s ?", NET_AdrToString(adr, sizeof(adr), &info->adr));
Q_snprintfz(info->name, sizeof(info->name), "%s ?", Master_ServerToString(adr, sizeof(adr), info));
info->next = firstserver;
firstserver = info;
@ -3114,7 +3088,7 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor
memset(&pa, 0, sizeof(pa));
remaining /= 8;
NET_AdrToString(adr, sizeof(adr), &info->adr);
//Master_ServerToString(adr, sizeof(adr), info);
Z_Free(info->peers);
info->numpeers = 0;
@ -3135,7 +3109,7 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor
if (NET_ClassifyAddress(&pa, NULL) >= ASCOPE_NET)
{
peer->peer = Master_InfoForServer(&pa);
peer->peer = Master_InfoForServer(&pa, NULL);
if (!peer->peer)
{
//generate some lame peer node that we can use.
@ -3146,7 +3120,7 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor
peer->peer->refreshtime = 0;
peer->peer->ping = PING_DEAD;
peer->peer->next = firstserver;
snprintf(peer->peer->name, sizeof(peer->peer->name), "%s p", NET_AdrToString(adr, sizeof(adr), &pa));
Q_snprintfz(peer->peer->name, sizeof(peer->peer->name), "%s p", Master_ServerToString(adr, sizeof(adr), peer->peer));
firstserver = peer->peer;
}
peer++;
@ -3443,9 +3417,9 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor
}
if (!info->moreinfo && ((slist_cacheinfo.value == 2 || NET_CompareAdr(&info->adr, &selectedserver.adr)) || (info->special & SS_KEEPINFO)))
if (!info->moreinfo && ((slist_cacheinfo.value == 2 || (NET_CompareAdr(&info->adr, &selectedserver.adr)&&!strcmp(info->brokerid,selectedserver.brokerid))) || (info->special & SS_KEEPINFO)))
info->moreinfo = Z_Malloc(sizeof(serverdetailedinfo_t));
if (NET_CompareAdr(&info->adr, &selectedserver.adr))
if (NET_CompareAdr(&info->adr, &selectedserver.adr)&&!strcmp(info->brokerid,selectedserver.brokerid))
selectedserver.detail = info->moreinfo;
if (info->moreinfo)
@ -3540,7 +3514,7 @@ void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad)
Z_Free(info);
break;
}
if ((old = Master_InfoForServer(&info->adr))) //remove if the server already exists.
if ((old = Master_InfoForServer(&info->adr, NULL))) //remove if the server already exists.
{
if ((old->special & (SS_PROTOCOLMASK)) != (type & (SS_PROTOCOLMASK)))
old->special = type | (old->special & (SS_FAVORITE|SS_LOCAL));
@ -3556,7 +3530,7 @@ void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad)
info->refreshtime = 0;
info->status |= SRVSTATUS_GLOBAL;
Q_snprintfz(info->name, sizeof(info->name), "%s (via %s)", NET_AdrToString(adr, sizeof(adr), &info->adr), madr);
Q_snprintfz(info->name, sizeof(info->name), "%s (via %s)", Master_ServerToString(adr, sizeof(adr), info), madr);
info->next = last;
last = info;
@ -3586,11 +3560,11 @@ void CL_Connect_c(int argn, const char *partial, struct xcommandargcompletioncb_
{
if (len && !Q_strncasecmp(partial, info->name, len))
{
ctx->cb(info->name, va("^[%s^], %i players, %i ping", info->name, info->players, info->ping), NET_AdrToString(buf, sizeof(buf), &info->adr), ctx);
ctx->cb(info->name, va("^[%s^], %i players, %i ping", info->name, info->players, info->ping), Master_ServerToString(buf, sizeof(buf), info), ctx);
continue;
}
NET_AdrToString(buf, sizeof(buf), &info->adr);
Master_ServerToString(buf, sizeof(buf), info);
//there are too many meaningless servers out there, so only suggest IP addresses if those servers are actually significant (ie: active, or favourite)
if (!strncmp(partial, buf, len))
{
@ -3612,23 +3586,52 @@ void CL_Connect_c(int argn, const char *partial, struct xcommandargcompletioncb_
#endif
#ifdef Q3CLIENT
#if defined(CL_MASTER)
static void NetQ3_LocalServers_f(void)
{
#if defined(CL_MASTER)
netadr_t na;
MasterInfo_Refresh(true);
if (NET_StringToAdr("255.255.255.255", PORT_Q3SERVER, &na))
NET_SendPollPacket (14, va("%c%c%c%cgetstatus\n", 255, 255, 255, 255), na);
#endif
}
static void NetQ3_GlobalServers_Request(size_t masternum, int protocol, const char *keywords)
{
if (masternum == countof(net_masterlist))
{
const char *url;
struct dl_download *dl;
COM_Parse(com_protocolname.string);
if (!strncmp(fs_manifest->rtcbroker, "tls://", 6))
url = va("https://%s/raw/%s", fs_manifest->rtcbroker+6, com_token);
else if (!strncmp(fs_manifest->rtcbroker, "tcp://", 6))
url = va("http://%s/raw/%s", fs_manifest->rtcbroker+6, com_token);
else
url = va("http://%s/raw/%s", fs_manifest->rtcbroker, com_token);
dl = HTTP_CL_Get(url, NULL, MasterInfo_ProcessHTTP);
if (dl)
dl->isquery = true;
}
if (masternum >= countof(net_masterlist))
return; //erk
if (net_masterlist[masternum].protocol == MP_QUAKE3)
{
netadr_t adr[16];
char *str;
size_t i, n;
COM_Parse(net_masterlist[masternum].cv.string); //only want the first one
n = NET_StringToAdr2(com_token, 0, adr, countof(adr), NULL);
str = va("%c%c%c%cgetservers %u %s\n", 255, 255, 255, 255, protocol, keywords);
for (i = 0; i < n; i++)
NET_SendPollPacket (strlen(str), str, adr[i]);
}
}
static void NetQ3_GlobalServers_f(void)
{
#if defined(CL_MASTER)
size_t masternum = atoi(Cmd_Argv(1));
int protocol = atoi(Cmd_Argv(2));
char *keywords;
size_t i;
MasterInfo_Refresh(true);
Cmd_ShiftArgs(2, false);
@ -3636,25 +3639,16 @@ static void NetQ3_GlobalServers_f(void)
if (!masternum)
{
for (i = 0; i < countof(net_masterlist); i++)
Cbuf_AddText(va("globalservers %u %i %s\n", (unsigned)(i+1), protocol, keywords), Cmd_ExecLevel);
for (masternum = 0; masternum <= countof(net_masterlist); masternum++)
NetQ3_GlobalServers_Request(masternum, protocol, keywords);
}
masternum--;
if (masternum >= countof(net_masterlist))
return; //erk
if (net_masterlist[masternum].protocol == MP_QUAKE3)
{
netadr_t adr[16];
char *str;
size_t n;
COM_Parse(net_masterlist[masternum].cv.string); //only want the first one
n = NET_StringToAdr2(com_token, 0, adr, countof(adr));
str = va("%c%c%c%cgetservers %u empty full\n", 255, 255, 255, 255, 68);
for (i = 0; i < n; i++)
NET_SendPollPacket (strlen(str), str, adr[i]);
}
#endif
else
NetQ3_GlobalServers_Request(masternum-1, protocol, keywords);
}
#else
static void NetQ3_LocalServers_f(void){}
static void NetQ3_GlobalServers_f(void){}
#endif
#endif
void Net_Master_Init(void)
{

View file

@ -719,13 +719,13 @@ static void QCBUILTIN PF_cs_remove (pubprogfuncs_t *prinst, struct globalvars_s
if (!ed->entnum)
{
Con_Printf("Unable to remove the world. Try godmode.");
Con_Printf("Unable to remove the world. Try godmode.\n");
PR_StackTrace (prinst, false);
return;
}
if (ed->readonly)
{
Con_Printf("Entity %i is readonly.", ed->entnum);
Con_Printf("Entity %i is readonly.\n", ed->entnum);
return;
}
@ -734,6 +734,35 @@ static void QCBUILTIN PF_cs_remove (pubprogfuncs_t *prinst, struct globalvars_s
World_UnlinkEdict((wedict_t*)ed);
ED_Free (prinst, (void*)ed);
}
static void QCBUILTIN PF_cs_removeinstant (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
csqcedict_t *ed;
ed = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);
if (ED_ISFREE(ed))
{
csqc_deprecated("Tried removing free entity");
return;
}
if (!ed->entnum)
{
Con_Printf("Unable to remove the world. Try godmode.\n");
PR_StackTrace (prinst, false);
return;
}
if (ed->readonly)
{
Con_Printf("Entity %i is readonly.\n", ed->entnum);
return;
}
if (pe)
pe->DelinkTrailstate(&ed->trailstate);
World_UnlinkEdict((wedict_t*)ed);
prinst->EntFree(prinst, (void*)ed, true);
}
static void QCBUILTIN PF_cvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
@ -2243,6 +2272,11 @@ nogameaccess:
*r = r_refdef.useperspective;
break;
case VF_PROJECTIONOFFSET:
r[0] = r_refdef.projectionoffset[0];
r[1] = r_refdef.projectionoffset[1];
break;
case VF_SCREENVSIZE:
r[0] = vid.width;
r[1] = vid.height;
@ -2458,6 +2492,11 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_
r_refdef.useperspective = *p;
break;
case VF_PROJECTIONOFFSET:
r_refdef.projectionoffset[0] = p[0];
r_refdef.projectionoffset[1] = p[1];
break;
case VF_RT_DESTCOLOUR0:
case VF_RT_DESTCOLOUR1:
case VF_RT_DESTCOLOUR2:
@ -3651,6 +3690,12 @@ static void cs_get_input_state (usercmd_t *cmd)
cmd->cursor_entitynumber = *csqcg.input_cursor_entitynumber;
}
//sets implicit pause (only works when singleplayer)
static void QCBUILTIN PF_cl_setpause (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
cl.implicitpause = !!G_FLOAT(OFS_PARM0);
}
//get the input commands, and stuff them into some globals.
static void QCBUILTIN PF_cs_getinputstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
@ -6327,6 +6372,7 @@ static struct {
{"vectoyaw", PF_vectoyaw, 13}, // #13 float(vector v) vectoyaw (QUAKE)
{"spawn", PF_Spawn, 14}, // #14 entity() spawn (QUAKE)
{"remove", PF_cs_remove, 15}, // #15 void(entity e) remove (QUAKE)
{"removeinstant", PF_cs_removeinstant, 0},
{"traceline", PF_cs_traceline, 16}, // #16 void(vector v1, vector v2, float nomonst, entity forent) traceline (QUAKE)
{"checkclient", PF_NoCSQC, 17}, // #17 entity() checkclient (QUAKE) (don't support)
{"find", PF_FindString, 18}, // #18 entity(entity start, .string fld, string match) findstring (QUAKE)
@ -6944,6 +6990,7 @@ static struct {
{"loadfromdata", PF_loadfromdata, 529},
{"loadfromfile", PF_loadfromfile, 530},
{"setpause", PF_cl_setpause, 531},
{"log", PF_Logarithm, 532},
{"stopsound", PF_stopsound, 0},

View file

@ -1659,6 +1659,21 @@ static void QCBUILTIN PF_Remove_ (pubprogfuncs_t *prinst, struct globalvars_s *p
ED_Free (prinst, (void*)ed);
}
static void QCBUILTIN PF_RemoveInstant (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
menuedict_t *ed;
ed = (void*)G_EDICT(prinst, OFS_PARM0);
if (ed->ereftype == ER_FREE)
{
Con_DPrintf("Tried removing free entity\n");
PR_StackTrace(prinst, false);
return;
}
prinst->EntFree(prinst, (void*)ed, true);
}
static void QCBUILTIN PF_CopyEntity (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
@ -2238,6 +2253,7 @@ static struct {
{"spawn", PF_Spawn, 22},
{"remove", PF_Remove_, 23},
{"removeinstant", PF_RemoveInstant, 0},
{"find", PF_FindString, 24},
{"findfloat", PF_FindFloat, 25},
{"findentity", PF_FindFloat, 25},

View file

@ -546,7 +546,6 @@ void Mod_LoadLighting (struct model_s *loadmodel, bspx_header_t *bspx, qbyte *mo
struct mleaf_s *Mod_PointInLeaf (struct model_s *model, float *p);
void Mod_Think (void);
void Mod_NowLoadExternal(struct model_s *loadmodel);
void GLR_LoadSkys (void);
void R_BloomRegister(void);
@ -560,12 +559,16 @@ void Mod_ModelLoaded(void *ctx, void *data, size_t a, size_t b);
#ifdef RUNTIMELIGHTING
struct relight_ctx_s;
struct llightinfo_s;
void LightFace (struct relight_ctx_s *ctx, struct llightinfo_s *threadctx, int surfnum); //version that is aware of bsp trees
void LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *threadctx, lightstyleindex_t surf_styles[4], unsigned int *surf_expsamples, qbyte *surf_rgbsamples, qbyte *surf_deluxesamples, vec4_t surf_plane, vec4_t surf_texplanes[2], vec2_t exactmins, vec2_t exactmaxs, int texmins[2], int texsize[2], float lmscale); //special version that doesn't know what a face is or anything.
struct relight_ctx_s *LightStartup(struct relight_ctx_s *ctx, struct model_s *model, qboolean shadows, qboolean skiplit);
void LightReloadEntities(struct relight_ctx_s *ctx, const char *entstring, qboolean ignorestyles);
void LightShutdown(struct relight_ctx_s *ctx, struct model_s *mod);
void LightShutdown(struct relight_ctx_s *ctx);
extern const size_t lightthreadctxsize;
qboolean RelightSetup (struct model_s *model, size_t lightsamples, qboolean generatelit);
void RelightThink (void);
const char *RelightGetProgress(float *progress); //reports filename and progress
void RelightTerminate(struct model_s *mod); //NULL acts as a wildcard
#endif

View file

@ -1505,6 +1505,7 @@ qboolean R_ApplyRenderer_Load (rendererstate_t *newr)
if (host_basepal)
BZ_Free(host_basepal);
host_basepal = (qbyte *)FS_LoadMallocFile ("gfx/palette.lmp", &sz);
vid.fullbright = host_basepal?32:0; //q1-like mods are assumed to have 32 fullbright pixels, even if the colormap is missing.
if (!host_basepal)
host_basepal = (qbyte *)FS_LoadMallocFile ("wad/playpal", &sz);
if (!host_basepal || sz != 768)

View file

@ -35,6 +35,7 @@ cvar_t scr_scoreboard_newstyle = CVARD("scr_scoreboard_newstyle", "1", "Display
cvar_t scr_scoreboard_showfrags = CVARD("scr_scoreboard_showfrags", "0", "Display kills+deaths+teamkills, as determined by fragfile.dat-based conprint parsing. These may be inaccurate if you join mid-game.");
cvar_t scr_scoreboard_showflags = CVARD("scr_scoreboard_showflags", "2", "Display flag caps+touches on the scoreboard, where our fragfile.dat supports them.\n0: off\n1: on\n2: on only if someone appears to have interacted with a flag.");
cvar_t scr_scoreboard_fillalpha = CVARD("scr_scoreboard_fillalpha", "0.7", "Transparency amount for newstyle scoreboard.");
cvar_t scr_scoreboard_backgroundalpha = CVARD("scr_scoreboard_backgroundalpha", "0.5", "Further multiplier for the background alphas.");
cvar_t scr_scoreboard_teamscores = CVARD("scr_scoreboard_teamscores", "1", "Makes +showscores act as +showteamscores. Because reasons.");
cvar_t scr_scoreboard_teamsort = CVARD("scr_scoreboard_teamsort", "0", "On the scoreboard, sort players by their team BEFORE their personal score.");
cvar_t scr_scoreboard_titleseperator = CVAR("scr_scoreboard_titleseperator", "1");
@ -91,7 +92,7 @@ static apic_t *sb_ibar;
static apic_t *sb_sbar;
static apic_t *sb_scorebar;
static apic_t *sb_weapons[7][8]; // 0 is active, 1 is owned, 2-5 are flashes
apic_t *sb_weapons[7][8]; // 0 is active, 1 is owned, 2-5 are flashes
static apic_t *sb_ammo[4];
static apic_t *sb_sigil[4];
static apic_t *sb_armor[3];
@ -972,6 +973,7 @@ static apic_t *Sbar_PicFromWad(char *name)
void Sbar_Flush (void)
{
sbar_loaded = false;
memset(sb_weapons, 0, sizeof(sb_weapons));
}
void Sbar_Start (void) //if one of these fails, skip the entire status bar.
{
@ -981,6 +983,8 @@ void Sbar_Start (void) //if one of these fails, skip the entire status bar.
if (sbar_loaded)
return;
memset(sb_weapons, 0, sizeof(sb_weapons));
sbar_loaded = true;
COM_FlushFSCache(false, true); //make sure the fs cache is built if needed. there's lots of loading here.
@ -1138,6 +1142,7 @@ void Sbar_Init (void)
Cvar_Register(&scr_scoreboard_showfrags, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_showflags, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_fillalpha, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_backgroundalpha, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_teamscores, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_teamsort, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_titleseperator, "Scoreboard settings");
@ -3023,7 +3028,7 @@ void Sbar_IntermissionNumber (float x, float y, int num, int digits, int color,
#define COL_TEAM_LOWAVGHIGH COLUMN("low/avg/high", 12*8, {sprintf (num, "%3i/%3i/%3i", plow, pavg, phigh); Draw_FunString ( x, y, num); })
#define COL_TEAM_TEAM COLUMN("team", 4*8, {Draw_FunStringWidth ( x, y, tm->team, 4*8, false, false); \
if (!strncmp(cl.players[trackplayer].team, tm->team, 16))\
if (ourteam)\
{\
Draw_FunString ( x - 1*8, y, "^Ue010");\
Draw_FunString ( x + 4*8, y, "^Ue011");\
@ -3057,6 +3062,8 @@ void Sbar_TeamOverlay (playerview_t *pv)
int startx;
int trackplayer;
qboolean ourteam;
if (!pv)
pv = &cl.playerview[0];
@ -3146,11 +3153,14 @@ void Sbar_TeamOverlay (playerview_t *pv)
k = teamsort[i];
tm = teams + k;
ourteam = !strncmp(cl.players[trackplayer].team, tm->team, 16);
if (scr_scoreboard_newstyle.ival)
{
// Electro's scoreboard eyecandy: Render the main background transparencies behind players row
// TODO: Alpha values on the background
int background_color;
float backalpha;
if (!(strcmp("red", tm->team)))
background_color = 4; // forced red
@ -3159,7 +3169,11 @@ void Sbar_TeamOverlay (playerview_t *pv)
else
background_color = tm->bottomcolour;
Sbar_FillPCDark (startx - 2, y, rank_width - 3, 8, background_color, scr_scoreboard_fillalpha.value);
backalpha = scr_scoreboard_backgroundalpha.value*scr_scoreboard_fillalpha.value;
if (ourteam)
backalpha *= 1.7;
Sbar_FillPCDark (startx - 2, y, rank_width - 3, 8, background_color, backalpha);
R2D_ImagePaletteColour (0, scr_scoreboard_fillalpha.value);
R2D_FillBlock (startx - 3, y, 1, 8); // Electro - Border - Left
@ -3275,8 +3289,7 @@ ping time frags name
Font_BeginString(font_default, x+24, y, &cx, &cy); \
Font_DrawChar(cx, cy, CON_WHITEMASK, num[2] | 0xe000); \
\
if ((pv->cam_state == CAM_FREECAM && k == pv->playernum) || \
(pv->cam_state != CAM_FREECAM && k == pv->cam_spec_track)) \
if (isme) \
{ \
Font_BeginString(font_default, x, y, &cx, &cy); \
Font_DrawChar(cx, cy, CON_WHITEMASK, 16 | 0xe000); \
@ -3335,9 +3348,11 @@ void Sbar_DeathmatchOverlay (playerview_t *pv, int start)
int skip = 10;
int showcolumns;
int startx, rank_width;
qboolean isme;
vrect_t gr = r_refdef.grect;
int namesize = (cl.teamplay ? 12*8 : 16*8);
float backalpha;
if (!pv)
return;
@ -3525,6 +3540,8 @@ if (showcolumns & (1<<COLUMN##title)) \
y += skip;
if (y > vid.height-10)
break;
isme = (pv->cam_state == CAM_FREECAM && k == pv->playernum) ||
(pv->cam_state != CAM_FREECAM && k == pv->cam_spec_track);
// Electro's scoreboard eyecandy: Moved this up here for usage with the row background color
top = Sbar_TopColour(s);
@ -3532,6 +3549,10 @@ if (showcolumns & (1<<COLUMN##title)) \
if (scr_scoreboard_newstyle.ival)
{
backalpha = scr_scoreboard_backgroundalpha.value*scr_scoreboard_fillalpha.value;
if (isme)
backalpha *= 1.7;
// Electro's scoreboard eyecandy: Render the main background transparencies behind players row
// TODO: Alpha values on the background
if ((cl.teamplay) && (!s->spectator))
@ -3549,13 +3570,13 @@ if (showcolumns & (1<<COLUMN##title)) \
else
background_color = bottom;
Sbar_FillPCDark (startx - 2, y, rank_width - 3, skip, background_color, scr_scoreboard_fillalpha.value);
Sbar_FillPCDark (startx - 2, y, rank_width - 3, skip, background_color, backalpha);
}
else if (S_Voip_Speaking(k))
Sbar_FillPCDark (startx - 2, y, rank_width - 3, skip, 0x00ff00, scr_scoreboard_fillalpha.value);
Sbar_FillPCDark (startx - 2, y, rank_width - 3, skip, 0x00ff00, backalpha);
else
{
R2D_ImagePaletteColour (2, scr_scoreboard_fillalpha.value);
R2D_ImagePaletteColour (2, backalpha);
R2D_FillBlock (startx - 2, y, rank_width - 3, skip);
}
@ -3594,6 +3615,8 @@ if (showcolumns & (1<<COLUMN##title)) \
y += skip;
if (y > vid.height-10)
break;
isme = (pv->cam_state == CAM_FREECAM && k == pv->playernum) ||
(pv->cam_state != CAM_FREECAM && k == pv->cam_spec_track);
x = startx;
#define COLUMN(title, width, code, fills) \

View file

@ -3435,7 +3435,7 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc)
for (i = MUSIC_FIRST; i < MUSIC_STOP; i++)
{
chanupdatereason_t changed = false;
chanupdatereason_t changed = CUR_SPACIALISEONLY;
chan = &sc->channel[i];
if (!chan->sfx)
{
@ -4218,6 +4218,9 @@ void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels,
qbyte *newcache;
streaming_t *s, *free=NULL;
if (!sound_started)
return;
for (s = s_streamers, i = 0; i < MAX_RAW_SOURCES; i++, s++)
{
if (!s->inuse)

View file

@ -614,7 +614,7 @@ static int Sys_EnumerateFiles2 (const char *truepath, int apathofs, const char *
{
if (wildcmp(match, ent->d_name))
{
Q_snprintfz(file, sizeof(file), "%s/%s", truepath, ent->d_name);
Q_snprintfz(file, sizeof(file), "%s%s", truepath, ent->d_name);
if (stat(file, &st) == 0)
{
@ -628,7 +628,7 @@ static int Sys_EnumerateFiles2 (const char *truepath, int apathofs, const char *
}
}
else
printf("Stat failed for \"%s\"\n", file);
Con_DPrintf("Stat failed for \"%s\"\n", file); //can happen with dead symlinks
}
}
} while(1);
@ -640,6 +640,7 @@ int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const
char apath[MAX_OSPATH];
char truepath[MAX_OSPATH];
char *s;
int suboffset;
if (!gpath)
gpath = "";
@ -658,8 +659,15 @@ int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const
if (s < apath) //didn't find a '/'
*apath = '\0';
Q_snprintfz(truepath, sizeof(truepath), "%s/%s", gpath, apath);
return Sys_EnumerateFiles2(truepath, strlen(gpath)+1, match, func, parm, spath);
suboffset = strlen(gpath);
if (suboffset + 1 + strlen(apath) >= sizeof(truepath))
return false; //overflow...
memcpy(truepath, gpath, suboffset);
if (suboffset && truepath[suboffset-1] != '/')
truepath[suboffset++] = '/';
Q_strncpyz(truepath+suboffset, apath, sizeof(truepath)-suboffset);
return Sys_EnumerateFiles2(truepath, suboffset, match, func, parm, spath);
}
int secbase;

View file

@ -990,9 +990,6 @@ int watchdogthreadfunction(void *arg)
}
#endif
int *debug;
#ifndef SERVERONLY
#if (_WIN32_WINNT < 0x0400)
@ -1012,7 +1009,16 @@ int *debug;
#endif
#endif
HHOOK llkeyboardhook;
static struct
{
HHOOK llkeyboardhook;
//windows hooks can be used for code injection etc.
//hide these symbols from shitty exports scanners so we don't look like the keylogger that we aren't. Note the 'vid.activeapp' requirement below - we are not a keylogger, we only see a limited set of keys and only when we already have focus.
HHOOK (WINAPI *pSetWindowsHookEx) (int idHook, HOOKPROC lpfn, HINSTANCE hmod, DWORD dwThreadId); //W and A versions have the same signature.
LRESULT (WINAPI *pCallNextHookEx) (HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam);
WINBOOL (WINAPI *pUnhookWindowsHookEx) (HHOOK hhk);
} winkeys;
cvar_t sys_disableWinKeys = CVAR("sys_disableWinKeys", "0");
cvar_t sys_disableTaskSwitch = CVARF("sys_disableTaskSwitch", "0", CVAR_NOTFROMSERVER); // please don't encourage people to use this...
@ -1071,7 +1077,7 @@ LRESULT CALLBACK LowLevelKeyboardProc (INT nCode, WPARAM wParam, LPARAM lParam)
default:
break;
}
return CallNextHookEx (llkeyboardhook, nCode, wParam, lParam);
return winkeys.pCallNextHookEx (winkeys.llkeyboardhook, nCode, wParam, lParam);
}
void SetHookState(qboolean state)
@ -1079,16 +1085,27 @@ void SetHookState(qboolean state)
if (!sys_disableTaskSwitch.ival && !sys_disableWinKeys.ival)
state = false;
if (!state == !llkeyboardhook) //not so types are comparable
if (!state == !winkeys.llkeyboardhook) //not so types are comparable
return;
if (llkeyboardhook)
if (!winkeys.pSetWindowsHookEx)
{
UnhookWindowsHookEx(llkeyboardhook);
llkeyboardhook = NULL;
HMODULE dll = LoadLibraryA("user32.dll");
if (!dll)
return;
winkeys.pSetWindowsHookEx = (void*)GetProcAddress(dll, WinNT?"SetWindowsHookExW":"SetWindowsHookExA");
winkeys.pCallNextHookEx = (void*)GetProcAddress(dll, "CallNextHookEx");
winkeys.pUnhookWindowsHookEx = (void*)GetProcAddress(dll, "UnhookWindowsHookEx");
if (!winkeys.pSetWindowsHookEx)
return;
}
if (winkeys.llkeyboardhook)
{
winkeys.pUnhookWindowsHookEx(winkeys.llkeyboardhook);
winkeys.llkeyboardhook = NULL;
}
if (state)
llkeyboardhook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0);
winkeys.llkeyboardhook = winkeys.pSetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0);
}
#endif

View file

@ -155,13 +155,16 @@ cvar_t v_viewheight = CVARF("v_viewheight", "0", CVAR_ARCHIVE);
static cvar_t v_depthsortentities = CVARAD("v_depthsortentities", "0", "v_reorderentitiesrandomly", "Reorder entities for transparency such that the furthest entities are drawn first, allowing nearer transparent entities to draw over the top of them.");
#ifdef QUAKESTATS
static cvar_t scr_autoid = CVARD("scr_autoid", "1", "Display nametags above all players while spectating.");
static cvar_t scr_autoid_team = CVARD("scr_autoid_team", "1", "Display nametags above team members. 0: off. 1: display with half-alpha if occluded. 2: hide when occluded.");
static cvar_t scr_autoid_team = CVARD("scr_autoid_team", "2", "Display nametags above team members. 0: off. 1: display with half-alpha if occluded. 2: hide when occluded.");
static cvar_t scr_autoid_health = CVARD("scr_autoid_health", "1", "Display health as part of nametags (when known).");
static cvar_t scr_autoid_armour = CVARD("scr_autoid_armor", "1", "Display armour as part of nametags (when known).");
static cvar_t scr_autoid_weapon = CVARD("scr_autoid_weapon", "1", "Display the player's best weapon as part of their nametag (when known).");
static cvar_t scr_autoid_weapon_mask = CVARD("scr_autoid_weapon_mask", "126", "Mask of bits for weapon icons to actually show.\n+1: Shotgun.\n+2: Super Shotgun.\n+4: Nail Gun.\n+8: Super Nail Gun.\n+16: Grenade Launcher.\n+32: Rocket Launcher.\n+64: Lightning\nShowing only RL and GL is 96\n");
static cvar_t scr_autoid_teamcolour = CVARD("scr_autoid_teamcolour", STRINGIFY(COLOR_BLUE), "The colour for the text on the nametags of team members.");
static cvar_t scr_autoid_enemycolour = CVARD("scr_autoid_enemycolour", STRINGIFY(COLOR_WHITE), "The colour for the text on the nametags of non-team members.");
#endif
cvar_t chase_active = CVAR("chase_active", "0");
cvar_t chase_back = CVAR("chase_back", "48");
@ -1768,6 +1771,7 @@ static qboolean SCR_VRectForPlayer(vrect_t *vrect, int pnum, unsigned maxseats)
void Draw_ExpandedString(struct font_s *font, float x, float y, conchar_t *str);
#ifdef QUAKESTATS
static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam)
{
conchar_t buffer[256];
@ -1795,7 +1799,6 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam)
{1, 0.4, 0, 1},
{1, 1, 1, 1}
};
#ifdef QUAKESTATS
static vec4_t armourcolours[] =
{
{25, 170, 0, 0.2},
@ -1817,7 +1820,6 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam)
&tp_name_rl,
&tp_name_lg
};
#endif
struct font_s *font = font_default;
VectorCopy(org, tagcenter);
@ -1848,7 +1850,6 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam)
x = center[0]*r_refdef.vrect.width+r_refdef.vrect.x;
y = (1-center[1])*r_refdef.vrect.height+r_refdef.vrect.y;
#ifdef QUAKESTATS
if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV)
{
health = pl->statsf[STAT_HEALTH];
@ -1858,7 +1859,6 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam)
haveinfo = true;
}
else
#endif
{
health = pl->tinfo.health;
armour = pl->tinfo.armour;
@ -1897,16 +1897,18 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam)
h += 8;
y -= 8;
R2D_ImageColours(healthcolours[r][0], healthcolours[r][1], healthcolours[r][2], healthcolours[r][3]*alpha);
R2D_FillBlock(x - barwidth*0.5 + barwidth * frac, y, barwidth * (1-frac), 8);
R2D_FillBlock(x - barwidth*0.5 + barwidth * frac, y, barwidth * (1-frac), 4);
r++;
R2D_ImageColours(healthcolours[r][0], healthcolours[r][1], healthcolours[r][2], healthcolours[r][3]*alpha);
R2D_FillBlock(x - barwidth*0.5, y, barwidth * frac, 8);
R2D_FillBlock(x - barwidth*0.5, y, barwidth * frac, 4);
}
if (health <= 0) //armour+weapons are not relevant when dead
{
R2D_ImageColours(1, 1, 1, 1);
return;
}
#ifdef QUAKESTATS
if (scr_autoid_armour.ival)
{
//display armour bar above that
@ -1919,30 +1921,56 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam)
else r = -1;
if (r >= 0)
{
h += 8;
y -= 8;
h += 5;
y -= 5;
armour = bound(0, armour, health);
barwidth = 32;
R2D_ImageColours(armourcolours[r][0], armourcolours[r][1], armourcolours[r][2], armourcolours[r][3]*alpha);
R2D_FillBlock(x - barwidth*0.5 + barwidth * armour/(float)health, y, barwidth * (health-armour)/(float)health, 8);
R2D_FillBlock(x - barwidth*0.5 + barwidth * armour/(float)health, y, barwidth * (health-armour)/(float)health, 4);
r++;
R2D_ImageColours(armourcolours[r][0], armourcolours[r][1], armourcolours[r][2], armourcolours[r][3]*alpha);
R2D_FillBlock(x - barwidth*0.5, y, barwidth * armour/(float)health, 8);
R2D_FillBlock(x - barwidth*0.5, y, barwidth * armour/(float)health, 4);
}
}
R2D_ImageColours(1, 1, 1, 1);
if (scr_autoid_weapon.ival)
{
if (h < 8)
h = 8;
y += (h-8)/2;
for (r = countof(wbitnames)-1; r>=0; r--)
if (items & (1<<r))
break;
R2D_ImageColours(1, 1, 1, 1);
if (r >= 0)
if (r >= 0 && (scr_autoid_weapon_mask.ival&(1<<1)))
{
if (scr_autoid_weapon.ival==1)
{
extern apic_t *sb_weapons[7][8];
float w = 0;
if (r < 8 && sb_weapons[0][r])
{
if (h < 16)
{
y-= 16-h;
h = 16;
}
y += (h-16)/2;
if (sb_weapons[0][r]->width)
w = sb_weapons[0][r]->width;
else
w = sb_weapons[0][r]->atlas->width;
R2D_ImageAtlas(x-barwidth*.5-24-4, y, 24, 16, 0, 0, 24/w, 1, sb_weapons[0][r]);
return;
}
}
if (h < 8)
{
y-= 8-h;
h = 8;
}
y += (h-8)/2;
len = COM_ParseFunString(textflags, wbitnames[r]->string, buffer, sizeof(buffer), false) - buffer;
if (textflags & CON_HALFALPHA)
{
@ -1953,17 +1981,15 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam)
if (len && (buffer[0] & CON_CHARMASK) == '{' && (buffer[len-1] & CON_CHARMASK) == '}')
{ //these are often surrounded by {} to make them white in chat messages, and recoloured.
buffer[len-1] = 0;
Draw_ExpandedString(font, x + barwidth*0.5 + 4, y, buffer+1);
len = 1;
}
else
Draw_ExpandedString(font, x + barwidth*0.5 + 4, y, buffer);
len = 0;
Draw_ExpandedString(font, x + barwidth*0.5 + 4, y, buffer+len);
}
}
#else
(void)items;
(void)armour;
#endif
}
#endif
#include "pr_common.h"
msurface_t *Mod_GetSurfaceNearPoint(model_t *model, vec3_t point);
@ -1974,10 +2000,12 @@ extern cvar_t r_showshaders, r_showfields, r_projection;
void R_DrawNameTags(void)
{
int i;
#ifdef QUAKESTATS
lerpents_t *le;
qboolean isteam;
char *ourteam;
int ourcolour;
#endif
if (r_projection.ival) //we don't actually know how to transform the points unless the projection is coded in advance. and it isn't.
return;
@ -2184,6 +2212,7 @@ void R_DrawNameTags(void)
}
}
#ifdef QUAKESTATS
if (cls.protocol == CP_QUAKE2)
return; //FIXME: q2 has its own ent logic, which messes stuff up here.
@ -2203,6 +2232,7 @@ void R_DrawNameTags(void)
ourcolour = cl.players[r_refdef.playerview->playernum].rbottomcolor;
}
pmove.skipent = r_refdef.playerview->playernum+1;
for (i = 0; i < cl.allocated_client_slots; i++)
{
if (!*cl.players[i].name)
@ -2247,6 +2277,7 @@ void R_DrawNameTags(void)
SCR_DrawAutoID(nametagorg[i], &cl.players[i], isteam);
}
#endif
}
void R2D_PolyBlend (void);
@ -2615,13 +2646,16 @@ void V_Init (void)
Cvar_Register (&v_deathtilt, VIEWVARS);
Cvar_Register (&v_skyroom, VIEWVARS);
#ifdef QUAKESTATS
Cvar_Register (&scr_autoid, VIEWVARS);
Cvar_Register (&scr_autoid_team, VIEWVARS);
Cvar_Register (&scr_autoid_health, VIEWVARS);
Cvar_Register (&scr_autoid_armour, VIEWVARS);
Cvar_Register (&scr_autoid_weapon, VIEWVARS);
Cvar_Register (&scr_autoid_weapon_mask, VIEWVARS);
Cvar_Register (&scr_autoid_teamcolour, VIEWVARS);
Cvar_Register (&scr_autoid_enemycolour, VIEWVARS);
#endif
#ifdef SIDEVIEWS
#define SECONDARYVIEWVARS "Secondary view vars"

View file

@ -470,11 +470,18 @@ qbyte *W_GetTexture(const char *name, int *width, int *height, uploadfmt_t *form
p = W_GetLumpName(name+4, &lumpsize, &lumptype);
if (p)
{
if (/*lumptype == TYP_QPIC && */!strcmp(name+4, "conchars") && lumpsize==128*128)
if (/*lumptype == TYP_MIPTEX && */!strcmp(name+4, "conchars") && (lumpsize==128*128
#ifdef HAVE_LEGACY
|| (lumptype == TYP_QPIC&&lumpsize==8+128*128)
#endif
))
{ //conchars has no header.
qbyte *lump = (qbyte*)p;
extern cvar_t con_ocranaleds;
if (lumpsize==8+128*128)
Con_Printf(CON_WARNING"WARNING: gfx.wad conchars lump has incorrect lump size.\n");
if (con_ocranaleds.ival)
{
if (con_ocranaleds.ival != 2 || QCRC_Block(lump, 128*128) == 798)

View file

@ -4262,7 +4262,7 @@ void Cmd_Init (void)
#endif
Cmd_AddCommandAD ("seta", Cmd_set_f, Cmd_Set_c, "Changes the current value of the named cvar, creating it if it doesn't yet exist. Also forces the archive flag so that the cvar will always be written into any saved configs.");
Cmd_AddCommandAD ("seta_calc", Cmd_set_f, Cmd_Set_c, "Sets the named cvar to the result of a (complex) expression. Also forces the archive flag so that the cvar will always be written into any saved configs.");
Cmd_AddCommand ("vstr", Cmd_Vstr_f);
Cmd_AddCommandD ("vstr", Cmd_Vstr_f, "Executes the string value of the cvar, much like if it were an alias. For compatibility with q3.");
Cmd_AddCommandAD ("inc", Cvar_Inc_f, Cmd_Set_c, "Adds a value to the named cvar. Use a negative value if you wish to decrease the cvar's value.");
//FIXME: Add seta some time.
Cmd_AddCommand ("if", Cmd_if_f);

View file

@ -25,18 +25,53 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
/*FIXME: rewrite this to use something like the following
typedef struct
{
qbyte level:8;
qbyte seat:4; //for splitscreen binds etc
qbyte pad:2;
qbyte insecure:1; //from gamecode, untrusted configs, stuffed binds, etc. flagged for many reasons.
qbyte cvarlatch:1; //latches cvars so the user cannot change them till next map. should be RESTRICT_LOCAL to avoid users cheating by restrict-to-block server commands.
} cmdpermissions_t;
qbyte groups:27;
qbyte cvarlatch:1; //latches cvars so the user cannot change them till next map. should be RESTRICT_LOCAL to avoid users cheating by restrict-to-block server commands.
qbyte seat:4; //for splitscreen binds etc. 1 based! 0 uses in_forceseat/first.
} cmdaccess_t;
enum {
//core groups
CAG_OWNER = 1<<0, //commands that came from the keyboard (or stdin).
CAG_MENUQC = 1<<1, //menuqc (mostly allowed to do stuff - but often also has CAG_DOWNLOADED)
CAG_CSQC = 1<<2, //csqc localcmds, nearly always has CAG_DOWNLOADED, so a load of denies
CAG_SSQC = 1<<3, //ssqc localcmds, able to poke quite a lot of things, generally unrestricted.
CAG_SERVER = 1<<4, //ssqc stuffcmds, typically same access as csqc
//special groups:
CAG_DOWNLOADED = 1<<5, //for execs that read from a downloaded(untrusted) package. also flagged by qc modules if they were from such packages (so usually included from csqc).
CAG_SCRIPT = 1<<6, //added for binds (with more than one command), or for aliases/configs. exists for +lookdown's denies (using explicit checks, to avoid cheat bypasses).
//custom groups:
CAG_RCON = 1<<7, //commands that came from rcon.
//groups of groups, for default masks or special denies
CAG_DEFAULTALLOW = CAG_OWNER|CAG_MENUQC|CAG_CSQC|CAG_SSQC|CAG_SERVER|CAG_RCON,
CAG_INSECURE = CAG_DOWNLOADED|CAG_SERVER|CAG_CSQC, //csqc included mostly for consistency.
// userN: defaults to no commands
// vip: BAN_VIP users
// mapper: BAN_MAPPER users
};
//stuff can be accessed when:
// if ((command.allows&cbuf.groups) && !(command.denies&cbuf.groups)) accessisallowed;
//cvars:
// separate allow-read, deny-read, allow-write, deny-write masks (set according to the older flags, to keep things simple)
//aliases:
// alias execution ors in the group(s) that created it (and 'script'). some execution chains could end up with a LOT of groups... FIXME: is that a problem? just don't put potentially restricted things in aliases?
//binds:
// also ors the creator's group - no `bind w doevil` and waiting.
//multiple cbufs:
// ssqc still has a dedicated cbuf, so that wait commands cause THAT cbuf to wait, not others.
// rcon+readcmd have a separate cbuf, to try to isolate prints
//seats:
// there's no security needed between seats, but server should maybe be blocked from seat switching commands (acting only as asserts), to catch bugs.
//
//'p2 nameofalias' sets seat to 2, overriding in_forceseat.
//+lookup etc is blocked when allow_scripts==0 and indirect is set.
typedef struct
{
cbuf_t *cbuf; //exec etc inserts into this
cmdpermissions_t p; //access rights
} cmdstate_t;
void Cbuf_AddText(const char *text, qboolean addnl, qboolean insert, const cmdstate_t *cstate); //null for local?
void Cbuf_AddText(const char *text, qboolean addnl, qboolean insert); //null for local? all commands must be \n terminated.
char *Cbuf_GetNext(cmdpermissions_t permissions, qboolean ignoresemicolon);
void Cbuf_Execute(cbuf_t *cbuf);
void Cmd_ExecuteString (const char *text, const cmdstate_t *cstate);
@ -193,9 +228,10 @@ void Cmd_Args_Set(const char *newargs, size_t len);
#define RESTRICT_MAX RESTRICT_MAX_USER
#define RESTRICT_LOCAL RESTRICT_MAX //commands typed at the console
#define RESTRICT_INSECURE RESTRICT_MAX+1 //commands from csqc or untrusted sources (really should be a separate flag, requires cbuf rewrite)
#define RESTRICT_SERVER RESTRICT_MAX+2 //commands from ssqc (untrusted, but allowed to lock cvars)
#define RESTRICT_LOCAL (RESTRICT_MAX) //commands typed at the console
#define RESTRICT_INSECURE (RESTRICT_MAX+1) //commands from csqc or untrusted sources (really should be a separate flag, requires cbuf rewrite)
#define RESTRICT_SERVER (RESTRICT_MAX+2) //commands from ssqc (untrusted, but allowed to lock cvars)
#define RESTRICT_SERVERSEAT(x) (RESTRICT_SERVER+x)
#define RESTRICT_RCON rcon_level.ival
//#define RESTRICT_SSQC RESTRICT_MAX-2

View file

@ -3612,7 +3612,6 @@ static void *Q1MDL_LoadSkins_GL (galiasinfo_t *galias, dmdl_t *pq1inmodel, model
galiasskin_t *outskin = galias->ofsskins;
const char *slash;
unsigned int texflags;
const char *defaultshader = NULL;
s = pq1inmodel->skinwidth*pq1inmodel->skinheight;
for (i = 0; i < pq1inmodel->numskins; i++)
@ -3664,7 +3663,7 @@ static void *Q1MDL_LoadSkins_GL (galiasinfo_t *galias, dmdl_t *pq1inmodel, model
{
default: //urk
case TF_SOLID8:
frames[0].defaultshader = defaultshader;
frames[0].defaultshader = NULL; //default skin...
break;
case TF_H2_T7G1:
frames[0].defaultshader =
@ -3682,6 +3681,7 @@ static void *Q1MDL_LoadSkins_GL (galiasinfo_t *galias, dmdl_t *pq1inmodel, model
case TF_H2_TRANS8_0:
frames[0].defaultshader =
"{\n"
// "program defaultskin#MASK=0.5#MASKLT\n"
"{\n"
"map $diffuse\n"
"blendfunc gl_src_alpha gl_one_minus_src_alpha\n"
@ -3690,11 +3690,26 @@ static void *Q1MDL_LoadSkins_GL (galiasinfo_t *galias, dmdl_t *pq1inmodel, model
"alphagen entity\n"
"depthwrite\n"
"}\n"
"{\n"
"map $loweroverlay\n"
"rgbgen bottomcolor\n"
"blendfunc gl_src_alpha gl_one\n"
"}\n"
"{\n"
"map $upperoverlay\n"
"rgbgen topcolor\n"
"blendfunc gl_src_alpha gl_one\n"
"}\n"
"{\n"
"map $fullbright\n"
"blendfunc add\n"
"}\n"
"}\n";
break;
case TF_H2_T4A4:
frames[0].defaultshader =
"{\n"
// "program defaultskin\n"
"cull disable\n"
"{\n"
"map $diffuse\n"
@ -3703,6 +3718,20 @@ static void *Q1MDL_LoadSkins_GL (galiasinfo_t *galias, dmdl_t *pq1inmodel, model
"rgbgen lightingDiffuse\n"
"depthwrite\n"
"}\n"
"{\n"
"map $loweroverlay\n"
"rgbgen bottomcolor\n"
"blendfunc gl_src_alpha gl_one\n"
"}\n"
"{\n"
"map $upperoverlay\n"
"rgbgen topcolor\n"
"blendfunc gl_src_alpha gl_one\n"
"}\n"
"{\n"
"map $fullbright\n"
"blendfunc add\n"
"}\n"
"}\n";
break;
}

View file

@ -2804,6 +2804,7 @@ char *COM_DeFunString(conchar_t *str, conchar_t *stop, char *out, int outsize, q
if (!outsize)
Sys_Error("COM_DeFunString given outsize=0");
#endif
outsize--;
/*if (ignoreflags)
{

View file

@ -396,6 +396,8 @@ static ftemanifest_t *FS_Manifest_Create(const char *syspath)
#else
man->mainconfig = Z_StrDup("fte.cfg");
#endif
man->rtcbroker = Z_StrDup("tls://master.frag-net.com:27950"); //This is eukara's server. fixme: this really ought to be a cvar instead.
return man;
}
@ -3358,7 +3360,7 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
#define EZQUAKECOMPETITIVE "set ruleset_allow_fbmodels 1\nset sv_demoExtensions \"\"\n"
/*quake requires a few settings for compatibility*/
#define QRPCOMPAT "set cl_cursor_scale 0.2\nset cl_cursor_bias_x 7.5\nset cl_cursor_bias_y 0.8\n"
#define QUAKESPASMSUCKS "mod_h2holey_bugged 1\n"
#define QUAKESPASMSUCKS "set mod_h2holey_bugged 1\n"
#define QCFG "set v_gammainverted 1\nset con_stayhidden 0\nset com_parseutf8 0\nset allow_download_pakcontents 1\nset allow_download_refpackages 0\nset sv_bigcoords \"\"\nmap_autoopenportals 1\n" "sv_port "STRINGIFY(PORT_QWSERVER)" "STRINGIFY(PORT_NQSERVER)"\n" ZFIXHACK EZQUAKECOMPETITIVE QRPCOMPAT QUAKESPASMSUCKS
/*NetQuake reconfiguration, to make certain people feel more at home...*/
#define NQCFG "//-nohome\ncfg_save_auto 1\n" QCFG "sv_nqplayerphysics 1\ncl_loopbackprotocol auto\ncl_sbar 1\nplug_sbar 0\nsv_port "STRINGIFY(PORT_NQSERVER)"\ncl_defaultport "STRINGIFY(PORT_NQSERVER)"\n"
@ -3470,13 +3472,13 @@ const gamemode_info_t gamemode_info[] = {
{"-hexen2", "hexen2", "FTE-Hexen2", {"data1/pak0.pak"}, HEX2CFG,{"data1", "*fteh2"}, "Hexen II", UPDATEURL(H2)},
#endif
#if defined(Q2CLIENT) || defined(Q2SERVER)
{"-quake2", "q2", "FTE-Quake2", {"baseq2/pak0.pak"}, Q2CFG, {"baseq2", "*fteq2"}, "Quake II", UPDATEURL(Q2)},
{"-quake2", "q2", "Quake2", {"baseq2/pak0.pak"}, Q2CFG, {"baseq2", "*fteq2"}, "Quake II", UPDATEURL(Q2)},
//mods of the above that should generally work.
{"-dday", "dday", "FTE-Quake2", {"dday/pak0.pak"}, Q2CFG, {"baseq2", "dday", "*fteq2"}, "D-Day: Normandy"},
{"-dday", "dday", "Quake2", {"dday/pak0.pak"}, Q2CFG, {"baseq2", "dday", "*fteq2"}, "D-Day: Normandy"},
#endif
#if defined(Q3CLIENT) || defined(Q3SERVER)
{"-quake3", "q3", "FTE-Quake3", {"baseq3/pak0.pk3"}, Q3CFG, {"baseq3", "*fteq3"}, "Quake III Arena", UPDATEURL(Q3)},
{"-quake3", "q3", "Quake3", {"baseq3/pak0.pk3"}, Q3CFG, {"baseq3", "*fteq3"}, "Quake III Arena", UPDATEURL(Q3)},
//the rest are not supported in any real way. maps-only mostly, if that
// {"-quake4", "q4", "FTE-Quake4", {"q4base/pak00.pk4"}, NULL, {"q4base", "*fteq4"}, "Quake 4"},
// {"-et", NULL, "FTE-EnemyTerritory", {"etmain/pak0.pk3"}, NULL, {"etmain", "*fteet"}, "Wolfenstein - Enemy Territory"},
@ -5897,12 +5899,14 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
char *oldprefix = "http://fte.";
char *newprefix = "https://updates.";
e = COM_ParseFunString(CON_WHITEMASK, ENGINEWEBSITE, musite, sizeof(musite), false);
COM_DeFunString(musite, e, site, sizeof(site), true, true);
COM_DeFunString(musite, e, site, sizeof(site)-1, true, true);
printf("site: %s\n", site);
if (!strncmp(site, oldprefix, strlen(oldprefix)))
{
memmove(site+strlen(newprefix), site+strlen(oldprefix), strlen(site)-strlen(oldprefix));
memmove(site+strlen(newprefix), site+strlen(oldprefix), strlen(site)-strlen(oldprefix)+1);
memcpy(site, newprefix, strlen(newprefix));
}
printf("site: %s\n", site);
Cmd_TokenizeString(va("downloadsurl \"%s%s\"", site, gamemode_info[i].downloadsurl), false, false);
}
else

View file

@ -56,6 +56,8 @@ typedef enum {
NP_WS,
NP_WSS,
NP_NATPMP, //server-only scheme for registering public ports.
NP_RTC_TCP,
NP_RTC_TLS, //really need a better way to do this than two copies of every protocol...
NP_INVALID
} netproto_t;
@ -164,8 +166,8 @@ char *NET_SockadrToString (char *s, int len, struct sockaddr_qstorage *a, size_
char *NET_BaseAdrToString (char *s, int len, netadr_t *a);
size_t NET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhint, struct sockaddr_qstorage *sadr, int *addrfamily, int *addrsize, size_t addrcount);
#define NET_StringToSockaddr(s,p,a,f,z) (NET_StringToSockaddr2(s,p,NA_INVALID,a,f,z,1)>0)
size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t addrcount);
#define NET_StringToAdr(s,p,a) NET_StringToAdr2(s,p,a,1)
size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t addrcount, const char **pathstart);
#define NET_StringToAdr(s,p,a) NET_StringToAdr2(s,p,a,1,NULL)
qboolean NET_PortToAdr (netadrtype_t adrfamily, netproto_t adrprot, const char *s, netadr_t *a);
qboolean NET_IsClientLegal(netadr_t *adr);
@ -211,6 +213,7 @@ typedef struct
qbyte nqunreliableonly; //nq can't cope with certain reliables some times. if 2, we have a reliable that result in a block (that should be sent). if 1, we are blocking. if 0, we can send reliables freely. if 3, then we just want to ignore clc_moves
#endif
qboolean pext_fragmentation; //fte's packet fragmentation extension, to avoid issues with low mtus.
qboolean pext_stunaware; //prevent the two lead-bits of packets from being either 0(stun), so stray stun packets cannot mess things up for us.
struct netprim_s netprim;
int mtu; //the path mtu, if known
int dupe; //how many times to dupe packets

View file

@ -28,6 +28,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define PACKET_HEADER 8
#define ANTISTUNBIAS 0x40000000 //adding this to sequences in the header ensures that we our packets will not get confused for stun or rtp packets.
/*
packet header
@ -225,6 +227,8 @@ unsigned int Net_PextMask(unsigned int protover, qboolean fornq)
if (mask & PEXT2_REPLACEMENTDELTAS)
mask |= PEXT2_NEWSIZEENCODING; //use if we can
mask |= PEXT2_STUNAWARE;
if (fornq)
{
//only ones that are tested
@ -736,6 +740,12 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate)
w1 = chan->outgoing_sequence | (send_reliable<<31);
w2 = chan->incoming_sequence | (chan->incoming_reliable_sequence<<31);
if (chan->pext_stunaware)
{
w1 = BigLong(w1+ANTISTUNBIAS);
w2 = BigLong(w2);
}
chan->outgoing_sequence++;
MSG_WriteLong (&send, w1);
@ -945,6 +955,15 @@ qboolean Netchan_Process (netchan_t *chan)
sequence = MSG_ReadLong ();
sequence_ack = MSG_ReadLong ();
if (chan->pext_stunaware)
{
sequence = BigLong(sequence);
if (!(sequence&ANTISTUNBIAS))
return false;
sequence -= ANTISTUNBIAS;
sequence_ack = BigLong(sequence_ack);
}
// skip over the qport if we are a server (its handled elsewhere)
#ifndef CLIENTONLY
if (chan->sock == NS_SERVER)

File diff suppressed because it is too large Load diff

View file

@ -634,9 +634,14 @@ static int SSL_DoHandshake(gnutlsfile_t *file)
//(e_again or e_intr)
if (!qgnutls_error_is_fatal(err))
return 0;
//certificate errors etc
qgnutls_perror (err);
if (developer.ival)
{
if (err == GNUTLS_E_FATAL_ALERT_RECEIVED)
; //peer doesn't like us.
else
//we didn't like the peer.
qgnutls_perror (err);
}
SSL_Close(&file->funcs);
// Con_Printf("%s: abort\n", file->certname);
@ -658,6 +663,9 @@ static int QDECL SSL_Read(struct vfsfile_s *f, void *buffer, int bytestoread)
return read;
}
if (!bytestoread) //gnutls doesn't like this.
return -1;
read = qgnutls_record_recv(file->session, buffer, bytestoread);
if (read < 0)
{
@ -675,7 +683,7 @@ static int QDECL SSL_Read(struct vfsfile_s *f, void *buffer, int bytestoread)
return 0; //caller is expected to try again later, no real need to loop here, just in case it repeats (eg E_AGAIN)
else
{
Con_Printf("TLS Read Error %i (bufsize %i)\n", read, bytestoread);
Con_Printf("TLS Read Warning %i (bufsize %i)\n", read, bytestoread);
return -1;
}
}
@ -702,7 +710,7 @@ static int QDECL SSL_Write(struct vfsfile_s *f, const void *buffer, int bytestow
return 0;
else
{
Con_Printf("TLS Send Error %i (%i bytes)\n", written, bytestowrite);
Con_Printf("TLS Send Warning %i (%i bytes)\n", written, bytestowrite);
return -1;
}
}
@ -738,8 +746,8 @@ static ssize_t SSL_Push(gnutls_transport_ptr_t p, const void *data, size_t size)
return -1;
}
qgnutls_transport_set_errno(file->session, done<0?errno:0);
if (done < 0)
return 0;
// if (done < 0)
// return 0;
return done;
}
static ssize_t SSL_Pull(gnutls_transport_ptr_t p, void *data, size_t size)
@ -753,10 +761,10 @@ static ssize_t SSL_Pull(gnutls_transport_ptr_t p, void *data, size_t size)
return -1;
}
qgnutls_transport_set_errno(file->session, done<0?errno:0);
if (done < 0)
{
return 0;
}
// if (done < 0)
// {
// return 0;
// }
return done;
}
@ -1058,6 +1066,15 @@ static qboolean SSL_InitConnection(gnutlsfile_t *newf, qboolean isserver, qboole
if (!isserver)
qgnutls_server_name_set(newf->session, GNUTLS_NAME_DNS, newf->certname, strlen(newf->certname));
/*else
{
size_t size = sizeof(newf->certname);
unsigned int type = GNUTLS_NAME_DNS;
int err;
err=qgnutls_server_name_get(newf->session, newf->certname, &size, &type, 0);
if (err!=GNUTLS_E_SUCCESS)
*newf->certname = 0;
}*/
qgnutls_session_set_ptr(newf->session, newf);

File diff suppressed because it is too large Load diff

View file

@ -226,7 +226,7 @@ struct icecandinfo_s
{
char candidateid[64];
char addr[64]; //v4/v6/fqdn. fqdn should prefer ipv6
int port;
int port; //native endian...
int transport; //0=udp. other values not supported
int foundation; //to figure out...
int component; //1-based. allows rtp+rtcp in a single ICE... we only support one.
@ -289,8 +289,8 @@ extern icefuncs_t iceapi;
typedef struct ftenet_generic_connection_s {
char name[MAX_QPATH];
int (*GetLocalAddresses)(struct ftenet_generic_connection_s *con, unsigned int *adrflags, netadr_t *addresses, int maxaddresses);
qboolean (*ChangeLocalAddress)(struct ftenet_generic_connection_s *con, netadr_t *newadr);
int (*GetLocalAddresses)(struct ftenet_generic_connection_s *con, unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses);
qboolean (*ChangeLocalAddress)(struct ftenet_generic_connection_s *con, const char *addressstring, netadr_t *newadr);
qboolean (*GetPacket)(struct ftenet_generic_connection_s *con);
neterr_t (*SendPacket)(struct ftenet_generic_connection_s *con, int length, const void *data, netadr_t *to);
void (*Close)(struct ftenet_generic_connection_s *con);
@ -376,11 +376,33 @@ void ICE_Tick(void);
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(qboolean isserver, const char *address, netadr_t adr);
enum icemsgtype_s
{ //shared by rtcpeers+broker
ICEMSG_PEERDROP=0, //other side dropped connection
ICEMSG_GREETING=1, //master telling us our unique game name
ICEMSG_NEWPEER=2, //relay established, send an offer now.
ICEMSG_OFFER=3, //peer's initial details
ICEMSG_CANDIDATE=4, //candidate updates. may arrive late as new ones are discovered.
ICEMSG_ACCEPT=5, //go go go (response from offer)
ICEMSG_SERVERINFO=6,//server->broker (for advertising the server properly)
ICEMSG_SERVERUPDATE=7,//broker->browser (for querying available server lists)
};
enum websocketpackettype_e
{ //websocket packet types, used by both our tcp/http/broker/etc server and our ice client.
WS_PACKETTYPE_CONTINUATION=0,
WS_PACKETTYPE_TEXTFRAME=1,
WS_PACKETTYPE_BINARYFRAME=2,
WS_PACKETTYPE_CLOSE=8,
WS_PACKETTYPE_PING=9,
WS_PACKETTYPE_PONG=10,
};
ftenet_connections_t *FTENET_CreateCollection(qboolean listen);
void FTENET_CloseCollection(ftenet_connections_t *col);
qboolean FTENET_AddToCollection(struct ftenet_connections_s *col, const char *name, const char *address, netadrtype_t addrtype, netproto_t addrprot);
int NET_EnumerateAddresses(ftenet_connections_t *collection, struct ftenet_generic_connection_s **con, unsigned int *adrflags, netadr_t *addresses, int maxaddresses);
int NET_EnumerateAddresses(ftenet_connections_t *collection, struct ftenet_generic_connection_s **con, unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses);
void *TLS_GetKnownCertificate(const char *certname, size_t *size);
vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server);

View file

@ -1515,7 +1515,7 @@ void Plug_Close(plugin_t *plug)
prev->next = plug->next;
}
if (!com_workererror)
if (!com_workererror && plug->lib)
Con_DPrintf("Closing plugin %s\n", plug->name);
//ensure any active contexts provided by the plugin are closed (stuff with destroy callbacks)
@ -1893,10 +1893,12 @@ static void *QDECL PlugBI_GetEngineInterface(const char *interfacename, size_t s
Plug_GetTeamInfo,
Plug_GetWeaponStats,
Plug_GetTrackerOwnFrags,
Plug_GetPredInfo,
#else
NULL,
NULL,
NULL,
NULL,
#endif
};
if (structsize == sizeof(funcs))

View file

@ -82,8 +82,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define PEXT2_PREDINFO 0x00000020 //movevar stats, NQ input sequences+acks.
#define PEXT2_NEWSIZEENCODING 0x00000040 //richer size encoding.
#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_NEWINTENTS 0x00000100 //clc_move changes, more buttons etc
#define PEXT2_CLIENTSUPPORT (PEXT2_PRYDONCURSOR|PEXT2_VOICECHAT|PEXT2_SETANGLEDELTA|PEXT2_REPLACEMENTDELTAS|PEXT2_MAXPLAYERS|PEXT2_PREDINFO|PEXT2_NEWSIZEENCODING|PEXT2_INFOBLOBS)
#define PEXT2_STUNAWARE 0x00000100 //changes the netchan to biased-bigendian (so lead two bits are 1 and not stun's 0, so we don't get confused)
//#define PEXT2_NEWINTENTS 0x00000200 //clc_move changes, more buttons etc
#define PEXT2_CLIENTSUPPORT (PEXT2_PRYDONCURSOR|PEXT2_VOICECHAT|PEXT2_SETANGLEDELTA|PEXT2_REPLACEMENTDELTAS|PEXT2_MAXPLAYERS|PEXT2_PREDINFO|PEXT2_NEWSIZEENCODING|PEXT2_INFOBLOBS|PEXT2_STUNAWARE)
//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...
@ -150,6 +151,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define PORT_Q2SERVER 27910
#define PORT_Q3MASTER 27950
#define PORT_Q3SERVER 27960
#define PORT_ICEBROKER PORT_DPMASTER
#ifdef GAME_DEFAULTPORT
#define PORT_DEFAULTSERVER GAME_DEFAULTPORT

View file

@ -560,6 +560,7 @@ void MSG_WriteBits(sizebuf_t *msg, int value, int bits)
#define MAX_PACKETLEN 1400
#define STUNDEMULTIPLEX_MASK 0x40000000 //stun requires that we set this bit to avoid surprises, and ignore packets without it.
#define FRAGMENT_MASK 0x80000000
#define FRAGMENTATION_TRESHOLD (MAX_PACKETLEN-100)
qboolean Netchan_ProcessQ3 (netchan_t *chan)
@ -575,6 +576,15 @@ qboolean Netchan_ProcessQ3 (netchan_t *chan)
MSG_BeginReading(msg_nullnetprim);
sequence = MSG_ReadBits(32);
if (chan->pext_stunaware)
{
sequence = BigLong(sequence);
if (sequence & STUNDEMULTIPLEX_MASK)
sequence-= STUNDEMULTIPLEX_MASK;
else
return false;
}
// Read the qport if we are a server
if (chan->sock == NS_SERVER)
{
@ -710,13 +720,20 @@ void Netchan_TransmitNextFragment( netchan_t *chan )
sizebuf_t send;
qbyte send_buf[MAX_PACKETLEN];
int fragmentLength;
int sequence = chan->outgoing_sequence | FRAGMENT_MASK;
if (chan->pext_stunaware)
{
sequence+= STUNDEMULTIPLEX_MASK;
sequence = BigLong(sequence);
}
// Write the packet header
memset(&send, 0, sizeof(send));
send.packing = SZ_RAWBYTES;
send.maxsize = sizeof(send_buf);
send.data = send_buf;
MSG_WriteLong( &send, chan->outgoing_sequence | FRAGMENT_MASK );
MSG_WriteLong( &send, sequence );
#ifndef SERVERONLY
// Send the qport if we are a client
if( chan->sock == NS_CLIENT )
@ -773,6 +790,7 @@ void Netchan_TransmitQ3( netchan_t *chan, int length, const qbyte *data )
sizebuf_t send;
qbyte send_buf[MAX_OVERALLMSGLEN+6];
char adr[MAX_ADR_SIZE];
int sequence;
// Check for message overflow
if( length > MAX_OVERALLMSGLEN )
@ -810,6 +828,13 @@ void Netchan_TransmitQ3( netchan_t *chan, int length, const qbyte *data )
return;
}
sequence = chan->outgoing_sequence;
if (chan->pext_stunaware)
{
sequence+= STUNDEMULTIPLEX_MASK;
sequence = BigLong(sequence);
}
// Write the packet header
memset(&send, 0, sizeof(send));
send.packing = SZ_RAWBYTES;

View file

@ -195,6 +195,7 @@ void *ZF_Malloc(size_t size)
return ret;
#elif defined(__linux__)
void *ret = NULL;
if (!posix_memalign(&ret, max(sizeof(float)*4, sizeof(void*)), size))
memset(ret, 0, size);
return ret;

View file

@ -759,9 +759,14 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e
{
texnums_t *tex = shader->defaulttextures;
//do this for the loading case too, in the hope that it'll avoid generating a per-player skin at all
if ((tex->loweroverlay && (tex->loweroverlay->status == TEX_LOADING || tex->loweroverlay->status == TEX_LOADED)) ||
if ((tex->base && (tex->base->status == TEX_LOADING)) ||
(tex->loweroverlay && (tex->loweroverlay->status == TEX_LOADING || tex->loweroverlay->status == TEX_LOADED)) ||
(tex->upperoverlay && (tex->upperoverlay->status == TEX_LOADING || tex->upperoverlay->status == TEX_LOADED)))
return shader;
//if we've got a replacement texture (read: its size differs from the proper skin size) then don't use the base texels for colourmapping.
if (tex->base && skins &&
(tex->base->width!=skins->skinwidth || tex->base->height!=skins->skinheight))
return shader;
}
if ((shader->flags & SHADER_HASTOPBOTTOM) && !h2playertranslations)
{ //this shader will try to do top+bottom colours. this means we can generate only a black image, with separate top+bottom textures.

View file

@ -2211,7 +2211,7 @@ void Terr_FreeModel(model_t *mod)
}
#ifdef RUNTIMELIGHTING
if (hm->relightcontext)
LightShutdown(hm->relightcontext, mod);
LightShutdown(hm->relightcontext);
if (hm->lightthreadmem && !hm->inheritedlightthreadmem)
BZ_Free(hm->lightthreadmem);
#endif

View file

@ -83,32 +83,7 @@ void Mod_SortShaders(model_t *mod);
void Mod_LoadAliasShaders(model_t *mod);
#ifdef RUNTIMELIGHTING
model_t *lightmodel;
static struct relight_ctx_s *lightcontext;
static int numlightdata;
static qboolean writelitfile;
long relitsurface;
#ifndef MULTITHREAD
static void Mod_UpdateLightmap(int snum)
{
msurface_t *s;
if (lightmodel)
{
// int i;
// for (s = lightmodel->surfaces,i=0; i < lightmodel->numsurfaces; i++,s++)
// s->cached_dlight = -1;
if (snum < lightmodel->numsurfaces)
{
s = lightmodel->surfaces + snum;
s->cached_dlight = -1;
}
else
Con_Printf("lit non-existant surface\n");
}
}
#endif
extern model_t *lightmodel;
#endif
static void Mod_MemList_f(void)
@ -304,167 +279,6 @@ static void Mod_BlockTextureColour_f (void)
}
#endif
#ifdef RUNTIMELIGHTING
#if defined(MULTITHREAD)
#ifdef _WIN32
#include <windows.h>
#elif defined(__linux__)
#include <unistd.h>
#endif
static void *relightthread[8];
static unsigned int relightthreads;
static volatile qboolean wantrelight;
static int RelightThread(void *arg)
{
int surf;
void *threadctx = malloc(lightthreadctxsize);
while (wantrelight)
{
#ifdef _WIN32
surf = InterlockedIncrement(&relitsurface);
#elif defined(__GNUC__)
surf = __sync_add_and_fetch(&relitsurface, 1);
#else
surf = relitsurface++;
#endif
if (surf >= lightmodel->numsurfaces)
break;
LightFace(lightcontext, threadctx, surf);
lightmodel->surfaces[surf].cached_dlight = -1;
}
free(threadctx);
return 0;
}
#else
static void *lightmainthreadctx;
#endif
#endif
void Mod_Think (void)
{
#ifdef RUNTIMELIGHTING
if (lightmodel)
{
#ifdef MULTITHREAD
if (!relightthreads)
{
int i;
#if defined(_WIN32) && !defined(WINRT)
HANDLE me = GetCurrentProcess();
DWORD_PTR proc, sys;
/*count cpus*/
GetProcessAffinityMask(me, &proc, &sys);
relightthreads = 0;
for (i = 0; i < sizeof(proc)*8; i++)
if (proc & ((size_t)1u<<i))
relightthreads++;
/*subtract 1*/
if (relightthreads <= 1)
relightthreads = 1;
else
relightthreads--;
#elif defined(__GNUC__)
#ifdef __linux__
relightthreads = sysconf(_SC_NPROCESSORS_ONLN)-1;
if (relightthreads < 1)
relightthreads = 1;
#else
relightthreads = 2; //erm, lets hope...
#endif
#else
/*can't do atomics*/
relightthreads = 1;
#endif
if (relightthreads > sizeof(relightthread)/sizeof(relightthread[0]))
relightthreads = sizeof(relightthread)/sizeof(relightthread[0]);
wantrelight = true;
for (i = 0; i < relightthreads; i++)
relightthread[i] = Sys_CreateThread("relight", RelightThread, lightmodel, THREADP_NORMAL, 0);
}
if (relitsurface < lightmodel->numsurfaces)
{
return;
}
#else
if (!lightmainthreadctx)
lightmainthreadctx = malloc(lightthreadctxsize);
LightFace(lightcontext, lightmainthreadctx, relitsurface);
Mod_UpdateLightmap(relitsurface);
relitsurface++;
#endif
if (relitsurface >= lightmodel->numsurfaces)
{
vfsfile_t *f;
char filename[MAX_QPATH];
Con_Printf("Finished lighting %s\n", lightmodel->name);
#ifdef MULTITHREAD
if (relightthreads)
{
int i;
wantrelight = false;
for (i = 0; i < relightthreads; i++)
{
Sys_WaitOnThread(relightthread[i]);
relightthread[i] = NULL;
}
relightthreads = 0;
}
#else
free(lightmainthreadctx);
lightmainthreadctx = NULL;
#endif
LightShutdown(lightcontext, lightmodel);
lightcontext = NULL;
if (lightmodel->deluxdata)
{
COM_StripExtension(lightmodel->name, filename, sizeof(filename));
COM_DefaultExtension(filename, ".lux", sizeof(filename));
f = FS_OpenVFS(filename, "wb", FS_GAME);
if (f)
{
VFS_WRITE(f, "QLIT\1\0\0\0", 8);
VFS_WRITE(f, lightmodel->deluxdata, numlightdata*3);
VFS_CLOSE(f);
}
else
Con_Printf("Unable to write \"%s\"\n", filename);
}
if (writelitfile) //the user might already have a lit file (don't overwrite it).
{
COM_StripExtension(lightmodel->name, filename, sizeof(filename));
COM_DefaultExtension(filename, ".lit", sizeof(filename));
f = FS_OpenVFS(filename, "wb", FS_GAME);
if (f)
{
if (lightmodel->lightmaps.fmt == LM_E5BGR9)
{
VFS_WRITE(f, "QLIT\x01\0\x01\0", 8);
VFS_WRITE(f, lightmodel->lightdata, numlightdata*4);
}
else
{
VFS_WRITE(f, "QLIT\1\0\0\0", 8);
VFS_WRITE(f, lightmodel->lightdata, numlightdata*3);
}
VFS_CLOSE(f);
}
else
Con_Printf("Unable to write \"%s\"\n", filename);
}
lightmodel = NULL;
}
}
#endif
}
void Mod_RebuildLightmaps (void)
{
int i, j;
@ -663,20 +477,7 @@ static int mod_datasequence;
void Mod_ClearAll (void)
{
#ifdef RUNTIMELIGHTING
#ifdef MULTITHREAD
int i;
wantrelight = false;
for (i = 0; i < relightthreads; i++)
{
Sys_WaitOnThread(relightthread[i]);
relightthread[i] = NULL;
}
relightthreads = 0;
#else
free(lightmainthreadctx);
lightmainthreadctx = NULL;
#endif
lightmodel = NULL;
RelightTerminate(NULL);
#endif
mod_datasequence++;
@ -692,23 +493,7 @@ qboolean Mod_PurgeModel(model_t *mod, enum mod_purge_e ptype)
}
#ifdef RUNTIMELIGHTING
if (lightmodel == mod)
{
#ifdef MULTITHREAD
int i;
wantrelight = false;
for (i = 0; i < relightthreads; i++)
{
Sys_WaitOnThread(relightthread[i]);
relightthread[i] = NULL;
}
relightthreads = 0;
#else
free(lightmainthreadctx);
lightmainthreadctx = NULL;
#endif
lightmodel = NULL;
}
RelightTerminate(mod);
#endif
#ifdef TERRAIN
@ -1793,6 +1578,9 @@ void Mod_LoadLighting (model_t *loadmodel, bspx_header_t *bspx, qbyte *mod_base,
qbyte *lumdata = NULL; //l8
qbyte *out;
unsigned int samples;
#ifdef RUNTIMELIGHTING
qboolean relighting = false;
#endif
extern cvar_t gl_overbright;
loadmodel->lightmaps.fmt = LM_L8;
@ -2116,28 +1904,23 @@ void Mod_LoadLighting (model_t *loadmodel, bspx_header_t *bspx, qbyte *mod_base,
#ifdef RUNTIMELIGHTING
if ((loadmodel->type == mod_brush && loadmodel->fromgame == fg_quake) || loadmodel->type == mod_heightmap)
{ //we only support a couple of formats. :(
if (!lightmodel && r_loadlits.value >= 2 && ((!litdata&&!expdata) || (!luxdata && r_deluxemapping)))
if (r_loadlits.value >= 2 && ((!litdata&&!expdata) || (!luxdata && r_deluxemapping)))
{
writelitfile = !litdata&&!expdata;
numlightdata = l->filelen;
lightmodel = loadmodel;
relitsurface = 0;
relighting = RelightSetup(loadmodel, l->filelen, !litdata&&!expdata);
}
else if (!lightmodel && r_deluxemapping_cvar.value>1 && r_deluxemapping && !luxdata
else if (r_deluxemapping_cvar.value>1 && r_deluxemapping && !luxdata
#ifdef RTLIGHTS
&& !(r_shadow_realtime_world.ival && r_shadow_realtime_world_lightmaps.value<=0)
#endif
)
{ //if deluxemapping is on, generate missing lux files a little more often, but don't bother if we have rtlights on anyway.
writelitfile = false;
numlightdata = l->filelen;
lightmodel = loadmodel;
relitsurface = 0;
relighting = RelightSetup(loadmodel, l->filelen, false);
}
}
/*if we're relighting, make sure there's the proper lit data to be updated*/
if (lightmodel == loadmodel && !litdata && !expdata)
if (relighting && !litdata && !expdata)
{
int i;
unsigned int *ergb;
@ -2171,7 +1954,7 @@ void Mod_LoadLighting (model_t *loadmodel, bspx_header_t *bspx, qbyte *mod_base,
}
}
/*if we're relighting, make sure there's the proper lux data to be updated*/
if (lightmodel == loadmodel && r_deluxemapping && !luxdata)
if (relighting && r_deluxemapping && !luxdata)
{
int i;
luxdata = ZG_Malloc(&loadmodel->memgroup, samples*3);
@ -5609,14 +5392,6 @@ TRACE(("LoadBrushModel %i\n", __LINE__));
}
TRACE(("LoadBrushModel %i\n", __LINE__));
}
#ifdef RUNTIMELIGHTING
TRACE(("LoadBrushModel %i\n", __LINE__));
if (lightmodel == mod)
{
lightcontext = LightStartup(NULL, lightmodel, true, !writelitfile);
LightReloadEntities(lightcontext, Mod_GetEntitiesString(lightmodel), false);
}
#endif
TRACE(("LoadBrushModel %i\n", __LINE__));
if (!isDedicated)
Mod_FixupMinsMaxs(mod);

View file

@ -5380,7 +5380,7 @@ done:;
Shader_SetBlendmode (pass, i?pass-1:NULL);
if (pass->blendmode == PBM_ADD)
if (pass->blendmode == PBM_ADD && !s->defaulttextures->fullbright)
s->defaulttextures->fullbright = pass->anim_frames[0];
}

View file

@ -2328,6 +2328,9 @@ static void Sh_GenShadowFace(dlight_t *l, vec3_t axis[3], int lighttype, shadowm
if (lighttype & LSHADER_ORTHO)
r_refdef.frustum_numplanes = 4; //kill the near clip plane - we allow ANYTHING nearer through.
if (lighttype & LSHADER_FAKESHADOWS)
r_refdef.flipcull ^= SHADER_CULL_FLIP;
#ifdef SHADOWDBG_COLOURNOTDEPTH
BE_SelectMode(BEM_STANDARD);
#else
@ -2806,10 +2809,7 @@ void Sh_GenerateFakeShadows(void) //generates shadowmaps and selects the dlight,
if (!BE_SelectDLight(l, vec3_origin, l->axis, LSHADER_SMAP|LSHADER_ORTHO))
return;
if (!Sh_GenShadowMap(l, LSHADER_SMAP|LSHADER_ORTHO|LSHADER_FAKESHADOWS, l->axis, NULL, smsize, texwidth))
return;
RQuantAdd(RQUANT_RTLIGHT_DRAWN, 1);
Sh_GenShadowMap(l, LSHADER_SMAP|LSHADER_ORTHO|LSHADER_FAKESHADOWS, l->axis, NULL, smsize, texwidth);
}
static void Sh_DrawShadowMapLight(dlight_t *l, vec3_t colour, vec3_t axis[3], qbyte *vvis)

View file

@ -19,7 +19,10 @@ typedef struct mentity_s {
struct relight_ctx_s
{
unsigned int nummodels;
model_t *models[2048];
model_t *models[2048]; //0 is the worldmodel and must be valid
qboolean parsed; //ents have been parsed okay.
qboolean loaded; //needed models are all loaded.
float minlight;
qboolean skiplit; //lux only
@ -27,6 +30,8 @@ struct relight_ctx_s
mentity_t *entities;
unsigned int num_entities;
unsigned int max_entities;
size_t lightmapsamples;
unsigned long nextface;
};
@ -120,19 +125,8 @@ static void ParseEpair (mentity_t *mapent, char *key, char *value)
}
}
void LightShutdown(struct relight_ctx_s *ctx, model_t *mod)
void LightShutdown(struct relight_ctx_s *ctx)
{
qboolean stillheld = false;
unsigned int i;
for (i = 0; i < ctx->nummodels; i++)
{
if (ctx->models[i] == mod)
ctx->models[i] = NULL;
if (ctx->models[i])
stillheld = true;
}
if (stillheld)
return;
Z_Free(ctx->entities);
Z_Free(ctx);
}
@ -979,7 +973,7 @@ void LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *l, lightstylein
}
}
}
void LightFace (struct relight_ctx_s *ctx, struct llightinfo_s *threadctx, int facenum)
static void LightFace (struct relight_ctx_s *ctx, struct llightinfo_s *threadctx, int facenum)
{
dface_t *f = ctx->models[0]->surfaces + facenum;
vec4_t plane;
@ -1005,4 +999,243 @@ void LightFace (struct relight_ctx_s *ctx, struct llightinfo_s *threadctx, int f
else
LightPlane(ctx, threadctx, f->styles, NULL, f->samples, f->samples - ctx->models[0]->lightdata + ctx->models[0]->deluxdata, plane, f->texinfo->vecs, exactmins, exactmaxs, texmins, texsize, 1<<f->lmshift);
}
#if defined(MULTITHREAD)
#ifdef _WIN32
#include <windows.h>
#elif defined(__linux__)
#include <unistd.h>
#endif
static void *relightthread[8];
static unsigned int relightthreads;
static volatile qboolean wantrelight;
static struct relight_ctx_s *lightcontext;
static int RelightThread(void *ctx)
{
int surf;
struct relight_ctx_s *lightcontext = ctx;
model_t *lightmodel = lightcontext->models[0];
void *threadctx = malloc(lightthreadctxsize);
while (wantrelight)
{
#ifdef _WIN32
surf = InterlockedIncrement(&lightcontext->nextface);
#elif defined(__GNUC__)
surf = __sync_add_and_fetch(&lightcontext->nextface, 1);
#else
surf = relitsurface++;
#endif
if (surf >= lightmodel->numsurfaces)
break;
LightFace(lightcontext, threadctx, surf);
lightmodel->surfaces[surf].cached_dlight = -1; //invalidate it (slightly racey buy w/e
}
free(threadctx);
return 0;
}
#else
static void *lightmainthreadctx;
#endif
void RelightTerminate(model_t *mod)
{
model_t *lightmodel;
size_t u;
if (!lightcontext)
return;
//if one of the models we're using is being purged then we have to abort the relight to avoid caching partial results (especially if its the model we're actually relighting)
if (mod)
{
for (u = 0; u < lightcontext->nummodels; u++)
if (lightcontext->models[u] == mod)
break;
}
else
u = 0;
if (u < lightcontext->nummodels)
{
lightmodel = lightcontext->models[0];
#ifdef MULTITHREAD
wantrelight = false;
if (relightthreads)
{
int i;
wantrelight = false;
for (i = 0; i < relightthreads; i++)
{
Sys_WaitOnThread(relightthread[i]);
relightthread[i] = NULL;
}
relightthreads = 0;
}
#else
free(lightmainthreadctx);
lightmainthreadctx = NULL;
#endif
if (lightcontext->nextface >= lightmodel->numsurfaces)
{
vfsfile_t *f;
char filename[MAX_QPATH];
if (lightmodel->deluxdata)
{
COM_StripExtension(lightmodel->name, filename, sizeof(filename));
COM_DefaultExtension(filename, ".lux", sizeof(filename));
f = FS_OpenVFS(filename, "wb", FS_GAME);
if (f)
{
VFS_WRITE(f, "QLIT\1\0\0\0", 8);
VFS_WRITE(f, lightmodel->deluxdata, lightcontext->lightmapsamples*3);
VFS_CLOSE(f);
}
else
Con_Printf("Unable to write \"%s\"\n", filename);
}
if (!lightcontext->skiplit) //the user might already have a lit file (don't overwrite it).
{
COM_StripExtension(lightmodel->name, filename, sizeof(filename));
COM_DefaultExtension(filename, ".lit", sizeof(filename));
f = FS_OpenVFS(filename, "wb", FS_GAME);
if (f)
{
if (lightmodel->lightmaps.fmt == LM_E5BGR9)
{
VFS_WRITE(f, "QLIT\x01\0\x01\0", 8);
VFS_WRITE(f, lightmodel->lightdata, lightcontext->lightmapsamples*4);
}
else
{
VFS_WRITE(f, "QLIT\1\0\0\0", 8);
VFS_WRITE(f, lightmodel->lightdata, lightcontext->lightmapsamples*3);
}
VFS_CLOSE(f);
}
else
Con_Printf("Unable to write \"%s\"\n", filename);
}
}
else
Con_Printf("Relighting aborted before completion\n");
LightShutdown(lightcontext);
lightcontext = NULL;
}
}
qboolean RelightSetup (model_t *model, size_t lightsamples, qboolean generatelit)
{
Sys_LockMutex(com_resourcemutex); //models can be loaded on different threads, so no race conditions please.
if (!lightcontext)
{
lightcontext = LightStartup(NULL, model, true, !generatelit);
lightcontext->lightmapsamples = lightsamples;
Sys_UnlockMutex(com_resourcemutex);
return true;
}
Sys_UnlockMutex(com_resourcemutex);
return false;
}
const char *RelightGetProgress(float *progress)
{
char filename[MAX_QPATH];
if (!lightcontext)
return NULL;
*progress = (lightcontext->nextface*100.0f) / lightcontext->models[0]->numsurfaces;
COM_StripExtension(lightcontext->models[0]->name, filename, sizeof(filename));
COM_DefaultExtension(filename, lightcontext->skiplit?".lux":".lit", sizeof(filename));
return va("%s", filename);
}
void RelightThink (void)
{
if (lightcontext)
{
model_t *lightmodel = lightcontext->models[0];
if (!lightcontext->loaded)
{ //make sure everything finished loading properly before we start poking things.
size_t u;
if (!lightcontext->parsed)
{
if (lightcontext->models[0]->loadstate != MLS_LOADED)
return; //not ready yet...
LightReloadEntities(lightcontext, Mod_GetEntitiesString(lightmodel), false);
lightcontext->parsed = true;
}
for (u = 0; u < lightcontext->nummodels; u++)
if (lightcontext->models[u]->loadstate != MLS_LOADED)
return;
lightcontext->loaded = true;
}
#ifdef MULTITHREAD
if (!relightthreads)
{
int i;
#if defined(_WIN32) && !defined(WINRT)
HANDLE me = GetCurrentProcess();
DWORD_PTR proc, sys;
/*count cpus*/
GetProcessAffinityMask(me, &proc, &sys);
relightthreads = 0;
for (i = 0; i < sizeof(proc)*8; i++)
if (proc & ((size_t)1u<<i))
relightthreads++;
/*subtract 1*/
if (relightthreads <= 1)
relightthreads = 1;
else
relightthreads--;
#elif defined(__GNUC__)
#ifdef __linux__
relightthreads = sysconf(_SC_NPROCESSORS_ONLN)-1;
if (relightthreads < 1)
relightthreads = 1;
#else
relightthreads = 2; //erm, lets hope...
#endif
#else
/*can't do atomics*/
relightthreads = 1;
#endif
if (relightthreads > sizeof(relightthread)/sizeof(relightthread[0]))
relightthreads = sizeof(relightthread)/sizeof(relightthread[0]);
wantrelight = true;
for (i = 0; i < relightthreads; i++)
relightthread[i] = Sys_CreateThread("relight", RelightThread, lightcontext, THREADP_NORMAL, 0);
}
if (lightcontext->nextface < lightmodel->numsurfaces)
{
return;
}
#else
if (!lightmainthreadctx)
lightmainthreadctx = malloc(lightthreadctxsize);
LightFace(lightcontext, lightmainthreadctx, relitsurface);
lightmodel->surfaces[relitsurface].cached_dlight = -1;
relitsurface++;
#endif
if (lightcontext->nextface >= lightmodel->numsurfaces)
{
Con_Printf("Finished lighting %s\n", lightmodel->name);
RelightTerminate(lightmodel);
}
}
}
#endif

View file

@ -828,7 +828,7 @@ static qboolean HTTP_DL_Work(struct dl_download *dl)
con->file = dl->file;
con->state = HC_GETTING;
dl->status = DL_ACTIVE;
//Fall through
goto firstread; //oh noes! an evil goto! this is the easiest way to avoid the 'return' here when no data follows a 0-byte download
case HC_GETTING:
if (con->bufferlen - con->bufferused < 1530)
ExpandBuffer(con, 1530);
@ -851,6 +851,7 @@ static qboolean HTTP_DL_Work(struct dl_download *dl)
con->bufferused+=ammount;
firstread:
if (con->chunking)
{
//9\r\n
@ -1053,7 +1054,7 @@ void HTTPDL_Establish(struct dl_download *dl)
netadr_t adr = {0};
//fixme: support more than one address possibility?
//https uses a different default port
if (NET_StringToAdr2(con->server, https?443:80, &adr, 1))
if (NET_StringToAdr2(con->server, https?443:80, &adr, 1, NULL))
con->sock = TCP_OpenStream(&adr);
con->stream = FS_OpenTCPSocket(con->sock, true, con->server);
}

View file

@ -179,7 +179,7 @@ Marks the edict as free
FIXME: walk all entities and NULL out references to this entity
=================
*/
void PDECL ED_Free (pubprogfuncs_t *ppf, struct edict_s *ed)
void PDECL ED_Free (pubprogfuncs_t *ppf, struct edict_s *ed, pbool instant)
{
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
edictrun_t *e = (edictrun_t *)ed;
@ -201,7 +201,7 @@ void PDECL ED_Free (pubprogfuncs_t *ppf, struct edict_s *ed)
return;
e->ereftype = ER_FREE;
e->freetime = (float)*externs->gametime;
e->freetime = instant?0:(float)*externs->gametime;
/*
ed->v.model = 0;

View file

@ -403,7 +403,7 @@ void *PRHunkAlloc(progfuncs_t *progfuncs, int ammount, const char *name);
void PR_Profile_f (void);
struct edict_s *PDECL ED_Alloc (pubprogfuncs_t *progfuncs, pbool object, size_t extrasize);
void PDECL ED_Free (pubprogfuncs_t *progfuncs, struct edict_s *ed);
void PDECL ED_Free (pubprogfuncs_t *progfuncs, struct edict_s *ed, pbool instant);
pbool PR_RunGC (progfuncs_t *progfuncs);
string_t PDECL PR_AllocTempString (pubprogfuncs_t *ppf, const char *str);

View file

@ -105,7 +105,7 @@ struct pubprogfuncs_s
void (PDECL *PrintEdict) (pubprogfuncs_t *prinst, struct edict_s *ed); //get a listing of all vars on an edict (sent back via 'print')
struct edict_s *(PDECL *EntAlloc) (pubprogfuncs_t *prinst, pbool object, size_t extrasize);
void (PDECL *EntFree) (pubprogfuncs_t *prinst, struct edict_s *ed);
void (PDECL *EntFree) (pubprogfuncs_t *prinst, struct edict_s *ed, pbool instant);
struct edict_s *(PDECL *EdictNum) (pubprogfuncs_t *prinst, unsigned int n); //get the nth edict
unsigned int (PDECL *NumForEdict) (pubprogfuncs_t *prinst, struct edict_s *e); //so you can find out what that 'n' will be
@ -279,7 +279,7 @@ typedef union eval_s
#define PR_RegisterFieldVar(pf,type,name,reqofs,qcofs) (*pf->RegisterFieldVar) (pf,type,name,reqofs,qcofs)
#define ED_Alloc(pf,isobj,extsize) (*pf->EntAlloc) (pf, isobj, extsize)
#define ED_Free(pf, ed) (*pf->EntFree) (pf, ed)
#define ED_Free(pf, ed) (*pf->EntFree) (pf, ed, false)
#define ED_Clear(pf, ed) (*pf->EntClear) (pf, ed)
#define PR_LoadEnts(pf, s, ctx, entcb, extcb) (*pf->load_ents) (pf, s, ctx, entcb, extcb)

View file

@ -4288,9 +4288,27 @@ static void QCBUILTIN PF_Remove (pubprogfuncs_t *prinst, struct globalvars_s *pr
return; //yeah, alright, so this is hacky.
}
ED_Free (prinst, ed);
prinst->EntFree (prinst, ed, false);
}
static void QCBUILTIN PF_RemoveInstant (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
edict_t *ed;
ed = G_EDICT(prinst, OFS_PARM0);
if (ED_ISFREE(ed))
{
ED_CanFree(ed); //fake it
if (developer.value)
{
Con_Printf("Tried removing free entity at:\n");
PR_StackTrace(prinst, false);
}
return; //yeah, alright, so this is hacky.
}
prinst->EntFree (prinst, ed, true);
}
/*
@ -10511,7 +10529,8 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"vlen", PF_vlen, 12, 12, 12, 0, D("float(vector v)", "Returns the square root of the dotproduct of a vector with itself. Or in other words the length of a distance vector, in quake units.")},
{"vectoyaw", PF_vectoyaw, 13, 13, 13, 0, D("float(vector v, optional entity reference)", "Given a direction vector, returns the yaw angle in which that direction vector points. If an entity is passed, the yaw angle will be relative to that entity's gravity direction.")},
{"spawn", PF_Spawn, 14, 14, 14, 0, D("entity()", "Adds a brand new entity into the world! Hurrah, you're now a parent!")},
{"remove", PF_Remove, 15, 15, 15, 0, D("void(entity e)", "Destroys the given entity and clears some limited fields (including model, modelindex, solid, classname). Any references to the entity following the call are an error. After two seconds, the entity will be reused, in the interim you can unfortunatly still read its fields to see if the reference is no longer valid.")},
{"remove", PF_Remove, 15, 15, 15, 0, D("void(entity e)", "Destroys the given entity and clears some limited fields (including model, modelindex, solid, classname). Any references to the entity following the call are an error. After half a second the entity will be reused, in the interim you can unfortunatly still read its fields to see if the reference is no longer valid.")},
{"removeinstant", PF_RemoveInstant, 0, 0, 0, 0, D("void(entity e)", "Same thing as the regular remove builtin, but bypasses the half-second rule. The entity slot may be reused instantly. Be CERTAIN that you have no lingering references, because if they're followed they will end up poking an entirely different type of entity! So only use this where you're sure its safe.")},
{"traceline", PF_svtraceline, 16, 16, 16, 0, D("void(vector v1, vector v2, float flags, entity ent)", "Traces a thin line through the world from v1 towards v2.\nWill not collide with ent, ent.owner, or any entity who's owner field refers to ent.\nThe passed entity will also be used to determine whether to use a capsule trace, the contents that the trace should impact, and a couple of other extra fields that define the trace.\nThere are no side effects beyond the trace_* globals being written.\nflags&MOVE_NOMONSTERS will not impact on non-bsp entities.\nflags&MOVE_MISSILE will impact with increased size.\nflags&MOVE_HITMODEL will impact upon model meshes, instead of their bounding boxes.\nflags&MOVE_TRIGGERS will also stop on triggers\nflags&MOVE_EVERYTHING will stop if it hits anything, even non-solid entities.\nflags&MOVE_LAGGED will backdate entity positions for the purposes of this builtin according to the indicated player ent's latency, to provide lag compensation.")},
{"checkclient", PF_checkclient, 17, 17, 17, 0, D("entity()", "Returns one of the player entities. The returned player will change periodically.")},
{"find", PF_FindString, 18, 18, 18, 0, D("entity(entity start, .string fld, string match)", "Scan for the next entity with a given field set to the given 'match' value. start should be either world, or the previous entity that was found. Returns world on failure/if there are no more.\nIf you have many many entities then you may find that hashtables will give more performance (but requires extra upkeep).")},

View file

@ -460,12 +460,12 @@ static void QDECL Q1QVMPF_ClearEdict(pubprogfuncs_t *pf, edict_t *e)
Q1QVMED_ClearEdict(e, true);
}
static void QDECL Q1QVMPF_EntRemove(pubprogfuncs_t *pf, edict_t *e)
static void QDECL Q1QVMPF_EntRemove(pubprogfuncs_t *pf, edict_t *e, qboolean instant)
{
if (!ED_CanFree(e))
return;
e->ereftype = ER_FREE;
e->freetime = sv.time;
e->freetime = instant?0:sv.time;
}
static edict_t *QDECL Q1QVMPF_EntAlloc(pubprogfuncs_t *pf, pbool object, size_t extrasize)
@ -811,7 +811,7 @@ static qintptr_t QVM_Remove_Ent (void *offset, quintptr_t mask, const qintptr_t
{
if (arg[0] >= sv.world.max_edicts)
return false;
Q1QVMPF_EntRemove(svprogfuncs, q1qvmprogfuncs.edicttable[arg[0]]);
Q1QVMPF_EntRemove(svprogfuncs, q1qvmprogfuncs.edicttable[arg[0]], false);
return true;
}

View file

@ -1378,7 +1378,9 @@ void SV_ClientProtocolExtensionsChanged(client_t *client);
//sv_master.c
void SVM_Think(int port);
vfsfile_t *SVM_GenerateIndex(const char *fname);
vfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname);
void SVM_AddBrokerGame(const char *brokerid, const char *info);
void SVM_RemoveBrokerGame(const char *brokerid);
//
@ -1637,7 +1639,7 @@ typedef struct
{
qboolean hasauthed;
qboolean isreverse;
char challenge[32];
char challenge[64];
} qtvpendingstate_t;
int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *headerend, qtvpendingstate_t *p);
#endif

View file

@ -1950,7 +1950,7 @@ static void SV_Status_f (void)
extern cvar_t net_enable_tls;
#endif
#ifdef HAVE_HTTPSV
extern cvar_t net_enable_http, net_enable_webrtcbroker, net_enable_websockets;
extern cvar_t net_enable_http, net_enable_rtcbroker, net_enable_websockets;
#endif
extern cvar_t net_enable_qizmo, net_enable_qtv;
#endif
@ -2013,6 +2013,7 @@ static void SV_Status_f (void)
Con_Printf("client types :%s\n", sv_listen_qw.ival?" Q2":"");
break;
#endif
default:
Con_Printf("client types :%s", sv_listen_qw.ival?" QW":"");
#ifdef NQPROT
@ -2027,7 +2028,9 @@ static void SV_Status_f (void)
else if (net_enable_dtls.ival)
Con_Printf(" DTLS");
#endif
Con_Printf("\n");
#if defined(TCPCONNECT) && !defined(CLIENTONLY)
Con_Printf("tcp services :");
#if defined(HAVE_SSL)
if (net_enable_tls.ival)
Con_Printf(" TLS");
@ -2035,8 +2038,8 @@ static void SV_Status_f (void)
#ifdef HAVE_HTTPSV
if (net_enable_http.ival)
Con_Printf(" HTTP");
if (net_enable_webrtcbroker.ival)
Con_Printf(" WebRTC");
if (net_enable_rtcbroker.ival)
Con_Printf(" RTC");
if (net_enable_websockets.ival)
Con_Printf(" WS");
#endif

View file

@ -978,6 +978,7 @@ void SSV_UpdateAddresses(void)
netadr_t addr[64];
struct ftenet_generic_connection_s *con[sizeof(addr)/sizeof(addr[0])];
unsigned int flags[sizeof(addr)/sizeof(addr[0])];
const char *params[sizeof(addr)/sizeof(addr[0])];
int count;
sizebuf_t send;
qbyte send_buf[MAX_QWMSGLEN];
@ -986,7 +987,7 @@ void SSV_UpdateAddresses(void)
if (!SSV_IsSubServer() && !msv_loop_from_ss)
return;
count = NET_EnumerateAddresses(svs.sockets, con, flags, addr, sizeof(addr)/sizeof(addr[0]));
count = NET_EnumerateAddresses(svs.sockets, con, flags, addr, params, sizeof(addr)/sizeof(addr[0]));
if (*sv_serverip.string)
{
@ -994,7 +995,7 @@ void SSV_UpdateAddresses(void)
{
if (addr[i].type == NA_IP)
{
NET_StringToAdr2(sv_serverip.string, BigShort(addr[i].port), &addr[0], sizeof(addr)/sizeof(addr[0]));
NET_StringToAdr2(sv_serverip.string, BigShort(addr[i].port), &addr[0], sizeof(addr)/sizeof(addr[0]), NULL);
count = 1;
break;
}

View file

@ -108,9 +108,21 @@ cvar_t allow_download_other = CVARD("allow_download_other", "0", "0 blocks down
extern cvar_t sv_allow_splitscreen;
#ifdef SUPPORT_ICE
static void QDECL SV_Public_Callback(struct cvar_s *var, char *oldvalue)
{
if (var->ival == 2)
FTENET_AddToCollection(svs.sockets, var->name, "/", NA_INVALID, NP_RTC_TLS);
else
FTENET_AddToCollection(svs.sockets, var->name, "", NA_INVALID, NP_INVALID);
}
cvar_t sv_public = CVARCD("sv_public", "0", SV_Public_Callback, "-1: Fully blocks all inbound connections.\n0: Disable subscribing to master servers (for private lan-only games).\n1: Subscribe to public master servers. Your IP address will be listed publicly. Make sure your Router/NAT+Firewall are set to allow inbound connections.\n2: Subscribe to a broker master, allowing firewall hole punching.");
#else
cvar_t sv_public = CVARD("sv_public", "0", "-1: Fully blocks all inbound connections.\n0: Disable subscribing to master servers (for private lan-only games).\n1: Subscribe to public master servers. Your IP address will be listed publicly. Make sure your Router/NAT+Firewall are set to allow inbound connections.");
#endif
cvar_t sv_guidhash = CVARD("sv_guidkey", "", "If set, clients will calculate their GUID values against this string instead of the server's IP address. This allows consistency between multiple servers (for stats tracking), but do NOT treat the client's GUID as something that is secure.");
cvar_t sv_serverip = CVARD("sv_serverip", "", "Set this cvar to the server's public ip address if the server is behind a firewall and cannot detect its own public address. Providing a port is required if the firewall/nat remaps it, but is otherwise optional.");
cvar_t sv_public = CVARD("sv_public", "0", "Controls whether the server will publically advertise itself to master servers or not. Additionally, if set to -1, will block all new connection requests even on lan.");
cvar_t sv_listen_qw = CVARAFD("sv_listen_qw", "1", "sv_listen", 0, "Specifies whether normal clients are allowed to connect.");
cvar_t sv_listen_nq = CVARD("sv_listen_nq", "2", "Allow new (net)quake clients to connect to the server.\n0 = don't let them in.\n1 = allow them in (WARNING: this allows 'qsmurf' DOS attacks).\n2 = accept (net)quake clients by emulating a challenge (as secure as QW/Q2 but does not fully conform to the NQ protocol).");
cvar_t sv_listen_dp = CVARD("sv_listen_dp", "0", "Allows the server to respond with the DP-specific handshake protocol.\nWarning: this can potentially get confused with quake2, and results in race conditions with both vanilla netquake and quakeworld protocols.\nOn the plus side, DP clients can usually be identified correctly, enabling a model+sound limit boost.");
@ -118,7 +130,7 @@ cvar_t sv_listen_dp = CVARD("sv_listen_dp", "0", "Allows the server to respond
cvar_t sv_listen_q3 = CVAR("sv_listen_q3", "0");
#endif
cvar_t sv_reconnectlimit = CVARD("sv_reconnectlimit", "0", "Blocks dupe connection within the specified length of time .");
cvar_t sv_use_dns = CVARD("sv_use_dns", "", "Performs a reverse-dns lookup in order to report actual ip addresses of clients.");
cvar_t sv_use_dns = CVARD("sv_use_dns", "", "Performs a reverse-dns lookup in order to report more info about where clients are connecting from.");
extern cvar_t net_enable_dtls;
cvar_t sv_reportheartbeats = CVARD("sv_reportheartbeats", "2", "Print a notice each time a heartbeat is sent to a master server. When set to 2, the message will be displayed once.");
cvar_t sv_heartbeat_interval = CVARD("sv_heartbeat_interval", "110", "Interval between heartbeats. Low values are abusive, high values may cause NAT/ghost issues.");
@ -2502,7 +2514,9 @@ void SV_DoDirectConnect(svconnectinfo_t *fte_restrict info)
newcl->userid = ++nextuserid;
newcl->fteprotocolextensions = info->ftepext1;
newcl->fteprotocolextensions2 = info->ftepext2;
newcl->ezprotocolextensions1 = info->ezpext1;
newcl->protocol = info->protocol;
newcl->pextknown = info->ftepext1||info->ftepext2;
Q_strncpyz(newcl->guid, info->guid, sizeof(newcl->guid));
// Con_TPrintf("%s:%s:connect\n", sv.name, NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));
@ -2838,6 +2852,7 @@ void SV_DoDirectConnect(svconnectinfo_t *fte_restrict info)
#endif
newcl->netchan.compresstable = NULL;
newcl->netchan.pext_fragmentation = info->mtu?true:false;
newcl->netchan.pext_stunaware = !!(info->ftepext2&PEXT2_STUNAWARE);
//this is the upper bound of the mtu, if its too high we'll get EMSGSIZE and we'll reduce it.
//however, if it drops below newcl->netchan.message.maxsize then we'll start to see undeliverable reliables, which means dropped clients.
newcl->netchan.mtu = MAX_DATAGRAM; //vanilla qw clients are assumed to have an mtu of this size.
@ -4509,7 +4524,10 @@ qboolean SV_ReadPackets (float *delay)
if (svs.gametype == GT_QUAKE3)
{
received++;
SVQ3_HandleClient();
if (SVQ3_HandleClient())
;
else if (NET_WasSpecialPacket(svs.sockets))
continue;
continue;
}
#endif
@ -4997,7 +5015,7 @@ float SV_Frame (void)
#endif
#ifdef HAVE_CLIENT
isidle = !isDedicated && sv.allocated_client_slots == 1 && Key_Dest_Has(~kdm_game) && cls.state == ca_active;
isidle = !isDedicated && sv.allocated_client_slots == 1 && Key_Dest_Has(~kdm_game) && cls.state == ca_active && !cl.implicitpause;
/*server is effectively paused in SP/coop if there are no clients/spectators*/
if (sv.spawned_client_slots == 0 && sv.spawned_observer_slots == 0 && !deathmatch.ival)
isidle = true;
@ -5110,6 +5128,8 @@ float SV_Frame (void)
// get packets
isidle = !SV_ReadPackets (&delay);
if (isDedicated)
NET_Tick();
if (pr_imitatemvdsv.ival || dpcompat_nopreparse.ival)
{
@ -5186,8 +5206,6 @@ float SV_Frame (void)
if (isDedicated)
#endif
{
NET_Tick();
if (sv.framenum != 1)
{
#ifndef SERVERONLY
@ -5372,6 +5390,7 @@ void SV_InitLocal (void)
Cvar_Register (&sv_guidhash, cvargroup_servercontrol);
Cvar_Register (&sv_serverip, cvargroup_servercontrol);
Cvar_Register (&sv_public, cvargroup_servercontrol);
sv_public.restriction = RESTRICT_MAX; //no disabling this over rcon.
Cvar_Register (&sv_listen_qw, cvargroup_servercontrol);
Cvar_Register (&sv_listen_nq, cvargroup_servercontrol);
Cvar_Register (&sv_listen_dp, cvargroup_servercontrol);

View file

@ -35,6 +35,7 @@ enum gametypes_e
};
typedef struct svm_server_s {
netadr_t adr;
const char *brokerid; //from rtc broker, for ICE connections (persistent until killed).
int protover;
unsigned int clients;
unsigned int maxclients;
@ -44,7 +45,7 @@ typedef struct svm_server_s {
unsigned short gametype;
float expiretime;
bucket_t bucket;
bucket_t bucket; //for faster address lookups.
struct svm_game_s *game;
struct svm_server_s *next;
} svm_server_t;
@ -54,6 +55,7 @@ typedef struct svm_game_s {
svm_server_t *firstserver;
size_t numservers;
qboolean persistent;
char name[1];
} svm_game_t;
@ -92,11 +94,16 @@ static void QDECL SVM_Port_Callback(struct cvar_s *var, char *oldvalue)
FTENET_AddToCollection(svm_sockets, var->name, var->string, NA_IP, NP_DGRAM);
}
static cvar_t sv_heartbeattimeout = CVARD("sv_heartbeattimeout", "600", "How many seconds a server should remain listed after its latest heartbeat. Larger values can avoid issues from packetloss, but can also make dos attacks easier.");
static cvar_t sv_masterport = CVARC("sv_masterport", "27000 28000", SVM_Port_Callback);
static cvar_t sv_masterport_tcp = CVARC("sv_masterport_tcp", "27000 28000", SVM_Tcpport_Callback);
static cvar_t sv_masterport = CVARC("sv_masterport", STRINGIFY(PORT_QWMASTER)" "STRINGIFY(PORT_ICEBROKER), SVM_Port_Callback);
static cvar_t sv_masterport_tcp = CVARC("sv_masterport_tcp", STRINGIFY(PORT_ICEBROKER), SVM_Tcpport_Callback);
static cvar_t sv_maxgames = CVARD("sv_maxgames", "100", "Limits the number of games that may be known. This is to reduce denial of service attacks.");
static cvar_t sv_maxservers = CVARD("sv_maxservers", "1000", "Limits the number of servers (total from all games) that may be known. This is to reduce denial of service attacks.");
static unsigned int SVM_GenerateBrokerKey(const char *brokerid)
{
return Hash_Key(brokerid, svm.serverhash.numbuckets);
}
//returns a hash key for a server's address.
static unsigned int SVM_GenerateAddressKey(const netadr_t *adr)
{
@ -127,16 +134,20 @@ static svm_server_t *SVM_GetServer(netadr_t *adr)
unsigned int key = SVM_GenerateAddressKey(adr);
server = Hash_GetKey(&svm.serverhash, key);
while (server && !NET_CompareAdr(&server->adr, adr))
while (server)
{
if (!server->brokerid) //don't report brokered servers by address.
if (NET_CompareAdr(&server->adr, adr))
return server;
server = Hash_GetNextKey(&svm.serverhash, key, server);
}
return server;
return NULL;
}
static svm_game_t *SVM_FindGame(const char *game, qboolean create)
static svm_game_t *SVM_FindGame(const char *game, int create)
{
svm_game_t *g;
svm_game_t *g, **link;
const char *sanitise;
for (g = svm.firstgame; g; g = g->next)
{
if (!Q_strcasecmp(game, g->name))
@ -151,16 +162,28 @@ static svm_game_t *SVM_FindGame(const char *game, qboolean create)
return NULL;
}
//block some chars that may cause issues/exploits. sorry.
if (strchr(game, '.') || strchr(game, '\"') || strchr(game, '/') || strchr(game, '?') || strchr(game, '&') || strchr(game, '+') || strchr(game, '\'') || strchr(game, '<') || strchr(game, '>'))
for (sanitise = game; *sanitise; sanitise++)
{
if ((*sanitise >= 'a' && *sanitise <= 'z') || //allow lowercase
(*sanitise >= 'A' && *sanitise <= 'Z') || //allow uppercase
(*sanitise >= '0' && *sanitise <= '9') || //allow numbers (but not leeding, see below)
(*sanitise == '-' || *sanitise == '_')) //allow a little punctuation, to make up for the lack of spaces.
continue;
return NULL;
}
if (!*game || (*game >= '0' && *game <= '9'))
return NULL; //must not start with a number either.
g = ZF_Malloc(sizeof(*g) + strlen(game));
if (g)
{
strcpy(g->name, game);
g->next = svm.firstgame;
svm.firstgame = g;
g->persistent = create==2;
g->next = NULL;
//add it at the end.
for (link = &svm.firstgame; *link; link = &(*link)->next)
;
*link = g;
svm.numgames++;
Con_DPrintf("Creating game \"%s\"\n", g->name);
}
@ -176,7 +199,8 @@ static void SVM_RemoveOldServers(void)
{
for (serverlink = &g->firstserver; (s=*serverlink); )
{
if (s->expiretime < svm.time)
//brokered servers don't time out (they drop when their tcp connection dies)
if (!s->brokerid && s->expiretime < svm.time)
{
if (developer.ival)
{
@ -196,7 +220,7 @@ static void SVM_RemoveOldServers(void)
serverlink = &s->next;
}
if (!g->firstserver)
if (!g->firstserver && !g->persistent)
{
Con_DPrintf("game \"%s\" has no active servers\n", g->name);
*gamelink = g->next;
@ -208,7 +232,7 @@ static void SVM_RemoveOldServers(void)
}
}
int SVM_AddIPAddresses(sizebuf_t *sb, int first, const char *gamename, int v4, int v6, qboolean prefixes, int gametype)
int SVM_AddIPAddresses(sizebuf_t *sb, int first, int ver, const char *gamename, int v4, int v6, qboolean empty, qboolean full, qboolean prefixes, int gametype)
{
int number = 0;
svm_server_t *server;
@ -227,9 +251,14 @@ int SVM_AddIPAddresses(sizebuf_t *sb, int first, const char *gamename, int v4, i
for (; server; server = server->next)
{
//FIXME
// if (gametype != -1 && server->gametype != gametype)
// continue;
if (server->protover != ver)
continue;
if (server->clients == 0 && !empty)
continue;
if (server->clients >= server->maxclients && !full)
continue;
if (gametype != -1 && server->gametype != gametype)
continue;
switch(server->adr.type)
{
case NA_IP:
@ -260,7 +289,101 @@ int SVM_AddIPAddresses(sizebuf_t *sb, int first, const char *gamename, int v4, i
return number;
}
vfsfile_t *SVM_GenerateIndex(const char *fname)
static char *QuakeCharsToHTML(char *outhtml, size_t outsize, const char *quake)
{
char *ret = outhtml;
conchar_t chars[8192], *c=chars, *end;
unsigned int codeflags, codepoint, oldflags=CON_WHITEMASK;
unsigned int b;
const char *htmlnames[16] = {
"#000000", //black
"#0000AA", //dark blue
"#00AA00", //dark green
"#00AAAA", //dark cyan
"#AA0000", //dark red
"#AA00AA", //dark magenta
"#AA5500", //brown
"#AAAAAA", //grey
"#555555", //dark grey
"#5555FF", //blue
"#55FF55", //green
"#55FFFF", //cyan
"#FF5555", //red
"#FF55FF", //magenta
"#FFFF55", //yellow
"#FFFFFF", //white
};
if (!outsize--)
return NULL; //no space for the null
end = COM_ParseFunString(oldflags, quake, chars, sizeof(chars), false);
while (c < end)
{
c = Font_Decode(c, &codeflags, &codepoint);
if (codeflags & CON_HIDDEN)
continue; //erk...?
if (codeflags & CON_RICHFORECOLOUR) //fixme...?
codeflags = CON_WHITEMASK;
if ((codeflags&CON_FGMASK) != (oldflags&CON_FGMASK))
{
if (oldflags != CON_WHITEMASK)
{
Q_strncpyz(outhtml, "</span>", outsize);
b=strlen(outhtml);
outhtml += b;
outsize -= b;
}
if (codeflags != CON_WHITEMASK)
{
Q_snprintfz(outhtml, outsize, "<span style=\"color:%s\">", htmlnames[(codeflags&CON_FGMASK)>>CON_FGSHIFT]);
b=strlen(outhtml);
outhtml += b;
outsize -= b;
}
oldflags = codeflags;
}
if (codepoint == '<')
{
Q_strncpyz(outhtml, "&lt;", outsize);
b=strlen(outhtml);
}
else if (codepoint == '>')
{
Q_strncpyz(outhtml, "&gt;", outsize);
b=strlen(outhtml);
}
else if (codepoint == '&')
{
Q_strncpyz(outhtml, "&amp;", outsize);
b=strlen(outhtml);
}
else if (codepoint == '\"')
{
Q_strncpyz(outhtml, "&quot;", outsize);
b=strlen(outhtml);
}
else if (codepoint == '\'')
{
Q_strncpyz(outhtml, "&apos;", outsize);
b=strlen(outhtml);
}
else
b = utf8_encode(outhtml, codepoint, outsize);
if (b > 0)
{
outhtml += b;
outsize -= b;
}
}
*outhtml = 0;
return ret;
}
vfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname)
{
static const char *thecss =
"<style type=\"text/css\">"
@ -286,23 +409,29 @@ vfsfile_t *SVM_GenerateIndex(const char *fname)
"tr:hover { background-color: #202020; }"
"</style>";
char tmpbuf[256];
char hostname[1024];
const char *url;
svm_game_t *game;
svm_server_t *server;
vfsfile_t *f = NULL;
unsigned clients = 0, maxclients=0;
unsigned clients = 0, maxclients=0, totalclients=0;
if (!strcmp(fname, "index.html"))
{
f = VFSPIPE_Open(1, false);
VFS_PRINTF(f, "%s", thecss);
VFS_PRINTF(f, "<h1>FTE-Master</h1>\n");
VFS_PRINTF(f, "<table border=1>\n");
VFS_PRINTF(f, "<tr><th>Active Games</th><th>Players</th><th>Server Count</th></tr>\n");
for (game = svm.firstgame; game; game = game->next)
{
VFS_PRINTF(f, "<tr><td><a href=\"game/%s.html\">%s</a></td><td%u server(s)</td></tr>\n", game->name, game->name, (unsigned)game->numservers);
clients += game->numservers;
for (clients=0, server = game->firstserver; server; server = server->next)
clients += server->clients;
if (game->numservers) //only show active servers
VFS_PRINTF(f, "<tr><td><a href=\"game/%s\">%s</a></td><td>%u player(s)</td><td>%u server(s)</td></tr>\n", game->name, game->name, clients, (unsigned)game->numservers);
totalclients += clients;
}
VFS_PRINTF(f, "</table>\n");
VFS_PRINTF(f, "%u game(s), %u server(s)\n", (unsigned)svm.numgames, clients);
VFS_PRINTF(f, "%u game(s), %u player(s), %u server(s)\n", (unsigned)svm.numgames, totalclients, (unsigned)svm.numservers);
}
else if (!strncmp(fname, "server/", 7))
{
@ -315,7 +444,8 @@ vfsfile_t *SVM_GenerateIndex(const char *fname)
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");
count = NET_StringToAdr2(fname+7, 0, adr, countof(adr));
//FIXME: block dns lookups here?
count = NET_StringToAdr2(fname+7, 0, adr, countof(adr), NULL);
while(count-->0)
{
server = SVM_GetServer(&adr[count]);
@ -328,12 +458,12 @@ vfsfile_t *SVM_GenerateIndex(const char *fname)
}
else if (!strncmp(fname, "game/", 5))
{
COM_StripExtension(fname+5, tmpbuf, sizeof(tmpbuf));
game = SVM_FindGame(tmpbuf, false);
const char *gamename = fname+5;
game = SVM_FindGame(gamename, false);
f = VFSPIPE_Open(1, false);
VFS_PRINTF(f, "%s", thecss);
VFS_PRINTF(f, "<h1>Servers for %s</h1>\n", tmpbuf);
VFS_PRINTF(f, "<h1>Servers for %s</h1>\n", gamename);
if(game)
{
@ -341,7 +471,15 @@ vfsfile_t *SVM_GenerateIndex(const char *fname)
VFS_PRINTF(f, "<tr><th>Address</th><th>Hostname</th><th>Gamedir</th><th>Mapname</th><th>Players</th></tr>\n");
for (server = game->firstserver; server; server = server->next)
{
VFS_PRINTF(f, "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%u/%u</td></tr>\n", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr), server->hostname, server->gamedir, server->mapname, server->clients, server->maxclients);
if (server->brokerid)
{
url = tmpbuf;
Q_snprintfz(tmpbuf, sizeof(tmpbuf), "rtc://%s/%s", requesthost, server->brokerid);
}
else
url = NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr);
QuakeCharsToHTML(hostname, sizeof(hostname), server->hostname);
VFS_PRINTF(f, "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%u/%u</td></tr>\n", url, hostname, server->gamedir, server->mapname, server->clients, server->maxclients);
clients += server->clients;
maxclients += server->maxclients;
}
@ -349,7 +487,7 @@ vfsfile_t *SVM_GenerateIndex(const char *fname)
VFS_PRINTF(f, "%u server(s), %u/%u client(s)\n", (unsigned)game->numservers, clients, maxclients);
}
else
VFS_PRINTF(f, "No servers known for %s\n", tmpbuf);
VFS_PRINTF(f, "Protocol '%s' is not known\n", gamename);
}
else if (!strncmp(fname, "raw/", 4))
{ //just spews all
@ -357,12 +495,116 @@ vfsfile_t *SVM_GenerateIndex(const char *fname)
game = SVM_FindGame(tmpbuf, false);
f = VFSPIPE_Open(1, false);
VFS_PRINTF(f, "#Server list for \"%s\"\n", tmpbuf);
for (server = (game?game->firstserver:NULL); server; server = server->next)
VFS_PRINTF(f, "%s\n", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr));
{
if (server->brokerid)
VFS_PRINTF(f, "rtc:///%s \\maxclients\\%u\\clients\\%u\\hostname\\%s\\modname\\%s\\mapname\\%s\n", server->brokerid, server->maxclients, server->clients, server->hostname, server->gamedir, server->mapname);
else
VFS_PRINTF(f, "%s\n", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr));
}
}
return f;
}
static svm_game_t *SVM_GameFromBrokerID(const char **brokerid)
{
size_t l;
char name[128];
const char *in = *brokerid;
if (*in == '/')
in++;
for (l = 0; *in && *in != '/' && *in != '?' && *in != '#'; in++)
if (l < countof(name)-1)
name[l++] = *in;
name[l] = 0;
if (*in == '/')
in++;
else
return NULL; //only one? no game specified? get lost.
*brokerid = in;
return SVM_FindGame(name, true);
}
static svm_server_t *SVM_FindBrokerHost(const char *brokerid)
{
svm_server_t *server;
unsigned int key = SVM_GenerateBrokerKey(brokerid);
server = Hash_GetKey(&svm.serverhash, key);
while (server)
{
if (server->brokerid) //don't report brokered servers by address.
if (server->brokerid == brokerid)
return server;
server = Hash_GetNextKey(&svm.serverhash, key, server);
}
return NULL;
}
void SVM_RemoveBrokerGame(const char *brokerid)
{
svm_server_t *s, **link;
svm_game_t *game = SVM_GameFromBrokerID(&brokerid);
if (!game)
{
Con_Printf("SVM_RemoveBrokerGame: failed to find game for brokered server: %s\n", brokerid);
return;
}
for (link = &game->firstserver; (s=*link); )
{
if (s->brokerid == brokerid)
{
*link = s->next;
Z_Free(s);
game->numservers--;
svm.numservers--;
return;
}
else
link = &(*link)->next;
}
Con_Printf("SVM_RemoveBrokerGame: failed to remove brokered server: %s\n", brokerid);
}
void SVM_AddBrokerGame(const char *brokerid, const char *info)
{
svm_game_t *game = SVM_GameFromBrokerID(&brokerid);
svm_server_t *server = SVM_FindBrokerHost(brokerid);
if (!server)
{
if (!game)
return;
if (svm.numservers >= sv_maxservers.ival)
{
Con_DPrintf("server limit exceeded\n");
return;
}
Con_DPrintf("heartbeat(new - %s): /%s\n", game->name, brokerid);
server = Z_Malloc(sizeof(svm_server_t));
server->game = game;
server->brokerid = brokerid;
server->next = game->firstserver;
game->firstserver = server;
game->numservers++;
svm.numservers++;
svm.total.adds++;
Hash_AddKey(&svm.serverhash, SVM_GenerateBrokerKey(brokerid), server, &server->bucket);
}
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"));
server->clients = atoi(Info_ValueForKey(info, "clients"));
Q_strncpyz(server->hostname, Info_ValueForKey(info, "hostname"), sizeof(server->hostname));
Q_strncpyz(server->gamedir, Info_ValueForKey(info, "modname"), sizeof(server->gamedir));
Q_strncpyz(server->mapname, Info_ValueForKey(info, "mapname"), sizeof(server->mapname));
}
static svm_server_t *SVM_Heartbeat(const char *gamename, netadr_t *adr, int numclients, float validuntil)
{
svm_server_t *server = SVM_GetServer(adr);
@ -416,6 +658,25 @@ static svm_server_t *SVM_Heartbeat(const char *gamename, netadr_t *adr, int numc
return server;
}
#ifdef TCPCONNECT
void tobase64(unsigned char *out, int outlen, unsigned char *in, int inlen);
#endif
void SVM_GenChallenge(char *out, size_t outsize, netadr_t *foradr)
{ //this function needs to return some sort of unguessable string so that you can't spoof the server with fake responses
char adr[64];
const unsigned char *strings[] = {"somethingrandom", (const unsigned char *)NET_AdrToString(adr, sizeof(adr), foradr)};
size_t lengths[] = {strlen(strings[0]), strlen(strings[1])};
char digest[4*5];
int digestsize = SHA1_m(digest, sizeof(digest), countof(lengths), strings, lengths);
#ifdef TCPCONNECT
tobase64(out, outsize, digest, min(16, digestsize)); //truncate it, so its not excessive
#else
Q_snprintfz(out, outsize, "%08x", *(int*)digest);
(void)digestsize;
#endif
}
void SVM_Think(int port)
{
char *s;
@ -426,6 +687,23 @@ void SVM_Think(int port)
{
net_message.data[net_message.cursize] = '\0'; //null term all strings.
//well that's annoying. why is our networking code not doing this? LAME!
if (net_from.type == NA_IPV6 &&
!*(int*)&net_from.address.ip6[0] &&
!*(int*)&net_from.address.ip6[4] &&
!*(short*)&net_from.address.ip6[8] &&
*(short*)&net_from.address.ip6[10]==(short)0xffff)
{ //convert this ipv4-mapped-ipv6 address back to actual ipv4, so we don't get confused about stuff.
net_from.type = NA_IP;
*(int*)&net_from.address.ip[0] = *(int*)&net_from.address.ip6[12];
//and null it out, just in case.
*(int*)&net_from.address.ip6[8]=0;
*(int*)&net_from.address.ip6[12]=0;
}
if (NET_WasSpecialPacket(svm_sockets))
continue;
svm.time = Sys_DoubleTime();
MSG_BeginReading(msg_nullnetprim);
@ -442,6 +720,7 @@ void SVM_Think(int port)
char *eos;
char game[64];
qboolean ext = !strcmp(com_token, "getserversExt");
const char *resp=ext?"getserversExtResponse":"getserversResponse";
qboolean empty = false;
qboolean full = false;
qboolean ipv4 = !ext;
@ -450,11 +729,11 @@ void SVM_Think(int port)
s = COM_ParseOut(s, game, sizeof(game));
ver = strtol(game, &eos, 0);
if (*eos)
{
{ //not a number, must have been a game name.
s = COM_Parse(s);
ver = strtol(com_token, NULL, 0);
}
else
else //first arg was a number. that means its vanilla quake3.
Q_strncpyz(game, "Quake3", sizeof(game));
for(;s&&*s;)
{
@ -485,30 +764,22 @@ void SVM_Think(int port)
Con_DPrintf("Unknown request filter: %s\n", COM_QuotedString(com_token, buf, sizeof(buf), false));
}
}
if (!ipv4 && !ipv6)
ipv4 = ipv6 = true; //neither specified? use both
svm.total.queries++;
memset(&sb, 0, sizeof(sb));
sb.maxsize = sizeof(net_message_buffer)-2;
sb.data = net_message_buffer;
MSG_WriteLong(&sb, -1);
if (!ipv4 && !ipv6)
ipv4 = ipv6 = true; //neither specified? use both
(void)ver, (void)full, (void)empty;
if (ext)
{ //ipv6 and ipv4 addresses
MSG_WriteString(&sb, "getserversExtResponse");
SVM_AddIPAddresses(&sb, 0, game, ipv4, ipv6, true, gametype);
}
else
{ //ipv4 only
MSG_WriteString(&sb, "getserversResponse");
SVM_AddIPAddresses(&sb, 0, game, ipv4, ipv6, true, gametype);
}
SZ_Write(&sb, resp, strlen(resp)); //WriteString, but without the null.
SVM_AddIPAddresses(&sb, 0, ver, game, ipv4, ipv6, empty, full, true, gametype);
sb.maxsize+=2;
MSG_WriteByte(&sb, '\\'); //otherwise the last may be considered invalid and ignored.
// MSG_WriteByte(&sb, 'E');
// MSG_WriteByte(&sb, 'O');
// MSG_WriteByte(&sb, 'T');
MSG_WriteByte(&sb, 'E');
MSG_WriteByte(&sb, 'O');
MSG_WriteByte(&sb, 'T');
NET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);
}
else if (!strcmp(com_token, "heartbeat"))
@ -521,25 +792,29 @@ void SVM_Think(int port)
else
{ //dp/q3/etc are annoying, but we can query from an emphemerial socket to check NAT rules.
sizebuf_t sb;
char ourchallenge[256];
SVM_GenChallenge(ourchallenge, sizeof(ourchallenge), &net_from);
svm.total.queries++;
memset(&sb, 0, sizeof(sb));
sb.maxsize = sizeof(net_message_buffer);
sb.data = net_message_buffer;
MSG_WriteLong(&sb, -1);
MSG_WriteString(&sb, "getinfo CAKE\n");
MSG_WriteString(&sb, va("getinfo %s\n", ourchallenge));
sb.cursize--;
NET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);
}
}
else if (!strcmp(com_token, "infoResponse"))
{
char ourchallenge[256];
int clients;
const char *game, *chal;
svm_server_t *srv;
s = MSG_ReadStringLine();
svm.total.heartbeats++;
chal = Info_ValueForKey(s, "challenge");
if (!strcmp(chal, "CAKE"))
SVM_GenChallenge(ourchallenge, sizeof(ourchallenge), &net_from);
if (!strcmp(chal, ourchallenge))
{
clients = atoi(Info_ValueForKey(s, "clients"));
game = Info_ValueForKey(s, "gamename");
@ -566,7 +841,7 @@ void SVM_Think(int port)
MSG_WriteLong(&sb, -1);
MSG_WriteString(&sb, "servers\n");
sb.cursize--;
SVM_AddIPAddresses(&sb, 0, "Quake2", true, false, false, -1);
SVM_AddIPAddresses(&sb, 0, 0, "Quake2", true, false, true, true, false, -1);
NET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);
}
else if (*com_token == S2M_HEARTBEAT) //sequence, players
@ -589,7 +864,7 @@ void SVM_Think(int port)
MSG_WriteLong(&sb, -1);
MSG_WriteByte(&sb, M2C_MASTER_REPLY);
MSG_WriteByte(&sb, '\n');
SVM_AddIPAddresses(&sb, 0, "QuakeWorld", true, false, false, -1);
SVM_AddIPAddresses(&sb, 0, 0, "QuakeWorld", true, false, true, true, false, -1);
NET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);
}
else if (*com_token == A2A_PING)
@ -656,6 +931,7 @@ static void SVM_Status_f(void)
void SV_Init (struct quakeparms_s *parms)
{
int manarg;
char *g;
COM_InitArgv (parms->argc, parms->argv);
@ -711,6 +987,13 @@ void SV_Init (struct quakeparms_s *parms)
Cvar_ForceCallback(&sv_masterport);
Cvar_ForceCallback(&sv_masterport_tcp);
if (fs_manifest->protocolname)
for (g = fs_manifest->protocolname; *g; )
{
g = COM_Parse(g);
SVM_FindGame(com_token, 2);
}
Con_Printf ("Exe: %s\n", version_string());
Con_TPrintf ("======== %s Initialized ========\n", "FTEMaster");
@ -742,4 +1025,4 @@ float SV_Frame (void)
return 4;
}
#endif
#endif

View file

@ -61,6 +61,9 @@ cvar_t sv_demotxt = CVAR("sv_demotxt", "1");
void SV_WriteMVDMessage (sizebuf_t *msg, int type, int to, float time);
void SV_WriteRecordMVDMessage (sizebuf_t *msg);
#ifdef TCPCONNECT
void tobase64(unsigned char *out, int outlen, unsigned char *in, int inlen);
#endif
static struct
@ -83,6 +86,15 @@ int demomsgto;
static char demomsgbuf[MAX_OVERALLMSGLEN];
static mvddest_t *singledest; //used when a stream is starting up so redundant data doesn't get dumped into other streams
static struct reversedest_s
{
struct reversedest_s *next;
qtvpendingstate_t info;
vfsfile_t *stream;
char inbuffer[2048];
int inbuffersize;
double timeout;
} *reversedest; //used when a reverse stream is starting up
static mvddest_t *SV_MVD_InitStream(vfsfile_t *stream, const char *info);
qboolean SV_MVD_Record (mvddest_t *dest);
@ -253,12 +265,17 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
char password[256] = "";
char userinfo[1024];
enum {
//MUST BE ORDERED HIGHEST-PRIORITY-LAST
QTVAM_NONE,
QTVAM_PLAIN,
#ifdef TCPCONNECT
// QTVAM_CCITT, //16bit = ddos it
QTVAM_MD4, //fucked
// QTVAM_MD5, //no hash implemented
QTVAM_SHA1,
// QTVAM_MD5, //fucked, no hash implemented
QTVAM_SHA1, //fucked too nowadays
// QTVAM_SHA2_256, //no hash implemented
// QTVAM_SHA2_512, //no hash implemented
#endif
} authmethod = QTVAM_NONE;
start = headerstart;
@ -329,9 +346,10 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
int thisauth;
start = COM_ParseToken(start, NULL);
if (!strcmp(com_token, "NONE"))
thisauth = QTVAM_PLAIN;
thisauth = QTVAM_NONE;
else if (!strcmp(com_token, "PLAIN"))
thisauth = QTVAM_PLAIN;
#ifdef TCPCONNECT
// else if (!strcmp(com_token, "CCIT"))
// thisauth = QTVAM_CCITT;
else if (!strcmp(com_token, "MD4"))
@ -340,6 +358,7 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
// thisauth = QTVAM_MD5;
else if (!strcmp(com_token, "SHA1"))
thisauth = QTVAM_SHA1;
#endif
else
{
thisauth = QTVAM_NONE;
@ -391,7 +410,10 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
p->hasauthed = true; //no password, no need to auth.
else if (*password)
{
switch (authmethod)
if (!*p->challenge && authmethod>QTVAM_PLAIN)
e = ("QTVSV 1\n"
"PERROR: Challenge wasn't given...\n\n");
else switch (authmethod)
{
case QTVAM_NONE:
e = ("QTVSV 1\n"
@ -400,6 +422,7 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
case QTVAM_PLAIN:
p->hasauthed = !strcmp(qtv_password.string, password);
break;
#ifdef TCPCONNECT
/*case QTVAM_CCITT:
{
unsigned short ushort_result;
@ -414,9 +437,9 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
char hash[512];
int md4sum[4];
snprintf(hash, sizeof(hash), "%s%s", p->challenge, qtv_password.string);
Q_snprintfz(hash, sizeof(hash), "%s%s", p->challenge, qtv_password.string);
Com_BlockFullChecksum (hash, strlen(hash), (unsigned char*)md4sum);
sprintf(hash, "%X%X%X%X", md4sum[0], md4sum[1], md4sum[2], md4sum[3]);
Q_snprintfz(hash, sizeof(hash), "%X%X%X%X", md4sum[0], md4sum[1], md4sum[2], md4sum[3]);
p->hasauthed = !strcmp(password, hash);
}
break;
@ -425,16 +448,17 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
char hash[512];
int digest[5];
snprintf(hash, sizeof(hash), "%s%s", p->challenge, qtv_password.string);
Q_snprintfz(hash, sizeof(hash), "%s%s", p->challenge, qtv_password.string);
SHA1((char*)digest, sizeof(digest), hash, strlen(hash));
sprintf(hash, "%08X%08X%08X%08X%08X", digest[0], digest[1], digest[2], digest[3], digest[4]);
Q_snprintfz(hash, sizeof(hash), "%08X%08X%08X%08X%08X", digest[0], digest[1], digest[2], digest[3], digest[4]);
p->hasauthed = !strcmp(password, hash);
}
break;
// case QTVAM_MD5:
#endif
default:
e = ("QTVSV 1\n"
"PERROR: FTEQWSV bug detected.\n\n");
"PERROR: server bug detected.\n\n");
break;
}
if (!p->hasauthed && !e)
@ -461,34 +485,32 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
case QTVAM_PLAIN:
p->hasauthed = !strcmp(qtv_password.string, password);
break;
if (0)
{
#ifdef TCPCONNECT
/*case QTVAM_CCITT:
e = ("QTVSV 1\n"
"AUTH: CCITT\n"
"CHALLENGE: ");
}
else if (0)
{*/
e = ("QTVSV 1\n"
"AUTH: CCITT\n"
"CHALLENGE: ");
goto hashedpassword;*/
case QTVAM_MD4:
e = ("QTVSV 1\n"
"AUTH: MD4\n"
"CHALLENGE: ");
}
else
{
e = ("QTVSV 1\n"
"AUTH: MD4\n"
"CHALLENGE: ");
goto hashedpassword;
/*case QTVAM_MD5:
e = ("QTVSV 1\n"
"AUTH: MD5\n"
"CHALLENGE: ");
}
else
{*/
e = ("QTVSV 1\n"
"AUTH: MD5\n"
"CHALLENGE: ");
goto hashedpassword;*/
case QTVAM_SHA1:
e = ("QTVSV 1\n"
"AUTH: SHA1\n"
"CHALLENGE: ");
e = ("QTVSV 1\n"
"AUTH: SHA1\n"
"CHALLENGE: ");
goto hashedpassword;
hashedpassword:
{
char tmp[32];
Sys_RandomBytes(tmp, sizeof(tmp));
tobase64(p->challenge, sizeof(p->challenge), tmp, sizeof(tmp));
}
VFS_WRITE(clientstream, e, strlen(e));
@ -496,15 +518,15 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
e = "\n\n";
VFS_WRITE(clientstream, e, strlen(e));
return QTV_RETRY;
#endif
default:
e = ("QTVSV 1\n"
"PERROR: FTEQWSV bug detected.\n\n");
"PERROR: server bug detected.\n\n");
break;
}
}
if (*qtv_maxstreams.string)
if (*qtv_maxstreams.string && !p->isreverse)
{
int count = 0;
mvddest_t *dest;
@ -2128,10 +2150,80 @@ void SV_MVD_AutoRecord (void)
}
}
void SV_MVD_CheckReverse(void)
{
struct reversedest_s *rd, **link;
enum qtvstatus_e s;
int len;
for (link = &reversedest; *link; link = &rd->next)
{
rd = *link;
if (realtime > rd->timeout)
len = -1;
else
len = VFS_READ(rd->stream, rd->inbuffer+rd->inbuffersize, sizeof(rd->inbuffer)-1-rd->inbuffersize);
if (len < 0)
s = QTV_ERROR;
else if (!len)
continue; //keep waiting...
else
{
rd->inbuffersize += len;
rd->inbuffer[rd->inbuffersize] = 0;
if (rd->inbuffersize >= 3 && strncmp(rd->inbuffer, "QTV", 3))
s = QTV_ERROR; //not qtv server...
else
{
char *e = strstr(rd->inbuffer, "\n\n");
if (e)
s = SV_MVD_GotQTVRequest(rd->stream, rd->inbuffer, rd->inbuffer+rd->inbuffersize, &rd->info);
else
continue; //not nuff data yet
}
}
switch(s)
{
case QTV_RETRY: //need to parse new stuff.
continue;
case QTV_ACCEPT:
rd->stream = NULL;
//fallthrough
case QTV_ERROR:
if (rd->stream)
VFS_CLOSE(rd->stream);
*link = rd->next;
Z_Free(rd);
return;
}
}
}
void SV_MVD_QTVReverse_f (void)
{
#if 1//ndef HAVE_TCP
Con_Printf ("%s is not supported in this build\n", Cmd_Argv(0));
// Con_Printf ("%s is not supported in this build\n", Cmd_Argv(0));
const char *ip = Cmd_Argv(1);
vfsfile_t *f;
const char *msg = "QTV\n"
"VERSION: 1\n"
"REVERSE\n"
"\n";
struct reversedest_s *rd;
if (sv.state<ss_loading)
return;
f = FS_OpenTCP(ip, 27599);
if (!f)
return;
VFS_WRITE(f, msg, strlen(msg));
rd = Z_Malloc(sizeof(*rd));
rd->stream = f;
rd->info.isreverse = true;
rd->timeout = realtime + 10;
reversedest = rd;
#else
char *ip;
if (sv.state != ss_active)

View file

@ -3674,6 +3674,7 @@ void SV_SendClientMessages (void)
#ifdef MVD_RECORDING
void SV_WriteMVDMessage (sizebuf_t *msg, int type, int to, float time);
void SV_MVD_CheckReverse(void);
void DemoWriteQTVTimePad(int msecs);
#define Max(a, b) ((a>b)?a:b)
@ -3692,6 +3693,8 @@ void SV_SendMVDMessage(void)
// extern cvar_t sv_demoMaxSize;
sizebuf_t *dmsg;
SV_MVD_CheckReverse();
if (!sv.mvdrecording)
return;

View file

@ -1069,7 +1069,7 @@ static int Sys_EnumerateFiles2 (const char *truepath, int apathofs, const char *
if (!func(file, st.st_size, st.st_mtime, parm, spath))
{
Con_DPrintf("giving up on search after finding %s\n", file);
// Con_DPrintf("giving up on search after finding %s\n", file);
closedir(dir);
return false;
}

View file

@ -239,7 +239,7 @@ void SV_New_f (void)
int playernum;
int splitnum;
client_t *split;
unsigned int fteext1; //reported to client
unsigned int fteext1, fteext2; //reported to client
host_client->prespawn_stage = PRESPAWN_INVALID;
host_client->prespawn_idx = 0;
@ -298,6 +298,7 @@ void SV_New_f (void)
}
fteext1 = host_client->fteprotocolextensions;
fteext2 = host_client->fteprotocolextensions2;
switch(svs.netprim.coordtype)
{
case COORDTYPE_FLOAT_32:
@ -319,6 +320,7 @@ void SV_New_f (void)
host_client->drop = true;
return;
}
fteext2 &= ~PEXT2_STUNAWARE; //don't complicate demos.
ClientReliableCheckBlock(host_client, 800); //okay, so it might be longer, but I'm too lazy to work out the real size.
@ -329,10 +331,10 @@ void SV_New_f (void)
ClientReliableWrite_Long (host_client, PROTOCOL_VERSION_FTE1);
ClientReliableWrite_Long (host_client, fteext1);
}
if (host_client->fteprotocolextensions2)//let the client know
if (fteext2)//let the client know
{
ClientReliableWrite_Long (host_client, PROTOCOL_VERSION_FTE2);
ClientReliableWrite_Long (host_client, host_client->fteprotocolextensions2);
ClientReliableWrite_Long (host_client, fteext2);
}
ClientReliableWrite_Long (host_client, ISQ2CLIENT(host_client)?PROTOCOL_VERSION_Q2:PROTOCOL_VERSION_QW);
ClientReliableWrite_Long (host_client, svs.spawncount);
@ -340,7 +342,7 @@ void SV_New_f (void)
ClientReliableWrite_Byte (host_client, 0);
ClientReliableWrite_String (host_client, gamedir);
if (host_client->fteprotocolextensions2 & PEXT2_MAXPLAYERS)
if (fteext2 & PEXT2_MAXPLAYERS)
{
/*is this a sane way to do it? or should we split the spectator thing off entirely?*/
ClientReliableWrite_Byte (host_client, sv.allocated_client_slots);
@ -546,6 +548,7 @@ void SVNQ_New_f (void)
protext1 |= PEXT_FLOATCOORDS;
else
protext1 &= ~PEXT_FLOATCOORDS;
protext2 &= ~PEXT2_STUNAWARE; //always clear this, don't confuse demos.
op = host_client->protocol;
if (host_client->supportedprotocols)
@ -3892,19 +3895,13 @@ void SV_Say (qboolean team)
if (strlen(text)+strlen(p)+2 >= sizeof(text)-10)
{
SV_ClientTPrintf(host_client, PRINT_CHAT, "buffer overflow protection: failiure\n");
SV_ClientTPrintf(host_client, PRINT_CHAT, "buffer overflow protection: failure\n");
return;
}
if (svprogfuncs)
if (PR_QCChat(p, team)) //true if handled.
return;
if (strstr(p, "password"))
{
Z_Free(host_client->centerprintstring);
host_client->centerprintstring = Z_StrDup("big brother is watching you");
}
Q_strcat(text, p);
//filter out '\n' and '\r'
@ -3984,6 +3981,9 @@ void SV_Say (qboolean team)
#ifdef MVD_RECORDING
sv.mvdrecording = mvdrecording;
if (strstr(p, "password")) //just a friendly reminder.
SV_ClientPrintf(host_client, PRINT_HIGH, "DON'T SHARE PASSWORDS HERE, YOU MUPPET!\r");
if (!sv.mvdrecording || !cls)
return;

View file

@ -313,7 +313,7 @@ void Cluster_Run(cluster_t *cluster, qboolean dowait)
{
char buffer[8192];
char *result;
cluster->inputlength = read (0, cluster->commandinput, sizeof(cluster->commandinput));
cluster->inputlength = read (STDIN, cluster->commandinput, sizeof(cluster->commandinput));
if (cluster->inputlength >= 1)
{
cluster->commandinput[cluster->inputlength-1] = 0; // rip off the /n and terminate

View file

@ -862,7 +862,8 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
printf("pending drop\n");
if (pend->srcfile)
fclose(pend->srcfile);
closesocket(pend->sock);
if (pend->sock != INVALID_SOCKET)
closesocket(pend->sock);
free(pend);
cluster->numproxies--;
return true;
@ -1088,24 +1089,30 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
{ //this is actually a server trying to connect to us
//start up a new stream
//FIXME: does this work?
#if 0 //left disabled until properly tested
qtv = QTV_NewServerConnection(cluster, "reverse"/*server*/, "", true, AD_REVERSECONNECT, false, 0);
if (cluster->reverseallowed)
{
qtv = QTV_NewServerConnection(cluster, 0, "reverse"/*server*/, "", true, AD_REVERSECONNECT, false, 0);
Net_ProxySendString(cluster, pend, QTVSVHEADER);
Net_ProxySendString(cluster, pend, "REVERSED\n");
Net_ProxySendString(cluster, pend, "VERSION: 1\n");
Net_ProxySendString(cluster, pend, "\n");
Net_ProxySendString(cluster, pend, QTVSVHEADER);
Net_ProxySendString(cluster, pend, "VERSION: 1\n");
Net_ProxySendString(cluster, pend, "REVERSED\n");
Net_ProxySendString(cluster, pend, "\n");
//switch over the socket to the actual source connection rather than the pending
Net_TryFlushProxyBuffer(cluster, pend); //flush anything... this isn't ideal, but should be small enough
qtv->sourcesock = pend->sock;
pend->sock = 0;
//switch over the socket to the actual source connection rather than the pending
Net_TryFlushProxyBuffer(cluster, pend); //flush anything... this isn't ideal, but should be small enough
qtv->sourcesock = pend->sock;
pend->sock = INVALID_SOCKET;
memcpy(qtv->buffer, pend->inbuffer + headersize, pend->inbuffersize - headersize);
qtv->parsingqtvheader = true;
return false;
#endif
memcpy(qtv->buffer, pend->inbuffer + headersize, pend->inbuffersize - headersize);
qtv->parsingqtvheader = true;
return false;
}
else
{
Net_ProxySendString(cluster, pend, QTVSVHEADER
"PERROR: Reverse connections are disabled on this proxy\n");
pend->flushing = true;
}
}
else if (!ustrcmp(s, "RECEIVE"))
{ //a client connection request without a source

View file

@ -726,7 +726,7 @@ struct sv_s { //details about a server connection (also known as stream)
int nailcount;
char gamedir[MAX_QPATH];
char mapname[256];
char mapname[256]; //world.message
movevars_t movevars;
int cdtrack;
entity_t entity[MAX_ENTITIES];
@ -809,6 +809,7 @@ struct cluster_s {
qboolean nobsp;
qboolean allownqclients; //nq clients require no challenge
qboolean nouserconnects; //prohibit users from connecting to new streams.
qboolean reverseallowed; //demos can be submitted from servers via 'qtvreverse' without needing to keep idle connections live.
int anticheattime; //intial connection buffer delay (set high to block specing enemies)
int tooslowdelay; //if stream ran out of data, stop parsing for this long

View file

@ -1184,7 +1184,7 @@ void QTV_StatusResponse(cluster_t *cluster, char *msg, netadr_t *from)
strlcpy(sv->map.serverinfo, msg, sizeof(sv->map.serverinfo));
QTV_UpdatedServerInfo(sv);
Info_ValueForKey(sv->map.serverinfo, "map", sv->map.mapname, sizeof(sv->map.mapname));
// Info_ValueForKey(sv->map.serverinfo, "map", sv->map.mapname, sizeof(sv->map.mapname));
Info_ValueForKey(sv->map.serverinfo, "*gamedir", sv->map.gamedir, sizeof(sv->map.gamedir));
if (!*sv->map.gamedir)
strlcpy(sv->map.gamedir, "qw", sizeof(sv->map.gamedir));

View file

@ -712,6 +712,13 @@ void Cmd_Late(cmdctxt_t *ctx)
ctx->cluster->lateforward = !!atoi(Cmd_Argv(ctx, 1));
Cmd_Printf(ctx, "late forwarding set\n");
}
void Cmd_ReverseAllowed(cmdctxt_t *ctx)
{
if (Cmd_Argc(ctx) >= 2)
ctx->cluster->reverseallowed = !!atoi(Cmd_Argv(ctx, 1));
Cmd_Printf(ctx, "reverse connections are %s\n", ctx->cluster->reverseallowed?"enabled":"disabled");
}
void Cmd_Talking(cmdctxt_t *ctx)
{
if (Cmd_Argc(ctx) < 2)

166
imgtool.c
View file

@ -3,6 +3,8 @@
#undef stderr
#define stderr stdout
#define LittleLong(s) s
#include <limits.h>
#include <ctype.h>
#ifdef _WIN32
@ -703,7 +705,7 @@ static struct pendingtextureinfo *ImgTool_Read(struct opts_s *args, const char *
printf("%s: unable to read\n", inname);
else
{
in = Image_LoadMipsFromMemory(args->flags, inname, inname, indata, fsize);
in = Image_LoadMipsFromMemory(args->flags|IF_NOMIPMAP, inname, inname, indata, fsize);
if (!in)
{
printf("%s: unsupported format\n", inname);
@ -948,8 +950,11 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in,
if (in)
{
if (!(args->flags & IF_NOMIPMAP) && in->mipcount == 1)
Image_GenerateMips(in, args->flags);
if (!strcmp(outext, ".ktx") || !strcmp(outext, ".dds") || args->mipnum >= in->mipcount)
{
if (!(args->flags & IF_NOMIPMAP) && in->mipcount == 1)
Image_GenerateMips(in, args->flags);
}
if (args->mipnum >= in->mipcount)
{
@ -965,18 +970,13 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in,
in->mipcount -= k;
memmove(in->mip, &in->mip[k], sizeof(in->mip[0])*in->mipcount);
printf("%s(%s)->", inname, Image_FormatName(in->encoding));
if (args->newpixelformat != PTI_INVALID && (args->newpixelformat < PTI_BC1_RGB || allowcompressed) && ImgTool_ConvertPixelFormat(args, inname, in))
printf("\t(Converted to %s)\n", Image_FormatName(in->encoding));
printf("(%s)->\n", Image_FormatName(in->encoding));
if (!in->mipcount)
{
ImgTool_FreeMips(in);
printf("%s: unable to convert any mips\n", inname);
return;
}
if (0)
;
#ifdef IMAGEFMT_KTX
else if (!strcmp(outext, ".ktx"))
{
@ -1014,10 +1014,7 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in,
(k == PTI_BGR8) || (k == PTI_BGR8) ||
0;
if (!outformats[in->encoding])
{
Image_ChangeFormat(in, outformats, PTI_INVALID, outname);
printf("\t(Exporting as %s)\n", Image_FormatName(in->encoding));
}
Image_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh);
if (!Image_WritePNG(outname, FS_SYSTEM, 0, &in->mip[0].data, 1, in->mip[0].width*bb, in->mip[0].width, in->mip[0].height, in->encoding, false))
#endif
@ -1039,10 +1036,7 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in,
(k == PTI_BGR8) || (k == PTI_BGR8) ||
0;
if (!outformats[in->encoding])
{
Image_ChangeFormat(in, outformats, PTI_INVALID, outname);
printf("\t(Exporting as %s)\n", Image_FormatName(in->encoding));
}
Image_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh);
if (!WriteTGA(outname, FS_SYSTEM, in->mip[0].data, in->mip[0].width*bb, in->mip[0].width, in->mip[0].height, in->encoding))
Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding));
@ -1052,9 +1046,14 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in,
Con_Printf("%s: Unknown output file format\n", outname);
}
printf("%s: %s %s, %i*%i, %i mips\n", outname, imagetypename[in->type], Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height, in->mipcount);
for (k = 0; k < in->mipcount; k++)
printf("\t%u: %i*%i*%i, %u\n", (unsigned)k, in->mip[k].width, in->mip[k].height, in->mip[k].depth, (unsigned)in->mip[k].datasize);
if (in->mipcount > 1)
{
printf("%s(%s): %s %i*%i*%i, %i mips\n", outname, Image_FormatName(in->encoding), imagetypename[in->type], in->mip[0].width, in->mip[0].height, in->mip[0].depth, in->mipcount);
for (k = 0; k < in->mipcount; k++)
printf("\t%u: %i*%i*%i, %u\n", (unsigned)k, in->mip[k].width, in->mip[k].height, in->mip[k].depth, (unsigned)in->mip[k].datasize);
}
else
printf("%s(%s): %s %i*%i*%i, %u bytes\n", outname, Image_FormatName(in->encoding), imagetypename[in->type], in->mip[0].width, in->mip[0].height, in->mip[0].depth, (unsigned)in->mip[0].datasize);
ImgTool_FreeMips(in);
}
@ -1100,12 +1099,16 @@ static void ImgTool_Info(struct opts_s *args, const char *inname)
}
else
{
in = Image_LoadMipsFromMemory(args->flags, inname, inname, indata, fsize);
in = Image_LoadMipsFromMemory(args->flags|IF_NOMIPMAP, inname, inname, indata, fsize);
if (!in)
printf("%s: unsupported format\n", inname);
printf("%-20s: unsupported format\n", inname);
else if (in->mipcount == 1 && in->type == PTI_2D && in->mip[0].depth == 1)
printf("%-20s(%s): %4i*%-4i\n", inname, Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height);
else if (in->mipcount == 1)
printf("%-20s(%s): %s, %i*%i*%i, %u bytes\n", inname, Image_FormatName(in->encoding), imagetypename[in->type], in->mip[0].width, in->mip[0].height, in->mip[0].depth, (unsigned)in->mip[0].datasize);
else
{
printf("%s: %s %s, %i*%i, %i mips\n", inname, imagetypename[in->type], Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height, in->mipcount);
printf("%-20s(%s): %s, %i*%i*%i, %i mips\n", inname, Image_FormatName(in->encoding), imagetypename[in->type], in->mip[0].width, in->mip[0].height, in->mip[0].depth, in->mipcount);
for (m = 0; m < in->mipcount; m++)
printf("\t%u: %i*%i*%i, %u\n", (unsigned)m, in->mip[m].width, in->mip[m].height, in->mip[m].depth, (unsigned)in->mip[m].datasize);
@ -1314,6 +1317,8 @@ static void ImgTool_WadExtract(struct opts_s *args, const char *wadname)
{
const wad2_t *w = (const wad2_t *)indata;
const wad2entry_t *e = (const wad2entry_t *)(indata+w->offset);
int i;
char clean[sizeof(e->name)+1];
for (m = 0; m < w->num; m++, e++)
{
@ -1325,6 +1330,20 @@ static void ImgTool_WadExtract(struct opts_s *args, const char *wadname)
miptex_t *mip = (miptex_t *)(indata+e->offset);
struct pendingtextureinfo *out = Z_Malloc(sizeof(*out));
if (!strcmp(e->name, "CONCHARS") && e->size==128*128)
{ //special hack for conchars, which is listed as a miptex for some reason, with no qpic header (it not being a qpic lump)
out->encoding = TF_H2_TRANS8_0;
out->type = PTI_2D;
out->mip[0].width = 128;
out->mip[0].height = 128;
out->mip[0].depth = 1;
out->mip[0].datasize = out->mip[0].width*out->mip[0].height*out->mip[0].depth;
out->mip[0].data = (char*)mip;
out->mipcount = 1;
ImgTool_Convert(args, out, "conchars", NULL);
break;
}
out->encoding = PTI_P8;
out->type = PTI_2D;
for (out->mipcount = 0; out->mipcount < 4 && mip->offsets[out->mipcount]; out->mipcount++)
@ -1340,6 +1359,44 @@ static void ImgTool_WadExtract(struct opts_s *args, const char *wadname)
ImgTool_Convert(args, out, mip->name, NULL);
}
break;
case TYP_QPIC:
{
int *qpic = (int *)(indata+e->offset);
struct pendingtextureinfo *out = Z_Malloc(sizeof(*out));
size_t sz;
qbyte *p;
if (e->size < 8 || 8+qpic[0]*qpic[1] != e->size)
{
printf("invalid size/header for qpic lump: %s\n", e->name);
break;
}
out->type = PTI_2D;
out->mip[0].width = LittleLong(qpic[0]);
out->mip[0].height = LittleLong(qpic[1]);
out->mip[0].depth = 1;
out->mip[0].datasize = out->mip[0].width*out->mip[0].height*out->mip[0].depth;
out->mip[0].data = (char*)(qpic+2);
out->mipcount = 1;
for (sz = 0, p = out->mip[0].data; sz < out->mip[0].datasize; sz++)
if (p[sz] == 255)
break;
out->encoding = sz<out->mip[0].datasize?TF_TRANS8:PTI_P8;
for (i = 0; i < sizeof(e->name); i++)
{ //lowercase it.
if (e->name[i] >= 'A' && e->name[i] <= 'Z')
clean[i] = (e->name[i]-'A')+'a';
else
clean[i] = e->name[i];
}
clean[sizeof(e->name)] = 0;
ImgTool_Convert(args, out, clean, NULL);
}
break;
case TYP_PALETTE:
default:
printf("skipping %s\n", e->name);
@ -1391,7 +1448,7 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *destpath, const
sh_config.texfmt[u] = (u==PTI_RGBA8)||(u==PTI_RGBX8)||(u==PTI_P8);
if (wadtype == 2)
{ //WAD2 files generally have a palette lump.
{ //WAD2 texture files generally have a palette lump.
if (wad2.num == maxentries)
{
maxentries += 64;
@ -1468,10 +1525,6 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *destpath, const
in->mipcount = 4;
memmove(&in->mip[0], &in->mip[args->mipnum], sizeof(in->mip[0])*in->mipcount);
memset(&in->mip[in->mipcount], 0, sizeof(in->mip[0])*((args->mipnum+4)-in->mipcount)); //null it out, just in case.
if (!in->mip[0].width || (in->mip[0].width & 15))
Con_Printf("%s(%i): WARNING: miptex width is not a multiple of 16 - %i*%i\n", inname, args->mipnum, in->mip[0].width, in->mip[0].height);
if (!in->mip[0].height || (in->mip[0].height & 15))
Con_Printf("%s(%i): WARNING: miptex height is not a not multiple of 16 - %i*%i\n", inname, args->mipnum, in->mip[0].width, in->mip[0].height);
if (in->encoding != PTI_P8)
Image_ChangeFormat(in, wadpixelformats, (*inname=='{')?TF_TRANS8:PTI_INVALID, inname);
@ -1503,25 +1556,48 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *destpath, const
entry->dummy = 0;
entry->offset = VFS_TELL(f);
memcpy(mip.name, entry->name, sizeof(mip.name));
mip.width = in->mip[0].width;
mip.height = in->mip[0].height;
mip.offsets[0] = in->mip[0].datasize?sizeof(mip):0;
mip.offsets[1] = in->mip[1].datasize?mip.offsets[0]+in->mip[0].datasize:0;
mip.offsets[2] = in->mip[2].datasize?mip.offsets[1]+in->mip[1].datasize:0;
mip.offsets[3] = in->mip[3].datasize?mip.offsets[2]+in->mip[2].datasize:0;
if (!in->mip[0].width || (in->mip[0].width & 15))
Con_Printf("%s(%i): WARNING: miptex width is not a multiple of 16 - %i*%i\n", inname, args->mipnum, in->mip[0].width, in->mip[0].height);
if (!in->mip[0].height || (in->mip[0].height & 15))
Con_Printf("%s(%i): WARNING: miptex height is not a not multiple of 16 - %i*%i\n", inname, args->mipnum, in->mip[0].width, in->mip[0].height);
Con_Printf("%s: %ix%i\n", mip.name, mip.width, mip.height);
VFS_WRITE(f, &mip, sizeof(mip));
VFS_WRITE(f, in->mip[0].data, in->mip[0].datasize);
VFS_WRITE(f, in->mip[1].data, in->mip[1].datasize);
VFS_WRITE(f, in->mip[2].data, in->mip[2].datasize);
VFS_WRITE(f, in->mip[3].data, in->mip[3].datasize);
if (wad2.magic[3] == '3')
if (0)
{
VFS_WRITE(f, "\x00\x01", 2);
VFS_WRITE(f, host_basepal, 256*3);
if (!strcasecmp(entry->name, "CONCHARS") && in->mip[0].width==128&&in->mip[0].height==128)
entry->type = TYP_MIPTEX; //yes, weird. match vanilla quake. explicitly avoid qpic to avoid corruption in the first 8 bytes (due to the engine's early endian swapping)
//FIXME: encoding should be pti_trans8_0...
else
{
entry->type = TYP_QPIC;
//qpics need a header
VFS_WRITE(f, &in->mip[0].width, sizeof(int));
VFS_WRITE(f, &in->mip[0].height, sizeof(int));
}
//and now the 8bit pixel data itself
VFS_WRITE(f, in->mip[0].data, in->mip[0].datasize);
}
else
{
memcpy(mip.name, entry->name, sizeof(mip.name));
mip.width = in->mip[0].width;
mip.height = in->mip[0].height;
mip.offsets[0] = in->mip[0].datasize?sizeof(mip):0;
mip.offsets[1] = in->mip[1].datasize?mip.offsets[0]+in->mip[0].datasize:0;
mip.offsets[2] = in->mip[2].datasize?mip.offsets[1]+in->mip[1].datasize:0;
mip.offsets[3] = in->mip[3].datasize?mip.offsets[2]+in->mip[2].datasize:0;
Con_Printf("%s: %ix%i\n", mip.name, mip.width, mip.height);
VFS_WRITE(f, &mip, sizeof(mip));
VFS_WRITE(f, in->mip[0].data, in->mip[0].datasize);
VFS_WRITE(f, in->mip[1].data, in->mip[1].datasize);
VFS_WRITE(f, in->mip[2].data, in->mip[2].datasize);
VFS_WRITE(f, in->mip[3].data, in->mip[3].datasize);
if (wad2.magic[3] == '3')
{
VFS_WRITE(f, "\x00\x01", 2);
VFS_WRITE(f, host_basepal, 256*3);
}
}
entry->size = entry->dsize = VFS_TELL(f)-entry->offset;

View file

@ -669,9 +669,10 @@ int EZHud_Draw(int seat, float viewx, float viewy, float viewwidth, float viewhe
sb_lines = 24 + 16 + 8;
}
clientfuncs->GetPredInfo(seat, cl.simvel);
//cl.faceanimtime
//cl.simvel
//cl.item_gettime
//cls.state;
@ -679,8 +680,6 @@ int EZHud_Draw(int seat, float viewx, float viewy, float viewwidth, float viewhe
//cls.fps;
////cls.realtime;
////cls.trueframetime;
//cls.mvdplayback;
//cls.demoplayback;
host_screenupdatecount++;
HUD_Draw();

View file

@ -1104,7 +1104,291 @@ static void SCR_HUD_DrawNetStats(hud_t *hud)
//
// speed-o-meter
//
#ifdef HAXX
#define SPEED_TAG_LENGTH 2
#define SPEED_OUTLINE_SPACING SPEED_TAG_LENGTH
#define SPEED_FILL_SPACING SPEED_OUTLINE_SPACING + 1
#define SPEED_WHITE 10
#define SPEED_TEXT_ONLY 1
#define SPEED_TEXT_ALIGN_NONE 0
#define SPEED_TEXT_ALIGN_CLOSE 1
#define SPEED_TEXT_ALIGN_CENTER 2
#define SPEED_TEXT_ALIGN_FAR 3
// FIXME: hud-only now, can/should be moved
void SCR_DrawHUDSpeed (
int x, int y, int width, int height,
int type,
float tick_spacing,
float opacity,
int vertical,
int vertical_text,
int text_align,
byte color_stopped,
byte color_normal,
byte color_fast,
byte color_fastest,
byte color_insane,
int style,
float scale
)
{
byte color_offset;
byte color1, color2;
int player_speed;
vec_t *velocity;
if (scr_con_current == vid.height) {
return; // console is full screen
}
// Get the velocity.
// if (cl.players[cl.playernum].spectator && Cam_TrackNum() >= 0) {
// velocity = cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK].playerstate[Cam_TrackNum()].velocity;
// }
// else {
velocity = cl.simvel;
// }
// Calculate the speed
if (!type)
{
// Based on XY.
player_speed = sqrt(velocity[0]*velocity[0]
+ velocity[1]*velocity[1]);
}
else
{
// Based on XYZ.
player_speed = sqrt(velocity[0]*velocity[0]
+ velocity[1]*velocity[1]
+ velocity[2]*velocity[2]);
}
// Calculate the color offset for the "background color".
if (vertical) {
color_offset = height * (player_speed % 500) / 500;
}
else {
color_offset = width * (player_speed % 500) / 500;
}
// Set the color based on the speed.
switch (player_speed / 500)
{
case 0:
color1 = color_stopped;
color2 = color_normal;
break;
case 1:
color1 = color_normal;
color2 = color_fast;
break;
case 2:
color1 = color_fast;
color2 = color_fastest;
break;
default:
color1 = color_fastest;
color2 = color_insane;
break;
}
// Draw tag marks.
if (tick_spacing > 0.0 && style != SPEED_TEXT_ONLY)
{
float f;
for(f = tick_spacing; f < 1.0; f += tick_spacing)
{
if(vertical)
{
// Left.
Draw_AlphaFill(x, // x
y + (int)(f * height), // y
SPEED_TAG_LENGTH, // Width
1, // Height
SPEED_WHITE, // Color
opacity); // Opacity
// Right.
Draw_AlphaFill(x + width - SPEED_TAG_LENGTH + 1,
y + (int)(f * height),
SPEED_TAG_LENGTH,
1,
SPEED_WHITE,
opacity);
}
else
{
// Above.
Draw_AlphaFill(x + (int)(f * width),
y,
1,
SPEED_TAG_LENGTH,
SPEED_WHITE,
opacity);
// Below.
Draw_AlphaFill(x + (int)(f * width),
y + height - SPEED_TAG_LENGTH + 1,
1,
SPEED_TAG_LENGTH,
SPEED_WHITE,
opacity);
}
}
}
//
// Draw outline.
//
if (style != SPEED_TEXT_ONLY)
{
if(vertical)
{
// Left.
Draw_AlphaFill(x + SPEED_OUTLINE_SPACING,
y,
1,
height,
SPEED_WHITE,
opacity);
// Right.
Draw_AlphaFill(x + width - SPEED_OUTLINE_SPACING,
y,
1,
height,
SPEED_WHITE,
opacity);
}
else
{
// Above.
Draw_AlphaFill(x,
y + SPEED_OUTLINE_SPACING,
width,
1,
SPEED_WHITE,
opacity);
// Below.
Draw_AlphaFill(x,
y + height - SPEED_OUTLINE_SPACING,
width,
1,
SPEED_WHITE,
opacity);
}
}
//
// Draw fill.
//
if (style != SPEED_TEXT_ONLY)
{
if(vertical)
{
// Draw the right color (slower).
Draw_AlphaFill (x + SPEED_FILL_SPACING,
y,
width - (2 * SPEED_FILL_SPACING),
height - color_offset,
color1,
opacity);
// Draw the left color (faster).
Draw_AlphaFill (x + SPEED_FILL_SPACING,
y + height - color_offset,
width - (2 * SPEED_FILL_SPACING),
color_offset,
color2,
opacity);
}
else
{
// Draw the right color (slower).
Draw_AlphaFill (x + color_offset,
y + SPEED_FILL_SPACING,
width - color_offset,
height - (2 * SPEED_FILL_SPACING),
color1,
opacity);
// Draw the left color (faster).
Draw_AlphaFill (x,
y + SPEED_FILL_SPACING,
color_offset,
height - (2 * SPEED_FILL_SPACING),
color2,
opacity);
}
}
// Draw the speed text.
if(vertical && vertical_text)
{
int i = 1;
int len = 0;
// Align the text accordingly.
switch(text_align)
{
case SPEED_TEXT_ALIGN_NONE: return;
case SPEED_TEXT_ALIGN_FAR: y = y + height - 4*8; break;
case SPEED_TEXT_ALIGN_CENTER: y = Q_rint(y + height/2.0 - 16); break;
case SPEED_TEXT_ALIGN_CLOSE:
default: break;
}
len = strlen(va("%d", player_speed));
// 10^len
while(len > 0)
{
i *= 10;
len--;
}
// Write one number per row.
for(; i > 1; i /= 10)
{
int next;
next = (i/10);
// Really make sure we don't try division by zero :)
if(next <= 0)
{
break;
}
Draw_SString(Q_rint(x + width/2.0 - 4 * scale), y, va("%1d", (player_speed % i) / next), scale);
y += 8;
}
}
else
{
// Align the text accordingly.
switch(text_align)
{
case SPEED_TEXT_ALIGN_FAR:
x = x + width - 4 * 8 * scale;
break;
case SPEED_TEXT_ALIGN_CENTER:
x = Q_rint(x + width / 2.0 - 2 * 8 * scale);
break;
case SPEED_TEXT_ALIGN_CLOSE:
case SPEED_TEXT_ALIGN_NONE:
default:
break;
}
Draw_SString(x, Q_rint(y + height/2.0 - 4 * scale), va("%4d", player_speed), scale);
}
}
static void SCR_HUD_DrawSpeed(hud_t *hud)
{
int width, height;
@ -1123,7 +1407,8 @@ static void SCR_HUD_DrawSpeed(hud_t *hud)
*hud_speed_vertical,
*hud_speed_vertical_text,
*hud_speed_text_align,
*hud_speed_style;
*hud_speed_style,
*hud_speed_scale;
if (hud_speed_xyz == NULL) // first time
{
@ -1141,6 +1426,7 @@ static void SCR_HUD_DrawSpeed(hud_t *hud)
hud_speed_vertical_text = HUD_FindVar(hud, "vertical_text");
hud_speed_text_align = HUD_FindVar(hud, "text_align");
hud_speed_style = HUD_FindVar(hud, "style");
hud_speed_scale = HUD_FindVar(hud, "scale");
}
width = max(0, hud_speed_width->value);
@ -1160,10 +1446,10 @@ static void SCR_HUD_DrawSpeed(hud_t *hud)
hud_speed_color_fast->value,
hud_speed_color_fastest->value,
hud_speed_color_insane->value,
hud_speed_style->ival);
hud_speed_style->ival,
hud_speed_scale->value);
}
}
#endif
#define HUD_SPEED2_ORIENTATION_UP 0
#define HUD_SPEED2_ORIENTATION_DOWN 1
@ -7688,7 +7974,6 @@ static void SCR_HUD_DrawNotImplemented(hud_t *hud)
Draw_SString(x, y, line1, 1);
}
#define SCR_HUD_DrawSpeed SCR_HUD_DrawNotImplemented
#define SCR_HUD_DrawTeamHoldBar SCR_HUD_DrawNotImplemented
#define SCR_HUD_DrawTeamHoldInfo SCR_HUD_DrawNotImplemented
#define SCR_HUD_DrawItemsClock SCR_HUD_DrawNotImplemented
@ -7848,6 +8133,7 @@ void CommonDraw_Init(void)
"vertical_text", "1",
"text_align", "1",
"style", "0",
"scale", "1",
NULL);
// Init speed2 (half circle thingie).

View file

@ -1400,6 +1400,11 @@ static struct ircice_s *IRC_ICE_Create(ircclient_t *irc, const char *sender, enu
}
static void IRC_ICE_Update(ircclient_t *irc, struct ircice_s *ice, char updatetype)
{
//'+' propose ('hey, can I call you please?')
//'=' offer ('these are my details')
//'*' finalise ('this is what I'm going to use')
//'-' reject ('get lost, I don't want to talk to you any more')
//'%' candiate ('try this address')
//I was originally using colons to separate terms, but switched to slashes to avoid smilies for irc clients that print unknown CTCP messages.
char message[1024];
struct icecandinfo_s *c;

View file

@ -1,4 +1,4 @@
#ifndef GLQUAKE
#if !defined(GLQUAKE) && !defined(FTEENGINE)
#define GLQUAKE //this is shit, but ensures index sizes come out the right size
#endif
#include "quakedef.h"

View file

@ -262,6 +262,7 @@ typedef struct //q1 client/network info
F(size_t, GetTeamInfo, (teamplayerinfo_t *clients, size_t maxclients, qboolean showenemies, int seat));
F(int, GetWeaponStats, (int player, struct wstats_s *result, size_t maxresults));
F(float, GetTrackerOwnFrags, (int seat, char *text, size_t textsize));
F(void, GetPredInfo, (int seat, vec3_t outvel));
#define plugclientfuncs_name "Client"
} plugclientfuncs_t;

View file

@ -131,7 +131,6 @@ extern player_t *mplayers;
void CL_QueryServers(void);
int NET_CheckPollSockets(void);
void MasterInfo_Request(master_t *mast);
serverinfo_t *Master_InfoForServer (struct sockaddr_in addr);
serverinfo_t *Master_InfoForNum (int num);
int Master_TotalCount(void);

View file

@ -91,7 +91,12 @@ nonstatic void(mitem_desktop desktop) M_NewGame =
m.addm(banner, [(160-banner.item_size_x)*0.5, pos-32], [(160+banner.item_size_x)*0.5, pos-8]);
m.addm(menuitemeditt_spawn(_("Hostname"), "hostname", '280 8'), [-160, pos], [160, pos+8]); pos += 8;
m.addm(menuitemcheck_spawn(_("Public"), "sv_public", '280 8'), [-160, pos], [160, pos+8]); pos += 8;
m.addm(menuitemcombo_spawn(_("Public"), "sv_public", '280 8', _(
"-1 \"Reject All (Splitscreen)\" "
"0 \"Private (Manual IP Sharing)\" "
"1 \"Public (Manual Config)\" "
"2 \"Public (Holepunch)\" "
)), [-160, pos], [160, pos+8]); pos += 8;
m.addm(menuitemcombo_spawn(_("Max Clients"), "maxclients", '280 8', _(
"2 \"Two\" "
"3 \"Three\" "

View file

@ -15,6 +15,7 @@ FTEMANIFEST 1
GAME quake
NAME "In The Shadows"
PROTOCOLNAME ITShadows
RTCBROKER "tls://master.frag-net.com:27950"
DEFAULTEXEC ""
//UPDATEURL "http://example.com/mods/its.fmf"
BASEGAME id1
@ -25,7 +26,8 @@ GAMEDIR shadows
//this is a major pain for systems where there is no official installer (read: fte's webgl port, or android... or win64).
//its pak0 can be downloaded from sock's public server
ARCHIVEDPACKAGE shadows/pak0.pak "0xb1768147" "shadows/pak0.pak" "http://simonoc.com/files/maps/sp/its_demo_v1_1.zip"
-seta cl_forwardspeed 400
+bind w +forward
@ -46,11 +48,24 @@ This specifies what the game should be referred to by various console prints etc
PROTOCOLNAME "foo"
This is how the game/mod should be identified to master servers. A unique value here helps prevent servers from other games being listed, as well as preventing other games from seeing this game/mod.
FIXME: engine support for this should be improved.
This should be reasonably human readable - master servers might show it publically. This actually gets written into the com_protocolname cvar. You should use the com_protocolversion cvar in your default.cfg if you want to exclude older versions, avoiding annoying numbers etc.
RTCBROKER "url"
This should be a tls:// or tcp:// address that tells the engine where it can find a running instance of an 'ftemaster' program.
The broker program is used to allow client and target server to exchange address and port information in order to punch holes through NATs and Firewalls, allowing users to play without having to configure any of those monstrosities.
DEFAULTEXEC ""
If specified, this is inserted before the game's normal default.cfg file, and can be used to override the engine's default cvar settings for mods where the default.cfg was originally written for a different engine (and thus omits certain settings). This should only be used for games where the default.cfg itself is not replacable. Standalone mods should not need to specify this, and should instead just put everything in the mod's default.cfg file.
For recognised games, the engine will automatically assume usable defaults, but its possible that a mod depends upon a setting that I overlooked.
This is obsolete, other than to disable the built-in overrides for known games. See the following - and + prefixes for easier usage.
-set foo bar
The text following a leading minus are commands that are included before the mod's default.cfg, allowing to override engine behaviour without breaking mods that require a specific value. This mechanism is quite handy for mimicing other engines.
Any commands may be used, but its best to limit yourself to set/seta/setfl, alias, and bind.
+set foo bar
The text following a leading plus are commands that are included AFTER the mod's default.cfg, allowing you to stomp all over defaults that were stupid and flawed (or just outdated). This is useful for making quake's default settings and binds usable, but its generally better to just provide a new default.cfg instead.
Any commands may be used, but its best to limit yourself to set/seta/setfl, alias, and bind.
BASEGAME "id1"
Multiple basegame lines can be specified. These are the core subdirectories that should always be loaded, even if the gamedir command was used to load a different mod.
@ -71,6 +86,12 @@ UPDATEURL "http://example.com/foo.fmf"
This command gives the url from which to update the manifest file from. The updated manifest *MUST* contain the same url for the update to be accepted. Because of this, you should ensure that it is has a url that will be valid for as long as your mod will be relevent.
This replaces just the manifest, but because the manifest file specifies a list of packages that should be used, a new set of packages can be downloaded automatically, and this is the true power of the update url.
DOWNLOADSURL "https://updates.triptohell.info/downloadables.php?game=Q1"
This command tells the engine where it can obtain a list of available packages. The user is able to enable/disable as they desire.
INSTALL "package1;package2"
This command tells the engine which packages should be 'strongly encouraged' onto the user (ie: the user will be prompted to install them if they're missing). Naturally this should not be abused, however the user is better able to see what is happening (they must still accept installation).
PACKAGE "id1/pak0.pak" 0x4f069cac "http://mirror1.example.com/qsw106_pak0.pak" "http://mirror2.example.com/qsw106_pak0.pak"
WARNING: be sure to respect third-party copyrights.
This command, of which multiple can be specified, lists the packages which should automatically be downloaded.