What could possibly go wrong?...

Multiple consoles can now be printed/cleared via extra con commands.
Fixed the tab-completion alignment, by adding support for \t characters.
Changing the download mechanisms. Don't try downloading an ftp:// file. It'll probably crash you for now.
Trying to fix load time issues on q3bsps with a lot of curves.
Fixed sprites.
Added warning prints/spam where the new backend is bypassed, thus marking things that still need to be fixed.
QTV proxy fixed to not sit on qw servers unless someone is actually watching. Will ping for status requests still.
QTV proxy now supports ipv6.
QTV proxy now attempts to use the fte browser plugin.
Reworked the browser plugin code, now uses threads instead of ugly hacks. This should make cooperation with other such plugins work. Fixes unresponsiveness of opera, and gives an API that can be used from any other bit of software you want, tbh (read: internet explorer/activex plugins).

git-svn-id: https://svn.code.sf.net/p/fteqw/code/branches/wip@3516 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2010-03-14 14:35:56 +00:00
parent 738142b92a
commit c0680334c7
73 changed files with 3782 additions and 4690 deletions

View File

@ -821,7 +821,7 @@ static int CG_SystemCallsEx(void *offset, unsigned int mask, int fn, const int *
break;
case CG_R_DRAWSTRETCHPIC:
Draw_Image(VM_FLOAT(arg[0]), VM_FLOAT(arg[1]), VM_FLOAT(arg[2]), VM_FLOAT(arg[3]), VM_FLOAT(arg[4]), VM_FLOAT(arg[5]), VM_FLOAT(arg[6]), VM_FLOAT(arg[7]), (mpic_t *)VM_LONG(arg[8]));
Draw_Image(VM_FLOAT(arg[0]), VM_FLOAT(arg[1]), VM_FLOAT(arg[2]), VM_FLOAT(arg[3]), VM_FLOAT(arg[4]), VM_FLOAT(arg[5]), VM_FLOAT(arg[6]), VM_FLOAT(arg[7]), VM_FROMSHANDLE(VM_LONG(arg[8])));
break;
case CG_R_LERPTAG: //Lerp tag...

View File

@ -67,8 +67,8 @@ void CL_StopPlayback (void)
Media_CaptureDemoEnd();
VFS_CLOSE (cls.demofile);
cls.demofile = NULL;
VFS_CLOSE (cls.demoinfile);
cls.demoinfile = NULL;
cls.state = ca_disconnected;
cls.demoplayback = DPB_NONE;
@ -97,10 +97,10 @@ void CL_WriteDemoCmd (usercmd_t *pcmd)
//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, demtime);
fl = LittleFloat((float)demtime);
VFS_WRITE (cls.demofile, &fl, sizeof(fl));
VFS_WRITE (cls.demooutfile, &fl, sizeof(fl));
c = dem_cmd;
VFS_WRITE (cls.demofile, &c, sizeof(c));
VFS_WRITE (cls.demooutfile, &c, sizeof(c));
// correct for byte order, bytes don't matter
@ -115,15 +115,15 @@ void CL_WriteDemoCmd (usercmd_t *pcmd)
cmd.sidemove = LittleShort(pcmd->sidemove);
cmd.upmove = LittleShort(pcmd->upmove);
VFS_WRITE (cls.demofile, &cmd, sizeof(cmd));
VFS_WRITE (cls.demooutfile, &cmd, sizeof(cmd));
for (i=0 ; i<3 ; i++)
{
fl = LittleFloat (cl.viewangles[0][i]);
VFS_WRITE (cls.demofile, &fl, 4);
VFS_WRITE (cls.demooutfile, &fl, 4);
}
VFS_FLUSH (cls.demofile);
VFS_FLUSH (cls.demooutfile);
}
/*
@ -145,16 +145,16 @@ void CL_WriteDemoMessage (sizebuf_t *msg)
return;
fl = LittleFloat((float)demtime);
VFS_WRITE (cls.demofile, &fl, sizeof(fl));
VFS_WRITE (cls.demooutfile, &fl, sizeof(fl));
c = dem_read;
VFS_WRITE (cls.demofile, &c, sizeof(c));
VFS_WRITE (cls.demooutfile, &c, sizeof(c));
len = LittleLong (msg->cursize);
VFS_WRITE (cls.demofile, &len, 4);
VFS_WRITE (cls.demofile, msg->data, msg->cursize);
VFS_WRITE (cls.demooutfile, &len, 4);
VFS_WRITE (cls.demooutfile, msg->data, msg->cursize);
VFS_FLUSH (cls.demofile);
VFS_FLUSH (cls.demooutfile);
}
int demo_preparsedemo(unsigned char *buffer, int bytes)
@ -237,7 +237,7 @@ int readdemobytes(int *readpos, void *data, int len)
trybytes = sizeof(demobuffer)-demobuffersize;
i = VFS_READ(cls.demofile, demobuffer+demobuffersize, trybytes);
i = VFS_READ(cls.demoinfile, demobuffer+demobuffersize, trybytes);
if (i > 0)
{
demobuffersize += i;
@ -822,8 +822,8 @@ void CL_Stop_f (void)
CL_WriteDemoMessage (&net_message);
// finish up
VFS_CLOSE (cls.demofile);
cls.demofile = NULL;
VFS_CLOSE (cls.demooutfile);
cls.demooutfile = NULL;
cls.demorecording = false;
Con_Printf ("Completed demo\n");
@ -851,21 +851,21 @@ void CL_WriteRecordDemoMessage (sizebuf_t *msg, int seq)
return;
fl = LittleFloat((float)demtime);
VFS_WRITE (cls.demofile, &fl, sizeof(fl));
VFS_WRITE (cls.demooutfile, &fl, sizeof(fl));
c = dem_read;
VFS_WRITE (cls.demofile, &c, sizeof(c));
VFS_WRITE (cls.demooutfile, &c, sizeof(c));
len = LittleLong (msg->cursize + 8);
VFS_WRITE (cls.demofile, &len, 4);
VFS_WRITE (cls.demooutfile, &len, 4);
i = LittleLong(seq);
VFS_WRITE (cls.demofile, &i, 4);
VFS_WRITE (cls.demofile, &i, 4);
VFS_WRITE (cls.demooutfile, &i, 4);
VFS_WRITE (cls.demooutfile, &i, 4);
VFS_WRITE (cls.demofile, msg->data, msg->cursize);
VFS_WRITE (cls.demooutfile, msg->data, msg->cursize);
VFS_FLUSH (cls.demofile);
VFS_FLUSH (cls.demooutfile);
}
@ -881,17 +881,17 @@ void CL_WriteSetDemoMessage (void)
return;
fl = LittleFloat((float)demtime);
VFS_WRITE (cls.demofile, &fl, sizeof(fl));
VFS_WRITE (cls.demooutfile, &fl, sizeof(fl));
c = dem_set;
VFS_WRITE (cls.demofile, &c, sizeof(c));
VFS_WRITE (cls.demooutfile, &c, sizeof(c));
len = LittleLong(cls.netchan.outgoing_sequence);
VFS_WRITE (cls.demofile, &len, 4);
VFS_WRITE (cls.demooutfile, &len, 4);
len = LittleLong(cls.netchan.incoming_sequence);
VFS_WRITE (cls.demofile, &len, 4);
VFS_WRITE (cls.demooutfile, &len, 4);
VFS_FLUSH (cls.demofile);
VFS_FLUSH (cls.demooutfile);
}
@ -1029,8 +1029,8 @@ void CL_Record_f (void)
//
// open the demo file
//
cls.demofile = FS_OpenVFS (name, "wb", FS_GAME);
if (!cls.demofile)
cls.demooutfile = FS_OpenVFS (name, "wb", FS_GAME);
if (!cls.demooutfile)
{
Con_Printf ("ERROR: couldn't open.\n");
return;
@ -1354,8 +1354,8 @@ void CL_ReRecord_f (void)
//
COM_DefaultExtension (name, ".qwd", sizeof(name));
cls.demofile = FS_OpenVFS (name, "wb", FS_GAME);
if (!cls.demofile)
cls.demooutfile = FS_OpenVFS (name, "wb", FS_GAME);
if (!cls.demooutfile)
{
Con_Printf ("ERROR: couldn't open.\n");
return;
@ -1397,12 +1397,15 @@ void CL_PlayDemo_f (void)
}
#ifdef WEBCLIENT
#pragma message("playdemo http://blah is broken right now")
#if 0
if (!strncmp(Cmd_Argv(1), "ftp://", 6) || !strncmp(Cmd_Argv(1), "http://", 7))
{
if (Cmd_ExecLevel == RESTRICT_LOCAL)
HTTP_CL_Get(Cmd_Argv(1), COM_SkipPath(Cmd_Argv(1)), CL_PlayDownloadedDemo);
return;
}
#endif
#endif
CL_PlayDemo(Cmd_Argv(1));
@ -1430,28 +1433,28 @@ void CL_PlayDemo(char *demoname)
Q_strncpyz (name, demoname, sizeof(name));
COM_DefaultExtension (name, ".qwd", sizeof(name));
if (*name == '#')
cls.demofile = VFSOS_Open(name+1, "rb");
cls.demoinfile = VFSOS_Open(name+1, "rb");
else
cls.demofile = FS_OpenVFS(name, "rb", FS_GAME);
if (!cls.demofile)
cls.demoinfile = FS_OpenVFS(name, "rb", FS_GAME);
if (!cls.demoinfile)
{
Q_strncpyz (name, demoname, sizeof(name));
COM_DefaultExtension (name, ".dem", sizeof(name));
if (*name == '#')
cls.demofile = VFSOS_Open(name+1, "rb");
cls.demoinfile = VFSOS_Open(name+1, "rb");
else
cls.demofile = FS_OpenVFS(name, "rb", FS_GAME);
cls.demoinfile = FS_OpenVFS(name, "rb", FS_GAME);
}
if (!cls.demofile)
if (!cls.demoinfile)
{
Q_strncpyz (name, demoname, sizeof(name));
COM_DefaultExtension (name, ".mvd", sizeof(name));
if (*name == '#')
cls.demofile = VFSOS_Open(name+1, "rb");
cls.demoinfile = VFSOS_Open(name+1, "rb");
else
cls.demofile = FS_OpenVFS(name, "rb", FS_GAME);
cls.demoinfile = FS_OpenVFS(name, "rb", FS_GAME);
}
if (!cls.demofile)
if (!cls.demoinfile)
{
Con_Printf ("ERROR: couldn't open \"%s\".\n", demoname);
cls.demonum = -1; // stop demo loop
@ -1460,10 +1463,10 @@ void CL_PlayDemo(char *demoname)
Q_strncpyz (lastdemoname, demoname, sizeof(lastdemoname));
Con_Printf ("Playing demo from %s.\n", name);
if (!VFS_GETLEN (cls.demofile))
if (!VFS_GETLEN (cls.demoinfile))
{
VFS_CLOSE(cls.demofile);
cls.demofile = NULL;
VFS_CLOSE(cls.demoinfile);
cls.demoinfile = NULL;
Con_Printf ("demo \"%s\" is empty.\n", demoname);
cls.demonum = -1; // stop demo loop
return;
@ -1488,11 +1491,11 @@ void CL_PlayDemo(char *demoname)
cls.netchan.last_received=demtime;
start = VFS_TELL(cls.demofile);
VFS_READ(cls.demofile, &len, sizeof(len));
VFS_READ(cls.demofile, &type, sizeof(type));
VFS_READ(cls.demofile, &protocol, sizeof(protocol));
VFS_SEEK(cls.demofile, start);
start = VFS_TELL(cls.demoinfile);
VFS_READ(cls.demoinfile, &len, sizeof(len));
VFS_READ(cls.demoinfile, &type, sizeof(type));
VFS_READ(cls.demoinfile, &protocol, sizeof(protocol));
VFS_SEEK(cls.demoinfile, start);
if (len > 5 && type == svcq2_serverdata && protocol == PROTOCOL_VERSION_Q2)
{
#ifdef Q2CLIENT
@ -1509,7 +1512,7 @@ void CL_PlayDemo(char *demoname)
cls.protocol = CP_QUAKEWORLD;
ft = 0; //work out if the first line is a int for the track number.
while ((VFS_READ(cls.demofile, &chr, 1)==1) && (chr != '\n'))
while ((VFS_READ(cls.demoinfile, &chr, 1)==1) && (chr != '\n'))
{
if (chr == '-')
neg = true;
@ -1530,7 +1533,7 @@ void CL_PlayDemo(char *demoname)
#endif
}
else
VFS_SEEK(cls.demofile, start); //quakeworld demo, so go back to start.
VFS_SEEK(cls.demoinfile, start); //quakeworld demo, so go back to start.
}
TP_ExecTrigger ("f_demostart");
@ -1540,7 +1543,7 @@ void CL_QTVPlay (vfsfile_t *newf, qboolean iseztv)
{
CL_Disconnect_f ();
cls.demofile = newf;
cls.demoinfile = newf;
demo_flushcache(); //just in case
@ -1566,6 +1569,7 @@ void CL_QTVPlay (vfsfile_t *newf, qboolean iseztv)
TP_ExecTrigger ("f_demostart");
}
/*used with qtv*/
void CL_Demo_ClientCommand(char *commandtext)
{
unsigned char b = 1;
@ -1573,9 +1577,12 @@ void CL_Demo_ClientCommand(char *commandtext)
#ifndef _MSC_VER
#warning "this needs buffering safely"
#endif
VFS_WRITE(cls.demofile, &len, sizeof(len));
VFS_WRITE(cls.demofile, &b, sizeof(b));
VFS_WRITE(cls.demofile, commandtext, strlen(commandtext)+1);
if (cls.demoplayback == DPB_EZTV)
{
VFS_WRITE(cls.demoinfile, &len, sizeof(len));
VFS_WRITE(cls.demoinfile, &b, sizeof(b));
VFS_WRITE(cls.demoinfile, commandtext, strlen(commandtext)+1);
}
}
char qtvhostname[1024];

View File

@ -478,7 +478,17 @@ void CL_ParsePacketEntities (qboolean delta)
newp = &cl.frames[newpacket].packet_entities;
cl.frames[newpacket].invalid = false;
if (!(cls.fteprotocolextensions & PEXT_ACCURATETIMINGS) && cls.protocol == CP_QUAKEWORLD)
if (cls.protocol == CP_QUAKEWORLD && cls.demoplayback == DPB_MVD)
{
extern float nextdemotime, olddemotime, demtime;
cl.oldgametime = cl.gametime;
cl.oldgametimemark = cl.gametimemark;
cl.gametime = nextdemotime;
cl.gametimemark = realtime;
newp->servertime = cl.gametime;
}
else if (!(cls.fteprotocolextensions & PEXT_ACCURATETIMINGS) && cls.protocol == CP_QUAKEWORLD)
{
cl.oldgametime = cl.gametime;
cl.oldgametimemark = cl.gametimemark;
@ -1403,7 +1413,7 @@ static void CL_LerpNetFrameState(int fsanim, framestate_t *fs, lerpents_t *le)
fs->g[fsanim].lerpfrac = bound(0, fs->g[FS_REG].lerpfrac, 1);
}
void CL_UpdateNetFrameLerpState(qboolean force, unsigned int curframe, lerpents_t *le)
static void CL_UpdateNetFrameLerpState(qboolean force, unsigned int curframe, lerpents_t *le)
{
if (force || curframe != le->newframe)
{
@ -1432,7 +1442,10 @@ CL_LinkPacketEntities
*/
void R_FlameTrail(vec3_t start, vec3_t end, float seperation);
void CL_TransitionPacketEntities(packet_entities_t *newpack, packet_entities_t *oldpack, float servertime)
/*
Interpolates the two packets by the given time, writes its results into the lerpentities array.
*/
static void CL_TransitionPacketEntities(packet_entities_t *newpack, packet_entities_t *oldpack, float servertime)
{
lerpents_t *le;
entity_state_t *snew, *sold;
@ -1562,21 +1575,13 @@ void CL_TransitionPacketEntities(packet_entities_t *newpack, packet_entities_t *
}
}
packet_entities_t *CL_ProcessPacketEntities(float *servertime, qboolean nolerp)
static qboolean CL_ChooseInterpolationFrames(int *newf, int *oldf, float servertime)
{
packet_entities_t *packnew, *packold;
int i;
//, spnum;
int i;
float newtime = 0;
*oldf = -1;
*newf = -1;
if (nolerp)
{ //force our emulated time to as late as we can.
//this will disable all position interpolation
*servertime = cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities.servertime;
// Con_DPrintf("No lerp\n");
}
packnew = NULL;
packold = NULL;
//choose the two packets.
//we should be picking the packet just after the server time, and the one just before
for (i = cls.netchan.incoming_sequence; i >= cls.netchan.incoming_sequence-UPDATE_MASK; i--)
@ -1584,45 +1589,73 @@ packet_entities_t *CL_ProcessPacketEntities(float *servertime, qboolean nolerp)
if (cl.frames[i&UPDATE_MASK].receivedtime < 0 || cl.frames[i&UPDATE_MASK].invalid)
continue; //packetloss/choke, it's really only a problem for the oldframe, but...
if (cl.frames[i&UPDATE_MASK].packet_entities.servertime >= *servertime)
if (cl.frames[i&UPDATE_MASK].packet_entities.servertime >= servertime)
{
if (cl.frames[i&UPDATE_MASK].packet_entities.servertime)
{
if (!packnew || packnew->servertime != cl.frames[i&UPDATE_MASK].packet_entities.servertime) //if it's a duplicate, pick the latest (so just-shot rockets are still present)
packnew = &cl.frames[i&UPDATE_MASK].packet_entities;
if (!newtime || newtime != cl.frames[i&UPDATE_MASK].packet_entities.servertime) //if it's a duplicate, pick the latest (so just-shot rockets are still present)
{
newtime = cl.frames[i&UPDATE_MASK].packet_entities.servertime;
*newf = i;
}
}
}
else if (packnew)
else if (newtime)
{
if (cl.frames[i&UPDATE_MASK].packet_entities.servertime != packnew->servertime)
if (cl.frames[i&UPDATE_MASK].packet_entities.servertime != newtime)
{ //it does actually lerp, and isn't an identical frame.
packold = &cl.frames[i&UPDATE_MASK].packet_entities;
*oldf = i;
break;
}
}
}
//Note, hacking this to return anyway still needs the lerpent array to be valid for all contained entities.
if (!packnew) //should never happen
if (*newf == -1)
{
/*
This can happen if the client's predicted time is greater than the most recently received packet.
This should of course not happen...
*/
Con_DPrintf("Warning: No lerp-to frame packet\n");
return NULL;
/*just grab the most recent frame that is valid*/
for (i = cls.netchan.incoming_sequence; i >= cls.netchan.incoming_sequence-UPDATE_MASK; i--)
{
if (cl.frames[i&UPDATE_MASK].receivedtime < 0 || cl.frames[i&UPDATE_MASK].invalid)
continue; //packetloss/choke, it's really only a problem for the oldframe, but...
*oldf = *newf = i;
return true;
}
return false;
}
if (!packold) //can happem at map start, and really laggy games, but really shouldn't in a normal game
else if (*oldf == -1) //can happen at map start, and really laggy games, but really shouldn't in a normal game
{
// Con_DPrintf("Warning: No lerp-from frame packet\n");
packold = packnew;
*oldf = *newf;
}
return true;
}
/*obtains the current entity frame, and invokes CL_TransitionPacketEntities to process the interpolation details
*/
static packet_entities_t *CL_ProcessPacketEntities(float *servertime, qboolean nolerp)
{
packet_entities_t *packnew, *packold;
int newf, oldf;
if (nolerp)
{ //force our emulated time to as late as we can.
//this will disable all position interpolation
*servertime = cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities.servertime;
}
if (!CL_ChooseInterpolationFrames(&newf, &oldf, *servertime))
return NULL;
packnew = &cl.frames[newf&UPDATE_MASK].packet_entities;
packold = &cl.frames[oldf&UPDATE_MASK].packet_entities;
CL_TransitionPacketEntities(packnew, packold, *servertime);
// Con_DPrintf("%f %f %f %f %f %f\n", packnew->servertime, *servertime, packold->servertime, cl.gametime, cl.oldgametime, cl.servertime);
// if (packold->servertime < oldoldtime)
// Con_Printf("Spike screwed up\n");
// oldoldtime = packold->servertime;
return packnew;
}
@ -1660,24 +1693,32 @@ void CL_LinkPacketEntities (void)
float servertime;
CL_CalcClientTime();
if ((cls.fteprotocolextensions & PEXT_ACCURATETIMINGS) || cls.protocol != CP_QUAKEWORLD)
if (cls.protocol == CP_QUAKEWORLD && (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV))
{
servertime = cl.servertime;
nolerp = false;
}
else
servertime = realtime;
{
if ((cls.fteprotocolextensions & PEXT_ACCURATETIMINGS) || cls.protocol != CP_QUAKEWORLD)
servertime = cl.servertime;
else
servertime = realtime;
nolerp = !CL_MayLerp() && cls.demoplayback != DPB_MVD && cls.demoplayback != DPB_EZTV;
#ifdef NQPROT
nolerp = nolerp && cls.demoplayback != DPB_NETQUAKE;
#endif
nolerp = !CL_MayLerp() && cls.demoplayback != DPB_MVD && cls.demoplayback != DPB_EZTV;
#ifdef NQPROT
nolerp = nolerp && cls.demoplayback != DPB_NETQUAKE;
#endif
}
pack = CL_ProcessPacketEntities(&servertime, nolerp);
if (!pack)
return;
/*
if ((cls.fteprotocolextensions & PEXT_ACCURATETIMINGS) || cls.protocol != CP_QUAKEWORLD)
servertime = cl.servertime;
else
servertime = realtime;
*/
autorotate = anglemod(100*servertime);
@ -2574,6 +2615,7 @@ void CL_LinkPlayers (void)
int oldphysent;
vec3_t angles;
float *org;
qboolean predictplayers;
if (!cl.worldmodel || cl.worldmodel->needload)
return;
@ -2585,6 +2627,10 @@ void CL_LinkPlayers (void)
frame = &cl.frames[cl.validsequence&UPDATE_MASK];
fromf = &cl.frames[cl.oldvalidsequence&UPDATE_MASK];
predictplayers = cl_predict_players.ival || cl_predict_players2.ival;
if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV)
predictplayers = false;
for (j=0, info=cl.players, state=frame->playerstate ; j < MAX_CLIENTS
; j++, info++, state++)
{
@ -2755,7 +2801,7 @@ void CL_LinkPlayers (void)
if (pnum < cl.splitclients)
{ //this is a local player
}
else if (msec <= 0 || (!cl_predict_players.ival && !cl_predict_players2.ival))
else if (msec <= 0 || (!predictplayers))
{
VectorCopy (state->origin, ent->origin);
//Con_DPrintf ("nopredict\n");
@ -3382,8 +3428,8 @@ void MVD_Interpolate(void)
if (nextdemotime <= olddemotime)
return;
frame = &cl.frames[cl.parsecount & UPDATE_MASK];
oldframe = &cl.frames[cl.oldparsecount & UPDATE_MASK];
frame = &cl.frames[cl.validsequence & UPDATE_MASK];
oldframe = &cl.frames[cl.oldvalidsequence & UPDATE_MASK];
oldents = oldframe->packet_entities.entities;
f = (demtime - olddemotime) / (nextdemotime - olddemotime);

View File

@ -2838,13 +2838,14 @@ void CL_ServerInfo_f(void)
}
#endif
/*
#ifdef WEBCLIENT
void CL_FTP_f(void)
{
FTP_Client_Command(Cmd_Args(), NULL);
}
#endif
*/
void CL_Skygroup_f(void);
void SCR_ShowPic_Script_f(void);
@ -3022,9 +3023,9 @@ void CL_Init (void)
Cvar_Register (&qtvcl_forceversion1, cl_controlgroup);
Cvar_Register (&qtvcl_eztvextensions, cl_controlgroup);
#ifdef WEBCLIENT
Cmd_AddCommand ("ftp", CL_FTP_f);
#endif
//#ifdef WEBCLIENT
// Cmd_AddCommand ("ftp", CL_FTP_f);
//#endif
Cmd_AddCommand ("changing", CL_Changing_f);
Cmd_AddCommand ("disconnect", CL_Disconnect_f);
@ -3293,7 +3294,7 @@ void Host_Frame (double time)
time *= cl.gamespeed;
#ifdef WEBCLIENT
FTP_ClientThink();
// FTP_ClientThink();
HTTP_CL_Think();
#endif
@ -3703,7 +3704,7 @@ void Host_FinishInit(void)
#endif
#ifndef NOMEDIA
if (!cls.demofile && !cls.state && !Media_PlayingFullScreen())
if (!cls.demoinfile && !cls.state && !Media_PlayingFullScreen())
{
int ol_depth;
int idcin_depth;
@ -3740,7 +3741,7 @@ Con_TPrintf (TL_NL);
"See the GNU General Public License for more details.\n");
if (!*cls.servername)
if (!cls.demoinfile && !*cls.servername)
{
#ifndef CLIENTONLY
if (!sv.state)

View File

@ -646,7 +646,15 @@ void CL_CalcClientTime(void)
float want;
float oldst = realtime;
if (!(cls.fteprotocolextensions & PEXT_ACCURATETIMINGS) && cls.protocol == CP_QUAKEWORLD)
if (cls.protocol == CP_QUAKEWORLD && cls.demoplayback == DPB_MVD)
{
extern float nextdemotime, olddemotime, demtime;
float f;
f = (demtime - olddemotime) / (nextdemotime - olddemotime);
f = bound(0, f, 1);
cl.time = cl.servertime = cl.gametime*f + cl.oldgametime*(1-f);
}
else if (!(cls.fteprotocolextensions & PEXT_ACCURATETIMINGS) && cls.protocol == CP_QUAKEWORLD)
cl.servertime = cl.time;
else
{

View File

@ -440,7 +440,6 @@ void SCR_DrawCenterString (vrect_t *rect, cprint_t *p)
int bottom;
int remaining;
conchar_t *str;
conchar_t *line_start[MAX_CPRINT_LINES];
conchar_t *line_end[MAX_CPRINT_LINES];
int linecount;
@ -492,29 +491,23 @@ void SCR_DrawCenterString (vrect_t *rect, cprint_t *p)
{
if (p->flags & CPRINT_RALIGN)
{
x = right;
for (str = line_start[l]; str < line_end[l]; str++)
x -= Font_CharWidth(*str);
x = right - Font_LineWidth(line_start[l], line_end[l]);
}
else if (p->flags & CPRINT_LALIGN)
x = left;
else
{
x = 0;
for (str = line_start[l]; str < line_end[l]; str++)
x += Font_CharWidth(*str);
x = (right + left - x)/2;
x = (right + left - Font_LineWidth(line_start[l], line_end[l]))/2;
}
for (str = line_start[l]; str < line_end[l]; str++)
remaining -= line_end[l]-line_start[l];
if (remaining <= 0)
{
if (!remaining--)
{
l = linecount-1;
line_end[l] += remaining;
if (line_end[l] <= line_start[l])
break;
}
x = Font_DrawChar(x, y, *str);
}
Font_LineDraw(x, y, line_start[l], line_end[l]);
}
Font_EndString(font_conchar);
}
@ -1618,8 +1611,6 @@ void SCR_SetUpToDrawConsole (void)
#ifdef TEXTEDITOR
extern qboolean editoractive;
#endif
Con_CheckResize ();
if (scr_drawloading)
return; // never a console with loading plaque

View File

@ -910,7 +910,7 @@ int UI_SystemCallsEx(void *offset, unsigned int mask, int fn, const int *arg)
break;
case UI_R_DRAWSTRETCHPIC:
Draw_Image(VM_FLOAT(arg[0]), VM_FLOAT(arg[1]), VM_FLOAT(arg[2]), VM_FLOAT(arg[3]), VM_FLOAT(arg[4]), VM_FLOAT(arg[5]), VM_FLOAT(arg[6]), VM_FLOAT(arg[7]), (mpic_t *)VM_LONG(arg[8]));
Draw_Image(VM_FLOAT(arg[0]), VM_FLOAT(arg[1]), VM_FLOAT(arg[2]), VM_FLOAT(arg[3]), VM_FLOAT(arg[4]), VM_FLOAT(arg[5]), VM_FLOAT(arg[6]), VM_FLOAT(arg[7]), VM_FROMSHANDLE(VM_LONG(arg[8])));
break;
case UI_CM_LERPTAG: //Lerp tag...

View File

@ -361,6 +361,8 @@ typedef struct
// demo recording info must be here, because record is started before
// entering a map (and clearing client_state_t)
qboolean demorecording;
vfsfile_t *demooutfile;
enum{DPB_NONE,DPB_QUAKEWORLD,DPB_MVD,DPB_EZTV,
#ifdef NQPROT
DPB_NETQUAKE,
@ -370,7 +372,7 @@ typedef struct
#endif
} demoplayback;
qboolean timedemo;
vfsfile_t *demofile;
vfsfile_t *demoinfile;
float td_lastframe; // to meter out one message a frame
int td_startframe; // host_framecount at start
float td_starttime; // realtime at second frame of timedemo

View File

@ -77,12 +77,27 @@ static unsigned int selstartoffset, selendoffset;
qboolean con_initialized;
void Con_ResizeCon (console_t *con);
/*makes sure the console object works*/
void Con_Finit (console_t *con)
{
if (con->current == NULL)
{
con->oldest = con->current = Z_Malloc(sizeof(conline_t));
con->linecount = 0;
}
if (con->display == NULL)
con->display = con->current;
}
qboolean Con_IsActive (console_t *con)
/*returns a bitmask:
1: currently active
2: has text that has not been seen yet
*/
int Con_IsActive (console_t *con)
{
return (con == con_current) | (con->unseentext*2);
}
/*kills a console_t object. will never destroy the main console*/
void Con_Destroy (console_t *con)
{
console_t *prev;
@ -104,6 +119,7 @@ void Con_Destroy (console_t *con)
if (con_current == con)
con_current = &con_main;
}
/*obtains a console_t without creating*/
console_t *Con_FindConsole(char *name)
{
console_t *con;
@ -114,22 +130,25 @@ console_t *Con_FindConsole(char *name)
}
return NULL;
}
/*creates a potentially duplicate console_t - please use Con_FindConsole first, as its confusing otherwise*/
console_t *Con_Create(char *name)
{
console_t *con;
con = Z_Malloc(sizeof(console_t));
Q_strncpyz(con->name, name, sizeof(con->name));
Con_ResizeCon(con);
Con_Finit(con);
con->next = con_main.next;
con_main.next = con;
return con;
}
/*sets a console as the active one*/
void Con_SetActive (console_t *con)
{
con_current = con;
}
/*for enumerating consoles*/
qboolean Con_NameForNum(int num, char *buffer, int buffersize)
{
console_t *con;
@ -146,6 +165,7 @@ qboolean Con_NameForNum(int num, char *buffer, int buffersize)
return false;
}
/*print text to a console*/
void Con_PrintCon (console_t *con, char *txt);
@ -364,6 +384,23 @@ void Con_ToggleChat_f (void)
Con_ClearNotify ();
}
void Con_ClearCon(console_t *con)
{
conline_t *t;
while (con->current)
{
t = con->current;
con->current = t->older;
Z_Free(t);
}
con->display = con->current = con->oldest = NULL;
selstartline = NULL;
selendline = NULL;
/*reset the line pointers, create an active line*/
Con_Finit(con);
}
/*
================
Con_Clear_f
@ -371,20 +408,46 @@ Con_Clear_f
*/
void Con_Clear_f (void)
{
conline_t *t;
while (con_main.current)
{
t = con_main.current;
con_main.current = t->older;
Z_Free(t);
}
con_main.display = con_main.current = con_main.oldest = NULL;
selstartline = NULL;
selendline = NULL;
Con_ResizeCon(&con_main);
Con_ClearCon(&con_main);
}
void Cmd_ConEcho_f(void)
{
console_t *con;
con = Con_FindConsole(Cmd_Argv(1));
if (!con)
con = Con_Create(Cmd_Argv(1));
if (con)
{
Cmd_ShiftArgs(1, false);
Con_PrintCon(con, Cmd_Args());
Con_PrintCon(con, "\n");
}
}
void Cmd_ConClear_f(void)
{
console_t *con;
con = Con_FindConsole(Cmd_Argv(1));
if (con)
Con_ClearCon(con);
}
void Cmd_ConClose_f(void)
{
console_t *con;
con = Con_FindConsole(Cmd_Argv(1));
if (con)
Con_Destroy(con);
}
void Cmd_ConActivate_f(void)
{
console_t *con;
con = Con_FindConsole(Cmd_Argv(1));
if (con)
Con_SetActive(con);
}
/*
================
@ -422,37 +485,6 @@ void Con_MessageMode2_f (void)
key_dest = key_message;
}
/*
================
Con_Resize
================
*/
void Con_ResizeCon (console_t *con)
{
if (con->current == NULL)
{
con->oldest = con->current = Z_Malloc(sizeof(conline_t));
con->linecount = 0;
}
if (con->display == NULL)
con->display = con->current;
}
/*
================
Con_CheckResize
If the line width has changed, reformat the buffer.
================
*/
void Con_CheckResize (void)
{
console_t *c;
for (c = &con_main; c; c = c->next)
Con_ResizeCon (c);
}
void Con_ForceActiveNow(void)
{
key_dest = key_console;
@ -469,9 +501,10 @@ void Log_Init (void);
void Con_Init (void)
{
con_current = &con_main;
Con_Finit(&con_main);
con_main.linebuffered = Con_ExecuteLine;
con_main.commandcompletion = true;
Con_CheckResize ();
Con_Printf ("Console initialized.\n");
//
@ -492,6 +525,12 @@ void Con_Init (void)
#ifdef QTERM
Cmd_AddCommand ("qterm", Con_QTerm_f);
#endif
Cmd_AddCommand ("conecho", Cmd_ConEcho_f);
Cmd_AddCommand ("conclear", Cmd_ConClear_f);
Cmd_AddCommand ("conclose", Cmd_ConClose_f);
Cmd_AddCommand ("conactivate", Cmd_ConActivate_f);
con_initialized = true;
Log_Init();
@ -521,15 +560,17 @@ void Con_PrintCon (console_t *con, char *txt)
{
conchar_t *o;
if ((*c&CON_CHARMASK)=='\t')
/*if ((*c&CON_CHARMASK)=='\t')
*c = (*c&~CON_CHARMASK)|' ';
*/
switch (*c & (CON_CHARMASK&~CON_HIGHCHARSMASK))
{
case '\r':
cr = true;
break;
case '\n':
#pragma message("Really inefficient consistancy checking, with no side effects other than sys_errors")
{
conline_t *cl;
cl = con->oldest;
@ -544,6 +585,7 @@ cl = cl->newer;
if (cl != con->current)
Sys_Error("not newest?\n");
}
cr = false;
while (con->linecount >= con_maxlines.value)
{
@ -664,26 +706,6 @@ void VARGS Con_Printf (const char *fmt, ...)
// write it to the scrollable buffer
Con_Print (msg);
/*
if (con != &con_main)
return;
// update the screen immediately if the console is displayed
if (cls.state != ca_active && !filmactive)
#ifndef CLIENTONLY
if (progfuncs != svprogfuncs) //cover our back - don't do rendering stuff that will change this
#endif
{
// protect against infinite loop if something in SCR_UpdateScreen calls
// Con_Printd
if (!inupdate)
{
inupdate = true;
SCR_UpdateScreen ();
inupdate = false;
}
}
*/
}
void VARGS Con_SafePrintf (char *fmt, ...)
@ -946,8 +968,7 @@ void Con_DrawNotify (void)
}
x = (vid.width - x) / 2;
}
for (c = starts[lines]; c < ends[lines]; c++)
x = Font_DrawChar(x, y, *c);
Font_LineDraw(x, y, starts[lines], ends[lines]);
y += Font_CharHeight();
@ -967,12 +988,8 @@ void Con_DrawNotify (void)
lines = Font_LineBreaks(markup, c, Font_ScreenWidth(), 8, starts, ends);
for (i = 0; i < lines; i++)
{
c = starts[i];
x = 0;
while (c < ends[i])
{
x = Font_DrawChar(x, y, *c++);
}
Font_LineDraw(x, y, starts[i], ends[i]);
y += Font_CharHeight();
}
}
@ -1190,6 +1207,7 @@ void Con_DrawConsole (int lines, qboolean noback)
conchar_t *starts[64], *ends[sizeof(starts)/sizeof(starts[0])];
int i;
qboolean haveprogress;
int hidelines;
if (lines <= 0)
return;
@ -1207,6 +1225,8 @@ void Con_DrawConsole (int lines, qboolean noback)
con_current->vislines = lines;
top = Con_DrawAlternateConsoles(lines);
x = 8;
y = lines;
@ -1269,11 +1289,10 @@ void Con_DrawConsole (int lines, qboolean noback)
seley += y;
}
top = Con_DrawAlternateConsoles(lines);
if (!con_current->display)
con_current->display = con_current->current;
l = con_current->display;
hidelines = con_current->subline;
if (l != con_current->current)
{
@ -1297,6 +1316,15 @@ void Con_DrawConsole (int lines, qboolean noback)
linecount = 1;
starts[0] = ends[0] = NULL;
}
l->lines = linecount;
if (hidelines > 0)
{
linecount -= hidelines;
if (linecount < 0)
linecount = 0;
hidelines -= linecount;
}
while (linecount-- > 0)
{
@ -1365,10 +1393,7 @@ void Con_DrawConsole (int lines, qboolean noback)
}
x = sx;
for (i = 0; i < linelength; i++)
{
x = Font_DrawChar(x, y, *s++);
}
Font_LineDraw(x, y, s, s+linelength);
if (y < top)
break;

View File

@ -262,7 +262,7 @@ int PaddedPrint (char *s, int x)
Con_Printf ("\n");
x=0;
}
*/
if (x)
{
Con_Printf (" ");
@ -272,8 +272,8 @@ int PaddedPrint (char *s, int x)
{
Con_Printf (" ");
x++;
}
Con_Printf ("%s", s);
}*/
Con_Printf ("%s\t", s);
x+=strlen(s);
return x;
@ -1484,6 +1484,11 @@ void Key_Event (int key, unsigned int unicode, qboolean down)
if (key_dest == key_game)
#endif
{
if (Media_PlayingFullScreen())
{
Media_PlayFilm("");
return;
}
if (UI_KeyPress(key, unicode, down)) //Allow the UI to see the escape key. It is possible that a developer may get stuck at a menu.
return;
}

View File

@ -242,15 +242,15 @@ static void ConcatPackageLists(package_t *l2)
}
}
static void dlnotification(char *localfile, qboolean sucess)
static void dlnotification(struct dl_download *dl)
{
int i;
vfsfile_t *f;
COM_RefreshFSCache_f();
f = dl->file;
dl->file = NULL;
i = atoi(localfile+7);
i = dl->user_num;
f = FS_OpenVFS (localfile, "rb", FS_GAME);
if (f)
{
downloadablelistreceived[i] = 1;
@ -329,7 +329,7 @@ qboolean MD_PopMenu (union menuoption_s *mo,struct menu_s *m,int key)
return false;
}
static void Menu_Download_Got(char *fname, qboolean successful);
static void Menu_Download_Got(struct dl_download *dl);
qboolean MD_ApplyDownloads (union menuoption_s *mo,struct menu_s *m,int key)
{
if (key == K_ENTER || key == K_MOUSE1)
@ -485,6 +485,7 @@ void M_AddItemsToDownloadMenu(menu_t *m)
void M_Download_UpdateStatus(struct menu_s *m)
{
struct dl_download *dl;
dlmenu_t *info = m->data;
int i;
@ -499,7 +500,10 @@ void M_Download_UpdateStatus(struct menu_s *m)
if (!downloadablelistreceived[info->parsedsourcenum])
{
sprintf(basename, "dlinfo_%i.inf", info->parsedsourcenum);
if (!HTTP_CL_Get(downloadablelist[info->parsedsourcenum], basename, dlnotification))
dl = HTTP_CL_Get(downloadablelist[info->parsedsourcenum], basename, dlnotification);
if (dl)
dl->user_num = info->parsedsourcenum;
else
Con_Printf("Could not contact server\n");
return;
}
@ -590,8 +594,10 @@ static void M_Download_Draw (int x, int y, struct menucustom_s *c, struct menu_s
}
}
*/
static void Menu_Download_Got(char *fname, qboolean successful)
static void Menu_Download_Got(struct dl_download *dl)
{
char *fname = dl->localname;
qboolean successful = dl->status == DL_FINISHED;
char *ext;
package_t *p;
int dlnum = atoi(fname+3);

View File

@ -417,9 +417,9 @@ static void ApplyPreset (int presetnum)
for (i = 1; preset[i].cvarname; i++)
{
Cbuf_AddText(preset[i].cvarname, Cmd_ExecLevel);
Cbuf_AddText(" ", Cmd_ExecLevel);
Cbuf_AddText(" \"", Cmd_ExecLevel);
Cbuf_AddText(preset[i].value[presetnum], Cmd_ExecLevel);
Cbuf_AddText("\n", Cmd_ExecLevel);
Cbuf_AddText("\"\n", Cmd_ExecLevel);
}
}

View File

@ -1086,35 +1086,30 @@ void SListOptionChanged(serverinfo_t *newserver)
}
#ifdef WEBCLIENT
void MasterInfo_ProcessHTTP(char *name, qboolean success, int type)
void MasterInfo_ProcessHTTP(vfsfile_t *file, int type)
{
netadr_t adr;
char *s;
char *el;
serverinfo_t *info;
char adrbuf[MAX_ADR_SIZE];
char linebuffer[2048];
if (!success)
if (!file)
return;
el = COM_LoadTempFile(name);
if (!el)
return;
while(*el)
while(VFS_GETS(file, linebuffer, sizeof(linebuffer)))
{
s = el;
while(*s <= ' ' && *s != '\n' && *s)
s = linebuffer;
while (*s == '\t' || *s == ' ')
s++;
el = strchr(s, '\n');
if (!el)
el = s + strlen(s);
else if (el>s && el[-1] == '\r')
el = s + strlen(s);
if (el>s && el[-1] == '\r')
el[-1] = '\0';
if (*s == '#') //hash is a comment, apparently.
continue;
*el = '\0';
el++;
if (!NET_StringToAdr(s, &adr))
continue;
@ -1139,19 +1134,17 @@ void MasterInfo_ProcessHTTP(char *name, qboolean success, int type)
Master_ResortServer(info);
}
}
FS_Remove(name, FS_GAME);
}
// wrapper functions for the different server types
void MasterInfo_ProcessHTTPNQ(char *name, qboolean success)
void MasterInfo_ProcessHTTPNQ(struct dl_download *dl)
{
MasterInfo_ProcessHTTP(name, success, SS_NETQUAKE);
MasterInfo_ProcessHTTP(dl->file, SS_NETQUAKE);
}
void MasterInfo_ProcessHTTPQW(char *name, qboolean success)
void MasterInfo_ProcessHTTPQW(struct dl_download *dl)
{
MasterInfo_ProcessHTTP(name, success, 0);
MasterInfo_ProcessHTTP(dl->file, SS_GENERICQUAKEWORLD);
}
#endif
@ -1887,3 +1880,4 @@ void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad)
}
#endif

View File

@ -511,11 +511,11 @@ void PF_CL_drawcharacter (progfuncs_t *prinst, struct globalvars_s *pr_globals)
return;
}
#pragma message("fixme: this doesn't scale or colour chars")
#pragma message("fixme: this doesn't scale")
Font_BeginString(font_conchar, pos[0], pos[1], &x, &y);
Font_ForceColour(rgb[0], rgb[1], rgb[2], alpha);
Font_DrawChar(x, y, CON_WHITEMASK | 0xe000|(chara&0xff));
Font_ForceColour(1, 1, 1, 1);
Font_InvalidateColour();
Font_EndString(font_conchar);
G_FLOAT(OFS_RETURN) = 1;
@ -544,7 +544,7 @@ void PF_CL_drawrawstring (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
x = Font_DrawChar(x, y, CON_WHITEMASK|0xe000|(*text++&0xff));
}
Font_ForceColour(1, 1, 1, 1);
Font_InvalidateColour();
Font_EndString(font_conchar);
}
@ -757,17 +757,15 @@ static void PF_menu_cvar (progfuncs_t *prinst, struct globalvars_s *pr_globals)
str = PR_GetStringOfs(prinst, OFS_PARM0);
str = RemapCvarNameFromDPToFTE(str);
var = Cvar_Get(str, "", 0, "menu cvars");
if (var)
{
var = Cvar_Get(str, "", 0, "menu cvars");
if (var)
{
if (var->latched_string)
G_FLOAT(OFS_RETURN) = atof(var->latched_string); else
G_FLOAT(OFS_RETURN) = var->value;
}
else
G_FLOAT(OFS_RETURN) = 0;
if (var->latched_string)
G_FLOAT(OFS_RETURN) = atof(var->latched_string); else
G_FLOAT(OFS_RETURN) = var->value;
}
else
G_FLOAT(OFS_RETURN) = 0;
}
static void PF_menu_cvar_set (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
@ -788,7 +786,7 @@ static void PF_menu_cvar_string (progfuncs_t *prinst, struct globalvars_s *pr_gl
G_INT( OFS_RETURN ) = (int)PR_SetString( prinst, cv->string );
}
qboolean M_Vid_GetMove(int num, int *w, int *h);
qboolean M_Vid_GetMode(int num, int *w, int *h);
//a bit pointless really
void PF_cl_getresolution (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
@ -797,7 +795,7 @@ void PF_cl_getresolution (progfuncs_t *prinst, struct globalvars_s *pr_globals)
int w, h;
w=h=0;
M_Vid_GetMove(mode, &w, &h);
M_Vid_GetMode(mode, &w, &h);
ret[0] = w;
ret[1] = h;

View File

@ -2486,12 +2486,19 @@ void Surf_BuildSurfaceDisplayList (model_t *model, msurface_t *fa)
float *vec;
float s, t;
int lm;
extern mesh_t nullmesh;
// reconstruct the polygon
pedges = model->edges;
lnumverts = fa->numedges;
vertpage = 0;
if (!lnumverts)
{
fa->mesh = &nullmesh;
return;
}
{ //build a nice mesh instead of a poly.
int size = sizeof(mesh_t) + sizeof(index_t)*(lnumverts-2)*3 + (sizeof(vecV_t) + 3*sizeof(vec3_t) + 2*sizeof(vec2_t) + sizeof(vec4_t))*lnumverts;
mesh_t *mesh;

View File

@ -634,6 +634,7 @@ void Renderer_Init(void)
Cvar_Register (&r_menutint, GRAPHICALNICETIES);
Cvar_Register (&r_fb_models, GRAPHICALNICETIES);
Cvar_Register (&r_skin_overlays, GRAPHICALNICETIES);
Cvar_Register (&r_replacemodels, GRAPHICALNICETIES);
@ -934,7 +935,7 @@ vidmode_t vid_modes[] =
};
#define NUMVIDMODES sizeof(vid_modes)/sizeof(vid_modes[0])
qboolean M_Vid_GetMove(int num, int *w, int *h)
qboolean M_Vid_GetMode(int num, int *w, int *h)
{
if ((unsigned)num >= NUMVIDMODES)
return false;

View File

@ -106,8 +106,12 @@ int Font_CharHeight(void);
int Font_CharWidth(unsigned int charcode);
int Font_DrawChar(int px, int py, unsigned int charcode);
void Font_ForceColour(float r, float g, float b, float a); //This colour will be applied while the char mask remains WHITE. If you print char by char, make sure to include the mask.
void Font_InvalidateColour(void);
void Font_EndString(struct font_s *font);
/*these three functions deal with formatted blocks of text (including tabs and new lines)*/
int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int maxlines, conchar_t **starts, conchar_t **ends);
int Font_LineWidth(conchar_t *start, conchar_t *end);
void Font_LineDraw(int x, int y, conchar_t *start, conchar_t *end);
extern struct font_s *font_conchar;
extern struct font_s *font_tiny;
/*end fonts*/

View File

@ -145,6 +145,7 @@ void S_RunCapture(void)
}
sounddriver pOPENAL_InitCard;
sounddriver pDSOUND_InitCard;
sounddriver pALSA_InitCard;
sounddriver pOSS_InitCard;
@ -159,13 +160,16 @@ typedef struct {
} sdriver_t;
sdriver_t drivers[] = {
//in order of preference
{"DSound", &pDSOUND_InitCard},
{"ALSA", &pALSA_InitCard},
{"OSS", &pOSS_InitCard},
{"MacOS", &pMacOS_InitCard},
{"SDL", &pSDL_InitCard},
{"WaveOut", &pWAV_InitCard},
{"AHI", &pAHI_InitCard},
{"OpenAL", &pOPENAL_InitCard}, //yay, get someone else to sort out sound support, woot
{"DSound", &pDSOUND_InitCard}, //prefered on windows
{"MacOS", &pMacOS_InitCard}, //prefered on mac
{"AHI", &pAHI_InitCard}, //prefered on morphos
{"SDL", &pSDL_InitCard}, //prefered on linux
{"ALSA", &pALSA_InitCard}, //pure shite
{"OSS", &pOSS_InitCard}, //good, but not likely to work any more
{"WaveOut", &pWAV_InitCard}, //doesn't work properly in vista, etc.
{NULL, NULL}
};
@ -585,6 +589,10 @@ void S_Init (void)
Cvar_Register(&snd_linearresample, "Sound controls");
Cvar_Register(&snd_linearresample_stream, "Sound controls");
#ifdef AVAIL_OPENAL
OpenAL_CvarInit();
#endif
if (COM_CheckParm("-nosound"))
{
Cvar_ForceSet(&nosound, "1");
@ -883,6 +891,11 @@ void S_StartSoundCard(soundcardinfo_t *sc, int entnum, int entchannel, sfx_t *sf
if (nosound.ival)
return;
#ifdef AVAIL_OPENAL
if (sc->openal)
OpenAL_StartSound(entnum, entchannel, sfx, origin, fvol, attenuation);
#endif
vol = fvol*255;
// pick a channel to play on
@ -1303,6 +1316,14 @@ void S_UpdateCard(soundcardinfo_t *sc)
return;
}
#ifdef AVAIL_OPENAL
if (sc->openal == 1)
{
OpenAL_Update_Listener(listener_origin, listener_forward, listener_right, listener_up);
return;
}
#endif
// update general area ambient sound sources
S_UpdateAmbientSounds (sc);

View File

@ -888,13 +888,18 @@ sfxcache_t *S_LoadSound (sfx_t *s)
s->failedload = false;
#ifdef AVAIL_OPENAL
OpenAL_LoadSound(s, sc, com_filesize, data);
#endif
for (i = sizeof(AudioInputPlugins)/sizeof(AudioInputPlugins[0])-1; i >= 0; i--)
{
if (AudioInputPlugins[i])
{
sc = AudioInputPlugins[i](s, data, com_filesize, snd_speed);
if (sc)
{
return sc;
}
}
}

View File

@ -2,6 +2,7 @@
#include "winquake.h"
#include <SDL.h>
#pragma comment(lib, "sdl.lib")
//SDL calls a callback each time it needs to repaint the 'hardware' buffers
//This results in extra latency.
@ -17,36 +18,30 @@ static void SSDL_Shutdown(soundcardinfo_t *sc)
{
Con_Printf("Shutdown SDL sound\n");
SDL_CloseAudio();
Con_Printf("buffer\n");
if (sc->sn.buffer)
free(sc->sn.buffer);
sc->sn.buffer = NULL;
Con_Printf("down\n");
}
static unsigned int SSDL_GetDMAPos(soundcardinfo_t *sc)
{
sc->sn.samplepos = (sc->snd_sent / (sc->sn.samplebits/8));
// printf("%i\n", sc->sn.samplepos);
sc->sn.samplepos = sc->snd_sent / (sc->sn.samplebits/8);
return sc->sn.samplepos;
}
//this function is called from inside SDL.
//transfer the 'dma' buffer into the buffer it requests.
static void SSDL_Paint(void *userdata, qbyte *stream, int len)
static void VARGS SSDL_Paint(void *userdata, qbyte *stream, int len)
{
soundcardinfo_t *sc = userdata;
int buffersize = sc->sn.samples*(sc->sn.samplebits/8);
//printf("SDL_Paint (%i)\n", len);
if (len > buffersize)
{
// printf("SDLSound: len(%i) > SOUND_BUFFER_SIZE(%i)\n", len, buffersize);
len = buffersize; //whoa nellie!
}
if (len + sc->snd_sent%buffersize > buffersize)
{ //buffer will wrap, fill in the rest
//printf("Wrap\n");
memcpy(stream, (char*)sc->sn.buffer + (sc->snd_sent%buffersize), buffersize - (sc->snd_sent%buffersize));
stream += buffersize - sc->snd_sent%buffersize;
len -= buffersize - (sc->snd_sent%buffersize);
@ -55,10 +50,6 @@ static void SSDL_Paint(void *userdata, qbyte *stream, int len)
} //and finish from the start
memcpy(stream, (char*)sc->sn.buffer + (sc->snd_sent%buffersize), len);
sc->snd_sent += len;
//memcpy(stream, sc->sn.buffer, len);
}
static void *SSDL_LockBuffer(soundcardinfo_t *sc)
@ -103,7 +94,7 @@ static int SDL_InitCard(soundcardinfo_t *sc, int cardnum)
memset(&desired, 0, sizeof(desired));
desired.freq = sc->sn.speed;
desired.channels = 2; //fixme!
desired.channels = sc->sn.numchannels; //fixme!
desired.samples = 0x0100;
desired.format = AUDIO_S16SYS;
desired.callback = SSDL_Paint;

View File

@ -48,6 +48,9 @@ typedef struct {
typedef struct sfx_s
{
char name[MAX_OSPATH];
#ifdef AVAIL_OPENAL
unsigned int openal_buffer;
#endif
qboolean failedload; //no more super-spammy
cache_user_t cache;
sfxdecode_t *decoder;
@ -163,6 +166,16 @@ void CLVC_Poll (void);
void SNDVC_MicInput(qbyte *buffer, int samples, int freq, int width);
#ifdef AVAIL_OPENAL
void OpenAL_LoadSound (sfx_t *s, sfxcache_t *sc, size_t size, void *data);
void OpenAL_StartSound(int entnum, int entchannel, sfx_t * sfx, vec3_t origin, float fvol, float attenuation);
void OpenAL_Update_Listener(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up);
void OpenAL_CvarInit(void);
#endif
// ====================================================================
// User-setable variables
// ====================================================================
@ -209,6 +222,7 @@ void S_AmbientOn (void);
//inititalisation functions.
typedef int (*sounddriver) (soundcardinfo_t *sc, int cardnum);
extern sounddriver pOPENAL_InitCard;
extern sounddriver pDSOUND_InitCard;
extern sounddriver pALSA_InitCard;
extern sounddriver pOSS_InitCard;
@ -252,6 +266,11 @@ struct soundcardinfo_s { //windows has one defined AFTER directsound
int snd_sent;
int snd_completed;
int audio_fd;
// no clue how else to handle this yet!
#ifdef AVAIL_OPENAL
int openal;
#endif
};
extern soundcardinfo_t *sndcardinfo;

File diff suppressed because it is too large Load Diff

View File

@ -35,8 +35,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#endif
#ifdef _DEBUG
#if _MSC_VER >= 1300
#define CATCHCRASH
#endif
#endif
#if !defined(CLIENTONLY) && !defined(SERVERONLY)
qboolean isDedicated = false;
@ -250,7 +252,7 @@ void *Sys_GetGameAPI (void *parms)
#define MINIMUM_WIN_MEMORY 0x0800000
#define MAXIMUM_WIN_MEMORY 0x4000000
#define MAXIMUM_WIN_MEMORY 0x8000000
#define PAUSE_SLEEP 50 // sleep time on pause or minimization
#define NOT_FOCUS_SLEEP 20 // sleep time when not focus
@ -327,7 +329,7 @@ typedef BOOL (WINAPI *MINIDUMPWRITEDUMP) (
PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);
static DWORD CrashExceptionHandler (DWORD exceptionCode, LPEXCEPTION_POINTERS exceptionInfo)
DWORD CrashExceptionHandler (DWORD exceptionCode, LPEXCEPTION_POINTERS exceptionInfo)
{
char dumpPath[1024];
HANDLE hProc = GetCurrentProcess();
@ -1071,7 +1073,6 @@ void Sys_Sleep (void)
void Sys_SendKeyEvents (void)
{
#ifndef NPQTV
MSG msg;
if (isDedicated)
@ -1094,7 +1095,6 @@ void Sys_SendKeyEvents (void)
// continue;
DispatchMessage (&msg);
}
#endif
}
@ -1303,7 +1303,6 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
cpuid
mov idedx, edx
}
// MessageBox(NULL, cpuname, cpuname, 0);
#if _M_IX86_FP >= 2
if (!(idedx&(1<<26)))
MessageBox(NULL, "This is an SSE2 optimised build, and your cpu doesn't seem to support it", DISTRIBUTION, 0);
@ -1634,7 +1633,8 @@ void *Sys_CreateThread(int (*func)(void *), void *args, int stacksize)
if (!tw)
return NULL;
stacksize += 128; // wrapper overhead, also prevent default stack size
if (stacksize)
stacksize += 128; // wrapper overhead, also prevent default stack size
tw->func = func;
tw->args = args;
#ifdef WIN32CRTDLL
@ -1653,7 +1653,13 @@ void *Sys_CreateThread(int (*func)(void *), void *args, int stacksize)
void Sys_WaitOnThread(void *thread)
{
WaitForSingleObject((HANDLE)thread, INFINITE);
while (WaitForSingleObject((HANDLE)thread, 10) == WAIT_TIMEOUT)
{
/*keep responding to window messages*/
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
DispatchMessage(&msg);
}
CloseHandle((HANDLE)thread);
}
@ -1670,6 +1676,13 @@ qboolean Sys_TryLockMutex(void *mutex)
qboolean Sys_LockMutex(void *mutex)
{
#ifdef _DEBUG
/*in debug builds, trigger a debug break if we sit on a mutex for longer than 20 secs*/
if (WaitForSingleObject(mutex, 20000) == WAIT_OBJECT_0)
return true;
OutputDebugString("Warning: Suspected mutex deadlock\n");
DebugBreak();
#endif
return WaitForSingleObject(mutex, INFINITE) == WAIT_OBJECT_0;
}

View File

@ -54,6 +54,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define AVAIL_ZLIB
#define AVAIL_OGGVORBIS
// #define AVAIL_OPENAL /* Jogi's OpenAL support */
#endif
#define AVAIL_MASM
@ -179,6 +181,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// #define OFFSCREENGECKO
#endif
//#define SQL
#define CSQC_DAT //support for csqc
#define MENU_DAT //support for menu.dat
@ -195,6 +199,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//fix things a little...
#ifdef NPQTV
/*plugins require threads and stuff now, and http download support*/
#ifndef MULTITHREAD
#define MULTITHREAD
#define WEBCLIENT
#endif
#endif
#ifndef _WIN32
#undef QTERM //not supported - FIXME: move to native plugin
@ -356,7 +367,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifdef _MSC_VER
#define VARGS __cdecl
#define MSVCDISABLEWARNINGS
#define FTE_DEPRECATED __declspec(deprecated)
#if _MSC_VER >= 1300
#define FTE_DEPRECATED __declspec(deprecated)
#endif
#define NORETURN __declspec(noreturn)
#endif
#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))

View File

@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "quakedef.h"
cvar_t com_fs_cache = SCVARF("fs_cache", "0", CVAR_ARCHIVE);
cvar_t com_fs_cache = SCVARF("fs_cache", "1", CVAR_ARCHIVE);
cvar_t rcon_level = SCVAR("rcon_level", "20");
cvar_t cmd_maxbuffersize = SCVAR("cmd_maxbuffersize", "65536");
int Cmd_ExecLevel;
@ -450,7 +450,7 @@ void Cmd_StuffCmds (void)
{
if (!com_argv[i])
continue; // NEXTSTEP nulls out -NXHost
if (strchr(com_argv[i], ' ') || strchr(com_argv[i], '\t'))
if (strchr(com_argv[i], ' ') || strchr(com_argv[i], '\t') || strchr(com_argv[i], '@'))
{
Q_strcat (text,"\"");
Q_strcat (text,com_argv[i]);
@ -472,7 +472,7 @@ void Cmd_StuffCmds (void)
{
i++;
for (j=i ; (text[j] != '+') && (text[j] != '-') && (text[j] != 0) ; j++)
for (j=i ; ((text[j-1] != ' ') || ((text[j] != '+') && (text[j] != '-'))) && (text[j] != 0) ; j++)
;
c = text[j];

View File

@ -212,7 +212,7 @@ void Mod_NormaliseTextureVectors(vec3_t *n, vec3_t *s, vec3_t *t, int v)
#ifdef SKELETALMODELS
void Alias_TransformVerticies(float *bonepose, galisskeletaltransforms_t *weights, int numweights, float *xyzout, float *normout)
void Alias_TransformVerticies(float *bonepose, galisskeletaltransforms_t *weights, int numweights, vecV_t *xyzout, vec3_t *normout)
{
int i;
float *out, *matrix;
@ -223,8 +223,8 @@ void Alias_TransformVerticies(float *bonepose, galisskeletaltransforms_t *weight
{
for (i = 0;i < numweights;i++, v++)
{
out = xyzout + v->vertexindex * 3;
normo = normout + v->vertexindex * 3;
out = xyzout[v->vertexindex];
normo = normout[ + v->vertexindex];
matrix = bonepose+v->boneindex*12;
// FIXME: this can very easily be optimized with SSE or 3DNow
out[0] += v->org[0] * matrix[0] + v->org[1] * matrix[1] + v->org[2] * matrix[ 2] + v->org[3] * matrix[ 3];
@ -244,7 +244,7 @@ void Alias_TransformVerticies(float *bonepose, galisskeletaltransforms_t *weight
{
for (i = 0;i < numweights;i++, v++)
{
out = xyzout + v->vertexindex * 3;
out = xyzout[v->vertexindex];
matrix = bonepose+v->boneindex*12;
// FIXME: this can very easily be optimized with SSE or 3DNow
out[0] += v->org[0] * matrix[0] + v->org[1] * matrix[1] + v->org[2] * matrix[ 2] + v->org[3] * matrix[ 3];
@ -265,7 +265,7 @@ static void Alias_CalculateSkeletalNormals(galiasinfo_t *model)
(n)[2] = ((a)[0] - (b)[0]) * ((c)[1] - (b)[1]) - ((a)[1] - (b)[1]) * ((c)[0] - (b)[0]) \
)
int i, j;
vec3_t *xyz;
vecV_t *xyz;
vec3_t *normals;
int *mvert;
float *inversepose;
@ -288,7 +288,7 @@ static void Alias_CalculateSkeletalNormals(galiasinfo_t *model)
else
next = NULL;
xyz = Z_Malloc(numverts*sizeof(vec3_t));
xyz = Z_Malloc(numverts*sizeof(vecV_t));
normals = Z_Malloc(numverts*sizeof(vec3_t));
inversepose = Z_Malloc(numbones*sizeof(float)*9);
mvert = Z_Malloc(numverts*sizeof(*mvert));
@ -307,7 +307,7 @@ static void Alias_CalculateSkeletalNormals(galiasinfo_t *model)
}
//build the actual base pose positions
Alias_TransformVerticies(bonepose, v, numweights, (float*)xyz, NULL);
Alias_TransformVerticies(bonepose, v, numweights, xyz, NULL);
//work out which verticies are identical
//this is needed as two verts can have same origin but different tex coords
@ -945,9 +945,9 @@ static void R_LerpFrames(mesh_t *mesh, galiaspose_t *p1, galiaspose_t *p2, float
#ifndef SERVERONLY
static void Alias_BuildSkeletalMesh(mesh_t *mesh, float *bonepose, galisskeletaltransforms_t *weights, int numweights)
{
memset(mesh->xyz_array, 0, mesh->numvertexes*sizeof(vec3_t));
memset(mesh->xyz_array, 0, mesh->numvertexes*sizeof(vecV_t));
memset(mesh->normals_array, 0, mesh->numvertexes*sizeof(vec3_t));
Alias_TransformVerticies(bonepose, weights, numweights, (float*)mesh->xyz_array, (float*)mesh->normals_array);
Alias_TransformVerticies(bonepose, weights, numweights, mesh->xyz_array, mesh->normals_array);
}
#ifdef GLQUAKE
@ -1202,7 +1202,7 @@ qboolean Mod_Trace(model_t *model, int forcehullnum, int frame, vec3_t start, ve
vec3_t impactpoint;
float *posedata;
vecV_t *posedata;
index_t *indexes;
while(mod)
@ -1210,12 +1210,12 @@ qboolean Mod_Trace(model_t *model, int forcehullnum, int frame, vec3_t start, ve
indexes = (index_t*)((char*)mod + mod->ofs_indexes);
group = (galiasgroup_t*)((char*)mod + mod->groupofs);
pose = (galiaspose_t*)((char*)&group[0] + group[0].poseofs);
posedata = (float*)((char*)pose + pose->ofsverts);
posedata = (vecV_t*)((char*)pose + pose->ofsverts);
#ifdef SKELETALMODELS
if (mod->numbones && !mod->sharesverts)
{
float bonepose[MAX_BONES][12];
posedata = alloca(mod->numverts*sizeof(vec3_t));
posedata = alloca(mod->numverts*sizeof(vecV_t));
frac = 1;
if (group->isheirachical)
{
@ -1230,9 +1230,9 @@ qboolean Mod_Trace(model_t *model, int forcehullnum, int frame, vec3_t start, ve
for (i = 0; i < mod->numindexes; i+=3)
{
p1 = posedata + 3*indexes[i+0];
p2 = posedata + 3*indexes[i+1];
p3 = posedata + 3*indexes[i+2];
p1 = posedata[indexes[i+0]];
p2 = posedata[indexes[i+1]];
p3 = posedata[indexes[i+2]];
VectorSubtract(p1, p2, edge1);
VectorSubtract(p3, p2, edge2);

View File

@ -118,7 +118,7 @@ typedef struct {
float *Alias_GetBonePositions(galiasinfo_t *inf, framestate_t *fstate, float *buffer, int buffersize);
#ifdef SKELETALMODELS
void Alias_TransformVerticies(float *bonepose, galisskeletaltransforms_t *weights, int numweights, float *xyzout, float *normals);
void Alias_TransformVerticies(float *bonepose, galisskeletaltransforms_t *weights, int numweights, vecV_t *xyzout, vec3_t *normout);
#endif
qboolean Alias_GAliasBuildMesh(mesh_t *mesh, galiasinfo_t *inf,
entity_t *e,

View File

@ -1844,6 +1844,7 @@ static void World_Physics_Frame_BodyFromEntity(world_t *world, wedict_t *ed)
{
case SOLID_BSP:
Matrix4_Identity(ed->ode.ode_offsetmatrix);
ed->ode.ode_geom = NULL;
if (!model)
{
Con_Printf("entity %i (classname %s) has no model\n", NUM_FOR_EDICT(world->progs, (edict_t*)ed), PR_GetString(world->progs, ed->v->classname));

View File

@ -2107,7 +2107,7 @@ messedup:
*out++ = (unsigned char)(*str++) | ext;
else
{
if (strchr("\n\r ", *str))
if (strchr("\n\r\t ", *str))
*out++ = (unsigned char)(*str++) | (ext&~CON_HIGHCHARSMASK);
else
*out++ = (unsigned char)(*str++) | ext | 0xe000;

View File

@ -89,8 +89,9 @@ extern conchar_t q3codemasks[MAXQ3COLOURS];
typedef struct conline_s {
struct conline_s *older;
unsigned int length;
struct conline_s *newer;
unsigned short length;
unsigned short lines;
} conline_t;
typedef struct console_s
@ -101,6 +102,7 @@ typedef struct console_s
conline_t *current; // line where next message will be printed
int x; // offset in current line for next print
conline_t *display; // bottom of console displays this line
int subline;
int vislines; // pixel lines
int linesprinted; // for notify times
qboolean unseentext;

View File

@ -2125,6 +2125,21 @@ qboolean Sys_FindGameData(const char *poshname, const char *gamename, char *base
return true;
}
}
if (!strcmp(gamename, "wop"))
{
DWORD resultlen;
HKEY key = NULL;
//reads HKEY_LOCAL_MACHINE\SOFTWARE\World Of Padman\Path
if (!FAILED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\World Of Padman", 0, STANDARD_RIGHTS_READ|KEY_QUERY_VALUE, &key)))
{
resultlen = basepathlen;
RegQueryValueEx(key, "Path", NULL, NULL, basepath, &resultlen);
RegCloseKey(key);
return true;
}
}
/*
if (!strcmp(gamename, "d3"))
{

View File

@ -17,7 +17,7 @@ typedef struct
typedef struct zipfile_s
{
char filename[MAX_QPATH];
char filename[MAX_OSPATH];
unzFile handle;
int numfiles;
zpackfile_t *files;
@ -451,3 +451,4 @@ searchpathfuncs_t zipfilefuncs = {
};
#endif

View File

@ -215,7 +215,7 @@ typedef struct
{
int contents;
int numsides;
int firstbrushside;
q2cbrushside_t *brushside;
int checkcount; // to avoid repeated testings
} q2cbrush_t;
@ -236,8 +236,10 @@ typedef struct
{
vec3_t absmins, absmaxs;
int numbrushes;
q2cbrush_t *brushes;
int numfacets;
q2cbrush_t *facets;
#define numbrushes numfacets
#define brushes facets
q2mapsurface_t *surface;
int checkcount; // to avoid repeated testings
@ -426,37 +428,31 @@ qboolean BoundsIntersect (vec3_t mins1, vec3_t maxs1, vec3_t mins2, vec3_t maxs2
Patch_FlatnessTest
===============
*/
static int Patch_FlatnessTest ( float maxflat, const vec3_t point0, const vec3_t point1, const vec3_t point2 )
static int Patch_FlatnessTest( float maxflat2, const float *point0, const float *point1, const float *point2 )
{
vec3_t v1, v2, v3;
vec3_t t, n;
float dist, d, l;
float d;
int ft0, ft1;
vec3_t t, n;
vec3_t v1, v2, v3;
VectorSubtract ( point2, point0, n );
l = VectorNormalize ( n );
if ( !l ) {
VectorSubtract( point2, point0, n );
if( !VectorNormalize( n ) )
return 0;
}
VectorSubtract ( point1, point0, t );
d = -DotProduct ( t, n );
VectorMA ( t, d, n, t );
dist = VectorLength ( t );
if ( fabs(dist) <= maxflat ) {
VectorSubtract( point1, point0, t );
d = -DotProduct( t, n );
VectorMA( t, d, n, t );
if( DotProduct( t, t ) < maxflat2 )
return 0;
}
VectorAvg ( point1, point0, v1 );
VectorAvg ( point2, point1, v2 );
VectorAvg ( v1, v2, v3 );
VectorAvg( point1, point0, v1 );
VectorAvg( point2, point1, v2 );
VectorAvg( v1, v2, v3 );
ft0 = Patch_FlatnessTest ( maxflat, point0, v1, v3 );
ft1 = Patch_FlatnessTest ( maxflat, v3, v2, point2 );
ft0 = Patch_FlatnessTest( maxflat2, point0, v1, v3 );
ft1 = Patch_FlatnessTest( maxflat2, v3, v2, point2 );
return 1 + (int)floor( max ( ft0, ft1 ) + 0.5f );
return 1 + (int)( floor( max( ft0, ft1 ) ) + 0.5f );
}
/*
@ -464,30 +460,31 @@ static int Patch_FlatnessTest ( float maxflat, const vec3_t point0, const vec3_t
Patch_GetFlatness
===============
*/
void Patch_GetFlatness ( float maxflat, const vec3_t *points, int *patch_cp, int *flat )
void Patch_GetFlatness( float maxflat, const float *points, int comp, const int *patch_cp, int *flat )
{
int i, p, u, v;
float maxflat2 = maxflat * maxflat;
flat[0] = flat[1] = 0;
for (v = 0; v < patch_cp[1] - 1; v += 2)
for( v = 0; v < patch_cp[1] - 1; v += 2 )
{
for (u = 0; u < patch_cp[0] - 1; u += 2)
for( u = 0; u < patch_cp[0] - 1; u += 2 )
{
p = v * patch_cp[0] + u;
i = Patch_FlatnessTest ( maxflat, points[p], points[p+1], points[p+2] );
flat[0] = max ( flat[0], i );
i = Patch_FlatnessTest ( maxflat, points[p+patch_cp[0]], points[p+patch_cp[0]+1], points[p+patch_cp[0]+2] );
flat[0] = max ( flat[0], i );
i = Patch_FlatnessTest ( maxflat, points[p+2*patch_cp[0]], points[p+2*patch_cp[0]+1], points[p+2*patch_cp[0]+2] );
flat[0] = max ( flat[0], i );
i = Patch_FlatnessTest( maxflat2, &points[p*comp], &points[( p+1 )*comp], &points[( p+2 )*comp] );
flat[0] = max( flat[0], i );
i = Patch_FlatnessTest( maxflat2, &points[( p+patch_cp[0] )*comp], &points[( p+patch_cp[0]+1 )*comp], &points[( p+patch_cp[0]+2 )*comp] );
flat[0] = max( flat[0], i );
i = Patch_FlatnessTest( maxflat2, &points[( p+2*patch_cp[0] )*comp], &points[( p+2*patch_cp[0]+1 )*comp], &points[( p+2*patch_cp[0]+2 )*comp] );
flat[0] = max( flat[0], i );
i = Patch_FlatnessTest ( maxflat, points[p], points[p+patch_cp[0]], points[p+2*patch_cp[0]] );
flat[1] = max ( flat[1], i );
i = Patch_FlatnessTest ( maxflat, points[p+1], points[p+patch_cp[0]+1], points[p+2*patch_cp[0]+1] );
flat[1] = max ( flat[1], i );
i = Patch_FlatnessTest ( maxflat, points[p+2], points[p+patch_cp[0]+2], points[p+2*patch_cp[0]+2] );
flat[1] = max ( flat[1], i );
i = Patch_FlatnessTest( maxflat2, &points[p*comp], &points[( p+patch_cp[0] )*comp], &points[( p+2*patch_cp[0] )*comp] );
flat[1] = max( flat[1], i );
i = Patch_FlatnessTest( maxflat2, &points[( p+1 )*comp], &points[( p+patch_cp[0]+1 )*comp], &points[( p+2*patch_cp[0]+1 )*comp] );
flat[1] = max( flat[1], i );
i = Patch_FlatnessTest( maxflat2, &points[( p+2 )*comp], &points[( p+patch_cp[0]+2 )*comp], &points[( p+2*patch_cp[0]+2 )*comp] );
flat[1] = max( flat[1], i );
}
}
}
@ -497,21 +494,17 @@ void Patch_GetFlatness ( float maxflat, const vec3_t *points, int *patch_cp, int
Patch_Evaluate_QuadricBezier
===============
*/
static void Patch_Evaluate_QuadricBezier ( float t, vec4_t point0, vec4_t point1, vec3_t point2, vec4_t out )
static void Patch_Evaluate_QuadricBezier( float t, const vec_t *point0, const vec_t *point1, const vec_t *point2, vec_t *out, int comp )
{
float qt = t * t;
float dt = 2.0f * t, tt;
vec4_t tvec4;
int i;
vec_t qt = t * t;
vec_t dt = 2.0f * t, tt, tt2;
tt = 1.0f - dt + qt;
Vector4Scale ( point0, tt, out );
tt2 = dt - 2.0f * qt;
tt = dt - 2.0f * qt;
Vector4Scale ( point1, tt, tvec4 );
Vector4Add ( out, tvec4, out );
Vector4Scale ( point2, qt, tvec4 );
Vector4Add ( out, tvec4, out );
for( i = 0; i < comp; i++ )
out[i] = point0[i] * tt + point1[i] * tt2 + point2[i] * qt;
}
/*
@ -519,73 +512,109 @@ static void Patch_Evaluate_QuadricBezier ( float t, vec4_t point0, vec4_t point1
Patch_Evaluate
===============
*/
void Patch_Evaluate ( const vec4_t *p, const int *numcp, const int *tess, vec4_t *dest )
void Patch_Evaluate( const vec_t *p, const int *numcp, const int *tess, vec_t *dest, int comp )
{
int num_patches[2], num_tess[2];
int index[3], dstpitch, i, u, v, x, y;
float s, t, step[2];
vec4_t *tvec, pv[3][3], v1, v2, v3;
vec_t *tvec, *tvec2;
const vec_t *pv[3][3];
vec4_t v1, v2, v3;
num_patches[0] = numcp[0] / 2;
num_patches[1] = numcp[1] / 2;
dstpitch = num_patches[0] * tess[0] + 1;
dstpitch = ( num_patches[0] * tess[0] + 1 ) * comp;
step[0] = 1.0f / (float)tess[0];
step[1] = 1.0f / (float)tess[1];
for ( v = 0; v < num_patches[1]; v++ )
for( v = 0; v < num_patches[1]; v++ )
{
// last patch has one more row
if ( v < num_patches[1] - 1 ) {
if( v < num_patches[1] - 1 )
num_tess[1] = tess[1];
} else {
else
num_tess[1] = tess[1] + 1;
}
for ( u = 0; u < num_patches[0]; u++ )
for( u = 0; u < num_patches[0]; u++ )
{
// last patch has one more column
if ( u < num_patches[0] - 1 ) {
if( u < num_patches[0] - 1 )
num_tess[0] = tess[0];
} else {
else
num_tess[0] = tess[0] + 1;
}
index[0] = (v * numcp[0] + u) * 2;
index[0] = ( v * numcp[0] + u ) * 2;
index[1] = index[0] + numcp[0];
index[2] = index[1] + numcp[0];
// current 3x3 patch control points
for ( i = 0; i < 3; i++ )
for( i = 0; i < 3; i++ )
{
Vector4Copy ( p[index[0]+i], pv[i][0] );
Vector4Copy ( p[index[1]+i], pv[i][1] );
Vector4Copy ( p[index[2]+i], pv[i][2] );
pv[i][0] = &p[( index[0]+i ) * comp];
pv[i][1] = &p[( index[1]+i ) * comp];
pv[i][2] = &p[( index[2]+i ) * comp];
}
t = 0.0f;
tvec = dest + v * tess[1] * dstpitch + u * tess[0];
for ( y = 0; y < num_tess[1]; y++, t += step[1] )
tvec = dest + v * tess[1] * dstpitch + u * tess[0] * comp;
for( y = 0, t = 0.0f; y < num_tess[1]; y++, t += step[1], tvec += dstpitch )
{
Patch_Evaluate_QuadricBezier ( t, pv[0][0], pv[0][1], pv[0][2], v1 );
Patch_Evaluate_QuadricBezier ( t, pv[1][0], pv[1][1], pv[1][2], v2 );
Patch_Evaluate_QuadricBezier ( t, pv[2][0], pv[2][1], pv[2][2], v3 );
Patch_Evaluate_QuadricBezier( t, pv[0][0], pv[0][1], pv[0][2], v1, comp );
Patch_Evaluate_QuadricBezier( t, pv[1][0], pv[1][1], pv[1][2], v2, comp );
Patch_Evaluate_QuadricBezier( t, pv[2][0], pv[2][1], pv[2][2], v3, comp );
s = 0.0f;
for ( x = 0; x < num_tess[0]; x++, s += step[0] )
{
Patch_Evaluate_QuadricBezier ( s, v1, v2, v3, tvec[x] );
}
tvec += dstpitch;
for( x = 0, tvec2 = tvec, s = 0.0f; x < num_tess[0]; x++, s += step[0], tvec2 += comp )
Patch_Evaluate_QuadricBezier( s, v1, v2, v3, tvec2, comp );
}
}
}
}
#define PLANE_NORMAL_EPSILON 0.00001
#define PLANE_DIST_EPSILON 0.01
static qboolean ComparePlanes( const vec3_t p1normal, vec_t p1dist, const vec3_t p2normal, vec_t p2dist )
{
if( fabs( p1normal[0] - p2normal[0] ) < PLANE_NORMAL_EPSILON
&& fabs( p1normal[1] - p2normal[1] ) < PLANE_NORMAL_EPSILON
&& fabs( p1normal[2] - p2normal[2] ) < PLANE_NORMAL_EPSILON
&& fabs( p1dist - p2dist ) < PLANE_DIST_EPSILON )
return true;
return false;
}
static void SnapVector( vec3_t normal )
{
int i;
for( i = 0; i < 3; i++ )
{
if( fabs( normal[i] - 1 ) < PLANE_NORMAL_EPSILON )
{
VectorClear( normal );
normal[i] = 1;
break;
}
if( fabs( normal[i] - -1 ) < PLANE_NORMAL_EPSILON )
{
VectorClear( normal );
normal[i] = -1;
break;
}
}
}
#define Q_rint( x ) ( ( x ) < 0 ? ( (int)( ( x )-0.5f ) ) : ( (int)( ( x )+0.5f ) ) )
static void SnapPlane( vec3_t normal, vec_t *dist )
{
SnapVector( normal );
if( fabs( *dist - Q_rint( *dist ) ) < PLANE_DIST_EPSILON )
{
*dist = Q_rint( *dist );
}
}
/*
===============================================================================
@ -595,6 +624,283 @@ void Patch_Evaluate ( const vec4_t *p, const int *numcp, const int *tess, vec4_t
===============================================================================
*/
#if 1
#define MAX_FACET_PLANES 32
#define cm_subdivlevel 15
/*
* CM_CreateFacetFromPoints
*/
static int CM_CreateFacetFromPoints(q2cbrush_t *facet, vec3_t *verts, int numverts, q2mapsurface_t *shaderref, mplane_t *brushplanes )
{
int i, j, k;
int axis, dir;
vec3_t normal, mins, maxs;
float d, dist;
mplane_t mainplane;
vec3_t vec, vec2;
int numbrushplanes;
// set default values for brush
facet->numsides = 0;
facet->brushside = NULL;
facet->contents = shaderref->c.value;
// calculate plane for this triangle
PlaneFromPoints( verts, &mainplane );
if( ComparePlanes( mainplane.normal, mainplane.dist, vec3_origin, 0 ) )
return 0;
// test a quad case
if( numverts > 3 )
{
d = DotProduct( verts[3], mainplane.normal ) - mainplane.dist;
if( d < -0.1 || d > 0.1 )
return 0;
if( 0 )
{
vec3_t v[3];
mplane_t plane;
// try different combinations of planes
for( i = 1; i < 4; i++ )
{
VectorCopy( verts[i], v[0] );
VectorCopy( verts[( i+1 )%4], v[1] );
VectorCopy( verts[( i+2 )%4], v[2] );
PlaneFromPoints( v, &plane );
if( fabs( DotProduct( mainplane.normal, plane.normal ) ) < 0.9 )
return 0;
}
}
}
numbrushplanes = 0;
// add front plane
SnapPlane( mainplane.normal, &mainplane.dist );
VectorCopy( mainplane.normal, brushplanes[numbrushplanes].normal );
brushplanes[numbrushplanes].dist = mainplane.dist; numbrushplanes++;
// calculate mins & maxs
ClearBounds( mins, maxs );
for( i = 0; i < numverts; i++ )
AddPointToBounds( verts[i], mins, maxs );
// add the axial planes
for( axis = 0; axis < 3; axis++ )
{
for( dir = -1; dir <= 1; dir += 2 )
{
for( i = 0; i < numbrushplanes; i++ )
{
if( brushplanes[i].normal[axis] == dir )
break;
}
if( i == numbrushplanes )
{
VectorClear( normal );
normal[axis] = dir;
if( dir == 1 )
dist = maxs[axis];
else
dist = -mins[axis];
VectorCopy( normal, brushplanes[numbrushplanes].normal );
brushplanes[numbrushplanes].dist = dist; numbrushplanes++;
}
}
}
// add the edge bevels
for( i = 0; i < numverts; i++ )
{
j = ( i + 1 ) % numverts;
k = ( i + 2 ) % numverts;
VectorSubtract( verts[i], verts[j], vec );
if( VectorNormalize( vec ) < 0.5 )
continue;
SnapVector( vec );
for( j = 0; j < 3; j++ )
{
if( vec[j] == 1 || vec[j] == -1 )
break; // axial
}
if( j != 3 )
continue; // only test non-axial edges
// try the six possible slanted axials from this edge
for( axis = 0; axis < 3; axis++ )
{
for( dir = -1; dir <= 1; dir += 2 )
{
// construct a plane
VectorClear( vec2 );
vec2[axis] = dir;
CrossProduct( vec, vec2, normal );
if( VectorNormalize( normal ) < 0.5 )
continue;
dist = DotProduct( verts[i], normal );
for( j = 0; j < numbrushplanes; j++ )
{
// if this plane has already been used, skip it
if( ComparePlanes( brushplanes[j].normal, brushplanes[j].dist, normal, dist ) )
break;
}
if( j != numbrushplanes )
continue;
// if all other points are behind this plane, it is a proper edge bevel
for( j = 0; j < numverts; j++ )
{
if( j != i )
{
d = DotProduct( verts[j], normal ) - dist;
if( d > 0.1 )
break; // point in front: this plane isn't part of the outer hull
}
}
if( j != numverts )
continue;
// add this plane
VectorCopy( normal, brushplanes[numbrushplanes].normal );
brushplanes[numbrushplanes].dist = dist; numbrushplanes++;
if( numbrushplanes == MAX_FACET_PLANES )
break;
}
}
}
return ( facet->numsides = numbrushplanes );
}
/*
* CM_CreatePatch
*/
static void CM_CreatePatch( q3cpatch_t *patch, q2mapsurface_t *shaderref, const vec3_t *verts, const int *patch_cp )
{
int step[2], size[2], flat[2];
int i, j, k ,u, v;
int numsides, totalsides;
q2cbrush_t *facets, *facet;
vec3_t *points;
vec3_t tverts[4];
qbyte *data;
mplane_t *brushplanes;
// find the degree of subdivision in the u and v directions
Patch_GetFlatness( cm_subdivlevel, verts[0], 3, patch_cp, flat );
step[0] = 1 << flat[0];
step[1] = 1 << flat[1];
size[0] = ( patch_cp[0] >> 1 ) * step[0] + 1;
size[1] = ( patch_cp[1] >> 1 ) * step[1] + 1;
if( size[0] <= 0 || size[1] <= 0 )
return;
data = BZ_Malloc( size[0] * size[1] * sizeof( vec3_t ) +
( size[0]-1 ) * ( size[1]-1 ) * 2 * ( sizeof( q2cbrush_t ) + 32 * sizeof( mplane_t ) ) );
points = ( vec3_t * )data; data += size[0] * size[1] * sizeof( vec3_t );
facets = ( q2cbrush_t * )data; data += ( size[0]-1 ) * ( size[1]-1 ) * 2 * sizeof( q2cbrush_t );
brushplanes = ( mplane_t * )data; data += ( size[0]-1 ) * ( size[1]-1 ) * 2 * MAX_FACET_PLANES * sizeof( mplane_t );
// fill in
Patch_Evaluate( verts[0], patch_cp, step, points[0], 3 );
totalsides = 0;
patch->numfacets = 0;
patch->facets = NULL;
ClearBounds( patch->absmins, patch->absmaxs );
// create a set of facets
for( v = 0; v < size[1]-1; v++ )
{
for( u = 0; u < size[0]-1; u++ )
{
i = v * size[0] + u;
VectorCopy( points[i], tverts[0] );
VectorCopy( points[i + size[0]], tverts[1] );
VectorCopy( points[i + size[0] + 1], tverts[2] );
VectorCopy( points[i + 1], tverts[3] );
for( i = 0; i < 4; i++ )
AddPointToBounds( tverts[i], patch->absmins, patch->absmaxs );
// try to create one facet from a quad
numsides = CM_CreateFacetFromPoints( &facets[patch->numfacets], tverts, 4, shaderref, brushplanes + totalsides );
if( !numsides )
{ // create two facets from triangles
VectorCopy( tverts[3], tverts[2] );
numsides = CM_CreateFacetFromPoints( &facets[patch->numfacets], tverts, 3, shaderref, brushplanes + totalsides );
if( numsides )
{
totalsides += numsides;
patch->numfacets++;
}
VectorCopy( tverts[2], tverts[0] );
VectorCopy( points[v *size[0] + u + size[0] + 1], tverts[2] );
numsides = CM_CreateFacetFromPoints( &facets[patch->numfacets], tverts, 3, shaderref, brushplanes + totalsides );
}
if( numsides )
{
totalsides += numsides;
patch->numfacets++;
}
}
}
if( patch->numfacets )
{
qbyte *data;
data = Hunk_Alloc( patch->numfacets * sizeof( q2cbrush_t ) + totalsides * ( sizeof( q2cbrushside_t ) + sizeof( mplane_t ) ) );
patch->facets = ( q2cbrush_t * )data; data += patch->numfacets * sizeof( q2cbrush_t );
memcpy( patch->facets, facets, patch->numfacets * sizeof( q2cbrush_t ) );
for( i = 0, k = 0, facet = patch->facets; i < patch->numfacets; i++, facet++ )
{
mplane_t *planes;
q2cbrushside_t *s;
facet->brushside = ( q2cbrushside_t * )data; data += facet->numsides * sizeof( q2cbrushside_t );
planes = ( mplane_t * )data; data += facet->numsides * sizeof( mplane_t );
for( j = 0, s = facet->brushside; j < facet->numsides; j++, s++ )
{
planes[j] = brushplanes[k++];
s->plane = &planes[j];
SnapPlane( s->plane->normal, &s->plane->dist );
CategorizePlane( s->plane );
s->surface = shaderref;
}
}
patch->surface = shaderref;
for( i = 0; i < 3; i++ )
{
// spread the mins / maxs by a pixel
patch->absmins[i] -= 1;
patch->absmaxs[i] += 1;
}
}
BZ_Free( points );
}
#else
#define cm_subdivlevel 15
qboolean CM_CreateBrush ( q2cbrush_t *brush, vec3_t *verts, q2mapsurface_t *surface )
@ -607,9 +913,6 @@ qboolean CM_CreateBrush ( q2cbrush_t *brush, vec3_t *verts, q2mapsurface_t *surf
static mplane_t mainplane, patchplanes[20];
qboolean skip[20];
int numpatchplanes = 0;
float dot;
int matchplane;
// calc absmins & absmaxs
ClearBounds ( absmins, absmaxs );
@ -628,9 +931,13 @@ qboolean CM_CreateBrush ( q2cbrush_t *brush, vec3_t *verts, q2mapsurface_t *surf
plane->dist = -mainplane.dist;
// axial planes
for ( i = 0; i < 3; i++ ) {
for (sign = -1; sign <= 1; sign += 2) {
for ( i = 0; i < 3; i++ )
{
for (sign = -1; sign <= 1; sign += 2)
{
plane = &patchplanes[numpatchplanes++];
if (numpatchplanes > 20)
return false;
VectorClear ( plane->normal );
plane->normal[i] = sign;
plane->dist = sign > 0 ? absmaxs[i] : -absmins[i];
@ -638,13 +945,15 @@ qboolean CM_CreateBrush ( q2cbrush_t *brush, vec3_t *verts, q2mapsurface_t *surf
}
// edge planes
for ( i = 0; i < 3; i++ ) {
for ( i = 0; i < 3; i++ )
{
vec3_t normal;
VectorCopy (verts[i], v1);
VectorCopy (verts[(i + 1) % 3], v2);
for ( k = 0; k < 3; k++ ) {
for ( k = 0; k < 3; k++ )
{
normal[k] = 0;
normal[(k+1)%3] = v1[(k+2)%3] - v2[(k+2)%3];
normal[(k+2)%3] = -(v1[(k+1)%3] - v2[(k+1)%3]);
@ -653,6 +962,8 @@ qboolean CM_CreateBrush ( q2cbrush_t *brush, vec3_t *verts, q2mapsurface_t *surf
continue;
plane = &patchplanes[numpatchplanes++];
if (numpatchplanes > 20)
return false;
VectorNormalize ( normal );
VectorCopy ( normal, plane->normal );
@ -682,10 +993,13 @@ qboolean CM_CreateBrush ( q2cbrush_t *brush, vec3_t *verts, q2mapsurface_t *surf
}
brush->numsides = 0;
brush->firstbrushside = numbrushsides;
brush->brushside = Hunk_Alloc((sizeof(*plane) + sizeof(*side))*numpatchplanes);
plane = (mplane_t*)(brush->brushside+numpatchplanes);
for (k = 0; k < 2; k++) {
for (i = 0; i < numpatchplanes; i++) {
for (k = 0; k < 2; k++)
{
for (i = 0; i < numpatchplanes; i++)
{
if (skip[i])
continue;
@ -697,45 +1011,15 @@ qboolean CM_CreateBrush ( q2cbrush_t *brush, vec3_t *verts, q2mapsurface_t *surf
skip[i] = true;
for (matchplane = 0; matchplane < numplanes; matchplane++)
{
if (map_planes[matchplane].dist+0.1 > patchplanes[i].dist && map_planes[matchplane].dist-0.1 < patchplanes[i].dist)
{
dot = DotProduct(map_planes[matchplane].normal, patchplanes[i].normal);
if (dot >= 0.98)
{
plane = &map_planes[matchplane];
break;
}
}
}
if (matchplane == numplanes)
{
if (numplanes == MAX_Q2MAP_PLANES)
{
Con_Printf (CON_ERROR "CM_CreateBrush: numplanes == MAX_CM_PLANES");
return false;
}
plane = &map_planes[numplanes++];
*plane = patchplanes[i];
}
if (numbrushsides == MAX_CM_BRUSHSIDES)
{
Con_Printf (CON_ERROR "CM_CreateBrush: numbrushsides == MAX_CM_BRUSHSIDES\n");
return false;
}
side = &map_brushsides[numbrushsides++];
side->plane = plane;
side = brush->brushside + brush->numsides;
side->plane = plane+brush->numsides;
plane[brush->numsides] = patchplanes[i];
brush->numsides++;
if (DotProduct(plane->normal, mainplane.normal) >= 0)
side->surface = surface;
else
side->surface = NULL; // don't clip against this side
brush->numsides++;
}
}
@ -760,6 +1044,7 @@ qboolean CM_CreatePatch ( q3cpatch_t *patch, int numverts, const vec3_t *verts,
if ( size[0] * size[1] > MAX_CM_PATCH_VERTS )
{
return true;
Con_Printf (CON_ERROR "CM_CreatePatch: patch has too many vertices\n");
return false;
}
@ -824,7 +1109,7 @@ qboolean CM_CreatePatch ( q3cpatch_t *patch, int numverts, const vec3_t *verts,
return true;
}
#endif
//======================================================
@ -842,6 +1127,9 @@ qboolean CM_CreatePatchesForLeafs (void)
q3cpatch_t *patch;
int checkout[MAX_CM_FACES];
if (map_noCurves.ival)
return true;
memset (checkout, -1, sizeof(int)*MAX_CM_FACES);
for (i = 0, leaf = map_leafs; i < numleafs; i++, leaf++)
@ -849,7 +1137,7 @@ qboolean CM_CreatePatchesForLeafs (void)
leaf->numleafpatches = 0;
leaf->firstleafpatch = numleafpatches;
if (leaf->cluster == -1 || map_noCurves.value)
if (leaf->cluster == -1)
continue;
for (j=0 ; j<leaf->numleaffaces ; j++)
@ -894,13 +1182,11 @@ qboolean CM_CreatePatchesForLeafs (void)
}
patch = &map_patches[numpatches];
patch->surface = surf;
map_leafpatches[numleafpatches] = numpatches;
checkout[k] = numpatches++;
//gcc warns without this cast
if (!CM_CreatePatch ( patch, face->numverts, (const vec3_t *)map_verts + face->firstvert, face->patch_cp ))
return false;
CM_CreatePatch ( patch, surf, (const vec3_t *)map_verts + face->firstvert, face->patch_cp );
}
leaf->contents |= patch->surface->c.value;
@ -1415,7 +1701,8 @@ qboolean CMod_LoadBrushes (lump_t *l)
for (i=0 ; i<count ; i++, out++, in++)
{
out->firstbrushside = LittleLong(in->firstside);
//FIXME: missing bounds checks
out->brushside = &map_brushsides[LittleLong(in->firstside)];
out->numsides = LittleLong(in->numsides);
out->contents = LittleLong(in->contents);
}
@ -2207,7 +2494,7 @@ qboolean CModQ3_LoadFogs (lump_t *l)
}
brush = map_brushes + LittleLong ( in->brushNum );
brushsides = map_brushsides + brush->firstbrushside;
brushsides = brush->brushside;
visibleside = brushsides + LittleLong ( in->visibleSide );
out->visibleplane = visibleside->plane;
@ -2257,7 +2544,7 @@ mfog_t *CM_FogForOrigin(vec3_t org)
#define MAX_ARRAY_VERTS 2048
index_t tempIndexesArray[MAX_ARRAY_VERTS*3];
vec4_t tempxyz_array[MAX_ARRAY_VERTS]; //structure is used only at load.
vecV_t tempxyz_array[MAX_ARRAY_VERTS]; //structure is used only at load.
vec3_t tempnormals_array[MAX_ARRAY_VERTS]; //so what harm is there in doing this?
vec2_t tempst_array[MAX_ARRAY_VERTS];
vec2_t templmst_array[MAX_ARRAY_VERTS];
@ -2273,11 +2560,13 @@ mesh_t *GL_CreateMeshForPatch (model_t *mod, int patchwidth, int patchheight, in
mesh_t *mesh;
index_t *indexes;
float subdivlevel;
char *allocbuf;
int sz;
patch_cp[0] = patchwidth;
patch_cp[1] = patchheight;
if ( !patch_cp[0] || !patch_cp[1] )
if (patch_cp[0] <= 0 || patch_cp[1] <= 0 )
{
return NULL;
}
@ -2295,7 +2584,7 @@ mesh_t *GL_CreateMeshForPatch (model_t *mod, int patchwidth, int patchheight, in
}
// find the degree of subdivision in the u and v directions
Patch_GetFlatness ( subdivlevel, (const vec3_t *)map_verts+firstvert, patch_cp, flat );
Patch_GetFlatness ( subdivlevel, map_verts[firstvert], sizeof(vecV_t)/sizeof(vec_t), patch_cp, flat );
// allocate space for mesh
step[0] = (1 << flat[0]);
@ -2304,27 +2593,41 @@ mesh_t *GL_CreateMeshForPatch (model_t *mod, int patchwidth, int patchheight, in
size[1] = (patch_cp[1] / 2) * step[1] + 1;
numverts = size[0] * size[1];
if ( numverts > MAX_ARRAY_VERTS ) {
if ( numverts < 0 || numverts > MAX_ARRAY_VERTS ) {
return NULL;
}
mesh = (mesh_t *)Hunk_Alloc ( sizeof(mesh_t));
sz = sizeof(mesh_t) + numverts * (
sizeof(vecV_t)+
sizeof(vec3_t)+
sizeof(vec3_t)+
sizeof(vec3_t)+
sizeof(vec2_t)+
sizeof(vec2_t)+
sizeof(vec4_t));
allocbuf = Hunk_Alloc(sz);
mesh = (mesh_t *)(allocbuf+(sz-=sizeof(mesh_t)));
mesh->xyz_array = (vecV_t *)(allocbuf+(sz-=numverts*sizeof(vecV_t)));
mesh->normals_array = (vec3_t *)(allocbuf+(sz-=numverts*sizeof(vec3_t)));
mesh->snormals_array = (vec3_t *)(allocbuf+(sz-=numverts*sizeof(vec3_t)));
mesh->tnormals_array = (vec3_t *)(allocbuf+(sz-=numverts*sizeof(vec3_t)));
mesh->st_array = (vec2_t *)(allocbuf+(sz-=numverts*sizeof(vec2_t)));
mesh->lmst_array = (vec2_t *)(allocbuf+(sz-=numverts*sizeof(vec2_t)));
mesh->colors4f_array = (vec4_t *)(allocbuf+(sz-=numverts*sizeof(vec4_t)));
#ifdef _DEBUG
if (sz)
Sys_Error("Bug\n");
#endif
mesh->numvertexes = numverts;
mesh->xyz_array = Hunk_Alloc ( numverts * sizeof(vec3_t));
mesh->normals_array = Hunk_Alloc ( numverts * sizeof(vec3_t));
mesh->snormals_array = Hunk_Alloc ( numverts * sizeof(vec3_t));
mesh->tnormals_array = Hunk_Alloc ( numverts * sizeof(vec3_t));
mesh->st_array = Hunk_Alloc ( numverts * sizeof(vec2_t));
mesh->lmst_array = Hunk_Alloc ( numverts * sizeof(vec2_t));
mesh->colors4f_array = Hunk_Alloc ( numverts * sizeof(vec4_t));
// fill in
Patch_Evaluate ( (const vec4_t *)points, patch_cp, step, points2 );
Patch_Evaluate ( (const vec4_t *)colors, patch_cp, step, colors2 );
Patch_Evaluate ( (const vec4_t *)normals, patch_cp, step, normals2 );
Patch_Evaluate ( (const vec4_t *)lm_st, patch_cp, step, lm_st2 );
Patch_Evaluate ( (const vec4_t *)tex_st, patch_cp, step, tex_st2 );
Patch_Evaluate ( points[0], patch_cp, step, points2[0], 3 );
Patch_Evaluate ( colors[0], patch_cp, step, colors2[0], 4 );
Patch_Evaluate ( normals[0], patch_cp, step, normals2[0], 3 );
Patch_Evaluate ( lm_st[0], patch_cp, step, lm_st2[0], 2 );
Patch_Evaluate ( tex_st[0], patch_cp, step, tex_st2[0], 2 );
for (i = 0; i < numverts; i++)
{
@ -2483,14 +2786,17 @@ qboolean CModQ3_LoadRFaces (lump_t *l)
else if (LittleLong(in->facetype) == MST_PATCH)
{
out->mesh = GL_CreateMeshForPatch(loadmodel, LittleLong(in->patchwidth), LittleLong(in->patchheight), LittleLong(in->num_vertices), LittleLong(in->firstvertex));
Mod_AccumulateMeshTextureVectors(out->mesh);
Mod_NormaliseTextureVectors(out->mesh->normals_array, out->mesh->snormals_array, out->mesh->tnormals_array, out->mesh->numvertexes);
if (out->mesh)
{
Mod_AccumulateMeshTextureVectors(out->mesh);
Mod_NormaliseTextureVectors(out->mesh->normals_array, out->mesh->snormals_array, out->mesh->tnormals_array, out->mesh->numvertexes);
}
}
else if (LittleLong(in->facetype) == MST_PLANAR || LittleLong(in->facetype) == MST_TRIANGLE_SOUP)
{
numindexes = LittleLong(in->num_indexes);
numverts = LittleLong(in->num_vertices);
if (numindexes%3)
if (numindexes%3 || numindexes < 0 || numverts < 0)
{
Con_Printf(CON_ERROR "mesh indexes should be multiples of 3\n");
return false;
@ -2823,7 +3129,7 @@ qboolean CModQ3_LoadBrushes (lump_t *l)
{
shaderref = LittleLong ( in->shadernum );
out->contents = map_surfaces[shaderref].c.value;
out->firstbrushside = LittleLong ( in->firstside );
out->brushside = &map_brushsides[LittleLong ( in->firstside )];
out->numsides = LittleLong ( in->num_sides );
}
@ -4015,7 +4321,7 @@ void CM_InitBoxHull (void)
box_brush = &map_brushes[numbrushes];
box_brush->numsides = 6;
box_brush->firstbrushside = numbrushsides;
box_brush->brushside = &map_brushsides[numbrushsides];
box_brush->contents = Q2CONTENTS_MONSTER;
box_leaf = &map_leafs[numleafs];
@ -4245,7 +4551,7 @@ int CM_PointContents (model_t *mod, vec3_t p)
continue;
}
brushside = &map_brushsides[brush->firstbrushside];
brushside = brush->brushside;
for ( j = 0; j < brush->numsides; j++, brushside++ )
{
if ( PlaneDiff (p, brushside->plane) > 0 )
@ -4295,7 +4601,7 @@ unsigned int CM_NativeContents(struct model_s *model, int hulloverride, int fram
continue;
}
brushside = &map_brushsides[brush->firstbrushside];
brushside = brush->brushside;
for ( j = 0; j < brush->numsides; j++, brushside++ )
{
if ( PlaneDiff (p, brushside->plane) > 0 )
@ -4403,7 +4709,7 @@ void CM_ClipBoxToBrush (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2,
for (i=0 ; i<brush->numsides ; i++)
{
side = &map_brushsides[brush->firstbrushside+i];
side = brush->brushside+i;
plane = side->plane;
// FIXME: special case for axial
@ -4515,7 +4821,7 @@ void CM_ClipBoxToPatch (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2,
for (i=0 ; i<brush->numsides ; i++)
{
side = &map_brushsides[brush->firstbrushside+i];
side = brush->brushside+i;
plane = side->plane;
if (!trace_ispoint)
@ -4618,7 +4924,7 @@ void CM_TestBoxInBrush (vec3_t mins, vec3_t maxs, vec3_t p1,
for (i=0 ; i<brush->numsides ; i++)
{
side = &map_brushsides[brush->firstbrushside+i];
side = brush->brushside+i;
plane = side->plane;
// FIXME: special case for axial
@ -4667,7 +4973,7 @@ void CM_TestBoxInPatch (vec3_t mins, vec3_t maxs, vec3_t p1,
for (i=0 ; i<brush->numsides ; i++)
{
side = &map_brushsides[brush->firstbrushside+i];
side = brush->brushside+i;
plane = side->plane;
// general box case
@ -5659,3 +5965,4 @@ void CM_Init(void) //register cvars.
Cvar_Register(&r_subdivisions, MAPOPTIONS);
}
#endif

View File

@ -28,7 +28,7 @@ typedef vec_t vec5_t[5];
/*16-byte aligned vectors, for auto-vectorising, should propogate to structs
sse and altivec can unroll loops using aligned reads, which should be faster... 4 at once.
*/
#ifdef _MSC_VER
#if _MSC_VER >= 1300
typedef __declspec(align(16)) vec3_t avec3_t;
typedef __declspec(align(16)) vec4_t avec4_t;
typedef __declspec(align(4)) qbyte byte_vec4_t[4];

View File

@ -1341,7 +1341,7 @@ void *Hunk_AllocName (int size, char *name)
char *buf;
Hunk_Print(true);
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &buf, 0, NULL);
Sys_Error ("VirtualCommit failed\nNot enough RAM allocated on allocation of \"%s\". Try starting using \"-heapsize 64000\" on the QuakeWorld command line.", name);
Sys_Error ("VirtualCommit failed\nNot enough RAM allocated on allocation of \"%s\". Try starting using \"-heapsize %i\" on the QuakeWorld command line.", name, roundupold/512);
}
#endif

View File

@ -1190,7 +1190,6 @@ static void (D3D9_R_RenderView) (void)
d3d9error(IDirect3DDevice9_Clear(pD3DDev9, 0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1, 0));
Surf_DrawWorld();
P_DrawParticles ();
RQ_RenderBatchClear();
}
void (D3D9_R_NewMap) (void);

View File

@ -51,7 +51,6 @@ Global
{88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364}.Debug Dedicated Server|x64.ActiveCfg = GLDebug|x64
{88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364}.Debug Dedicated Server|x64.Build.0 = GLDebug|x64
{88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364}.Debug|Win32.ActiveCfg = MDebug|Win32
{88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364}.Debug|Win32.Build.0 = MDebug|Win32
{88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364}.Debug|x64.ActiveCfg = MDebug|x64
{88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364}.Debug|x64.Build.0 = MDebug|x64
{88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364}.GLDebug|Win32.ActiveCfg = GLDebug|Win32

View File

@ -12869,6 +12869,25 @@
/>
</FileConfiguration>
</File>
<File
RelativePath="..\client\snd_al.c"
>
<FileConfiguration
Name="GLDebug|Win32"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug Dedicated Server|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\client\snd_directx.c"
>
@ -28288,6 +28307,82 @@
/>
</FileConfiguration>
</File>
<File
RelativePath="..\client\snd_sdl.c"
>
<FileConfiguration
Name="MinGLDebug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="D3DDebug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="MinGLRelease|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="GLDebug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release Dedicated Server|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="MRelease|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug Dedicated Server|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="MDebug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="GLRelease|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\server\sv_sys_unix.c"
>

File diff suppressed because it is too large Load Diff

View File

@ -516,7 +516,7 @@ SOURCE=..\server\sv_master.c
# Begin Source File
SOURCE=..\server\sv_move.c
# ADD CPP /Yu"qwsvdef.h"
# ADD CPP /Yu"quakedef.h"
# End Source File
# Begin Source File
@ -643,7 +643,65 @@ SOURCE=..\server\svq3_game.c
# Begin Source File
SOURCE=..\server\world.c
!IF "$(CFG)" == "ftequake - Win32 Release"
# ADD CPP /Yu"qwsvdef.h"
!ELSEIF "$(CFG)" == "ftequake - Win32 Debug"
# ADD CPP /Yu"qwsvdef.h"
!ELSEIF "$(CFG)" == "ftequake - Win32 GLDebug"
# ADD CPP /Yu"qwsvdef.h"
!ELSEIF "$(CFG)" == "ftequake - Win32 GLRelease"
# ADD CPP /Yu"qwsvdef.h"
!ELSEIF "$(CFG)" == "ftequake - Win32 MDebug"
# ADD CPP /Yu"qwsvdef.h"
!ELSEIF "$(CFG)" == "ftequake - Win32 MRelease"
# ADD CPP /Yu"qwsvdef.h"
!ELSEIF "$(CFG)" == "ftequake - Win32 MinGLDebug"
# ADD CPP /Yu"qwsvdef.h"
!ELSEIF "$(CFG)" == "ftequake - Win32 MinGLRelease"
# ADD CPP /Yu"qwsvdef.h"
!ELSEIF "$(CFG)" == "ftequake - Win32 Debug Dedicated Server"
# ADD CPP /Yu"quakedef.h"
!ELSEIF "$(CFG)" == "ftequake - Win32 Release Dedicated Server"
# ADD CPP /Yu"qwsvdef.h"
!ELSEIF "$(CFG)" == "ftequake - Win32 MinSW"
# ADD CPP /Yu"qwsvdef.h"
!ELSEIF "$(CFG)" == "ftequake - Win32 GLDebugQ3"
# ADD CPP /Yu"qwsvdef.h"
!ELSEIF "$(CFG)" == "ftequake - Win32 Debug Dedicated ServerQ3"
# ADD CPP /Yu"qwsvdef.h"
!ELSEIF "$(CFG)" == "ftequake - Win32 D3DDebug"
# ADD CPP /Yu"qwsvdef.h"
!ENDIF
# End Source File
# End Group
# Begin Group "client"
@ -4099,10 +4157,6 @@ SOURCE=..\gl\gl_shadow.c
# End Source File
# Begin Source File
SOURCE=..\gl\gl_vbo.c
# End Source File
# Begin Source File
SOURCE=..\gl\gl_vidcommon.c
!IF "$(CFG)" == "ftequake - Win32 Release"
@ -4357,6 +4411,10 @@ SOURCE=..\common\com_mesh.c
# End Source File
# Begin Source File
SOURCE=..\common\com_phys_ode.c
# End Source File
# Begin Source File
SOURCE=..\common\common.c
# ADD CPP /Yc"quakedef.h"
# End Source File
@ -4504,7 +4562,7 @@ SOURCE=..\qclib\execloop.h
!IF "$(CFG)" == "ftequake - Win32 Release"
# Begin Custom Build
InputDir=\Games\Quake\ftesrc\engine\QCLIB
InputDir=\Games\Quake\wip\engine\qclib
InputPath=..\qclib\execloop.h
BuildCmds= \
@ -4526,7 +4584,7 @@ BuildCmds= \
!ELSEIF "$(CFG)" == "ftequake - Win32 Debug"
# Begin Custom Build
InputDir=\Games\Quake\ftesrc\engine\QCLIB
InputDir=\Games\Quake\wip\engine\qclib
InputPath=..\qclib\execloop.h
BuildCmds= \
@ -4548,7 +4606,7 @@ BuildCmds= \
!ELSEIF "$(CFG)" == "ftequake - Win32 GLDebug"
# Begin Custom Build
InputDir=\Games\Quake\ftesrc\engine\QCLIB
InputDir=\Games\Quake\wip\engine\qclib
InputPath=..\qclib\execloop.h
BuildCmds= \
@ -4570,7 +4628,7 @@ BuildCmds= \
!ELSEIF "$(CFG)" == "ftequake - Win32 GLRelease"
# Begin Custom Build
InputDir=\Games\Quake\ftesrc\engine\QCLIB
InputDir=\Games\Quake\wip\engine\qclib
InputPath=..\qclib\execloop.h
BuildCmds= \
@ -4592,7 +4650,7 @@ BuildCmds= \
!ELSEIF "$(CFG)" == "ftequake - Win32 MDebug"
# Begin Custom Build
InputDir=\Games\Quake\ftesrc\engine\QCLIB
InputDir=\Games\Quake\wip\engine\qclib
InputPath=..\qclib\execloop.h
BuildCmds= \
@ -4614,7 +4672,7 @@ BuildCmds= \
!ELSEIF "$(CFG)" == "ftequake - Win32 MRelease"
# Begin Custom Build
InputDir=\Games\Quake\ftesrc\engine\QCLIB
InputDir=\Games\Quake\wip\engine\qclib
InputPath=..\qclib\execloop.h
BuildCmds= \
@ -4636,7 +4694,7 @@ BuildCmds= \
!ELSEIF "$(CFG)" == "ftequake - Win32 MinGLDebug"
# Begin Custom Build
InputDir=\Games\Quake\ftesrc\engine\QCLIB
InputDir=\Games\Quake\wip\engine\qclib
InputPath=..\qclib\execloop.h
BuildCmds= \
@ -4658,7 +4716,7 @@ BuildCmds= \
!ELSEIF "$(CFG)" == "ftequake - Win32 MinGLRelease"
# Begin Custom Build
InputDir=\Games\Quake\ftesrc\engine\QCLIB
InputDir=\Games\Quake\wip\engine\qclib
InputPath=..\qclib\execloop.h
BuildCmds= \
@ -4680,7 +4738,7 @@ BuildCmds= \
!ELSEIF "$(CFG)" == "ftequake - Win32 Debug Dedicated Server"
# Begin Custom Build
InputDir=\Games\Quake\ftesrc\engine\QCLIB
InputDir=\Games\Quake\wip\engine\qclib
InputPath=..\qclib\execloop.h
BuildCmds= \
@ -4702,7 +4760,7 @@ BuildCmds= \
!ELSEIF "$(CFG)" == "ftequake - Win32 Release Dedicated Server"
# Begin Custom Build
InputDir=\Games\Quake\ftesrc\engine\QCLIB
InputDir=\Games\Quake\wip\engine\qclib
InputPath=..\qclib\execloop.h
BuildCmds= \
@ -4724,7 +4782,7 @@ BuildCmds= \
!ELSEIF "$(CFG)" == "ftequake - Win32 MinSW"
# Begin Custom Build
InputDir=\Games\Quake\ftesrc\engine\QCLIB
InputDir=\Games\Quake\wip\engine\qclib
InputPath=..\qclib\execloop.h
BuildCmds= \
@ -4746,7 +4804,7 @@ BuildCmds= \
!ELSEIF "$(CFG)" == "ftequake - Win32 GLDebugQ3"
# Begin Custom Build
InputDir=\Games\Quake\ftesrc\engine\QCLIB
InputDir=\Games\Quake\wip\engine\qclib
InputPath=..\qclib\execloop.h
BuildCmds= \
@ -4768,7 +4826,7 @@ BuildCmds= \
!ELSEIF "$(CFG)" == "ftequake - Win32 Debug Dedicated ServerQ3"
# Begin Custom Build
InputDir=\Games\Quake\ftesrc\engine\QCLIB
InputDir=\Games\Quake\wip\engine\qclib
InputPath=..\qclib\execloop.h
BuildCmds= \
@ -4790,7 +4848,7 @@ BuildCmds= \
!ELSEIF "$(CFG)" == "ftequake - Win32 D3DDebug"
# Begin Custom Build
InputDir=\Games\Quake\ftesrc\engine\QCLIB
InputDir=\Games\Quake\wip\engine\qclib
InputPath=..\qclib\execloop.h
BuildCmds= \
@ -4841,41 +4899,7 @@ SOURCE=..\QCLIB\pr_multi.c
# Begin Source File
SOURCE=..\qclib\pr_x86.c
!IF "$(CFG)" == "ftequake - Win32 Release"
# SUBTRACT CPP /YX /Yc /Yu
!ELSEIF "$(CFG)" == "ftequake - Win32 Debug"
# SUBTRACT CPP /YX /Yc /Yu
!ELSEIF "$(CFG)" == "ftequake - Win32 GLDebug"
!ELSEIF "$(CFG)" == "ftequake - Win32 GLRelease"
!ELSEIF "$(CFG)" == "ftequake - Win32 MDebug"
!ELSEIF "$(CFG)" == "ftequake - Win32 MRelease"
!ELSEIF "$(CFG)" == "ftequake - Win32 MinGLDebug"
!ELSEIF "$(CFG)" == "ftequake - Win32 MinGLRelease"
!ELSEIF "$(CFG)" == "ftequake - Win32 Debug Dedicated Server"
!ELSEIF "$(CFG)" == "ftequake - Win32 Release Dedicated Server"
!ELSEIF "$(CFG)" == "ftequake - Win32 MinSW"
!ELSEIF "$(CFG)" == "ftequake - Win32 GLDebugQ3"
!ELSEIF "$(CFG)" == "ftequake - Win32 Debug Dedicated ServerQ3"
!ELSEIF "$(CFG)" == "ftequake - Win32 D3DDebug"
!ENDIF
# End Source File
# Begin Source File
@ -6828,99 +6852,6 @@ SOURCE=..\server\svmodel.c
!ENDIF
# End Source File
# End Group
# Begin Group "d3d"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\common\com_mesh.h
# End Source File
# Begin Source File
SOURCE=..\d3d\d3d_draw.c
# End Source File
# Begin Source File
SOURCE=..\d3d\d3d_mesh.c
# End Source File
# Begin Source File
SOURCE=..\d3d\d3d_rmain.c
# End Source File
# Begin Source File
SOURCE=..\d3d\d3d_rsurf.c
# End Source File
# Begin Source File
SOURCE=..\d3d\d3dquake.h
# End Source File
# Begin Source File
SOURCE=..\d3d\vid_d3d.c
# End Source File
# End Group
# Begin Group "d3d9"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\d3d9\d3d9_draw.c
# End Source File
# Begin Source File
SOURCE=..\d3d9\d3d9_mesh.c
# End Source File
# Begin Source File
SOURCE=..\d3d9\d3d9_rmain.c
# End Source File
# Begin Source File
SOURCE=..\d3d9\d3d9_rsurf.c
# End Source File
# Begin Source File
SOURCE=..\d3d9\d3d9quake.h
# End Source File
# Begin Source File
SOURCE=..\d3d9\vid_d3d9.c
!IF "$(CFG)" == "ftequake - Win32 Release"
!ELSEIF "$(CFG)" == "ftequake - Win32 Debug"
!ELSEIF "$(CFG)" == "ftequake - Win32 GLDebug"
!ELSEIF "$(CFG)" == "ftequake - Win32 GLRelease"
!ELSEIF "$(CFG)" == "ftequake - Win32 MDebug"
# SUBTRACT CPP /YX /Yc /Yu
!ELSEIF "$(CFG)" == "ftequake - Win32 MRelease"
!ELSEIF "$(CFG)" == "ftequake - Win32 MinGLDebug"
!ELSEIF "$(CFG)" == "ftequake - Win32 MinGLRelease"
!ELSEIF "$(CFG)" == "ftequake - Win32 Debug Dedicated Server"
!ELSEIF "$(CFG)" == "ftequake - Win32 Release Dedicated Server"
!ELSEIF "$(CFG)" == "ftequake - Win32 MinSW"
!ELSEIF "$(CFG)" == "ftequake - Win32 GLDebugQ3"
!ELSEIF "$(CFG)" == "ftequake - Win32 Debug Dedicated ServerQ3"
!ELSEIF "$(CFG)" == "ftequake - Win32 D3DDebug"
!ENDIF
# End Source File
# End Group
# Begin Source File

View File

@ -2,4 +2,6 @@ EXPORTS
NP_GetEntryPoints
NP_GetMIMEDescription
NP_Initialize
NP_Shutdown
NP_Shutdown
Plug_GetFuncs

View File

@ -28,8 +28,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEVERSION 1,0,0,2
PRODUCTVERSION 1,0,0,2
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -48,15 +48,15 @@ BEGIN
VALUE "CompanyName", "Forethought Entertainment\0"
VALUE "FileDescription", "Quake in a browser\0"
VALUE "FileExtents", "qtv|mvd\0"
VALUE "FileVersion", "1, 0, 0, 1\0"
VALUE "FileVersion", "1, 0, 0, 2\0"
VALUE "InternalName", "npqtv\0"
VALUE "LegalCopyright", "Copyright © 2009\0"
VALUE "LegalCopyright", "Copyright © 2010\0"
VALUE "LegalTrademarks", "\0"
VALUE "MIMEType", "text/x-quaketvident|application/x-multiviewdemo|application/x-fteplugin\0"
VALUE "OriginalFilename", "npqtv.dll\0"
VALUE "PrivateBuild", "\0"
VALUE "ProductName", "QTV Viewer\0"
VALUE "ProductVersion", "1, 0, 0, 1\0"
VALUE "ProductVersion", "1, 0, 0, 2\0"
VALUE "SpecialBuild", "\0"
END
END

View File

@ -1,7 +1,7 @@
#include "quakedef.h"
#define STATEFIXME
#define FORCESTATE
//#define STATEFIXME
//#define FORCESTATE
#ifdef GLQUAKE
@ -306,6 +306,7 @@ extern cvar_t r_shadow_glsl_offsetmapping;
#define checkerror()
#endif
static void BE_SendPassBlendAndDepth(unsigned int sbits);
void PPL_CreateShaderObjects(void){}
void PPL_BaseBModelTextures(entity_t *e){}
@ -440,18 +441,20 @@ void GL_TexEnv(GLenum mode)
}
}
/*OpenGL requires glDepthMask(GL_TRUE) or glClear(GL_DEPTH_BUFFER_BIT) will fail*/
void GL_ForceDepthWritable(void)
{
if (!(shaderstate.shaderbits & SBITS_MISC_DEPTHWRITE))
{
shaderstate.shaderbits |= SBITS_MISC_DEPTHWRITE;
qglDepthMask(GL_TRUE);
}
}
void GL_SetShaderState2D(qboolean is2d)
{
shaderstate.force2d = is2d;
if (!is2d)
{
qglEnable(GL_DEPTH_TEST);
shaderstate.shaderbits &= ~SBITS_MISC_NODEPTHTEST;
qglDepthMask(GL_TRUE);
shaderstate.shaderbits |= SBITS_MISC_DEPTHWRITE;
}
BE_SelectMode(BEM_STANDARD, 0);
}
void GL_SelectTexture(int target)
@ -640,10 +643,13 @@ static void RevertToKnownState(void)
checkerror();
qglColor3f(1,1,1);
shaderstate.shaderbits &= ~(SBITS_MISC_DEPTHEQUALONLY|SBITS_MISC_DEPTHCLOSERONLY|SBITS_MISC_NODEPTHTEST);
shaderstate.shaderbits |= SBITS_MISC_DEPTHWRITE;
qglDepthFunc(GL_LEQUAL);
qglDepthMask(GL_TRUE);
shaderstate.shaderbits &= ~(SBITS_MISC_DEPTHEQUALONLY|SBITS_MISC_DEPTHCLOSERONLY);
shaderstate.shaderbits |= SBITS_MISC_DEPTHWRITE;
qglEnable(GL_DEPTH_TEST);
qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
@ -892,6 +898,8 @@ void BE_Init(void)
}
}
shaderstate.shaderbits = ~0;
BE_SendPassBlendAndDepth(0);
qglEnableClientState(GL_VERTEX_ARRAY);
}
@ -1702,6 +1710,24 @@ static void BE_SendPassBlendAndDepth(unsigned int sbits)
sbits &= ~(SBITS_MISC_DEPTHWRITE|SBITS_MISC_DEPTHEQUALONLY);
sbits |= SBITS_MISC_NODEPTHTEST;
}
if (shaderstate.flags)
{
if (shaderstate.flags & BEF_FORCEADDITIVE)
sbits = (sbits & ~SBITS_ATEST_BITS) | (SBITS_SRCBLEND_ONE | SBITS_DSTBLEND_ONE);
else if (shaderstate.flags & BEF_FORCETRANSPARENT) /*if transparency is forced, clear alpha test bits*/
sbits = (sbits & ~SBITS_ATEST_BITS) | (SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA);
if (shaderstate.flags & BEF_FORCENODEPTH) /*EF_NODEPTHTEST dp extension*/
sbits |= SBITS_MISC_NODEPTHTEST;
else
{
if (shaderstate.flags & BEF_FORCEDEPTHTEST) /*if transparency is forced, clear alpha test bits*/
sbits &= ~SBITS_MISC_NODEPTHTEST;
if (shaderstate.flags & BEF_FORCEDEPTHWRITE) /*if transparency is forced, clear alpha test bits*/
sbits |= SBITS_MISC_DEPTHWRITE;
}
}
delta = sbits^shaderstate.shaderbits;
@ -1764,6 +1790,7 @@ static void BE_SendPassBlendAndDepth(unsigned int sbits)
case SBITS_ATEST_GT0:
qglEnable(GL_ALPHA_TEST);
qglAlphaFunc(GL_GREATER, 0);
break;
case SBITS_ATEST_LT128:
qglEnable(GL_ALPHA_TEST);
qglAlphaFunc(GL_LESS, 0.5f);
@ -1775,13 +1802,6 @@ static void BE_SendPassBlendAndDepth(unsigned int sbits)
}
}
if (delta & SBITS_MISC_DEPTHWRITE)
{
if (sbits & SBITS_MISC_DEPTHWRITE)
qglDepthMask(GL_TRUE);
else
qglDepthMask(GL_FALSE);
}
if (delta & SBITS_MISC_NODEPTHTEST)
{
if (sbits & SBITS_MISC_NODEPTHTEST)
@ -1789,6 +1809,13 @@ static void BE_SendPassBlendAndDepth(unsigned int sbits)
else
qglEnable(GL_DEPTH_TEST);
}
if (delta & SBITS_MISC_DEPTHWRITE)
{
if (sbits & SBITS_MISC_DEPTHWRITE)
qglDepthMask(GL_TRUE);
else
qglDepthMask(GL_FALSE);
}
if (delta & (SBITS_MISC_DEPTHEQUALONLY|SBITS_MISC_DEPTHCLOSERONLY))
{
extern int gldepthfunc;

View File

@ -565,6 +565,9 @@ void R_BloomBlend (void)//refdef_t *fd, meshlist_t *meshlist )
bs.scr_h < bs.size_sample)
return;
#pragma message("backend fixme")
Con_Printf("bloom is not updated for the backend\n");
//set up full screen workspace
qglViewport(0, 0, vid.pixelwidth, vid.pixelheight);
qglDisable(GL_DEPTH_TEST);

View File

@ -429,8 +429,6 @@ void GLDraw_Init (void)
// memset(scrap_texels, 255, sizeof(scrap_texels));
GLDraw_ReInit();
R2D_Init();
}
void GLDraw_DeInit (void)
{
@ -986,11 +984,14 @@ void MediaGL_ShowFrameRGBA_32(qbyte *framedata, int inwidth, int inheight)//top
GL_Set2D ();
PPL_RevertToKnownState();
GL_Bind(filmtexture);
GL_Upload32("", (unsigned *)framedata, inwidth, inheight, IF_NOMIPMAP|IF_NOALPHA|IF_NOGAMMA); //we may need to rescale the image
qglDisable(GL_BLEND);
qglDisable(GL_ALPHA_TEST);
qglEnable(GL_TEXTURE_2D);
qglBegin(GL_QUADS);
qglTexCoord2f(0, 0);
qglVertex2f(0, 0);

View File

@ -14,6 +14,7 @@ void Font_Free(struct font_s *f);
void Font_BeginString(struct font_s *font, int vx, int vy, int *px, int *py);
int Font_CharHeight(void);
int Font_CharWidth(unsigned int charcode);
int Font_CharEndCoord(int x, unsigned int charcode);
int Font_DrawChar(int px, int py, unsigned int charcode);
void Font_EndString(struct font_s *font);
int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int maxlines, conchar_t **starts, conchar_t **ends);
@ -270,7 +271,7 @@ void Font_Init(void)
}
//flush the font buffer, by drawing it to the screen
void Font_Flush(void)
static void Font_Flush(void)
{
if (!font_mesh.numindexes)
return;
@ -892,6 +893,28 @@ int Font_CharHeight(void)
return curfont->charheight;
}
/*
This is where the character ends.
Note: this function supports tabs - x must always be based off 0, with Font_LineDraw actually used to draw the line.
*/
int Font_CharEndCoord(int x, unsigned int charcode)
{
struct charcache_s *c;
#define TABWIDTH (8*20)
if ((charcode&CON_CHARMASK) == '\t')
return x + ((TABWIDTH - (x % TABWIDTH)) % TABWIDTH);
c = Font_GetChar(curfont, (CHARIDXTYPE)(charcode&CON_CHARMASK));
if (!c)
{
c = Font_TryLoadGlyph(curfont, (CHARIDXTYPE)(charcode&CON_CHARMASK));
if (!c)
return x+0;
}
return x+c->advance;
}
//obtains the width of a character from a given font. This is how wide it is. The next char should be drawn at x + result.
int Font_CharWidth(unsigned int charcode)
{
@ -927,7 +950,7 @@ int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int max
if ((start[l]&CON_CHARMASK) == '\n' || (start+l >= end))
break;
l++;
px += Font_CharWidth(start[l]);
px = Font_CharEndCoord(px, start[l]);
}
//if we did get to the end
if (px > maxpixelwidth)
@ -940,7 +963,6 @@ int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int max
}
if (l == 0 && bt>0)
l = bt-1;
px -= Font_CharWidth(start[l]);
}
starts[foundlines] = start;
@ -958,6 +980,25 @@ int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int max
return foundlines;
}
int Font_LineWidth(conchar_t *start, conchar_t *end)
{
int x = 0;
for (; start < end; start++)
{
x = Font_CharEndCoord(x, *start);
}
return x;
}
void Font_LineDraw(int x, int y, conchar_t *start, conchar_t *end)
{
int lx = 0;
for (; start < end; start++)
{
Font_DrawChar(x+lx, y, *start);
lx = Font_CharEndCoord(lx, *start);
}
}
/*Note: *all* strings after the current one will inherit the same colour, until one changes it explicitly
correct usage of this function thus requires calling this with 1111 before Font_EndString*/
void Font_ForceColour(float r, float g, float b, float a)
@ -976,6 +1017,11 @@ void Font_ForceColour(float r, float g, float b, float a)
/*Any drawchars that are now drawn will get the forced colour*/
}
void Font_InvalidateColour(void)
{
Font_Flush();
font_colourmask = ~0;
}
void GLDraw_FillRGB (int x, int y, int w, int h, float r, float g, float b);

View File

@ -624,6 +624,7 @@ model_t *RMod_LoadModel (model_t *mod, qboolean crash)
//Binary Map formats
#ifdef Q2BSPS
case ('F'<<0)+('B'<<8)+('S'<<16)+('P'<<24):
case ('R'<<0)+('B'<<8)+('S'<<16)+('P'<<24):
case IDBSPHEADER: //looks like id switched to have proper ids
if (!Mod_LoadQ2BrushModel (mod, buf))
@ -3147,19 +3148,19 @@ void * RMod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum,
if (!TEXVALID(texnum))
{ //the dp way
Q_strncpyz(name, loadmodel->name, sizeof(name));
Q_strncatz(name, va("_%i", framenum), sizeof(name));
Q_strncatz(name, va("_%i.tga", framenum), sizeof(name));
texnum = R_LoadReplacementTexture(name, "sprites", 0);
}
if (!TEXVALID(texnum))
{ //the older fte way.
COM_StripExtension(loadmodel->name, name, sizeof(name));
Q_strncatz(name, va("_%i", framenum), sizeof(name));
Q_strncatz(name, va("_%i.tga", framenum), sizeof(name));
texnum = R_LoadReplacementTexture(name, "sprites", 0);
}
if (!TEXVALID(texnum))
{ //the fuhquake way
COM_StripExtension(COM_SkipPath(loadmodel->name), name, sizeof(name));
Q_strncatz(name, va("_%i", framenum), sizeof(name));
Q_strncatz(name, va("_%i.tga", framenum), sizeof(name));
texnum = R_LoadReplacementTexture(name, "sprites", 0);
}
@ -3180,16 +3181,16 @@ void * RMod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum,
else
{
if (!TEXVALID(texnum))
texnum = R_LoadTexture8 (name, width, height, (qbyte *)(pinframe + 1), IF_NOMIPMAP|IF_NOALPHA|IF_NOGAMMA, 1);
texnum = R_LoadTexture8 (name, width, height, (qbyte *)(pinframe + 1), IF_NOMIPMAP|IF_NOGAMMA, 1);
}
Q_strncpyz(name, loadmodel->name, sizeof(name));
Q_strncatz(name, va("_%i", framenum), sizeof(name));
Q_strncatz(name, va("_%i.tga", framenum), sizeof(name));
pspriteframe->shader = R_RegisterShader(name,
"{\n"
"{\n"
"map $diffuse\n"
"blendfunc blend\n"
"alphafunc ge128\n"
"rgbgen entity\n"
"alphagen entity\n"
"}\n"

View File

@ -180,6 +180,8 @@ void GLR_RenderDlights (void)
if (!r_flashblend.ival)
return;
#pragma message("backend fixme")
Con_Printf("flashblends are not updated for the backend\n");
// r_dlightframecount = r_framecount + 1; // because the count hasn't
// advanced yet for this frame
@ -229,6 +231,8 @@ void GLR_RenderDlights (void)
qglEnable (GL_TEXTURE_2D);
qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
qglDepthMask (1);
PPL_RevertToKnownState();
}
#endif

View File

@ -619,7 +619,7 @@ void R_DrawSpriteModel (entity_t *e)
mesh.numvertexes = 4;
mesh.st_array = texcoords;
mesh.istrifan = true;
BE_DrawMeshChain(frame->shader, &mesh, NULL, NULL);
BE_DrawMeshChain(frame->shader, &mesh, NULL, &frame->shader->defaulttextures);
}
//==================================================================================
@ -877,9 +877,12 @@ void R_PolyBlend (void)
if (r_refdef.flags & Q2RDF_NOWORLDMODEL)
return;
#pragma message("backend fixme")
Con_Printf("polyblends are not updated for the backend\n");
GLV_CalcBlendServer(shift); //figure out the shift we need (normally just the server specified one)
//Con_Printf("R_PolyBlend(): %4.2f %4.2f %4.2f %4.2f\n",shift[0], shift[1], shift[2], shift[3]);
Con_Printf("R_PolyBlend(): %4.2f %4.2f %4.2f %4.2f\n",shift[0], shift[1], shift[2], shift[3]);
PPL_RevertToKnownState();
@ -1119,7 +1122,7 @@ void R_SetupGL (void)
fov_x = r_refdef.fov_x;//+sin(cl.time)*5;
fov_y = r_refdef.fov_y;//-sin(cl.time+1)*5;
if (r_waterwarp.value<0 && r_viewleaf->contents <= Q1CONTENTS_WATER)
if (r_waterwarp.value<0 && r_viewleaf && r_viewleaf->contents <= Q1CONTENTS_WATER)
{
fov_x *= 1 + (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
fov_y *= 1 + (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
@ -1236,6 +1239,8 @@ R_Clear
int gldepthfunc = GL_LEQUAL;
void R_Clear (void)
{
/*tbh, this entire function should be in the backend*/
GL_ForceDepthWritable();
if (r_mirroralpha.value != 1.0)
{
if (gl_clear.value && !r_secondaryview)
@ -1299,6 +1304,9 @@ void R_Mirror (void)
return;
}
#pragma message("backend fixme")
Con_Printf("mirrors are not updated for the backend\n");
r_inmirror = true;
memcpy(oldangles, r_refdef.viewangles, sizeof(vec3_t));
@ -1600,6 +1608,8 @@ static void R_RenderMotionBlur(void)
{
int vwidth = 1, vheight = 1;
float vs, vt, cs, ct;
#pragma message("backend fixme")
Con_Printf("motionblur is not updated for the backend\n");
if (gl_config.arb_texture_non_power_of_two)
{ //we can use any size, supposedly
@ -1673,6 +1683,9 @@ static void R_RenderWaterWarp(void)
PPL_RevertToKnownState();
#pragma message("backend fixme")
Con_Printf("waterwarp is not updated for the backend\n");
// get the powers of 2 for the size of the texture that will hold the scene
if (gl_config.arb_texture_non_power_of_two)
@ -1819,6 +1832,9 @@ qboolean R_RenderScene_Fish(void)
vec3_t saveang;
int rot45 = 0;
#pragma message("backend fixme")
Con_Printf("fisheye/panorama is not updated for the backend\n");
if (!scenepp_panorama_program)
return false;

View File

@ -97,7 +97,6 @@ void GLVID_Console_Resize(void)
vid.height = vid.conheight = cheight;
vid.recalc_refdef = true;
Con_CheckResize();
if (font_conchar)
Font_Free(font_conchar);

View File

@ -1420,6 +1420,7 @@ qboolean Shader_Init (void)
// COM_EnumerateFiles("scripts/*.rscript", Shader_InitCallback, NULL);
Shader_NeedReload();
Shader_DoReload();
return true;
}
@ -2857,6 +2858,7 @@ void Shader_DoReload(void)
if (!shader_reload_needed)
return;
shader_reload_needed = false;
Font_InvalidateColour();
Con_Printf("Reloading all shaders\n");
for (s = r_shaders, i = 0; i < MAX_SHADERS; i++, s++)

View File

@ -1292,10 +1292,7 @@ void GL_EndRenderBuffer_DepthOnly(texid_t depthtexture, int texsize)
static void Sh_GenShadowFace(dlight_t *l, shadowmesh_t *smesh, int face)
{
int i;
msurface_t *s;
float mvm[16], proj[16];
int ve;
int smsize = 512;
int tno, sno;

View File

@ -750,13 +750,14 @@ int GLVID_SetMode (rendererstate_t *info, unsigned char *palette)
VID_SetPalette (palette);
#ifndef NPQTV
/*I don't like this, but if we */
while (PeekMessage (&msg, mainwindow, 0, 0, PM_REMOVE))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
#endif
Sleep (100);
#endif
SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0,
SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW |

View File

@ -600,7 +600,6 @@ static void GL_DrawSkySphere (msurface_t *fa)
{ //the shader route.
meshbuffer_t mb;
gl_skyspherecalc(2);
mb.sortkey = 0;
mb.infokey = -1;
mb.dlightbits = 0;
mb.entity = &r_worldentity;

View File

@ -278,6 +278,7 @@ extern qboolean gl_mtexable;
void GL_SelectTexture (int tmunum);
void GL_SetShaderState2D(qboolean is2d);
void GL_ForceDepthWritable(void);
void R_DrawRailCore(entity_t *e);
void R_DrawLightning(entity_t *e);

View File

@ -1,3 +1,5 @@
#ifndef SHADER_H
#define SHADER_H
typedef void (shader_gen_t)(char *name, shader_t*, const void *args);
#define SHADER_PASS_MAX 8
@ -54,15 +56,8 @@ typedef struct
float args[4]; // offset, amplitude, phase_offset, rate
} shaderfunc_t;
#if _MSC_VER || __BORLANDC__
typedef unsigned __int64 msortkey_t;
#else
typedef unsigned long long msortkey_t;
#endif
typedef struct meshbuffer_s
{
msortkey_t sortkey;
int infokey; // lightmap number or mesh number
unsigned int dlightbits;
entity_t *entity;
@ -423,3 +418,4 @@ void BE_SelectDLight(dlight_t *dl, vec3_t colour);
//Returns true if the mesh is not lit by the current light
qboolean BE_LightCullModel(vec3_t org, model_t *model);
#endif
#endif

View File

@ -1,6 +1,6 @@
#include "quakedef.h"
#ifdef WEBCLIENT
#if 0//def WEBCLIENT
#include "iweb.h"
@ -28,7 +28,7 @@ typedef struct FTPclientconn_s{
struct FTPclientconn_s *next;
void (*NotifyFunction)(char *localfile, qboolean sucess); //called when failed or succeeded, and only if it got a connection in the first place.
void (*NotifyFunction)(vfsfile_t *file, char *localfile, qboolean sucess); //called when failed or succeeded, and only if it got a connection in the first place.
//ftp doesn't guarentee it for anything other than getting though. :(
} FTPclientconn_t;

View File

@ -1,18 +1,18 @@
#include "quakedef.h"
#ifdef WEBCLIENT
#include "iweb.h"
#include "netinc.h"
#ifdef WEBCLIENT
/*
This file does one thing. Connects to servers and grabs the specified file. It doesn't do any uploading whatsoever. Live with it.
It doesn't use persistant connections.
*/
qboolean HTTP_CL_Get(char *url, char *localfile, void (*NotifyFunction)(char *localfile, qboolean sucess));
#if 0
typedef struct http_con_s {
int sock;
@ -20,7 +20,10 @@ typedef struct http_con_s {
enum {HC_REQUESTING,HC_GETTINGHEADER, HC_GETTING} state;
char *buffer;
char filename[MAX_QPATH];
vfsfile_t *file;
int bufferused;
int bufferlen;
@ -32,8 +35,6 @@ typedef struct http_con_s {
int contentlength;
vfsfile_t *file;
void (*NotifyFunction)(char *localfile, qboolean sucess); //called when failed or succeeded, and only if it got a connection in the first place.
struct http_con_s *next;
} http_con_t;
@ -186,11 +187,14 @@ static qboolean HTTP_CL_Run(http_con_t *con)
con->bufferused -= ammount;
con->file = FS_OpenVFS(con->filename, "wb", FS_GAME);
if (!con->file)
{
Con_Printf("HTTP: Couldn't open file %s\n", con->filename);
return false;
con->file = FS_OpenVFS(con->filename, "wb", FS_GAME);
if (!con->file)
{
Con_Printf("HTTP: Couldn't open file %s\n", con->filename);
return false;
}
}
if (!con->file)
@ -299,9 +303,7 @@ static qboolean HTTP_CL_Run(http_con_t *con)
else if (con->bufferused != con->contentlength && !con->file)
Con_Printf("Recieved file isn't the correct length - must be corrupt - %s\n", con->filename);
Con_Printf("Retrieved %s\n", con->filename);
if (con->file)
VFS_CLOSE(con->file);
else
if (!con->file && *con->filename)
{
FS_WriteFile(con->filename, con->buffer, con->bufferused, FS_GAME);
}
@ -513,3 +515,588 @@ qboolean HTTP_CL_Get(char *url, char *localfile, void (*NotifyFunction)(char *lo
}
#endif
struct http_dl_ctx_s {
struct dl_download *dlctx;
SOCKET sock;
char *buffer;
int bufferused;
int bufferlen;
int totalreceived; //useful when we're just dumping to a file.
qboolean chunking;
int chunksize;
int chunked;
enum {HC_REQUESTING,HC_GETTINGHEADER, HC_GETTING} state;
int contentlength;
};
void HTTP_Cleanup(struct dl_download *dl)
{
struct http_dl_ctx_s *con = dl->ctx;
dl->ctx = NULL;
if (con->sock != INVALID_SOCKET)
closesocket(con->sock);
con->sock = INVALID_SOCKET;
free(con->buffer);
free(con);
dl->status = DL_PENDING;
dl->completed = 0;
dl->totalsize = 0;
}
static void ExpandBuffer(struct http_dl_ctx_s *con, int quant)
{
int newlen;
newlen = con->bufferlen + quant;
con->buffer = realloc(con->buffer, newlen);
con->bufferlen = newlen;
}
static qboolean HTTP_DL_Work(struct dl_download *dl)
{
struct http_dl_ctx_s *con = dl->ctx;
char buffer[256];
char Location[256];
char *nl;
char *msg;
int ammount;
switch(con->state)
{
case HC_REQUESTING:
ammount = send(con->sock, con->buffer, con->bufferused, 0);
if (!ammount)
return false;
if (ammount < 0)
{
if (qerrno != EWOULDBLOCK)
return false;
return true;
}
con->bufferused -= ammount;
memmove(con->buffer, con->buffer+ammount, con->bufferused);
if (!con->bufferused) //that's it, all sent.
con->state = HC_GETTINGHEADER;
break;
case HC_GETTINGHEADER:
if (con->bufferlen - con->bufferused < 1530)
ExpandBuffer(con, 1530);
ammount = recv(con->sock, con->buffer+con->bufferused, con->bufferlen-con->bufferused-15, 0);
if (!ammount)
return false;
if (ammount < 0)
{
if (qerrno != EWOULDBLOCK)
return false;
return true;
}
con->bufferused+=ammount;
con->buffer[con->bufferused] = '\0';
//have we got the entire thing yet?
msg = con->buffer;
con->chunking = false;
if (strnicmp(msg, "HTTP/", 5))
{ //pre version 1. (lame servers.
con->state = HC_GETTING;
con->contentlength = -1; //meaning end of stream.
}
else
{
while(*msg)
{
if (*msg == '\n')
{
if (msg[1] == '\n')
{ //tut tut, not '\r'? that's not really allowed...
msg+=1;
break;
}
if (msg[2] == '\n')
{
msg+=2;
break;
}
}
msg++;
if (!strnicmp(msg, "Content-Length: ", 16))
con->contentlength = atoi(msg+16);
else if (!strnicmp(msg, "Location: ", 10))
{
nl = strchr(msg, '\n');
if (nl)
{
*nl = '\0';
Q_strncpyz(Location, COM_TrimString(msg+10), sizeof(Location));
*nl = '\n';
}
}
else if (!strnicmp(msg, "Transfer-Encoding: ", 19))
{
char *chunk = strstr(msg, "chunked");
nl = strchr(msg, '\n');
if (nl)
if (chunk < nl)
con->chunking = true;
}
}
if (!*msg)
break;//switch
msg++;
ammount = msg - con->buffer;
msg = COM_ParseOut(con->buffer, buffer, sizeof(buffer));
msg = COM_ParseOut(msg, buffer, sizeof(buffer));
if (!stricmp(buffer, "100"))
{ //http/1.1 servers can give this. We ignore it.
con->bufferused -= ammount;
memmove(con->buffer, con->buffer+ammount, con->bufferused);
return true;
}
if (!stricmp(buffer, "301") || !stricmp(buffer, "302") || !stricmp(buffer, "303"))
{
nl = strchr(msg, '\n');
if (nl)
*nl = '\0';
Con_Printf("HTTP: %s %s\n", buffer, COM_TrimString(msg));
if (!*Location)
Con_Printf("Server redirected to null location\n");
else
{
HTTP_Cleanup(dl);
Q_strncpyz(dl->redir, Location, sizeof(dl->redir));
}
return true;
}
if (stricmp(buffer, "200"))
{
nl = strchr(msg, '\n');
if (!nl)
return false; //eh?
if (nl>msg&&nl[-1] == '\r')
nl--;
*nl = '\0';
Con_Printf("HTTP: %s%s\n", buffer, msg);
return false; //something went wrong.
}
con->bufferused -= ammount;
if (!dl->file)
{
dl->file = FS_OpenVFS(dl->localname, "wb", FS_GAME);
if (!dl->file)
{
Con_Printf("HTTP: Couldn't open file %s\n", dl->localname);
return false;
}
}
if (!dl->file)
{
VFS_WRITE(dl->file, con->buffer+ammount, con->bufferused);
con->bufferused = 0;
}
else
memmove(con->buffer, con->buffer+ammount, con->bufferused);
con->state = HC_GETTING;
}
//Fall through
case HC_GETTING:
if (con->bufferlen - con->bufferused < 1530)
ExpandBuffer(con, 1530);
ammount = recv(con->sock, con->buffer+con->bufferused, con->bufferlen-con->bufferused-1, 0);
if (ammount < 0)
{
if (qerrno != EWOULDBLOCK)
return false;
return true;
}
con->bufferused+=ammount;
if (con->chunking) //FIXME: NEEDS TESTING!!!
{
int trim;
char *nl;
con->buffer[con->bufferused] = '\0';
for(;;)
{ //work out as we go.
if (con->chunksize)//we are trying to parse a chunk.
{
trim = con->bufferused - con->chunked;
if (trim > con->chunksize)
trim = con->chunksize; //don't go into the next size field.
con->chunksize -= trim;
con->chunked += trim;
if (!con->chunksize)
{ //we need to find the next \n and trim it.
nl = strchr(con->buffer+con->chunked, '\n');
if (!nl)
break;
nl++;
trim = nl - (con->buffer+con->chunked);
memmove(con->buffer + con->chunked, nl, con->buffer+con->bufferused-nl+1);
con->bufferused -= trim;
}
if (!(con->bufferused - con->chunked))
break;
}
else
{
nl = strchr(con->buffer+con->chunked, '\n');
if (!nl)
break;
con->chunksize = strtol(con->buffer+con->chunked, NULL, 16); //it's hex.
nl++;
trim = nl - (con->buffer+con->chunked);
memmove(con->buffer + con->chunked, nl, con->buffer+con->bufferused-nl+1);
con->bufferused -= trim;
}
}
con->totalreceived+=con->chunked;
if (dl->file && con->chunked) //we've got a chunk in the buffer
{ //write it
if (VFS_WRITE(dl->file, con->buffer, con->chunked) != con->chunked)
{
Con_Printf("Write error whilst downloading %s\nDisk full?\n", dl->localname);
return false;
}
//and move the unparsed chunk to the front.
con->bufferused -= con->chunked;
memmove(con->buffer, con->buffer+con->chunked, con->bufferused);
con->chunked = 0;
}
}
else
{
con->totalreceived+=ammount;
if (dl->file) //we've got a chunk in the buffer
{ //write it
if (VFS_WRITE(dl->file, con->buffer, con->bufferused) != con->bufferused)
{
Con_Printf("Write error whilst downloading %s\nDisk full?\n", dl->localname);
return false;
}
con->bufferused = 0;
}
}
if (!ammount)
{ //server closed off the connection.
if (con->chunksize)
dl->status = DL_FAILED;
else
dl->status = DL_FINISHED;
return false;
}
break;
}
return true;
}
void HTTPDL_Establish(struct dl_download *dl)
{
unsigned long _true = true;
struct sockaddr_qstorage from;
struct http_dl_ctx_s *con;
char server[128];
char uri[MAX_OSPATH];
char *slash;
const char *url = dl->redir;
if (!*url)
url = dl->url;
if (!strnicmp(url, "http://", 7))
url+=7;
slash = strchr(url, '/');
if (!slash)
{
Q_strncpyz(server, url, sizeof(server));
Q_strncpyz(uri, "/", sizeof(uri));
}
else
{
Q_strncpyz(uri, slash, sizeof(uri));
Q_strncpyz(server, url, sizeof(server));
server[slash-url] = '\0';
}
con = malloc(sizeof(*con));
memset(con, 0, sizeof(*con));
dl->ctx = con;
if ((con->sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
{
dl->status = DL_FAILED;
return;
}
dl->status = DL_RESOLVING;
{//quake routines using dns and stuff (Really, I wanna keep quake and ftp fairly seperate)
netadr_t qaddy;
if (!NET_StringToAdr (server, &qaddy))
{
dl->status = DL_FAILED;
return;
}
if (!qaddy.port)
qaddy.port = htons(80);
NetadrToSockadr(&qaddy, &from);
}//end of quake.
dl->status = DL_QUERY;
//not yet blocking.
if (connect(con->sock, (struct sockaddr *)&from, sizeof(struct sockaddr_in)) == -1)
{
dl->status = DL_FAILED;
return;
}
if (ioctlsocket (con->sock, FIONBIO, &_true) == -1) //now make it non blocking.
{
dl->status = DL_FAILED;
return;
}
ExpandBuffer(con, 2048);
sprintf(con->buffer, "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "Connection: close\r\n" "User-Agent: "FULLENGINENAME"\r\n" "\r\n", uri, server);
con->bufferused = strlen(con->buffer);
con->contentlength = -1;
if (!*dl->localname)
{
Q_strncpyz(dl->localname, url+1, sizeof(dl->localname));
slash = strchr(dl->localname, '?');
if (slash)
*slash = '_';
}
}
qboolean HTTPDL_Poll(struct dl_download *dl)
{
/*failed previously*/
if (dl->status == DL_FAILED)
return false;
if (!dl->ctx)
{
HTTPDL_Establish(dl);
if (dl->status == DL_FAILED)
{
HTTP_Cleanup(dl);
return false;
}
}
if (dl->ctx)
{
HTTP_DL_Work(dl);
if (dl->status == DL_FAILED)
{
HTTP_Cleanup(dl);
return false;
}
if (dl->status == DL_FINISHED)
return false;
}
return true;
}
qboolean HTTPDL_Decide(struct dl_download *dl)
{
const char *url = dl->redir;
if (!*url)
url = dl->url;
if (!strnicmp(url, "http://", 7))
dl->poll = HTTPDL_Poll;
else
{
dl->status = DL_FAILED;
return false;
}
return true;
}
#ifdef MULTITHREAD
static int DL_Thread_Work(void *arg)
{
struct dl_download *dl = arg;
while (!dl->threaddie)
{
if (!dl->poll(dl))
{
if (dl->notify)
dl->notify(dl);
if (dl->file)
VFS_CLOSE(dl->file);
break;
}
}
return 0;
}
/*create a thread to perform the given download
to use: call DL_Create (not HTTP_CL_Get!) to get a context, then call this.
note that you need to call DL_Close from another thread, NOT IN THE NOTIFY FUNC.
the file handle must be safe to write to in threads.
*/
qboolean DL_CreateThread(struct dl_download *dl, vfsfile_t *file, void (*NotifyFunction)(struct dl_download *dl))
{
if (!dl)
return false;
dl->file = file;
dl->notify = NotifyFunction;
dl->threadctx = Sys_CreateThread(DL_Thread_Work, dl, 0);
if (!dl->threadctx)
return false;
return true;
}
#endif
/*create a standalone download context*/
struct dl_download *DL_Create(const char *url)
{
struct dl_download *newdl;
newdl = malloc(sizeof(*newdl));
if (!newdl)
return NULL;
memset(newdl, 0, sizeof(*newdl));
Q_strncpyz(newdl->url, url, sizeof(newdl->url));
newdl->poll = HTTPDL_Decide;
return newdl;
}
/*destroys an entire download context*/
void DL_Close(struct dl_download *dl)
{
#ifdef MULTITHREAD
dl->threaddie = true;
if (dl->threadctx)
Sys_WaitOnThread(dl->threadctx);
#endif
if (dl->abort)
dl->abort(dl);
if (dl->file)
VFS_CLOSE(dl->file);
free(dl);
}
static struct dl_download *activedownloads;
/*create a download context and add it to the list, for lazy people*/
struct dl_download *HTTP_CL_Get(const char *url, const char *localfile, void (*NotifyFunction)(struct dl_download *dl))
{
struct dl_download *newdl = DL_Create(url);
if (!newdl)
return newdl;
newdl->notify = NotifyFunction;
Q_strncpyz(newdl->localname, localfile, sizeof(newdl->localname));
newdl->next = activedownloads;
activedownloads = newdl;
return newdl;
}
/*updates pending downloads*/
void HTTP_CL_Think(void)
{
struct dl_download *con = activedownloads;
struct dl_download **link = NULL;
link = &activedownloads;
while (*link)
{
con = *link;
if (!con->poll(con))
{
if (con->notify)
con->notify(con);
*link = con->next;
DL_Close(con);
continue;
}
link = &con->next;
if (!cls.downloadmethod)
{
cls.downloadmethod = DL_HTTP;
strcpy(cls.downloadlocalname, con->localname);
strcpy(cls.downloadremotename, con->localname);
}
if (cls.downloadmethod == DL_HTTP)
{
if (!strcmp(cls.downloadlocalname, con->localname))
{
if (con->status == DL_FINISHED)
cls.downloadpercent = 100;
else if (con->status != DL_ACTIVE)
cls.downloadpercent = 0;
else if (con->totalsize <= 0)
cls.downloadpercent = 50;
else
cls.downloadpercent = con->completed*100.0f/con->totalsize;
}
}
}
}
#endif /*WEBCLIENT*/

View File

@ -1,8 +1,7 @@
#ifdef WEBSERVER
#ifndef IWEB_H__
#define IWEB_H__
#ifdef WEBSERVER
#ifdef WEBSVONLY
@ -91,22 +90,68 @@ iwboolean FTP_StringToAdr (const char *s, qbyte ip[4], qbyte port[2]);
iwboolean FTP_ServerRun(iwboolean ftpserverwanted, int port);
qboolean HTTP_ServerPoll(qboolean httpserverwanted, int port);
void HTTP_CL_Think(void);
qboolean HTTP_CL_Get(char *url, char *localfile, void (*NotifyFunction)(char *localfile, qboolean sucess));
//server interface called from main server routines.
void IWebInit(void);
void IWebRun(void);
void IWebShutdown(void);
qboolean FTP_Client_Command (char *cmd, void (*NotifyFunction)(char *localfile, qboolean sucess));
/*
qboolean FTP_Client_Command (char *cmd, void (*NotifyFunction)(vfsfile_t *file, char *localfile, qboolean sucess));
void IRC_Command(char *imsg);
void FTP_ClientThink (void);
void IRC_Frame(void);
*/
qboolean SV_POP3(qboolean activewanted);
qboolean SV_SMTP(qboolean activewanted);
#endif
#if 1
struct dl_download
{
/*not used by anything in the download itself, useful for context*/
unsigned int user_num;
void *user_ctx;
/*not used internally by the backend, but used by HTTP_CL_Get/thread wrapper*/
struct dl_download *next;
void (*notify) (struct dl_download *dl);
/*stream config*/
char url[MAX_OSPATH]; /*original url*/
char redir[MAX_OSPATH]; /*current redirected url*/
char localname[MAX_OSPATH];
struct vfsfile_s *file; /*downloaded to, when starting will open localname if not already set*/
qboolean (*poll) (struct dl_download *);
/*stream status*/
enum
{
DL_PENDING, /*not started*/
DL_FAILED, /*gave up*/
DL_RESOLVING, /*resolving the host*/
DL_QUERY, /*sent query, waiting for response*/
DL_ACTIVE, /*receiving data*/
DL_FINISHED /*its complete*/
} status;
unsigned int totalsize; /*max size (can be 0 for unknown)*/
unsigned int completed; /*ammount correctly received so far*/
/*internals*/
#ifdef MULTITHREAD
qboolean threaddie;
void *threadctx;
#endif
void *ctx; /*internal context, depending on http/ftp/etc protocol*/
void (*abort) (struct dl_download *); /*cleans up the internal context*/
};
void HTTP_CL_Think(void);
struct dl_download *HTTP_CL_Get(const char *url, const char *localfile, void (*NotifyFunction)(struct dl_download *dl));
struct dl_download *DL_Create(const char *url);
qboolean DL_CreateThread(struct dl_download *dl, vfsfile_t *file, void (*NotifyFunction)(struct dl_download *dl));
void DL_Close(struct dl_download *dl);
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -260,7 +260,7 @@ void SV_Shutdown (void)
SV_MVDStop_f();
NET_Shutdown ();
#ifdef IWEB_H__
#ifdef WEBSERVER
IWebShutdown();
#endif
@ -3334,7 +3334,7 @@ void SV_Frame (void)
}
#ifdef IWEB_H__
#ifdef WEBSERVER
IWebRun();
#endif
@ -4381,7 +4381,7 @@ void SV_Init (quakeparms_t *parms)
SV_InitLocal ();
#ifdef IWEB_H__
#ifdef WEBSERVER
IWebInit();
#endif

View File

@ -728,7 +728,7 @@ qboolean SVQ2_InitGameProgs(void)
if (ge->apiversion != Q2GAME_API_VERSION)
{
Con_Printf("game is version %i, not %i", ge->apiversion, Q2GAME_API_VERSION);
SVQ2_ShutdownGameProgs();
Sys_UnloadGame ();
return false;
}

View File

@ -1,4 +1,7 @@
#include "qwsvdef.h"
#ifdef SQL
#include "win_mysql.h"
MYSQLDLL_FUNC1(my_ulonglong, mysql_affected_rows, MYSQL *)
@ -91,4 +94,5 @@ int mysql_dll_close()
FreeLibrary(mysqldll);
return 1;
}
}
#endif

View File

@ -256,11 +256,17 @@ void Cluster_Run(cluster_t *cluster, qboolean dowait)
FD_ZERO(&socketset);
m = 0;
if (cluster->qwdsocket != INVALID_SOCKET)
if (cluster->qwdsocket[0] != INVALID_SOCKET)
{
FD_SET(cluster->qwdsocket, &socketset);
if (cluster->qwdsocket >= m)
m = cluster->qwdsocket+1;
FD_SET(cluster->qwdsocket[0], &socketset);
if (cluster->qwdsocket[0] >= m)
m = cluster->qwdsocket[0]+1;
}
if (cluster->qwdsocket[1] != INVALID_SOCKET)
{
FD_SET(cluster->qwdsocket[1], &socketset);
if (cluster->qwdsocket[1] >= m)
m = cluster->qwdsocket[1]+1;
}
for (sv = cluster->servers; sv; sv = sv->next)
@ -375,7 +381,8 @@ void Cluster_Run(cluster_t *cluster, qboolean dowait)
QTV_Run(old);
}
SV_FindProxies(cluster->tcpsocket, cluster, NULL); //look for any other proxies wanting to muscle in on the action.
SV_FindProxies(cluster->tcpsocket[0], cluster, NULL); //look for any other proxies wanting to muscle in on the action.
SV_FindProxies(cluster->tcpsocket[1], cluster, NULL); //look for any other proxies wanting to muscle in on the action.
QW_UpdateUDPStuff(cluster);
@ -489,7 +496,7 @@ int main(int argc, char **argv)
#ifdef _WIN32
{
WSADATA discard;
WSAStartup(MAKEWORD(2,0), &discard);
WSAStartup(MAKEWORD(1,1), &discard);
}
#endif
@ -498,15 +505,16 @@ int main(int argc, char **argv)
{
memset(cluster, 0, sizeof(*cluster));
cluster->qwdsocket = INVALID_SOCKET;
cluster->tcpsocket = INVALID_SOCKET;
cluster->qwdsocket[0] = INVALID_SOCKET;
cluster->qwdsocket[1] = INVALID_SOCKET;
cluster->tcpsocket[0] = INVALID_SOCKET;
cluster->tcpsocket[1] = INVALID_SOCKET;
cluster->qwlistenportnum = 0;
cluster->allownqclients = true;
strcpy(cluster->hostname, DEFAULT_HOSTNAME);
cluster->buildnumber = build_number();
cluster->maxproxies = -1;
strcpy(cluster->downloaddir, "id1/");
strcpy(cluster->demodir, "qw/demos/");
Sys_Printf(cluster, "QTV Build %i.\n", cluster->buildnumber);
@ -515,17 +523,25 @@ int main(int argc, char **argv)
if (!cluster->numservers)
{ //probably running on a home user's computer
if (cluster->qwdsocket == INVALID_SOCKET && !cluster->qwlistenportnum)
{
cluster->qwdsocket = QW_InitUDPSocket(cluster->qwlistenportnum = 27599);
if (cluster->qwdsocket != INVALID_SOCKET)
Sys_Printf(cluster, "opened udp port %i\n", cluster->qwlistenportnum);
if (cluster->qwdsocket[0] == INVALID_SOCKET && cluster->qwdsocket[1] == INVALID_SOCKET && !cluster->qwlistenportnum)
{
cluster->qwlistenportnum = 27599;
cluster->qwdsocket[1] = QW_InitUDPSocket(cluster->qwlistenportnum, true);
if (cluster->qwdsocket[1] != INVALID_SOCKET)
Sys_Printf(cluster, "opened udp6 port %i\n", cluster->qwlistenportnum);
cluster->qwdsocket[0] = QW_InitUDPSocket(cluster->qwlistenportnum, false);
if (cluster->qwdsocket[0] != INVALID_SOCKET)
Sys_Printf(cluster, "opened udp4 port %i\n", cluster->qwlistenportnum);
}
if (cluster->tcpsocket == INVALID_SOCKET && !cluster->tcplistenportnum)
{
cluster->tcpsocket = Net_MVDListen(cluster->tcplistenportnum = 27599);
if (cluster->tcpsocket != INVALID_SOCKET)
Sys_Printf(cluster, "opened tcp port %i\n", cluster->tcplistenportnum);
if (cluster->tcpsocket[0] == INVALID_SOCKET && cluster->tcpsocket[1] == INVALID_SOCKET && !cluster->tcplistenportnum)
{
cluster->tcplistenportnum = 27599;
cluster->tcpsocket[1] = Net_TCPListen(cluster->tcplistenportnum, true);
if (cluster->tcpsocket[1] != INVALID_SOCKET)
Sys_Printf(cluster, "opened tcp6 port %i\n", cluster->tcplistenportnum);
cluster->tcpsocket[0] = Net_TCPListen(cluster->tcplistenportnum, false);
if (cluster->tcpsocket[0] != INVALID_SOCKET)
Sys_Printf(cluster, "opened tcp4 port %i\n", cluster->tcplistenportnum);
}
Sys_Printf(cluster, "\n"

View File

@ -67,6 +67,8 @@ void SV_FindProxies(SOCKET sock, cluster_t *cluster, sv_t *defaultqtv)
unsigned long nonblocking = true;
oproxy_t *prox;
if (sock == INVALID_SOCKET)
return;
sock = accept(sock, NULL, NULL);
if (sock == INVALID_SOCKET)
return;
@ -797,7 +799,7 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
//FIXME: does this work?
#if 0 //left disabled until properly tested
qtv = QTV_NewServerConnection(cluster, "reverse"/*server*/, "", true, 2, false, 0);
qtv = QTV_NewServerConnection(cluster, "reverse"/*server*/, "", true, AD_REVERSECONNECT, false, 0);
Net_ProxySendString(cluster, pend, QTVSVHEADER);
Net_ProxySendString(cluster, pend, "REVERSED\n");
@ -826,7 +828,7 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
sv_t *suitable = NULL; //shush noisy compilers
for (qtv = cluster->servers; qtv; qtv = qtv->next)
{
if (!qtv->disconnectwhennooneiswatching)
if (qtv->autodisconnect == AD_NO)
{
suitable = qtv;
numfound++;
@ -901,7 +903,7 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
if (*t < '0' || *t > '9')
break;
if (*t)
qtv = QTV_NewServerConnection(cluster, 0, colon, "", false, true, true, false);
qtv = QTV_NewServerConnection(cluster, 0, colon, "", false, AD_WHENEMPTY, true, false);
else
{
//numerical source, use a stream id.
@ -915,7 +917,7 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
char buf[256];
snprintf(buf, sizeof(buf), "demo:%s", colon);
qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, true, true, false);
qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, AD_WHENEMPTY, true, false);
if (!qtv)
{
Net_ProxySendString(cluster, pend, QTVSVHEADER

View File

@ -2,8 +2,6 @@
//main reason to use connection close is because we're lazy and don't want to give sizes in advance (yes, we could use chunks..)
//#define ALLOWDOWNLOADS
@ -131,11 +129,19 @@ static void HTTPSV_SendHTTPHeader(cluster_t *cluster, oproxy_t *dest, char *erro
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
}
static void HTTPSV_SendHTMLHeader(cluster_t *cluster, oproxy_t *dest, char *title)
static void HTTPSV_SendHTMLHeader(cluster_t *cluster, oproxy_t *dest, char *title, char *args)
{
char *s;
char buffer[2048];
qboolean plugin = false;
while (*args && *args != ' ')
{
if (*args == 'p')
plugin = true;
args++;
}
s = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
"<html>\n"
"<head>\n"
@ -144,10 +150,10 @@ static void HTTPSV_SendHTMLHeader(cluster_t *cluster, oproxy_t *dest, char *titl
" <link rel=\"StyleSheet\" href=\"/style.css\" type=\"text/css\" />\n"
"</head>\n"
"<body><div id=\"navigation\"><ul>"
"<li><a href=\"/nowplaying/\">Live</a></li><li><a href=\"/demos/\">Demos</a></li><li><a href=\"/admin/\">Admin</a></li>"
"<li><a href=\"/nowplaying.html%s\">Live</a></li><li><a href=\"/demos.html%s\">Demos</a></li><li><a href=\"/admin.html%s\">Admin</a></li>"
"</ul></div>";
snprintf(buffer, sizeof(buffer), s, title);
snprintf(buffer, sizeof(buffer), s, title, plugin?"?p":"", plugin?"?p":"", plugin?"?p":"");
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
}
@ -157,7 +163,7 @@ static void HTTPSV_SendHTMLFooter(cluster_t *cluster, oproxy_t *dest)
char *s;
char buffer[2048];
snprintf(buffer, sizeof(buffer), "<br/>QTV Version: %i <a href=\"http://www.fteqw.com\">www.fteqw.com</a><br />", cluster->buildnumber);
snprintf(buffer, sizeof(buffer), "<br/>QTV Version: %i <a href=\"http://www.fteqw.com\" target=\"_blank\">www.fteqw.com</a><br />", cluster->buildnumber);
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
s = "</body>\n"
@ -167,35 +173,76 @@ static void HTTPSV_SendHTMLFooter(cluster_t *cluster, oproxy_t *dest)
#define HTMLPRINT(str) Net_ProxySend(cluster, dest, str "\n", strlen(str "\n"))
static void HTTPSV_GenerateNowPlaying(cluster_t *cluster, oproxy_t *dest)
static void HTTPSV_GenerateNowPlaying(cluster_t *cluster, oproxy_t *dest, char *args)
{
int player;
char *s;
char buffer[1024];
char plname[64];
sv_t *streams;
qboolean plugin = false;
qboolean activeonly = false;
HTTPSV_SendHTTPHeader(cluster, dest, "200", "text/html", true);
HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Now Playing");
HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Now Playing", args);
while (*args && *args != ' ')
{
if (*args == 'p')
plugin = true;
else if (*args == 'a')
activeonly = true;
args++;
}
s =
"<script><!--\n"
"function joinserver(d)\n"
"{\n"
"parent.getplug().server = d;\n"
"parent.getplug().running = 1;\n"
"}\n"
"//--></script>"
;
Net_ProxySend(cluster, dest, s, strlen(s));
if (!strcmp(cluster->hostname, DEFAULT_HOSTNAME))
snprintf(buffer, sizeof(buffer), "<h1>QuakeTV: Now Playing</h1>"); //don't show the hostname if its set to the default
else
snprintf(buffer, sizeof(buffer), "<h1>QuakeTV: Now Playing on %s</h1>", cluster->hostname);
snprintf(buffer, sizeof(buffer), "<h1>%s: Now Playing</h1>", cluster->hostname);
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
HTMLPRINT("<dl class=\"nowplaying\">");
for (streams = cluster->servers; streams; streams = streams->next)
{
if (activeonly)
{
for (player = 0; player < MAX_CLIENTS; player++)
{
if (streams->isconnected && streams->map.thisplayer == player)
continue;
if (*streams->map.players[player].userinfo)
{
break;
}
}
if (player == MAX_CLIENTS)
continue;
}
HTMLPRINT("<dt>");
HTMLprintf(buffer, sizeof(buffer), "%s (%s: %s)", streams->server, streams->map.gamedir, streams->map.mapname);
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
snprintf(buffer, sizeof(buffer), "<span class=\"qtvfile\"> [ <a href=\"/watch.qtv?sid=%i\">Watch Now</a> ]</span>", streams->streamid);
if (plugin && !strncmp(streams->server, "udp:", 4))
snprintf(buffer, sizeof(buffer), "<span class=\"qtvfile\"> [ <a href=\"javascript:joinserver('%s')\">Join</a> ]</span>", streams->server+4);
else
snprintf(buffer, sizeof(buffer), "<span class=\"qtvfile\"> [ <a href=\"/watch.qtv?sid=%i\">Watch Now</a> ]</span>", streams->streamid);
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
HTMLPRINT("</dt><dd><ul class=\"playerslist\">");
for (player = 0; player < MAX_CLIENTS; player++)
{
if (streams->isconnected && streams->map.thisplayer == player)
continue;
if (*streams->map.players[player].userinfo)
{
Info_ValueForKey(streams->map.players[player].userinfo, "name", plname, sizeof(plname));
@ -243,6 +290,8 @@ static void HTTPSV_GenerateCSSFile(cluster_t *cluster, oproxy_t *dest)
HTMLPRINT("dl.nowplaying ul { margin: 0 0 0 1em; padding: 0; }");
HTMLPRINT("#navigation { background-color: #eef; }");
HTMLPRINT("#navigation li { display: inline; list-style: none; margin: 0 3em; }");
HTMLPRINT("div.optdiv { margin: 0px 0px 0px 0px; position: fixed; left: 0%; width: 50%; top: 0%; height: 100%; }");
HTMLPRINT("div.plugdiv { margin: 0px 0px 0px 0px; position: fixed; left: 50%; width: 50%; top: 0%; height: 100%; }");
}
static qboolean HTTPSV_GetHeaderField(char *s, char *field, char *buffer, int buffersize)
@ -304,7 +353,7 @@ static qboolean HTTPSV_GetHeaderField(char *s, char *field, char *buffer, int bu
static void HTTPSV_GenerateQTVStub(cluster_t *cluster, oproxy_t *dest, char *streamtype, char *streamid)
{
char *s;
char hostname[64];
char hostname[128];
char buffer[1024];
@ -361,7 +410,7 @@ static void HTTPSV_GenerateQTVStub(cluster_t *cluster, oproxy_t *dest, char *str
if (!HTTPSV_GetHeaderField((char*)dest->inbuffer, "Host", hostname, sizeof(hostname)))
{
HTTPSV_SendHTTPHeader(cluster, dest, "400", "text/html", true);
HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Error");
HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Error", "");
s = "Your client did not send a Host field, which is required in HTTP/1.1\n<BR />"
"Please try a different browser.\n"
@ -371,14 +420,17 @@ static void HTTPSV_GenerateQTVStub(cluster_t *cluster, oproxy_t *dest, char *str
Net_ProxySend(cluster, dest, s, strlen(s));
return;
}
/*if there's a port number on there, strip it*/
if (strchr(hostname, ':'))
*strchr(hostname, ':') = 0;
HTTPSV_SendHTTPHeader(cluster, dest, "200", "text/x-quaketvident", false);
snprintf(buffer, sizeof(buffer), "[QTV]\r\n"
"Stream: %s%s@%s\r\n"
"Stream: %s%s@%s:%i\r\n"
"",
//5, 256, 64. snprintf is not required, but paranoia is a wonderful thing.
streamtype, streamid, hostname);
streamtype, streamid, hostname, cluster->tcplistenportnum);
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
@ -487,7 +539,7 @@ static char *HTTPSV_ParsePOST(char *post, char *buffer, int buffersize)
return post;
}
static void HTTPSV_GenerateAdmin(cluster_t *cluster, oproxy_t *dest, int streamid, char *postbody)
static void HTTPSV_GenerateAdmin(cluster_t *cluster, oproxy_t *dest, int streamid, char *postbody, char *args)
{
char pwd[64];
char cmd[256];
@ -499,7 +551,7 @@ static void HTTPSV_GenerateAdmin(cluster_t *cluster, oproxy_t *dest, int streami
if (!*cluster->adminpassword)
{
HTTPSV_SendHTTPHeader(cluster, dest, "403", "text/html", true);
HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Admin Error");
HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Admin Error", args);
s = "The admin password is disabled. You may not log in remotely.</body></html>\n";
Net_ProxySend(cluster, dest, s, strlen(s));
@ -560,7 +612,7 @@ static void HTTPSV_GenerateAdmin(cluster_t *cluster, oproxy_t *dest, int streami
}
HTTPSV_SendHTTPHeader(cluster, dest, "200", "text/html", true);
HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Admin");
HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Admin", args);
s = "<H1>QuakeTV Admin: ";
Net_ProxySend(cluster, dest, s, strlen(s));
@ -608,14 +660,35 @@ static void HTTPSV_GenerateAdmin(cluster_t *cluster, oproxy_t *dest, int streami
HTTPSV_SendHTMLFooter(cluster, dest);
}
static void HTTPSV_GenerateDemoListing(cluster_t *cluster, oproxy_t *dest)
static void HTTPSV_GenerateDemoListing(cluster_t *cluster, oproxy_t *dest, char *args)
{
int i;
char link[256];
char *s;
qboolean plugframe = false;
for (s=args; *s && *s != ' ';)
{
if (*s == 'p')
plugframe = true;
s++;
}
HTTPSV_SendHTTPHeader(cluster, dest, "200", "text/html", true);
HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Demos");
HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Demos", args);
if (plugframe)
{
s =
"<script><!--\n"
"function playdemo(d)\n"
"{\n"
"parent.getplug().stream = \"file:\"+d+\"@\"+parent.host;\n"
"parent.getplug().running = 1;\n"
"}\n"
"//--></script>"
;
Net_ProxySend(cluster, dest, s, strlen(s));
}
s = "<h1>QuakeTV: Demo Listing</h1>";
Net_ProxySend(cluster, dest, s, strlen(s));
@ -623,7 +696,14 @@ static void HTTPSV_GenerateDemoListing(cluster_t *cluster, oproxy_t *dest)
Cluster_BuildAvailableDemoList(cluster);
for (i = 0; i < cluster->availdemoscount; i++)
{
snprintf(link, sizeof(link), "<A HREF=\"/watch.qtv?demo=%s\">%s</A> (%ikb)<br/>", cluster->availdemos[i].name, cluster->availdemos[i].name, cluster->availdemos[i].size/1024);
if (plugframe)
{
snprintf(link, sizeof(link), "<A HREF=\"javascript:playdemo('%s')\">%s</A> (%ikb)<br/>", cluster->availdemos[i].name, cluster->availdemos[i].name, cluster->availdemos[i].size/1024);
}
else
{
snprintf(link, sizeof(link), "<A HREF=\"/watch.qtv?demo=%s\">%s</A> (%ikb)<br/>", cluster->availdemos[i].name, cluster->availdemos[i].name, cluster->availdemos[i].size/1024);
}
Net_ProxySend(cluster, dest, link, strlen(link));
}
@ -633,26 +713,109 @@ static void HTTPSV_GenerateDemoListing(cluster_t *cluster, oproxy_t *dest)
HTTPSV_SendHTMLFooter(cluster, dest);
}
static void HTTPSV_GenerateDownload(cluster_t *cluster, oproxy_t *dest, char *filename)
static void HTTPSV_GeneratePlugin(cluster_t *cluster, oproxy_t *dest)
{
char hostname[1024];
char *html;
if (!HTTPSV_GetHeaderField((char*)dest->inbuffer, "Host", hostname, sizeof(hostname)))
{
HTTPSV_SendHTTPHeader(cluster, dest, "400", "text/html", true);
HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Error", "p");
html = "Your client did not send a Host field, which is required in HTTP/1.1\n<BR />"
"Please try a different browser.\n"
"</BODY>"
"</HTML>";
Net_ProxySend(cluster, dest, html, strlen(html));
return;
}
HTTPSV_SendHTTPHeader(cluster, dest, "200", "text/html", true);
html = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
"<HTML><HEAD><TITLE>QuakeTV With Plugin</TITLE>"
" <link rel=\"StyleSheet\" href=\"/style.css\" type=\"text/css\" />\n"
"</HEAD><body>"
"<div class=\"optdiv\">"
"<iframe frameborder=0 src=\"nowplaying.html?p\" width=\"100%\" height=\"100%\">"
"oh dear. your browser doesn't support this site"
"</iframe>"
"</div>"
"<div class=\"plugdiv\">"
/*once for IE*/
"<object name=\"ieplug\""
" type=\"text/x-quaketvident\""
" classid=\"clsid:7d676c9f-fb84-40b6-b3ff-e10831557eeb\""
//" codebase=\"http://fteqw.com/test.cab\""
" width=100%"
" height=100%"
" >"
"<param name=\"splash\" value=\"http://"
;
Net_ProxySend(cluster, dest, html, strlen(html));
Net_ProxySend(cluster, dest, hostname, strlen(hostname));
html =
"/plugimg.jpg\">"
"<param name=\"game\" value=\"q1\">"
"<param name=\"dataDownload\" value=\"id1/pak0.pak:http://random.nquake.com/qsw106.zip\">"
/*once again for firefox and similar friends*/
"<object name=\"npplug\""
" type=\"text/x-quaketvident\""
" width=100%"
" height=100%"
" >"
"<param name=\"splash\" value=\"http://"
;
Net_ProxySend(cluster, dest, html, strlen(html));
Net_ProxySend(cluster, dest, hostname, strlen(hostname));
html =
"/plugimg.jpg\">"
"<param name=\"game\" value=\"q1\">"
"<param name=\"dataDownload\" value=\"id1/pak0.pak:http://random.nquake.com/qsw106.zip\">"
"It looks like you either don't have the required plugin or its not supported by your browser.<br/>"
"<a href=\"npfte.xpi\">You can download one for firefox here.</a><br/>"
"<a href=\"iefte.exe\">You can download one for internet explorer here.</a><br/>"
"<a href=\"npfte.exe\">You can download one for other browsers here.</a><br/>"
"</object>"
"</object>"
"</div>"
"<script>"
"function getplug(d)\n"
"{\n"
"return document.npplug;\n"
"}\n"
"parent.getplug = getplug;\n"
"parent.host = \""
;
Net_ProxySend(cluster, dest, html, strlen(html));
Net_ProxySend(cluster, dest, hostname, strlen(hostname));
html =
"\";\n"
"</script>"
"</body></HTML>";
Net_ProxySend(cluster, dest, html, strlen(html));
}
static void HTTPSV_GenerateDownload(cluster_t *cluster, oproxy_t *dest, char *filename, char *svroot)
{
#ifdef ALLOWDOWNLOADS
char fname[256];
char link[512];
char *s, *suppliedname;
int len;
if (cluster->allowdownloads)
#endif
if (!svroot || !*svroot)
{
HTTPSV_SendHTTPHeader(cluster, dest, "403", "text/html", true);
HTTPSV_SendHTMLHeader(cluster, dest, "Permission denied");
HTTPSV_SendHTMLHeader(cluster, dest, "Permission denied", "");
HTMLPRINT("<h1>403: Forbidden</h1>");
HTMLPRINT("File downloads from this proxy are currently not permitted.");
HTTPSV_SendHTMLFooter(cluster, dest);
return;
}
#ifdef ALLOWDOWNLOADS
suppliedname = s = fname + strlcpy(fname, cluster->downloaddir, sizeof(fname));
suppliedname = s = fname + strlcpy(fname, svroot, sizeof(fname));
while (*filename > ' ')
{
if (s > fname + sizeof(fname)-4) //4 cos I'm too lazy to work out what the actual number should be
@ -689,12 +852,12 @@ static void HTTPSV_GenerateDownload(cluster_t *cluster, oproxy_t *dest, char *fi
}
*s = 0;
if (*suppliedname == '\\' || *suppliedname == '/' || strstr(suppliedname, "..") || suppliedname[1] == ':')
if (*suppliedname == '\\' || *suppliedname == '/' || strstr(suppliedname, "..") || strchr(suppliedname, ':'))
{
HTTPSV_SendHTTPHeader(cluster, dest, "403", "text/html", true);
HTTPSV_SendHTMLHeader(cluster, dest, "Permission denied");
HTTPSV_SendHTMLHeader(cluster, dest, "Permission denied", "");
HTMLPRINT("<h1>403: Forbidden</h1>");
HTMLPRINT("<p>");
HTMLprintf(link, sizeof(link), "The filename '%s' names an absolute path.", suppliedname);
Net_ProxySend(cluster, dest, link, strlen(link));
@ -704,10 +867,11 @@ static void HTTPSV_GenerateDownload(cluster_t *cluster, oproxy_t *dest, char *fi
len = strlen(fname);
if (len > 4)
{
/*protect id's content (prevent downloading of bsps from pak files - we don't do pak files so just prevent the entire pak)*/
if (!stricmp(link+len-4, ".pak"))
{
HTTPSV_SendHTTPHeader(cluster, dest, "403", "text/html", true);
HTTPSV_SendHTMLHeader(cluster, dest, "Permission denied");
HTTPSV_SendHTMLHeader(cluster, dest, "Permission denied", "");
HTMLPRINT("<h1>403: Forbidden</h1>");
HTMLPRINT("<p>");
@ -728,23 +892,53 @@ static void HTTPSV_GenerateDownload(cluster_t *cluster, oproxy_t *dest, char *fi
else
{
HTTPSV_SendHTTPHeader(cluster, dest, "404", "text/html", true);
HTTPSV_SendHTMLHeader(cluster, dest, "File not found");
HTTPSV_SendHTMLHeader(cluster, dest, "File not found", "");
HTMLPRINT("<h1>404: File not found</h1>");
HTMLPRINT("<p>");
HTMLprintf(link, sizeof(link), "The file '%s' could not be found on this server", fname);
HTMLprintf(link, sizeof(link), "The file '%s' could not be found on this server", suppliedname);
Net_ProxySend(cluster, dest, link, strlen(link));
HTMLPRINT("</p>");
HTTPSV_SendHTMLFooter(cluster, dest);
}
#endif
}
static qboolean urimatch(char *uri, char *match, int urilen)
{
int mlen = strlen(match);
if (urilen < mlen)
return false;
if (strncmp(uri, match, mlen))
return false;
if (urilen == mlen)
return true;
if (uri[mlen] == '?')
return true;
return false;
}
static qboolean uriargmatch(char *uri, char *match, int urilen, char **args)
{
int mlen = strlen(match);
if (urilen < mlen)
return false;
if (strncmp(uri, match, mlen))
return false;
if (urilen == mlen)
{
*args = "";
return true;
}
if (uri[mlen] == '?')
{
*args = uri+mlen+1;
return true;
}
return false;
}
void HTTPSV_PostMethod(cluster_t *cluster, oproxy_t *pend, char *postdata)
@ -753,6 +947,18 @@ void HTTPSV_PostMethod(cluster_t *cluster, oproxy_t *pend, char *postdata)
char *s;
int len;
char *uri, *uriend;
char *args;
int urilen;
uri = pend->inbuffer+4;
while (*uri == ' ')
uri++;
uriend = strchr(uri, '\n');
s = strchr(uri, ' ');
if (s && s < uriend)
uriend = s;
urilen = uriend - uri;
if (!HTTPSV_GetHeaderField((char*)pend->inbuffer, "Content-Length", tempbuf, sizeof(tempbuf)))
{
s = "HTTP/1.1 411 OK\n"
@ -776,9 +982,9 @@ void HTTPSV_PostMethod(cluster_t *cluster, oproxy_t *pend, char *postdata)
// if (len <= pend->inbuffersize)
{
if (!strncmp((char*)pend->inbuffer+5, "/admin", 6))
if (uriargmatch(uri, "/admin.html", urilen, &args))
{
HTTPSV_GenerateAdmin(cluster, pend, 0, postdata);
HTTPSV_GenerateAdmin(cluster, pend, 0, postdata, args);
}
else
{
@ -795,26 +1001,51 @@ void HTTPSV_PostMethod(cluster_t *cluster, oproxy_t *pend, char *postdata)
}
}
#define REDIRECTIF(uri_,url_) \
if (urimatch(uri, uri_, urilen)) \
{ \
s = "HTTP/1.0 302 Found\n" \
"Location: " url_ "\n" \
"\n"; \
Net_ProxySend(cluster, pend, s, strlen(s)); \
}
void HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend)
{
char *s;
if (!strncmp((char*)pend->inbuffer+4, "/nowplaying", 11))
char *uri, *uriend;
char *args;
int urilen;
uri = pend->inbuffer+4;
while (*uri == ' ')
uri++;
uriend = strchr(uri, '\n');
s = strchr(uri, ' ');
if (s && s < uriend)
uriend = s;
urilen = uriend - uri;
if (urimatch(uri, "/plugin.html", urilen))
{
HTTPSV_GenerateNowPlaying(cluster, pend);
HTTPSV_GeneratePlugin(cluster, pend);
}
else if (!strncmp((char*)pend->inbuffer+4, "/watch.qtv?sid=", 15))
else if (uriargmatch(uri, "/nowplaying.html", urilen, &args))
{
HTTPSV_GenerateNowPlaying(cluster, pend, args);
}
else if (!strncmp(uri, "/watch.qtv?sid=", 15))
{
HTTPSV_GenerateQTVStub(cluster, pend, "", (char*)pend->inbuffer+19);
}
else if (!strncmp((char*)pend->inbuffer+4, "/watch.qtv?demo=", 16))
else if (!strncmp(uri, "/watch.qtv?demo=", 16))
{
HTTPSV_GenerateQTVStub(cluster, pend, "file:", (char*)pend->inbuffer+20);
}
else if (!strncmp((char*)pend->inbuffer+4, "/watch.qtv?join=", 16))
else if (!strncmp(uri, "/watch.qtv?join=", 16))
{
HTTPSV_GenerateQWSVStub(cluster, pend, "Join", (char*)pend->inbuffer+16);
}
else if (!strncmp((char*)pend->inbuffer+4, "/watch.qtv?obsv=", 16))
else if (!strncmp(uri, "/watch.qtv?obsv=", 16))
{
HTTPSV_GenerateQWSVStub(cluster, pend, "Observe", (char*)pend->inbuffer+16);
}
@ -822,33 +1053,26 @@ void HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend)
// { //fixme: make this send the demo as an http download
// HTTPSV_GenerateQTVStub(cluster, pend, "file:", (char*)pend->inbuffer+10);
// }
else if (!strncmp((char*)pend->inbuffer+4, "/about", 6))
{ //redirect them to our funky website
s = "HTTP/1.0 302 Found\n"
"Location: http://www.fteqw.com/\n"
"\n";
Net_ProxySend(cluster, pend, s, strlen(s));
}
else if (!strncmp((char*)pend->inbuffer+4, "/admin", 6))
else if (uriargmatch(uri, "/admin.html", urilen, &args))
{
HTTPSV_GenerateAdmin(cluster, pend, 0, NULL);
HTTPSV_GenerateAdmin(cluster, pend, 0, NULL, args);
}
else if (!strncmp((char*)pend->inbuffer+4, "/ ", 2))
else REDIRECTIF("/", "/plugin.html")
else REDIRECTIF("/", "/nowplaying.html")
else REDIRECTIF("/about.html", "http://www.fteqw.com/")
else REDIRECTIF("/plugimg.jpg", "/file/plugimg.jpg") /*lame, very lame*/
else REDIRECTIF("/npfte.xpi", "/file/npfte.xpi") /*lame, very lame*/
else REDIRECTIF("/npfte.exe", "/file/npfte.exe") /*lame, very lame*/
else REDIRECTIF("/iefte.exe", "/file/iefte.exe") /*lame, very lame*/
else if (uriargmatch(uri, "/demos.html", urilen, &args))
{
s = "HTTP/1.0 302 Found\n"
"Location: /nowplaying/\n"
"\n";
Net_ProxySend(cluster, pend, s, strlen(s));
}
else if (!strncmp((char*)pend->inbuffer+4, "/demos", 6))
{
HTTPSV_GenerateDemoListing(cluster, pend);
HTTPSV_GenerateDemoListing(cluster, pend, args);
}
else if (!strncmp((char*)pend->inbuffer+4, "/file/", 6))
{
HTTPSV_GenerateDownload(cluster, pend, (char*)pend->inbuffer+10);
HTTPSV_GenerateDownload(cluster, pend, (char*)pend->inbuffer+10, cluster->downloaddir);
}
else if (!strncmp((char*)pend->inbuffer+4, "/style.css", 10))
else if (urimatch(uri, "/style.css", urilen))
{
HTTPSV_GenerateCSSFile(cluster, pend);
}
@ -856,7 +1080,7 @@ void HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend)
{
#define dest pend
HTTPSV_SendHTTPHeader(cluster, dest, "404", "text/html", true);
HTTPSV_SendHTMLHeader(cluster, dest, "Address not recognised");
HTTPSV_SendHTMLHeader(cluster, dest, "Address not recognised", "");
HTMLPRINT("<h1>Address not recognised</h1>");
HTTPSV_SendHTMLFooter(cluster, dest);
}

View File

@ -91,7 +91,7 @@ void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum)
sv = viewer->server;
if (i++ == viewer->menuop)
{ //auto disconnect
sv->disconnectwhennooneiswatching ^= 1;
sv->autodisconnect = sv->autodisconnect?AD_NO:AD_WHENEMPTY;
}
if (i++ == viewer->menuop)
{ //disconnect
@ -390,12 +390,22 @@ void Menu_Draw(cluster_t *cluster, viewer_t *viewer)
WriteString2(&m, " auto disconnect");
WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : ");
if (viewer->server->disconnectwhennooneiswatching == 2)
switch(viewer->server->autodisconnect)
{
default:
case AD_NO:
sprintf(str, "%-20s", "permanent connection");
break;
case AD_REVERSECONNECT:
sprintf(str, "%-20s", "when server disconnects");
else if (viewer->server->disconnectwhennooneiswatching)
break;
case AD_WHENEMPTY:
sprintf(str, "%-20s", "when inactive");
else
sprintf(str, "%-20s", "never");
break;
case AD_STATUSPOLL:
sprintf(str, "%-20s", "idle when empty");
break;
}
WriteString2(&m, str);
WriteString2(&m, "\n");

View File

@ -23,13 +23,20 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define curtime Sys_Milliseconds()
SOCKET NET_ChooseSocket(SOCKET sock[2], netadr_t *adr)
{
#ifdef AF_INET6
if (((struct sockaddr *)adr)->sa_family == AF_INET6)
return sock[1];
#endif
return sock[0];
}
void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, void *data, netadr_t adr)
{
int ret;
ret = sendto(sock, data, length, 0, (struct sockaddr *)adr, sizeof(struct sockaddr_in));
ret = sendto(sock, data, length, 0, (struct sockaddr *)&adr, sizeof(struct sockaddr_in));
if (ret < 0)
{
int er = qerrno;
@ -42,15 +49,41 @@ void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, void *data, net
int Netchan_IsLocal (netadr_t adr)
{
struct sockaddr_in *sadr = (struct sockaddr_in *)adr;
struct sockaddr_in *sadr = (struct sockaddr_in *)&adr;
unsigned char *bytes;
bytes = (unsigned char *)&sadr->sin_addr;
if (bytes[0] == 127 &&
bytes[1] == 0 &&
bytes[2] == 0 &&
bytes[3] == 1)
return true;
return false;
switch(((struct sockaddr *)&adr)->sa_family)
{
case AF_INET:
bytes = (unsigned char *)&((struct sockaddr_in *)&adr)->sin_addr;
if (bytes[0] == 127 &&
bytes[1] == 0 &&
bytes[2] == 0 &&
bytes[3] == 1)
return true;
return false;
case AF_INET6:
bytes = (unsigned char *)&((struct sockaddr_in6 *)&adr)->sin6_addr;
if (bytes[ 0] == 0 &&
bytes[ 1] == 0 &&
bytes[ 2] == 0 &&
bytes[ 3] == 0 &&
bytes[ 4] == 0 &&
bytes[ 5] == 0 &&
bytes[ 6] == 0 &&
bytes[ 7] == 0 &&
bytes[ 8] == 0 &&
bytes[ 9] == 0 &&
bytes[10] == 0 &&
bytes[11] == 0 &&
bytes[12] == 0 &&
bytes[13] == 0 &&
bytes[14] == 0 &&
bytes[15] == 1)
return true;
return false;
default:
return false;
}
}
@ -143,7 +176,7 @@ Netchan_OutOfBandPrint
Sends a text message in an out-of-band datagram
================
*/
void Netchan_OutOfBandPrint (cluster_t *cluster, SOCKET sock, netadr_t adr, char *format, ...)
void Netchan_OutOfBandPrint (cluster_t *cluster, SOCKET sock[], netadr_t adr, char *format, ...)
{
va_list argptr;
char string[8192];
@ -157,7 +190,7 @@ void Netchan_OutOfBandPrint (cluster_t *cluster, SOCKET sock, netadr_t adr, char
#endif // _WIN32
va_end (argptr);
Netchan_OutOfBand (cluster, sock, adr, strlen(string), (unsigned char *)string);
Netchan_OutOfBand (cluster, NET_ChooseSocket(sock, &adr), adr, strlen(string), (unsigned char *)string);
}
/*
@ -184,7 +217,7 @@ void Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport, qbool
memset (chan, 0, sizeof(*chan));
chan->sock = sock;
memcpy(&chan->remote_address, adr, sizeof(netadr_t));
memcpy(&chan->remote_address, &adr, sizeof(netadr_t));
chan->qport = qport;
chan->isclient = isclient;

View File

@ -183,6 +183,46 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma
strcpy(tv->status, "Receiving soundlist\n");
}
/*called if the server changed the map.serverinfo, so we can corrupt it again*/
void QTV_UpdatedServerInfo(sv_t *tv)
{
qboolean fromproxy;
char text[1024];
char value[256];
Info_ValueForKey(tv->map.serverinfo, "*qtv", value, sizeof(value));
if (*value)
{
fromproxy = true;
tv->serverisproxy = fromproxy;
}
else
fromproxy = false;
//add on our extra infos
Info_SetValueForStarKey(tv->map.serverinfo, "*qtv", VERSION, sizeof(tv->map.serverinfo));
Info_SetValueForStarKey(tv->map.serverinfo, "*z_ext", Z_EXT_STRING, sizeof(tv->map.serverinfo));
Info_ValueForKey(tv->map.serverinfo, "hostname", tv->map.hostname, sizeof(tv->map.hostname));
//change the hostname (the qtv's hostname with the server's hostname in brackets)
Info_ValueForKey(tv->map.serverinfo, "hostname", value, sizeof(value));
if (fromproxy && strchr(value, '(') && value[strlen(value)-1] == ')') //already has brackets
{ //the fromproxy check is because it's fairly common to find a qw server with brackets after it's name.
char *s;
s = strchr(value, '('); //so strip the parent proxy's hostname, and put our hostname first, leaving the origional server's hostname within the brackets
snprintf(text, sizeof(text), "%s %s", tv->cluster->hostname, s);
}
else
{
if (tv->sourcefile)
snprintf(text, sizeof(text), "%s (recorded from: %s)", tv->cluster->hostname, value);
else
snprintf(text, sizeof(text), "%s (live: %s)", tv->cluster->hostname, value);
}
Info_SetValueForStarKey(tv->map.serverinfo, "hostname", text, sizeof(tv->map.serverinfo));
}
static void ParseCDTrack(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
{
char nqversion[3];
@ -199,8 +239,6 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
{
viewer_t *v;
char text[1024];
char value[256];
qboolean fromproxy;
ReadString(m, text, sizeof(text));
// Sys_Printf(tv->cluster, "stuffcmd: %s", text);
@ -250,43 +288,17 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
}
else if (!strncmp(text, "fullserverinfo ", 15))
{
/*strip newline*/
text[strlen(text)-1] = '\0';
/*strip trailing quote*/
text[strlen(text)-1] = '\0';
//copy over the server's serverinfo
strlcpy(tv->map.serverinfo, text+16, sizeof(tv->map.serverinfo));
Info_ValueForKey(tv->map.serverinfo, "*qtv", value, sizeof(value));
if (*value)
{
fromproxy = true;
tv->serverisproxy = fromproxy;
}
else
fromproxy = false;
//add on our extra infos
Info_SetValueForStarKey(tv->map.serverinfo, "*qtv", VERSION, sizeof(tv->map.serverinfo));
Info_SetValueForStarKey(tv->map.serverinfo, "*z_ext", Z_EXT_STRING, sizeof(tv->map.serverinfo));
Info_ValueForKey(tv->map.serverinfo, "hostname", tv->map.hostname, sizeof(tv->map.hostname));
//change the hostname (the qtv's hostname with the server's hostname in brackets)
Info_ValueForKey(tv->map.serverinfo, "hostname", value, sizeof(value));
if (fromproxy && strchr(value, '(') && value[strlen(value)-1] == ')') //already has brackets
{ //the fromproxy check is because it's fairly common to find a qw server with brackets after it's name.
char *s;
s = strchr(value, '('); //so strip the parent proxy's hostname, and put our hostname first, leaving the origional server's hostname within the brackets
snprintf(text, sizeof(text), "%s %s", tv->cluster->hostname, s);
}
else
{
if (tv->sourcefile)
snprintf(text, sizeof(text), "%s (recorded from: %s)", tv->cluster->hostname, value);
else
snprintf(text, sizeof(text), "%s (live: %s)", tv->cluster->hostname, value);
}
Info_SetValueForStarKey(tv->map.serverinfo, "hostname", text, sizeof(tv->map.serverinfo));
QTV_UpdatedServerInfo(tv);
if (tv->controller && (tv->controller->netchan.isnqprotocol == false))
SendBufferToViewer(tv->controller, (char*)m->data+m->startpos, m->readpos - m->startpos, true);

View File

@ -53,7 +53,53 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifdef _WIN32
#include <conio.h>
#include <winsock.h> //this includes windows.h and is the reason for much compiling slowness with windows builds.
#include <winsock2.h> //this includes windows.h and is the reason for much compiling slowness with windows builds.
#ifdef IPPROTO_IPV6
#include <ws2tcpip.h>
#else
#define IPPROTO_IPV6
#define EAI_NONAME 8
struct ip6_scope_id
{
union
{
struct
{
u_long Zone : 28;
u_long Level : 4;
};
u_long Value;
};
};
struct in6_addr
{
u_char s6_addr[16]; /* IPv6 address */
};
typedef struct sockaddr_in6
{
short sin6_family;
u_short sin6_port;
u_long sin6_flowinfo;
struct in6_addr sin6_addr;
union
{
u_long sin6_scope_id;
struct ip6_scope_id sin6_scope_struct;
};
};
struct addrinfo
{
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
size_t ai_addrlen;
char* ai_canonname;
struct sockaddr * ai_addr;
struct addrinfo * ai_next;
};
#endif
#ifdef _MSC_VER
#pragma comment (lib, "wsock32.lib")
#endif
@ -191,7 +237,9 @@ typedef int qboolean;
extern "C" {
#endif
typedef unsigned char netadr_t[64];
typedef struct{
char blob[64];
} netadr_t;
#ifdef COMMENTARY
@ -551,7 +599,12 @@ struct sv_s { //details about a server connection (also known as stream)
errorstate_t errored;
qboolean disconnectwhennooneiswatching;
enum autodisconnect_e {
AD_NO,
AD_WHENEMPTY,
AD_REVERSECONNECT,
AD_STATUSPOLL
} autodisconnect;
unsigned int numviewers;
cluster_t *cluster;
@ -610,9 +663,10 @@ typedef struct {
int time, smalltime;
} availdemo_t;
#define SOCKETGROUPS 2
struct cluster_s {
SOCKET qwdsocket; //udp + quakeworld protocols
SOCKET tcpsocket; //tcp listening socket (for mvd and listings and stuff)
SOCKET qwdsocket[2]; //udp + quakeworld protocols
SOCKET tcpsocket[2]; //tcp listening socket (for mvd and listings and stuff)
char commandinput[512];
int inputlength;
@ -646,8 +700,6 @@ struct cluster_s {
qboolean allownqclients; //nq clients require no challenge
qboolean nouserconnects; //prohibit users from connecting to new streams.
qboolean allowdownloads;
int maxviewers;
int buildnumber;
@ -735,7 +787,7 @@ void Broadcast(cluster_t *cluster, void *buffer, int length, int suitablefor);
void ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask);
void BuildServerData(sv_t *tv, netmsg_t *msg, int servercount, viewer_t *spectatorflag);
void BuildNQServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount);
SOCKET QW_InitUDPSocket(int port);
SOCKET QW_InitUDPSocket(int port, qboolean ipv6);
void QW_UpdateUDPStuff(cluster_t *qtv);
unsigned int Sys_Milliseconds(void);
void Prox_SendInitialEnts(sv_t *qtv, oproxy_t *prox, netmsg_t *msg);
@ -754,9 +806,10 @@ void QTV_SayCommand(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *fullcomman
void PM_PlayerMove (pmove_t *pmove);
void Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport, qboolean isclient);
void Netchan_OutOfBandPrint (cluster_t *cluster, SOCKET sock, netadr_t adr, char *format, ...) PRINTFWARNING(4);
void Netchan_OutOfBandPrint (cluster_t *cluster, SOCKET sock[], netadr_t adr, char *format, ...) PRINTFWARNING(4);
int Netchan_IsLocal (netadr_t adr);
void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, void *data, netadr_t adr);
SOCKET NET_ChooseSocket(SOCKET sock[], netadr_t *adr);
qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2);
qboolean Netchan_Process (netchan_t *chan, netmsg_t *msg);
qboolean NQNetchan_Process(cluster_t *cluster, netchan_t *chan, netmsg_t *msg);
@ -797,8 +850,8 @@ void Sys_Printf(cluster_t *cluster, char *fmt, ...) PRINTFWARNING(2);
void Net_ProxySend(cluster_t *cluster, oproxy_t *prox, void *buffer, int length);
oproxy_t *Net_FileProxy(sv_t *qtv, char *filename);
sv_t *QTV_NewServerConnection(cluster_t *cluster, int streamid, char *server, char *password, qboolean force, qboolean autoclose, qboolean noduplicates, qboolean query);
SOCKET Net_MVDListen(int port);
sv_t *QTV_NewServerConnection(cluster_t *cluster, int streamid, char *server, char *password, qboolean force, enum autodisconnect_e autodisconnect, qboolean noduplicates, qboolean query);
SOCKET Net_TCPListen(int port, qboolean ipv6);
qboolean Net_StopFileProxy(sv_t *qtv);

View File

@ -153,22 +153,41 @@ void WriteDeltaUsercmd (netmsg_t *m, const usercmd_t *from, usercmd_t *move)
SOCKET QW_InitUDPSocket(int port)
SOCKET QW_InitUDPSocket(int port, qboolean ipv6)
{
int sock;
struct sockaddr_in address;
// int fromlen;
int pf;
struct sockaddr *address;
struct sockaddr_in address4;
struct sockaddr_in6 address6;
int addrlen;
unsigned long nonblocking = true;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons((u_short)port);
#pragma message("fixme")
if (ipv6)
{
pf = PF_INET6;
memset(&address6, 0, sizeof(address6));
address6.sin6_family = AF_INET6;
address6.sin6_port = htons((u_short)port);
address = (struct sockaddr*)&address6;
addrlen = sizeof(address6);
}
else
{
pf = PF_INET;
address4.sin_family = AF_INET;
address4.sin_addr.s_addr = INADDR_ANY;
address4.sin_port = htons((u_short)port);
address = (struct sockaddr*)&address4;
addrlen = sizeof(address4);
}
if ((sock = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
if ((sock = socket (pf, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
{
return INVALID_SOCKET;
}
@ -179,8 +198,9 @@ SOCKET QW_InitUDPSocket(int port)
return INVALID_SOCKET;
}
if( bind (sock, (void *)&address, sizeof(address)) == -1)
if( bind (sock, (void *)address, addrlen) == -1)
{
printf("socket bind error %i\n", qerrno);
closesocket(sock);
return INVALID_SOCKET;
}
@ -849,7 +869,7 @@ void NewNQClient(cluster_t *cluster, netadr_t *addr)
header = (buffer[0]<<24) + (buffer[1]<<16) + (buffer[2]<<8) + buffer[3];
*(int*)buffer = header;
NET_SendPacket (cluster, cluster->qwdsocket, len, buffer, *addr);
NET_SendPacket (cluster, NET_ChooseSocket(cluster->qwdsocket, addr), len, buffer, *addr);
if (!viewer)
return;
@ -858,7 +878,7 @@ void NewNQClient(cluster_t *cluster, netadr_t *addr)
memset(viewer, 0, sizeof(*viewer));
Netchan_Setup (cluster->qwdsocket, &viewer->netchan, *addr, 0, false);
Netchan_Setup (NET_ChooseSocket(cluster->qwdsocket, addr), &viewer->netchan, *addr, 0, false);
viewer->netchan.isnqprotocol = true;
viewer->netchan.maxdatagramlen = MAX_NQDATAGRAM;
viewer->netchan.maxreliablelen = MAX_NQMSGLEN;
@ -926,7 +946,7 @@ void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage)
}
memset(viewer, 0, sizeof(viewer_t));
Netchan_Setup (cluster->qwdsocket, &viewer->netchan, *addr, atoi(qport), false);
Netchan_Setup (NET_ChooseSocket(cluster->qwdsocket, addr), &viewer->netchan, *addr, atoi(qport), false);
viewer->netchan.message.maxsize = MAX_QWMSGLEN;
viewer->netchan.maxdatagramlen = MAX_QWMSGLEN;
viewer->netchan.maxreliablelen = MAX_QWMSGLEN;
@ -1137,18 +1157,103 @@ void QTV_Status(cluster_t *cluster, netadr_t *from)
{
sprintf(elem, "\\%i\\", sv->streamid);
WriteString2(&msg, elem);
WriteString2(&msg, (char*)sv->serveraddress);
sprintf(elem, " (%s)", sv->serveraddress);
WriteString2(&msg, elem);
WriteString2(&msg, sv->server);
// sprintf(elem, " (%s)", sv->serveraddress);
// WriteString2(&msg, elem);
}
WriteString2(&msg, "\n");
}
WriteByte(&msg, 0);
NET_SendPacket(cluster, cluster->qwdsocket, msg.cursize, msg.data, *from);
NET_SendPacket(cluster, NET_ChooseSocket(cluster->qwdsocket, from), msg.cursize, msg.data, *from);
}
void QTV_StatusResponse(cluster_t *cluster, char *msg, netadr_t *from)
{
int p, tc, bc;
char name[64], skin[64], token[64];
sv_t *sv;
char *eol;
for (sv = cluster->servers; sv; sv = sv->next)
{
/*ignore connected streams*/
if (sv->isconnected)
continue;
/*and only streams that we could have requested this from*/
if (sv->autodisconnect != AD_STATUSPOLL)
continue;
if (Net_CompareAddress(&sv->serveraddress, from, 0, 1))
break;
}
/*not a valid server... weird.*/
if (!sv)
return;
/*skip the n directive*/
msg++;
eol = strchr(msg, '\n');
if (!eol)
return;
*eol = 0;
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, "*gamedir", sv->map.gamedir, sizeof(sv->map.gamedir));
if (!*sv->map.gamedir)
strlcpy(sv->map.gamedir, "qw", sizeof(sv->map.gamedir));
for(p = 0; p < MAX_CLIENTS; p++)
{
msg = eol+1;
eol = strchr(msg, '\n');
if (!eol)
break;
*eol = 0;
sv->map.players[p].active = false;
//userid
msg = COM_ParseToken(msg, token, sizeof(token), NULL);
//frags
msg = COM_ParseToken(msg, token, sizeof(token), NULL);
sv->map.players[p].frags = atoi(token);
//time (minuites)
msg = COM_ParseToken(msg, token, sizeof(token), NULL);
//ping
msg = COM_ParseToken(msg, token, sizeof(token), NULL);
sv->map.players[p].ping = atoi(token);
//name
msg = COM_ParseToken(msg, name, sizeof(name), NULL);
//skin
msg = COM_ParseToken(msg, skin, sizeof(skin), NULL);
//tc
msg = COM_ParseToken(msg, token, sizeof(token), NULL);
tc = atoi(token);
//bc
msg = COM_ParseToken(msg, token, sizeof(token), NULL);
bc = atoi(token);
snprintf(sv->map.players[p].userinfo, sizeof(sv->map.players[p].userinfo), "\\name\\%s\\skin\\%s\\topcolor\\%i\\bottomcolor\\%i", name, skin, tc, bc);
}
for(; p < MAX_CLIENTS; p++)
{
sv->map.players[p].active = false;
*sv->map.players[p].userinfo = 0;
}
}
void ConnectionlessPacket(cluster_t *cluster, netadr_t *from, netmsg_t *m)
{
@ -1158,6 +1263,11 @@ void ConnectionlessPacket(cluster_t *cluster, netadr_t *from, netmsg_t *m)
ReadLong(m);
ReadString(m, buffer, sizeof(buffer));
if (!strncmp(buffer, "n\\", 2))
{
QTV_StatusResponse(cluster, buffer, from);
return;
}
if (!strncmp(buffer, "rcon ", 5))
{
QTV_Rcon(cluster, buffer+5, from);
@ -1165,7 +1275,7 @@ void ConnectionlessPacket(cluster_t *cluster, netadr_t *from, netmsg_t *m)
}
if (!strncmp(buffer, "ping", 4))
{ //ack
NET_SendPacket (cluster, cluster->qwdsocket, 1, "l", *from);
NET_SendPacket (cluster, NET_ChooseSocket(cluster->qwdsocket, from), 1, "l", *from);
return;
}
if (!strncmp(buffer, "status", 6))
@ -2737,7 +2847,7 @@ tuiadmin:
isjoin = true;
snprintf(buf, sizeof(buf), "udp:%s", args);
qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, true, !isjoin, false);
qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, AD_WHENEMPTY, !isjoin, false);
if (qtv)
{
QW_SetMenu(v, MENU_NONE);
@ -2756,7 +2866,7 @@ tuiadmin:
char buf[256];
snprintf(buf, sizeof(buf), "tcp:%s", args);
qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, true, true, false);
qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, AD_WHENEMPTY, true, false);
if (qtv)
{
QW_SetMenu(v, MENU_NONE);
@ -2802,7 +2912,7 @@ tuiadmin:
{
char buf[256];
snprintf(buf, sizeof(buf), "file:%s", args);
qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, true, true, false);
qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, AD_WHENEMPTY, true, false);
if (qtv)
{
QW_SetMenu(v, MENU_NONE);
@ -2984,7 +3094,7 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean
else if (!strcmp(v->expectcommand, "addserver"))
{
snprintf(buf, sizeof(buf), "tcp:%s", message);
qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, false, false, false);
qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, AD_NO, false, false);
if (qtv)
{
QW_SetViewersServer(cluster, v, qtv);
@ -3010,7 +3120,7 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean
else if (!strcmp(v->expectcommand, "insecadddemo"))
{
snprintf(buf, sizeof(buf), "file:%s", message);
qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, false, false, false);
qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, AD_NO, false, false);
if (!qtv)
QW_PrintfToViewer(v, "Failed to play demo \"%s\"\n", message);
else
@ -3023,7 +3133,7 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean
else if (!strcmp(v->expectcommand, "adddemo"))
{
snprintf(buf, sizeof(buf), "file:%s", message);
qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, false, false, false);
qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, AD_NO, false, false);
if (!qtv)
QW_PrintfToViewer(v, "Failed to play demo \"%s\"\n", message);
else
@ -3035,26 +3145,45 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean
else if (!strcmp(v->expectcommand, "setmvdport"))
{
int newp;
int news;
SOCKET news;
newp = atoi(message);
if (newp)
{
news = Net_MVDListen(newp);
news = Net_TCPListen(newp, true);
if (news != INVALID_SOCKET)
{
if (cluster->tcpsocket != INVALID_SOCKET)
closesocket(cluster->tcpsocket);
cluster->tcpsocket = news;
closesocket(cluster->tcpsocket[1]);
cluster->tcpsocket[1] = news;
cluster->tcplistenportnum = newp;
}
}
else if (cluster->tcpsocket != INVALID_SOCKET)
else if (cluster->tcpsocket[1] != INVALID_SOCKET)
{
closesocket(cluster->tcpsocket);
cluster->tcpsocket = INVALID_SOCKET;
closesocket(cluster->tcpsocket[1]);
cluster->tcpsocket[1] = INVALID_SOCKET;
}
if (newp)
{
news = Net_TCPListen(newp, false);
if (news != INVALID_SOCKET)
{
if (cluster->tcpsocket != INVALID_SOCKET)
closesocket(cluster->tcpsocket[0]);
cluster->tcpsocket[0] = news;
cluster->tcplistenportnum = newp;
}
}
else if (cluster->tcpsocket[0] != INVALID_SOCKET)
{
closesocket(cluster->tcpsocket[0]);
cluster->tcpsocket[0] = INVALID_SOCKET;
}
}
else
@ -3921,7 +4050,7 @@ void QW_FreeViewer(cluster_t *cluster, viewer_t *viewer)
{
if (viewer->server->controller == viewer)
{
if (viewer->server->disconnectwhennooneiswatching)
if (viewer->server->autodisconnect == AD_WHENEMPTY)
viewer->server->errored = ERR_DROP;
else
viewer->server->controller = NULL;
@ -4043,6 +4172,7 @@ void QW_UpdateUDPStuff(cluster_t *cluster)
int read;
int qport;
netmsg_t m;
int socketno;
viewer_t *v, *f;
sv_t *useserver;
@ -4053,7 +4183,7 @@ void QW_UpdateUDPStuff(cluster_t *cluster)
{
sprintf(buffer, "a\n%i\n0\n", cluster->mastersequence++); //fill buffer with a heartbeat
//why is there no \xff\xff\xff\xff ?..
NET_SendPacket(cluster, cluster->qwdsocket, strlen(buffer), buffer, from);
NET_SendPacket(cluster, NET_ChooseSocket(cluster->qwdsocket, &from), strlen(buffer), buffer, from);
}
else
Sys_Printf(cluster, "Cannot resolve master %s\n", cluster->master);
@ -4064,14 +4194,25 @@ void QW_UpdateUDPStuff(cluster_t *cluster)
/* initialised for reading */
InitNetMsg(&m, buffer, sizeof(buffer));
socketno = 0;
for (;;)
{
read = recvfrom(cluster->qwdsocket, buffer, sizeof(buffer), 0, (struct sockaddr*)from, (unsigned*)&fromsize);
if (cluster->qwdsocket[socketno] == INVALID_SOCKET)
{
socketno++;
if (socketno >= SOCKETGROUPS)
break;
}
read = recvfrom(cluster->qwdsocket[socketno], buffer, sizeof(buffer), 0, (struct sockaddr*)&from, (unsigned*)&fromsize);
if (read <= 5) //otherwise it's a runt or bad.
{
if (read < 0) //it's bad.
break;
{
socketno++;
if (socketno >= SOCKETGROUPS)
break;
}
continue;
}
@ -4189,7 +4330,7 @@ void QW_UpdateUDPStuff(cluster_t *cluster)
WriteByte(&m, cluster->maxviewers>255?255:cluster->maxviewers);
WriteByte(&m, NET_PROTOCOL_VERSION);
*(int*)m.data = BigLong(NETFLAG_CTL | m.cursize);
NET_SendPacket(cluster, cluster->qwdsocket, m.cursize, m.data, from);
NET_SendPacket(cluster, NET_ChooseSocket(cluster->qwdsocket, &from), m.cursize, m.data, from);
}
break;
case CCREQ_CONNECT:

View File

@ -19,6 +19,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "qtv.h"
#ifdef _WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include "bsd_string.h"
@ -387,7 +392,7 @@ void Cmd_Master(cmdctxt_t *ctx)
ctx->cluster->mastersendtime = ctx->cluster->curtime;
if (ctx->cluster->qwdsocket != INVALID_SOCKET)
NET_SendPacket (ctx->cluster, ctx->cluster->qwdsocket, 1, "k", addr);
NET_SendPacket (ctx->cluster, NET_ChooseSocket(ctx->cluster->qwdsocket, &addr), 1, "k", addr);
Cmd_Printf(ctx, "Master server set.\n");
}
@ -395,19 +400,32 @@ void Cmd_UDPPort(cmdctxt_t *ctx)
{
int news;
int newp = atoi(Cmd_Argv(ctx, 1));
news = QW_InitUDPSocket(newp);
news = QW_InitUDPSocket(newp, true);
if (news != INVALID_SOCKET)
{
ctx->cluster->mastersendtime = ctx->cluster->curtime;
closesocket(ctx->cluster->qwdsocket);
ctx->cluster->qwdsocket = news;
closesocket(ctx->cluster->qwdsocket[1]);
ctx->cluster->qwdsocket[1] = news;
ctx->cluster->qwlistenportnum = newp;
Cmd_Printf(ctx, "Opened udp port %i (all connected qw clients will time out)\n", newp);
Cmd_Printf(ctx, "Opened udp6 port %i (all connected qw clients will time out)\n", newp);
}
else
Cmd_Printf(ctx, "Failed to open udp port %i\n", newp);
Cmd_Printf(ctx, "Failed to open udp6 port %i\n", newp);
news = QW_InitUDPSocket(newp, false);
if (news != INVALID_SOCKET)
{
ctx->cluster->mastersendtime = ctx->cluster->curtime;
closesocket(ctx->cluster->qwdsocket[0]);
ctx->cluster->qwdsocket[0] = news;
ctx->cluster->qwlistenportnum = newp;
Cmd_Printf(ctx, "Opened udp4 port %i (all connected qw clients will time out)\n", newp);
}
else
Cmd_Printf(ctx, "Failed to open udp4 port %i\n", newp);
}
void Cmd_AdminPassword(cmdctxt_t *ctx)
{
@ -448,7 +466,7 @@ void Cmd_GenericQuery(cmdctxt_t *ctx, int dataset)
memmove(address+strlen(method), address, ARG_LEN-(1+strlen(method)));
strncpy(address, method, strlen(method));
if (!QTV_NewServerConnection(ctx->cluster, ctx->streamid, address, password, false, false, false, dataset))
if (!QTV_NewServerConnection(ctx->cluster, ctx->streamid, address, password, false, AD_NO, false, dataset))
Cmd_Printf(ctx, "Failed to connect to \"%s\", connection aborted\n", address);
Cmd_Printf(ctx, "Querying \"%s\"\n", address);
@ -464,7 +482,7 @@ void Cmd_QTVDemoList(cmdctxt_t *ctx)
Cmd_GenericQuery(ctx, 2);
}
void Cmd_GenericConnect(cmdctxt_t *ctx, char *method)
void Cmd_GenericConnect(cmdctxt_t *ctx, char *method, enum autodisconnect_e autoclose)
{
sv_t *sv;
char *address, *password;
@ -484,7 +502,7 @@ void Cmd_GenericConnect(cmdctxt_t *ctx, char *method)
memmove(address+strlen(method), address, ARG_LEN-(1+strlen(method)));
strncpy(address, method, strlen(method));
sv = QTV_NewServerConnection(ctx->cluster, ctx->streamid?ctx->streamid:1, address, password, false, false, false, false);
sv = QTV_NewServerConnection(ctx->cluster, ctx->streamid?ctx->streamid:1, address, password, false, autoclose, false, false);
if (!sv)
Cmd_Printf(ctx, "Failed to connect to \"%s\", connection aborted\n", address);
else
@ -493,15 +511,15 @@ void Cmd_GenericConnect(cmdctxt_t *ctx, char *method)
void Cmd_QTVConnect(cmdctxt_t *ctx)
{
Cmd_GenericConnect(ctx, "tcp:");
Cmd_GenericConnect(ctx, "tcp:", AD_NO);
}
void Cmd_QWConnect(cmdctxt_t *ctx)
{
Cmd_GenericConnect(ctx, "udp:");
Cmd_GenericConnect(ctx, "udp:", AD_STATUSPOLL);
}
void Cmd_MVDConnect(cmdctxt_t *ctx)
{
Cmd_GenericConnect(ctx, "file:");
Cmd_GenericConnect(ctx, "file:", AD_NO);
}
void Cmd_Exec(cmdctxt_t *ctx)
@ -646,8 +664,10 @@ void Cmd_Status(cmdctxt_t *ctx)
if (ctx->qtv->errored == ERR_DISABLED)
Cmd_Printf(ctx, " Stream is disabled\n");
if (ctx->qtv->disconnectwhennooneiswatching)
Cmd_Printf(ctx, " Stream is temporary\n");
if (ctx->qtv->autodisconnect == AD_WHENEMPTY)
Cmd_Printf(ctx, " Stream is user created\n");
else if (ctx->qtv->autodisconnect == AD_REVERSECONNECT)
Cmd_Printf(ctx, " Stream is server created\n");
/* if (ctx->qtv->tcpsocket != INVALID_SOCKET)
{
@ -784,7 +804,7 @@ void Cmd_Ping(cmdctxt_t *ctx)
char *val = Cmd_Argv(ctx, 1);
if (NET_StringToAddr(val, &addr, 27500))
{
NET_SendPacket (ctx->cluster, ctx->cluster->qwdsocket, 1, "k", addr);
NET_SendPacket (ctx->cluster, NET_ChooseSocket(ctx->cluster->qwdsocket, &addr), 1, "k", addr);
Cmd_Printf(ctx, "pinged\n");
}
Cmd_Printf(ctx, "couldn't resolve\n");
@ -844,6 +864,7 @@ void Cmd_Streams(cmdctxt_t *ctx)
break;
case ERR_PAUSED:
status = " (paused)";
break;
case ERR_DISABLED:
status = " (disabled)";
break;
@ -964,8 +985,10 @@ void Cmd_Stop(cmdctxt_t *ctx)
void Cmd_Reconnect(cmdctxt_t *ctx)
{
if (ctx->qtv->disconnectwhennooneiswatching == 2)
if (ctx->qtv->autodisconnect == AD_REVERSECONNECT)
Cmd_Printf(ctx, "Stream is a reverse connection (command rejected)\n");
// else if (ctx->qtv->autodisconnect == AD_STATUSPOLL && !ctx->qtv->numviewers && !ctx->qtv->proxies)
// Cmd_Printf(ctx, "Not reconnecting to idle server\n");
else if (QTV_Connect(ctx->qtv, ctx->qtv->server))
Cmd_Printf(ctx, "Reconnected\n");
else
@ -980,32 +1003,57 @@ void Cmd_MVDPort(cmdctxt_t *ctx)
if (!newp)
{
if (ctx->cluster->tcpsocket != INVALID_SOCKET)
if (ctx->cluster->tcpsocket[0] != INVALID_SOCKET && ctx->cluster->tcpsocket[1] != INVALID_SOCKET)
Cmd_Printf(ctx, "Already closed\n");
if (ctx->cluster->tcpsocket[0] != INVALID_SOCKET)
{
closesocket(ctx->cluster->tcpsocket);
ctx->cluster->tcpsocket = INVALID_SOCKET;
closesocket(ctx->cluster->tcpsocket[0]);
ctx->cluster->tcpsocket[0] = INVALID_SOCKET;
ctx->cluster->tcplistenportnum = 0;
Cmd_Printf(ctx, "mvd port is now closed\n");
Cmd_Printf(ctx, "tcp4 port is now closed\n");
}
if (ctx->cluster->tcpsocket[1] != INVALID_SOCKET)
{
closesocket(ctx->cluster->tcpsocket[1]);
ctx->cluster->tcpsocket[1] = INVALID_SOCKET;
ctx->cluster->tcplistenportnum = 0;
Cmd_Printf(ctx, "tcp6 port is now closed\n");
}
else
Cmd_Printf(ctx, "Already closed\n");
}
else
{
news = Net_MVDListen(newp);
news = Net_TCPListen(newp, true);
if (news != INVALID_SOCKET)
{
if (ctx->cluster->tcpsocket != INVALID_SOCKET)
closesocket(ctx->cluster->tcpsocket);
ctx->cluster->tcpsocket = news;
if (ctx->cluster->tcpsocket[1] != INVALID_SOCKET)
closesocket(ctx->cluster->tcpsocket[1]);
ctx->cluster->tcpsocket[1] = news;
ctx->cluster->tcplistenportnum = newp;
Cmd_Printf(ctx, "Opened tcp port %i\n", newp);
Cmd_Printf(ctx, "Opened tcp6 port %i\n", newp);
}
else
Cmd_Printf(ctx, "Failed to open tcp port %i\n", newp);
Cmd_Printf(ctx, "Failed to open tcp6 port %i\n", newp);
news = Net_TCPListen(newp, false);
if (news != INVALID_SOCKET)
{
if (ctx->cluster->tcpsocket[0] != INVALID_SOCKET)
closesocket(ctx->cluster->tcpsocket[0]);
ctx->cluster->tcpsocket[0] = news;
ctx->cluster->tcplistenportnum = newp;
Cmd_Printf(ctx, "Opened tcp4 port %i\n", newp);
}
else
Cmd_Printf(ctx, "Failed to open tcp4 port %i\n", newp);
}
}
@ -1072,6 +1120,36 @@ void Cmd_DemoDir(cmdctxt_t *ctx)
}
}
void Cmd_DLDir(cmdctxt_t *ctx)
{
char *val;
val = Cmd_Argv(ctx, 1);
if (!Cmd_IsLocal(ctx))
{
Cmd_Printf(ctx, "dldir may not be used remotely\n");
return;
}
if (*val)
{
while (*val > 0 &&*val <= ' ')
val++;
// if (strchr(val, '.') || strchr(val, ':') || *val == '/')
// Cmd_Printf(ctx, "Rejecting path\n");
// else
{
strlcpy(ctx->cluster->downloaddir, val, sizeof(ctx->cluster->downloaddir));
Cmd_Printf(ctx, "Changed download dir to \"%s\"\n", ctx->cluster->downloaddir);
}
}
else
{
Cmd_Printf(ctx, "Current download directory is \"%s\"\n", ctx->cluster->downloaddir);
}
}
void Cmd_MuteStream(cmdctxt_t *ctx)
{
char *val;
@ -1120,7 +1198,7 @@ extern const rconcommands_t rconcommands[];
void Cmd_Commands(cmdctxt_t *ctx)
{
rconcommands_t *cmd;
const rconcommands_t *cmd;
consolecommand_t lastfunc = NULL;
Cmd_Printf(ctx, "Commands:\n");
@ -1167,6 +1245,7 @@ const rconcommands_t rconcommands[] =
{"maxproxies", 0, 1, Cmd_MaxProxies, "sets a limit on tcp/qtv client connections"},
{"demodir", 0, 1, Cmd_DemoDir, "specifies where to get the demo list from"},
{"basedir", 0, 1, Cmd_BaseDir, "specifies where to get any files required by the game. this is prefixed to the server-specified game dir."},
{"dldir", 0, 1, Cmd_DLDir, "specifies the path to download stuff from (http://server/file/ maps here)"},
{"ping", 0, 1, Cmd_Ping, "sends a udp ping to a qtv proxy or server"},
{"reconnect", 0, 1, Cmd_Reconnect, "forces a stream to reconnect to its server (restarts demos)"},
{"echo", 0, 1, Cmd_Echo, "a useless command that echos a string"},
@ -1176,7 +1255,6 @@ const rconcommands_t rconcommands[] =
{"allownq", 0, 1, Cmd_AllowNQ, "permits nq clients to connect. This can be disabled as this code is less tested than the rest"},
{"halt", 1, 0, Cmd_Halt, "disables a stream, preventing it from reconnecting until someone tries watching it anew. Boots current spectators"},
{"disable", 1, 0, Cmd_Halt},
{"pause", 1, 0, Cmd_Pause, "Pauses a demo stream."},

View File

@ -63,6 +63,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define RECONNECT_TIME (1000*30)
#define RECONNECT_TIME_DEMO (1000*5)
#define UDPRECONNECT_TIME (1000)
#define STATUSPOLL_TIME 1000*30
#define PINGSINTERVAL_TIME (1000*5)
#define UDPTIMEOUT_LENGTH (1000*20)
#define UDPPACKETINTERVAL (1000/72)
@ -212,22 +213,41 @@ qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2)
return false;
}
SOCKET Net_MVDListen(int port)
SOCKET Net_TCPListen(int port, qboolean ipv6)
{
SOCKET sock;
struct sockaddr_in address;
struct sockaddr_in address4;
struct sockaddr_in6 address6;
struct sockaddr *address;
int prot;
int addrsize;
// int fromlen;
unsigned long nonblocking = true;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons((u_short)port);
if (ipv6)
{
prot = PF_INET6;
memset(&address6, 0, sizeof(address6));
address6.sin6_family = AF_INET6;
address6.sin6_port = htons((u_short)port);
address = (struct sockaddr *)&address6;
addrsize = sizeof(struct sockaddr_in6);
}
else
{
prot = PF_INET;
address4.sin_family = AF_INET;
address4.sin_addr.s_addr = INADDR_ANY;
address4.sin_port = htons((u_short)port);
address = (struct sockaddr *)&address4;
addrsize = sizeof(struct sockaddr_in);
}
if ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
if ((sock = socket (prot, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
return INVALID_SOCKET;
}
@ -238,8 +258,9 @@ SOCKET Net_MVDListen(int port)
return INVALID_SOCKET;
}
if( bind (sock, (void *)&address, sizeof(address)) == -1)
if( bind (sock, address, addrsize) == -1)
{
printf("socket bind error %i\n", qerrno);
closesocket(sock);
return INVALID_SOCKET;
}
@ -388,6 +409,9 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip)
int err;
netadr_t from;
unsigned long nonblocking = true;
int afam;
int pfam;
int asz;
if (!NET_StringToAddr(ip, &qtv->serveraddress, 27500))
{
@ -395,7 +419,10 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip)
strcpy(qtv->status, "Unable to resolve server\n");
return false;
}
qtv->sourcesock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
afam = ((struct sockaddr*)&qtv->serveraddress)->sa_family;
pfam = ((afam==AF_INET6)?PF_INET6:PF_INET);
asz = ((afam==AF_INET6)?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in));
qtv->sourcesock = socket(pfam, SOCK_STREAM, IPPROTO_TCP);
if (qtv->sourcesock == INVALID_SOCKET)
{
strcpy(qtv->status, "Network error\n");
@ -403,7 +430,7 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip)
}
memset(&from, 0, sizeof(from));
((struct sockaddr*)&from)->sa_family = ((struct sockaddr*)&qtv->serveraddress)->sa_family;
((struct sockaddr*)&from)->sa_family = afam;
if (bind(qtv->sourcesock, (struct sockaddr *)&from, sizeof(from)) == -1)
{
closesocket(qtv->sourcesock);
@ -420,7 +447,7 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip)
return false;
}
if (connect(qtv->sourcesock, (struct sockaddr *)&qtv->serveraddress, sizeof(qtv->serveraddress)) == INVALID_SOCKET)
if (connect(qtv->sourcesock, (struct sockaddr *)&qtv->serveraddress, asz) == INVALID_SOCKET)
{
err = qerrno;
if (err != EINPROGRESS && err != EAGAIN && err != EWOULDBLOCK) //bsd sockets are meant to return EINPROGRESS, but some winsock drivers use EWOULDBLOCK instead. *sigh*...
@ -445,19 +472,23 @@ qboolean Net_ConnectToUDPServer(sv_t *qtv, char *ip)
{
netadr_t from;
unsigned long nonblocking = true;
int afam, pfam, asz;
if (!NET_StringToAddr(ip, &qtv->serveraddress, 27500))
{
Sys_Printf(qtv->cluster, "Stream %i: Unable to resolve %s\n", qtv->streamid, ip);
return false;
}
qtv->sourcesock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
afam = ((struct sockaddr*)&qtv->serveraddress)->sa_family;
pfam = ((afam==AF_INET6)?PF_INET6:PF_INET);
asz = ((afam==AF_INET6)?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in));
qtv->sourcesock = socket(pfam, SOCK_DGRAM, IPPROTO_UDP);
if (qtv->sourcesock == INVALID_SOCKET)
return false;
memset(&from, 0, sizeof(from));
((struct sockaddr*)&from)->sa_family = ((struct sockaddr*)&qtv->serveraddress)->sa_family;
if (bind(qtv->sourcesock, (struct sockaddr *)&from, sizeof(from)) == -1)
((struct sockaddr*)&from)->sa_family = afam;
if (bind(qtv->sourcesock, (struct sockaddr *)&from, asz) == -1)
{
closesocket(qtv->sourcesock);
qtv->sourcesock = INVALID_SOCKET;
@ -522,7 +553,8 @@ qboolean DemoFilenameIsOkay(char *fname)
*/
}
qboolean Net_ConnectToServer(sv_t *qtv)
/*figures out the ip to connect to, and decides the protocol for it*/
char *Net_DiagnoseProtocol(sv_t *qtv)
{
char *at;
sourcetype_t type = SRC_BAD;
@ -557,6 +589,14 @@ qboolean Net_ConnectToServer(sv_t *qtv)
ip = at+1;
}
qtv->sourcetype = type;
return ip;
}
qboolean Net_ConnectToServer(sv_t *qtv)
{
char *ip = Net_DiagnoseProtocol(qtv);
qtv->usequakeworldprotocols = false;
if (qtv->sourcetype == SRC_DEMO)
@ -564,9 +604,7 @@ qboolean Net_ConnectToServer(sv_t *qtv)
else
qtv->nextconnectattempt = qtv->curtime + RECONNECT_TIME; //wait half a minuite before trying to reconnect
qtv->sourcetype = type;
switch(type)
switch( qtv->sourcetype)
{
case SRC_DEMO:
qtv->sourcesock = INVALID_SOCKET;
@ -585,7 +623,7 @@ qboolean Net_ConnectToServer(sv_t *qtv)
qtv->filelength = ftell(qtv->sourcefile);
//attempt to detect the end of the file
fseek(qtv->sourcefile, -sizeof(smallbuffer), SEEK_CUR);
fseek(qtv->sourcefile, 0-sizeof(smallbuffer), SEEK_CUR);
fread(smallbuffer, 1, 17, qtv->sourcefile);
//0 is the time
if (smallbuffer[1] == dem_all || smallbuffer[1] == dem_read) //mvdsv changed it to read...
@ -1019,6 +1057,19 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl)
qtv->sourcefile = NULL;
}
memcpy(qtv->server, serverurl, sizeof(qtv->server)-1);
if (qtv->autodisconnect == AD_STATUSPOLL && !qtv->numviewers && !qtv->proxies)
{
char *ip;
ip = Net_DiagnoseProtocol(qtv);
if (!NET_StringToAddr(ip, &qtv->serveraddress, 27500))
{
Sys_Printf(qtv->cluster, "Stream %i: Unable to resolve %s\n", qtv->streamid, ip);
return false;
}
return true;
}
*qtv->map.serverinfo = '\0';
Info_SetValueForStarKey(qtv->map.serverinfo, "*version", "FTEQTV", sizeof(qtv->map.serverinfo));
Info_SetValueForStarKey(qtv->map.serverinfo, "*qtv", VERSION, sizeof(qtv->map.serverinfo));
@ -1029,9 +1080,7 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl)
else
Info_SetValueForStarKey(qtv->map.serverinfo, "server", qtv->server, sizeof(qtv->map.serverinfo));
memcpy(qtv->server, serverurl, sizeof(qtv->server)-1);
if (qtv->disconnectwhennooneiswatching == 2)
if (qtv->autodisconnect == AD_REVERSECONNECT)
{ //added because of paranoia rather than need. Should never occur.
printf("bug: autoclose==2\n");
strcpy(qtv->status, "Network error\n");
@ -1115,7 +1164,6 @@ void QTV_Cleanup(sv_t *qtv, qboolean leaveadmins)
{ //disconnects the stream
viewer_t *v;
cluster_t *cluster;
int i;
oproxy_t *prox;
oproxy_t *old;
@ -1509,10 +1557,18 @@ void QTV_Run(sv_t *qtv)
int oldcurtime;
int packettime;
if (qtv->disconnectwhennooneiswatching == 1 && qtv->numviewers == 0 && qtv->proxies == NULL)
if (qtv->numviewers == 0 && qtv->proxies == NULL)
{
Sys_Printf(qtv->cluster, "Stream %i: %s became inactive\n", qtv->streamid, qtv->server);
qtv->errored = ERR_DROP;
if (qtv->autodisconnect == AD_WHENEMPTY)
{
Sys_Printf(qtv->cluster, "Stream %i: %s became inactive\n", qtv->streamid, qtv->server);
qtv->errored = ERR_DROP;
}
else if (qtv->autodisconnect == AD_STATUSPOLL && qtv->isconnected)
{
/*switch to status polling instead of packet spamming*/
qtv->errored = ERR_RECONNECT;
}
}
if (qtv->errored)
{
@ -1571,6 +1627,7 @@ void QTV_Run(sv_t *qtv)
qtv->buffersize = 0;
qtv->forwardpoint = 0;
QTV_DisconnectFromSource(qtv);
qtv->isconnected = 0;
qtv->errored = ERR_NONE;
qtv->nextconnectattempt = qtv->curtime; //make the reconnect happen _now_
}
@ -1583,12 +1640,19 @@ void QTV_Run(sv_t *qtv)
if (qtv->simtime > qtv->nextpackettime)
qtv->simtime = qtv->nextpackettime; //too old
if (!qtv->isconnected && (qtv->curtime >= qtv->nextconnectattempt || qtv->curtime < qtv->nextconnectattempt - UDPRECONNECT_TIME*2))
if (!qtv->isconnected && (qtv->curtime >= qtv->nextconnectattempt || qtv->curtime < qtv->nextconnectattempt - (UDPRECONNECT_TIME+STATUSPOLL_TIME)))
{
if (qtv->errored == ERR_DISABLED)
{
strcpy(qtv->status, "Given up connecting\n");
}
else if (qtv->autodisconnect == AD_STATUSPOLL)
{
QTV_DisconnectFromSource(qtv);
Netchan_OutOfBand(qtv->cluster, NET_ChooseSocket(qtv->cluster->qwdsocket, &qtv->serveraddress), qtv->serveraddress, 13, "status\n");
qtv->nextconnectattempt = qtv->curtime + STATUSPOLL_TIME;
return;
}
else
{
strcpy(qtv->status, "Attemping challenge\n");
@ -1746,7 +1810,7 @@ void QTV_Run(sv_t *qtv)
if (qtv->curtime >= qtv->nextconnectattempt || qtv->curtime < qtv->nextconnectattempt - RECONNECT_TIME*2)
{
if (qtv->disconnectwhennooneiswatching == 2) //2 means a reverse connection
if (qtv->autodisconnect == AD_REVERSECONNECT) //2 means a reverse connection
{
qtv->errored = ERR_DROP;
return;
@ -1877,7 +1941,7 @@ void QTV_Run(sv_t *qtv)
qtv->buffersize = 0;
qtv->forwardpoint = 0;
if (qtv->disconnectwhennooneiswatching)
if (qtv->autodisconnect == AD_WHENEMPTY || qtv->autodisconnect == AD_REVERSECONNECT)
qtv->errored = ERR_DROP; //if its a user registered stream, drop it immediatly
else
{ //otherwise close the socket (this will result in a timeout and reconnect)
@ -2107,7 +2171,7 @@ void QTV_Run(sv_t *qtv)
}
}
sv_t *QTV_NewServerConnection(cluster_t *cluster, int newstreamid, char *server, char *password, qboolean force, qboolean autoclose, qboolean noduplicates, qboolean query)
sv_t *QTV_NewServerConnection(cluster_t *cluster, int newstreamid, char *server, char *password, qboolean force, enum autodisconnect_e autoclose, qboolean noduplicates, qboolean query)
{
sv_t *qtv;
@ -2158,7 +2222,7 @@ sv_t *QTV_NewServerConnection(cluster_t *cluster, int newstreamid, char *server,
// qtv->tcpsocket = INVALID_SOCKET;
qtv->sourcesock = INVALID_SOCKET;
qtv->disconnectwhennooneiswatching = autoclose;
qtv->autodisconnect = autoclose;
qtv->parsingconnectiondata = true;
qtv->serverquery = query;
qtv->silentstream = true;
@ -2169,7 +2233,7 @@ sv_t *QTV_NewServerConnection(cluster_t *cluster, int newstreamid, char *server,
qtv->cluster = cluster;
qtv->next = cluster->servers;
if (autoclose != 2) //2 means reverse connection (don't ever try reconnecting)
if (autoclose != AD_REVERSECONNECT) //2 means reverse connection (don't ever try reconnecting)
{
if (!QTV_Connect(qtv, server) && !force)
{