1
0
Fork 0
forked from fte/fteqw

qtv changes allow you to spectate annother spectator. FTE changes fix independant physics stuff a bit, and fix AVI recording a little also.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@2440 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2006-11-03 15:53:04 +00:00
parent b444ca99ac
commit acf93f1548
24 changed files with 537 additions and 220 deletions

View file

@ -452,10 +452,57 @@ readnext:
CL_StopPlayback (); CL_StopPlayback ();
return 0; return 0;
} }
// Con_Printf("demo packet %x\n", (int)c);
switch (c&7) switch (c&7)
{ {
case dem_cmd : case dem_cmd :
/* if (cls.demoplayback == DPB_MVD)
{
unsigned short samps;
unsigned char bits;
unsigned char rateid;
unsigned char audio[8192];
if (readdemobytes (&samps, 2) == 2)
{
if (readdemobytes (&bits, 1) == 1)
{
if (samps > sizeof(audio))
{
Con_Printf("Corrupted/too large audio chunk\n");
CL_StopPlayback();
return 0;
}
if (readdemobytes (&rateid, 1) == 1)
{
if (readdemobytes (audio, samps) == samps)
{
FILE *f;
samps = samps/(bits/8);
f = fopen("c:/test.raw", "r+b");
if (f)
{
fseek(f, 0, SEEK_END);
fwrite(audio, samps, bits/8, f);
fclose(f);
}
S_RawAudio(0, audio, 11025, samps, 1, bits/8);
break;
}
unreadbytes(1, &rateid);
}
unreadbytes(1, &bits);
}
unreadbytes(1, &samps);
}
unreadbytes(1, &c);
r = 0;
unreadbytes(1, &r);
return 0;
}
else
{*/
// user sent input // user sent input
i = cls.netchan.outgoing_sequence & UPDATE_MASK; i = cls.netchan.outgoing_sequence & UPDATE_MASK;
pcmd = &cl.frames[i].cmd[0]; pcmd = &cl.frames[i].cmd[0];
@ -486,6 +533,7 @@ readnext:
readdemobytes (&f, 4); readdemobytes (&f, 4);
cl.viewangles[0][i] = LittleFloat (f); cl.viewangles[0][i] = LittleFloat (f);
} }
/* }*/
break; break;
case dem_read: case dem_read:
@ -1384,6 +1432,7 @@ void CL_PlayDemo(char *demoname)
void CL_QTVPlay (vfsfile_t *newf) void CL_QTVPlay (vfsfile_t *newf)
{ {
#define BUFFERTIME 10
CL_Disconnect_f (); CL_Disconnect_f ();
cls.demofile = newf; cls.demofile = newf;
@ -1396,11 +1445,11 @@ void CL_QTVPlay (vfsfile_t *newf)
cls.state = ca_demostart; cls.state = ca_demostart;
net_message.packing = SZ_RAWBYTES; net_message.packing = SZ_RAWBYTES;
Netchan_Setup (NS_CLIENT, &cls.netchan, net_from, 0); Netchan_Setup (NS_CLIENT, &cls.netchan, net_from, 0);
realtime = -10; realtime = -BUFFERTIME;
cl.gametime = -10; cl.gametime = -BUFFERTIME;
cl.gametimemark = realtime; cl.gametimemark = realtime;
Con_Printf("Buffering for ten seconds\n"); Con_Printf("Buffering for %i seconds\n", (int)-realtime);
cls.netchan.last_received=realtime; cls.netchan.last_received=realtime;
@ -1416,7 +1465,8 @@ void CL_QTVPoll (void)
{ {
char *s, *e, *colon; char *s, *e, *colon;
int len; int len;
qboolean error = false; qboolean streamavailable = false;
qboolean saidheader = false;
if (!qtvrequest) if (!qtvrequest)
return; return;
@ -1451,23 +1501,37 @@ void CL_QTVPoll (void)
{ {
*colon++ = '\0'; *colon++ = '\0';
if (!strcmp(s, "PERROR")) if (!strcmp(s, "PERROR"))
{ //printable error
Con_Printf("QTV Error:\n%s\n", colon);
}
else if (!strcmp(s, "PRINT"))
{ //printable error { //printable error
Con_Printf("QTV:\n%s\n", colon); Con_Printf("QTV:\n%s\n", colon);
error = true; }
else if (!strcmp(s, "TERROR"))
{ //printable error
Con_Printf("QTV Error:\n%s\n", colon);
} }
else if (!strcmp(s, "ADEMO")) else if (!strcmp(s, "ADEMO"))
{ //printable error { //printable error
Con_Printf("Demo%s is available\n", colon); Con_Printf("Demo%s is available\n", colon);
error = true; //not really an error, but meh
} }
else if (!strcmp(s, "ASOURCE")) else if (!strcmp(s, "ASOURCE"))
{ //printable error { //printable source
Con_Printf("Source%s is available\n", colon); if (!saidheader)
error = true; {
saidheader=true;
Con_Printf("Available Sources:\n");
} }
Con_Printf("%s\n", colon);
}
else if (!strcmp(s, "BEGIN"))
streamavailable = true;
} }
else else
{ {
if (!strcmp(s, "BEGIN"))
streamavailable = true;
} }
//from e to s, we have a line //from e to s, we have a line
s = e+1; s = e+1;
@ -1475,7 +1539,7 @@ void CL_QTVPoll (void)
e++; e++;
} }
if (!error) if (streamavailable)
{ {
CL_QTVPlay(qtvrequest); CL_QTVPlay(qtvrequest);
qtvrequest = NULL; qtvrequest = NULL;
@ -1488,6 +1552,16 @@ void CL_QTVPoll (void)
qtvrequestsize = 0; qtvrequestsize = 0;
} }
char *strchrrev(char *str, char chr)
{
char *firstchar = str;
for (str = str + strlen(str)-1; str>=firstchar; str--)
if (*str == chr)
return str;
return NULL;
}
void CL_QTVPlay_f (void) void CL_QTVPlay_f (void)
{ {
qboolean raw=0; qboolean raw=0;
@ -1495,6 +1569,12 @@ void CL_QTVPlay_f (void)
vfsfile_t *newf; vfsfile_t *newf;
char *host; char *host;
if (Cmd_Argc() < 2)
{
Con_Printf("Usage: qtvplay [stream@]hostname[:port] [password]\n");
return;
}
connrequest = Cmd_Argv(1); connrequest = Cmd_Argv(1);
if (*connrequest == '#') if (*connrequest == '#')
@ -1530,7 +1610,7 @@ void CL_QTVPlay_f (void)
host = connrequest; host = connrequest;
connrequest = strchr(connrequest, '@'); connrequest = strchrrev(connrequest, '@');
if (connrequest) if (connrequest)
host = connrequest+1; host = connrequest+1;
newf = FS_OpenTCP(host); newf = FS_OpenTCP(host);
@ -1541,8 +1621,7 @@ void CL_QTVPlay_f (void)
return; return;
} }
host = connrequest = Cmd_Argv(1); host = Cmd_Argv(1);
connrequest = strchr(connrequest, '@');
if (connrequest) if (connrequest)
*connrequest = '\0'; *connrequest = '\0';
else else
@ -1582,53 +1661,6 @@ void CL_QTVPlay_f (void)
} }
} }
/*
void CL_QTVPlay_f (void)
{
char *connrequest;
vfsfile_t *newf;
newf = FS_OpenTCP(Cmd_Argv(1));
if (!newf)
{
Con_Printf("Couldn't connect to proxy\n");
return;
}
connrequest = "QTV\n"
"VERSION: 1\n"
"RAW: 1\n";
VFS_WRITE(newf, connrequest, strlen(connrequest));
connrequest = "SOURCE: file:test.mvd\n";
VFS_WRITE(newf, connrequest, strlen(connrequest));
connrequest = "\n";
VFS_WRITE(newf, connrequest, strlen(connrequest));
CL_Disconnect_f ();
cls.demofile = newf;
unreadcount = 0; //just in case
cls.demoplayback = DPB_MVD;
cls.findtrack = true;
cls.state = ca_demostart;
net_message.packing = SZ_RAWBYTES;
Netchan_Setup (NS_CLIENT, &cls.netchan, net_from, 0);
realtime = -10;
cl.gametime = -10;
cl.gametimemark = realtime;
Con_Printf("Buffering for ten seconds\n");
cls.netchan.last_received=realtime;
cls.protocol = CP_QUAKEWORLD;
TP_ExecTrigger ("f_demostart");
}
*/
void CL_QTVList_f (void) void CL_QTVList_f (void)
{ {
char *connrequest; char *connrequest;
@ -1653,30 +1685,6 @@ void CL_QTVList_f (void)
VFS_CLOSE(qtvrequest); VFS_CLOSE(qtvrequest);
qtvrequest = newf; qtvrequest = newf;
qtvrequestsize = 0; qtvrequestsize = 0;
/*
CL_Disconnect_f ();
cls.demofile = newf;
unreadcount = 0; //just in case
cls.demoplayback = DPB_MVD;
cls.findtrack = true;
cls.state = ca_demostart;
net_message.packing = SZ_RAWBYTES;
Netchan_Setup (NS_CLIENT, &cls.netchan, net_from, 0);
realtime = 0;
cl.gametime = 0;
cl.gametimemark = realtime;
Con_Printf("Querying proxy\n");
cls.netchan.last_received=realtime;
cls.protocol = CP_QUAKEWORLD;
TP_ExecTrigger ("f_demostart");*/
} }
/* /*

View file

@ -2898,7 +2898,7 @@ void CL_LinkPlayers (void)
ent->origin[0] = cl.simorg[pnum][0]; ent->origin[0] = cl.simorg[pnum][0];
ent->origin[1] = cl.simorg[pnum][1]; ent->origin[1] = cl.simorg[pnum][1];
ent->origin[2] = cl.simorg[pnum][2]+cl.crouch[pnum]; ent->origin[2] = cl.simorg[pnum][2]+cl.crouch[pnum];
ent->flags |= 2; ent->flags |= Q2RF_EXTERNALMODEL;
break; break;
} }
} }

View file

@ -843,7 +843,7 @@ float CL_FilterTime (double time, float wantfps) //now returns the extra time no
if (wantfps < 1) if (wantfps < 1)
fps = fpscap; fps = fpscap;
else else
fps = bound (10.0, wantfps, fpscap); fps = bound (6.7, wantfps, fpscap); //we actually cap ourselves to 150msecs (1000/7 = 142)
} }
if (time < 1000 / fps) if (time < 1000 / fps)
@ -983,15 +983,21 @@ unsigned long _stdcall CL_IndepPhysicsThread(void *param)
int sleeptime; int sleeptime;
float fps; float fps;
float time, lasttime; float time, lasttime;
float spare;
lasttime = Sys_DoubleTime(); lasttime = Sys_DoubleTime();
while(1) while(1)
{ {
EnterCriticalSection(&indepcriticialsection);
time = Sys_DoubleTime(); time = Sys_DoubleTime();
spare = CL_FilterTime((time - lasttime)*1000, cl_netfps.value);
if (spare)
{
time -= spare/1000.0f;
EnterCriticalSection(&indepcriticialsection);
if (cls.state) if (cls.state)
CL_SendCmd(time - lasttime); CL_SendCmd(time - lasttime);
lasttime = time; lasttime = time;
LeaveCriticalSection(&indepcriticialsection); LeaveCriticalSection(&indepcriticialsection);
}
fps = cl_netfps.value; fps = cl_netfps.value;
if (fps < 4) if (fps < 4)
@ -1003,6 +1009,8 @@ unsigned long _stdcall CL_IndepPhysicsThread(void *param)
if (sleeptime) if (sleeptime)
Sleep(sleeptime); Sleep(sleeptime);
else
Sleep(1);
} }
} }
@ -1014,11 +1022,18 @@ void CL_UseIndepPhysics(qboolean allow)
if (allow) if (allow)
{ //enable it { //enable it
DWORD tid; //*sigh*... DWORD tid; //*sigh*...
// TIMECAPS tc;
// timeGetDevCaps(&tc, sizeof(TIMECAPS));
// Con_Printf("Timer has a resolution of %i millisecond%s\n", tc.wPeriodMin, tc.wPeriodMin!=1?"s":"");
InitializeCriticalSection(&indepcriticialsection); InitializeCriticalSection(&indepcriticialsection);
runningindepphys = true; runningindepphys = true;
indepphysicsthread = CreateThread(NULL, 8192, CL_IndepPhysicsThread, NULL, 0, &tid); indepphysicsthread = CreateThread(NULL, 8192, CL_IndepPhysicsThread, NULL, 0, &tid);
allowindepphys = 1; allowindepphys = 1;
SetThreadPriority(independantphysics, HIGH_PRIORITY_CLASS);
} }
else else
{ {
@ -1050,6 +1065,7 @@ CL_SendCmd
vec3_t accum[MAX_SPLITS]; vec3_t accum[MAX_SPLITS];
void CL_SendCmd (float frametime) void CL_SendCmd (float frametime)
{ {
extern cvar_t cl_indepphysics;
sizebuf_t buf; sizebuf_t buf;
qbyte data[512]; qbyte data[512];
int i, plnum; int i, plnum;
@ -1185,10 +1201,17 @@ void CL_SendCmd (float frametime)
independantphysics[plnum].msec = msecstouse; independantphysics[plnum].msec = msecstouse;
} }
// if (!CL_FilterTime(msecstouse, cl_netfps.value<=0?cl_maxfps.value:cl_netfps.value) && msecstouse<255 && cls.state == ca_active) if (cl_netfps.value && !cl_indepphysics.value)
// { {
// return; int spare;
// } spare = CL_FilterTime(msecstouse, cl_netfps.value<=0?cl_maxfps.value:cl_netfps.value);
if (!spare && msecstouse<255 && cls.state == ca_active)
{
return;
}
if (spare > 0)
msecstouse -= spare;
}
#ifdef NQPROT #ifdef NQPROT
if (cls.protocol == CP_NETQUAKE) if (cls.protocol == CP_NETQUAKE)

View file

@ -27,10 +27,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "cl_master.h" #include "cl_master.h"
#include "cl_ignore.h" #include "cl_ignore.h"
#if defined(_WIN32) && !defined(MINGW) && defined(RGLQUAKE)
//#define WINAVI
#endif
// callbacks // callbacks
void CL_Sbar_Callback(struct cvar_s *var, char *oldvalue); void CL_Sbar_Callback(struct cvar_s *var, char *oldvalue);
void Name_Callback(struct cvar_s *var, char *oldvalue); void Name_Callback(struct cvar_s *var, char *oldvalue);
@ -1029,6 +1025,7 @@ void CL_Disconnect (void)
qbyte final[12]; qbyte final[12];
connect_time = -1; connect_time = -1;
connect_tries = 0;
Cvar_ApplyLatches(CVAR_SERVEROVERRIDE); Cvar_ApplyLatches(CVAR_SERVEROVERRIDE);
@ -3038,11 +3035,6 @@ Host_Frame
Runs all active servers Runs all active servers
================== ==================
*/ */
#if defined(WINAVI) && !defined(NOMEDIA)
extern float recordavi_frametime;
qboolean Media_Capturing();
#endif
extern cvar_t cl_netfps; extern cvar_t cl_netfps;
int nopacketcount; int nopacketcount;
void SNDDMA_SetUnderWater(qboolean underwater); void SNDDMA_SetUnderWater(qboolean underwater);
@ -3054,7 +3046,7 @@ void Host_Frame (double time)
static double time3 = 0; static double time3 = 0;
int pass1, pass2, pass3; int pass1, pass2, pass3;
// float fps; // float fps;
float realframetime; double realframetime;
static double spare; static double spare;
RSpeedLocals(); RSpeedLocals();
@ -3062,14 +3054,7 @@ void Host_Frame (double time)
if (setjmp (host_abort) ) if (setjmp (host_abort) )
return; // something bad happened, or the server disconnected return; // something bad happened, or the server disconnected
realframetime = time; realframetime = time = Media_TweekCaptureFrameTime(time);
#if defined(WINAVI) && !defined(NOMEDIA)
if (cls.demoplayback && Media_Capturing() && recordavi_frametime>0.01)
{
realframetime = time = recordavi_frametime;
}
#endif
// if (cls.demoplayback && cl_demospeed.value>0) // if (cls.demoplayback && cl_demospeed.value>0)
// realframetime *= cl_demospeed.value; // this probably screws up other timings // realframetime *= cl_demospeed.value; // this probably screws up other timings
@ -3117,8 +3102,9 @@ void Host_Frame (double time)
*/ */
Mod_Think(); //think even on idle (which means small walls and a fast cpu can get more surfaces done. Mod_Think(); //think even on idle (which means small walls and a fast cpu can get more surfaces done.
if (cl_maxfps.value>0 && (cl_netfps.value>0 || cls.demoplayback)) if ((cl_netfps.value>0 || cls.demoplayback || cl_indepphysics.value))
{ //limit the fps freely, and expect the netfps to cope. { //limit the fps freely, and expect the netfps to cope.
if (cl_maxfps.value > 0)
if ((realtime - oldrealtime) < 1/cl_maxfps.value) if ((realtime - oldrealtime) < 1/cl_maxfps.value)
return; return;
} }

View file

@ -3716,6 +3716,17 @@ void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds from n
Cmd_ExecuteString(stufftext, RESTRICT_SERVER+destsplit); //do this NOW so that it's done before any models or anything are loaded Cmd_ExecuteString(stufftext, RESTRICT_SERVER+destsplit); //do this NOW so that it's done before any models or anything are loaded
else else
{ {
if (!strncmp(stufftext, "//querycmd ", 11))
{
COM_Parse(stufftext + 11);
if (Cmd_Exists(com_token))
{
Cbuf_AddText ("cmd cmdsupported ", RESTRICT_SERVER+destsplit);
Cbuf_AddText (com_token, RESTRICT_SERVER+destsplit);
Cbuf_AddText ("\n", RESTRICT_SERVER+destsplit);
}
}
else
#ifdef CSQC_DAT #ifdef CSQC_DAT
if (!CSQC_StuffCmd(stufftext)) if (!CSQC_StuffCmd(stufftext))
#endif #endif

View file

@ -767,7 +767,7 @@ char *Media_NextTrack(void)
//Avi files are specific to windows. Bit of a bummer really. //Avi files are specific to windows. Bit of a bummer really.
#if defined(_WIN32) && !defined(__GNUC__) #if defined(_WIN32) && !defined(__GNUC__)
//#define WINAVI #define WINAVI
#endif #endif
@ -1462,7 +1462,7 @@ unsigned long recordavi_codec_fourcc;
soundcardinfo_t *capture_fakesounddevice; soundcardinfo_t *capture_fakesounddevice;
int recordavi_video_frame_counter; int recordavi_video_frame_counter;
int recordavi_audio_frame_counter; int recordavi_audio_frame_counter;
float recordavi_frametime; float recordavi_frametime; //length of a frame in fractional seconds
float recordavi_videotime; float recordavi_videotime;
float recordavi_audiotime; float recordavi_audiotime;
int capturesize; int capturesize;
@ -1512,6 +1512,16 @@ qboolean Media_PausedDemo (void)
return false; return false;
} }
double Media_TweekCaptureFrameTime(double time)
{
if (cls.demoplayback && Media_Capturing() && recordavi_frametime>0.01)
{
return time = recordavi_frametime;
}
return time;
}
void Media_RecordFrame (void) void Media_RecordFrame (void)
{ {
if (!capturetype) if (!capturetype)
@ -1546,19 +1556,12 @@ void Media_RecordFrame (void)
} }
//time for annother frame? //time for annother frame?
/*if (recordavi_uncompressed_audio_stream) //sync video to the same frame as audio.
{
if (recordavi_video_frame_counter > recordavi_audio_frame_counter)
goto skipframe;
}
else*/
{
if (recordavi_videotime > realtime+1) if (recordavi_videotime > realtime+1)
recordavi_videotime = realtime; //urm, wrapped?.. recordavi_videotime = realtime; //urm, wrapped?..
if (recordavi_videotime > realtime) if (recordavi_videotime > realtime)
goto skipframe; goto skipframe;
recordavi_videotime += recordavi_frametime; recordavi_videotime += recordavi_frametime;
} //audio is mixed to match the video times
switch (capturetype) switch (capturetype)
{ {
@ -1796,7 +1799,7 @@ void Media_RecordFilm_f (void)
!strcmp(fourcc, "pcx")) !strcmp(fourcc, "pcx"))
{ {
capturetype = CT_SCREENSHOT; capturetype = CT_SCREENSHOT;
strcpy(capturefilenameprefix, Cmd_Argv(1)); Q_strncpyz(capturefilenameprefix, Cmd_Argv(1), sizeof(capturefilenameprefix));
} }
else else
{ {
@ -1836,7 +1839,7 @@ void Media_RecordFilm_f (void)
} }
snprintf(filename, 192, "%s%s", com_quakedir, Cmd_Argv(1)); snprintf(filename, sizeof(filename) - 5, "%s%s", com_quakedir, Cmd_Argv(1));
COM_StripExtension(filename, filename, sizeof(filename)); COM_StripExtension(filename, filename, sizeof(filename));
COM_DefaultExtension (filename, ".avi", sizeof(filename)); COM_DefaultExtension (filename, ".avi", sizeof(filename));
@ -1952,6 +1955,7 @@ void Media_RecordFilm_f (void)
else else
{ {
Con_Printf("That sort of video capturing is not supported in this build\n"); Con_Printf("That sort of video capturing is not supported in this build\n");
capturetype = CT_NONE;
} }
} }
void Media_CaptureDemoEnd(void) void Media_CaptureDemoEnd(void)
@ -1968,6 +1972,8 @@ void Media_RecordDemo_f(void)
if (capturetype != CT_NONE) if (capturetype != CT_NONE)
recordingdemo = true; recordingdemo = true;
else
CL_Stopdemo_f(); //capturing failed for some reason
} }
#else #else
void Media_CaptureDemoEnd(void){} void Media_CaptureDemoEnd(void){}

View file

@ -15,10 +15,12 @@ qboolean M_Script_Key (int key, menu_t *menu)
{ {
if (menu->selecteditem && menu->selecteditem->common.type == mt_edit) if (menu->selecteditem && menu->selecteditem->common.type == mt_edit)
return false; return false;
if (key >= '0' && key <= '9') if (key >= '0' && key <= '9' && *menualias.string)
{ {
if (key == '0') //specal case so that "hello" < "0"... (plus matches impulses) if (key == '0') //specal case so that "hello" < "0"... (plus matches common impulses)
Cbuf_AddText(va("set option %i\n%s\n", key-'0'+1, menualias.string), RESTRICT_LOCAL); Cbuf_AddText(va("set option %i\n%s\n", 10, menualias.string), RESTRICT_LOCAL);
else
Cbuf_AddText(va("set option %i\n%s\n", key-'0', menualias.string), RESTRICT_LOCAL);
return true; return true;
} }
return false; return false;
@ -150,6 +152,9 @@ void M_MenuS_Picture_f (void)
return; return;
} }
if (!strcmp(Cmd_Argv(1), "-"))
MC_AddCenterPicture(menu_script, y, picname);
else
MC_AddPicture(menu_script, x, y, picname); MC_AddPicture(menu_script, x, y, picname);
} }

View file

@ -311,6 +311,7 @@ qboolean Media_ShowFilm(void);
void Media_CaptureDemoEnd(void); void Media_CaptureDemoEnd(void);
void Media_RecordFrame (void); void Media_RecordFrame (void);
qboolean Media_PausedDemo (void); qboolean Media_PausedDemo (void);
double Media_TweekCaptureFrameTime(double time);
void R_SetRenderer(int wanted); void R_SetRenderer(int wanted);
void RQ_Init(void); void RQ_Init(void);

View file

@ -1732,7 +1732,14 @@ void Sbar_Draw (void)
if (scr_viewsize.value != 120) if (scr_viewsize.value != 120)
Cvar_Set(&scr_viewsize, "120"); Cvar_Set(&scr_viewsize, "120");
Sbar_DrawString (0, -8, va("%i", cl.stats[pnum][STAT_HEALTH])); Sbar_DrawString (0, -8, va("Health: %i", cl.stats[pnum][STAT_HEALTH]));
Sbar_DrawString (0, -16, va(" Armor: %i", cl.stats[pnum][STAT_ARMOR]));
if (cl.stats[pnum][STAT_H2_BLUEMANA])
Sbar_DrawString (0, -24, va(" Blue: %i", cl.stats[pnum][STAT_H2_BLUEMANA]));
if (cl.stats[pnum][STAT_H2_GREENMANA])
Sbar_DrawString (0, -32, va(" Green: %i", cl.stats[pnum][STAT_H2_GREENMANA]));
continue; continue;
} }

View file

@ -1640,10 +1640,24 @@ void Cmd_ForwardToServer_f (void)
return; return;
} }
if (Q_strcasecmp(Cmd_Argv(1), "snap") == 0) { if (Q_strcasecmp(Cmd_Argv(1), "snap") == 0)
{
if (SCR_RSShot()) if (SCR_RSShot())
return; return;
} }
if (Q_strcasecmp(Cmd_Argv(1), "ptrack") == 0)
{
if (!*Cmd_Argv(2))
{
Cam_Unlock(0);
}
else
{
Cam_Lock(0, atoi(Cmd_Argv(2)));
autocam[0] = CAM_TRACK;
}
return;
}
if (cls.demoplayback) if (cls.demoplayback)
return; // not really connected return; // not really connected

View file

@ -618,6 +618,7 @@ qboolean CM_CreateBrush ( q2cbrush_t *brush, vec3_t *verts, q2mapsurface_t *surf
static mplane_t mainplane, patchplanes[20]; static mplane_t mainplane, patchplanes[20];
qboolean skip[20]; qboolean skip[20];
int numpatchplanes = 0; int numpatchplanes = 0;
float dot;
int matchplane; int matchplane;
@ -709,12 +710,16 @@ qboolean CM_CreateBrush ( q2cbrush_t *brush, vec3_t *verts, q2mapsurface_t *surf
for (matchplane = 0; matchplane < numplanes; matchplane++) for (matchplane = 0; matchplane < numplanes; matchplane++)
{ {
if (!memcmp(&map_planes[matchplane], &patchplanes[i], sizeof(patchplanes[i]))) 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]; plane = &map_planes[matchplane];
break; break;
} }
} }
}
if (matchplane == numplanes) if (matchplane == numplanes)
{ {
if (numplanes == MAX_Q2MAP_PLANES) if (numplanes == MAX_Q2MAP_PLANES)

View file

@ -400,6 +400,28 @@ char *COM_TrimString(char *str)
return buffer; return buffer;
} }
#ifdef _WIN32
#if (_MSC_VER >= 1400)
//with MSVC 8, use MS extensions
#define snprintf linuxlike_snprintf_vc8
int VARGS linuxlike_snprintf_vc8(char *buffer, int size, const char *format, ...);
#define vsnprintf(a, b, c, d) vsnprintf_s(a, b, _TRUNCATE, c, d)
#else
//msvc crap
#define snprintf linuxlike_snprintf
int VARGS linuxlike_snprintf(char *buffer, int size, const char *format, ...);
#define vsnprintf linuxlike_vsnprintf
int VARGS linuxlike_vsnprintf(char *buffer, int size, const char *format, va_list argptr);
#endif
#ifdef _MSC_VER
//these are provided so we don't use them
//but mingw has some defines elsewhere and makes gcc moan
#define _vsnprintf unsafe_vsnprintf
#define _snprintf unsafe_snprintf
#endif
#endif
char *EvaluateDebugString(progfuncs_t *progfuncs, char *key) char *EvaluateDebugString(progfuncs_t *progfuncs, char *key)
{ {
static char buf[256]; static char buf[256];
@ -509,7 +531,7 @@ char *EvaluateDebugString(progfuncs_t *progfuncs, char *key)
if (!func) if (!func)
{ {
assignment[-1] = '='; assignment[-1] = '=';
sprintf(buf, "Can't find function %s\n", s); snprintf(buf, sizeof(buf), "Can't find function %s\n", s);
return buf; return buf;
} }
*(func_t *)val = (func - pr_progstate[i].functions) | (i<<24); *(func_t *)val = (func - pr_progstate[i].functions) | (i<<24);

View file

@ -384,8 +384,8 @@ void QC_AddSharedFieldVar(progfuncs_t *progfuncs, int num, char *stringtable)
} }
//oh well, must be a parameter. //oh well, must be a parameter.
if (*(int *)&pr_globals[pr_globaldefs16[num].ofs]) // if (*(int *)&pr_globals[pr_globaldefs16[num].ofs])
Sys_Error("QCLIB: Global field var with no matching field \"%s\", from offset %i", pr_globaldefs16[num].s_name+stringtable, *(int *)&pr_globals[pr_globaldefs16[num].ofs]); // Sys_Error("QCLIB: Global field var with no matching field \"%s\", from offset %i", pr_globaldefs16[num].s_name+stringtable, *(int *)&pr_globals[pr_globaldefs16[num].ofs]);
return; return;
case 32: case 32:
for (i=1 ; i<pr_progs->numfielddefs; i++) for (i=1 ; i<pr_progs->numfielddefs; i++)

View file

@ -1183,7 +1183,7 @@ void SV_MVDPings (void);
void SV_MVDWriteToDisk(int type, int to, float time); void SV_MVDWriteToDisk(int type, int to, float time);
void MVDWrite_Begin(qbyte type, int to, int size); void MVDWrite_Begin(qbyte type, int to, int size);
void MVDSetMsgBuf(demobuf_t *prev,demobuf_t *cur); void MVDSetMsgBuf(demobuf_t *prev,demobuf_t *cur);
void SV_MVDStop (int reason); void SV_MVDStop (int reason, qboolean mvdonly);
void SV_MVDStop_f (void); void SV_MVDStop_f (void);
void SV_MVDWritePackets (int num); void SV_MVDWritePackets (int num);
void MVD_Init (void); void MVD_Init (void);

View file

@ -1295,6 +1295,9 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us
} }
} }
} }
SV_MVD_SendInitialGamestate(NULL);
} }
#endif #endif

View file

@ -112,7 +112,7 @@ void DestFlush(qboolean compleate)
if (!demo.dest) if (!demo.dest)
{ {
SV_MVDStop(2); SV_MVDStop(2, false);
return; return;
} }
} }
@ -497,6 +497,7 @@ void SV_MVD_RunPendingConnections(void)
if (p->hasauthed == true) if (p->hasauthed == true)
{ {
e = ("QTVSV 1\n" e = ("QTVSV 1\n"
"BEGIN\n"
"\n"); "\n");
send(p->socket, e, strlen(e), 0); send(p->socket, e, strlen(e), 0);
e = NULL; e = NULL;
@ -531,18 +532,29 @@ void SV_MVD_RunPendingConnections(void)
} }
} }
void DestCloseAllFlush(qboolean destroyfiles) int DestCloseAllFlush(qboolean destroyfiles, qboolean mvdonly)
{ {
mvddest_t *d; int numclosed = 0;
mvddest_t *d, **prev;
DestFlush(true); //make sure it's all written. DestFlush(true); //make sure it's all written.
while (demo.dest) prev = &demo.dest;
{
d = demo.dest; d = demo.dest;
demo.dest = d->nextdest; while(d)
{
if (!mvdonly || d->desttype != DEST_STREAM)
{
*prev = d->nextdest;
DestClose(d, destroyfiles); DestClose(d, destroyfiles);
numclosed++;
} }
else
prev = &d;
d = (*prev)->nextdest;
}
return numclosed;
} }
@ -1504,8 +1516,9 @@ SV_Stop
stop recording a demo stop recording a demo
==================== ====================
*/ */
void SV_MVDStop (int reason) void SV_MVDStop (int reason, qboolean mvdonly)
{ {
int numclosed;
if (!sv.mvdrecording) if (!sv.mvdrecording)
{ {
Con_Printf ("Not recording a demo.\n"); Con_Printf ("Not recording a demo.\n");
@ -1514,7 +1527,7 @@ void SV_MVDStop (int reason)
if (reason == 2) if (reason == 2)
{ {
DestCloseAllFlush(true); DestCloseAllFlush(true, mvdonly);
// stop and remove // stop and remove
if (!demo.dest) if (!demo.dest)
@ -1539,14 +1552,17 @@ void SV_MVDStop (int reason)
SV_MVDWritePackets(demo.parsecount - demo.lastwritten + 1); SV_MVDWritePackets(demo.parsecount - demo.lastwritten + 1);
// finish up // finish up
DestCloseAllFlush(false); numclosed = DestCloseAllFlush(false, mvdonly);
if (!demo.dest)
sv.mvdrecording = false; sv.mvdrecording = false;
if (numclosed)
{
if (!reason) if (!reason)
SV_BroadcastPrintf (PRINT_CHAT, "Server recording completed\n"); SV_BroadcastPrintf (PRINT_CHAT, "Server recording completed\n");
else else
SV_BroadcastPrintf (PRINT_CHAT, "Server recording stoped\nMax demo size exceeded\n"); SV_BroadcastPrintf (PRINT_CHAT, "Server recording stoped\nMax demo size exceeded\n");
}
Cvar_ForceSet(Cvar_Get("serverdemo", "", CVAR_NOSET, ""), ""); Cvar_ForceSet(Cvar_Get("serverdemo", "", CVAR_NOSET, ""), "");
} }
@ -1558,7 +1574,7 @@ SV_Stop_f
*/ */
void SV_MVDStop_f (void) void SV_MVDStop_f (void)
{ {
SV_MVDStop(0); SV_MVDStop(0, true);
} }
/* /*
@ -1570,7 +1586,7 @@ Stops recording, and removes the demo
*/ */
void SV_MVD_Cancel_f (void) void SV_MVD_Cancel_f (void)
{ {
SV_MVDStop(2); SV_MVDStop(2, true);
} }
/* /*
@ -1631,9 +1647,10 @@ void SV_WriteSetMVDMessage (void)
DestFlush(false); DestFlush(false);
} }
void SV_MVD_SendInitialGamestate(mvddest_t *dest);
static qboolean SV_MVD_Record (mvddest_t *dest) static qboolean SV_MVD_Record (mvddest_t *dest)
{ {
sizebuf_t buf; /* sizebuf_t buf;
char buf_data[MAX_QWMSGLEN]; char buf_data[MAX_QWMSGLEN];
int n, i; int n, i;
char *s, info[MAX_INFO_STRING]; char *s, info[MAX_INFO_STRING];
@ -1641,6 +1658,8 @@ static qboolean SV_MVD_Record (mvddest_t *dest)
client_t *player; client_t *player;
char *gamedir; char *gamedir;
int seq = 1; int seq = 1;
*/
int i;
if (!dest) if (!dest)
return false; return false;
@ -1663,16 +1682,36 @@ static qboolean SV_MVD_Record (mvddest_t *dest)
demo.datagram.maxsize = sizeof(demo.datagram_data); demo.datagram.maxsize = sizeof(demo.datagram_data);
demo.datagram.data = demo.datagram_data; demo.datagram.data = demo.datagram_data;
sv.mvdrecording = true;
} }
// else // else
// SV_WriteRecordMVDMessage(&buf, dem_read); // SV_WriteRecordMVDMessage(&buf, dem_read);
demo.pingtime = demo.time = sv.time;
dest->nextdest = demo.dest; dest->nextdest = demo.dest;
demo.dest = dest; demo.dest = dest;
SV_MVD_SendInitialGamestate(dest);
return true;
}
void SV_MVD_SendInitialGamestate(mvddest_t *dest)
{
sizebuf_t buf;
char buf_data[MAX_QWMSGLEN];
int n, i;
char *s, info[MAX_INFO_STRING];
client_t *player;
char *gamedir;
int seq = 1;
if (!demo.dest)
return;
sv.mvdrecording = true;
demo.pingtime = demo.time = sv.time;
singledest = dest; singledest = dest;
/*-------------------------------------------------*/ /*-------------------------------------------------*/
@ -1937,9 +1976,6 @@ static qboolean SV_MVD_Record (mvddest_t *dest)
SV_WriteSetMVDMessage(); SV_WriteSetMVDMessage();
singledest = NULL; singledest = NULL;
// done
return true;
} }
/* /*

View file

@ -273,7 +273,7 @@ void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox)
prox->flushing = false; prox->flushing = false;
BuildServerData(qtv, &msg, true, 0, true); BuildServerData(qtv, &msg, 0, NULL);
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1); Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0; msg.cursize = 0;

View file

@ -28,6 +28,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, char *data, netadr_t adr) void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, char *data, netadr_t adr)
{ {
int ret; int ret;
Sys_Printf(cluster, "udp send size %i\n", length);
if (length > 1000)
Sys_Printf(cluster, "udp send size %i\n", length);
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) if (ret < 0)
{ {

View file

@ -649,11 +649,20 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers)
tv->players[num].current.angles[2] = nonnullcmd.angles[2]; tv->players[num].current.angles[2] = nonnullcmd.angles[2];
} }
else else
{ //the only reason we'd not get a command is if it's us.
if (tv->controller)
{
tv->players[num].current.angles[0] = tv->controller->ucmds[2].angles[0];
tv->players[num].current.angles[1] = tv->controller->ucmds[2].angles[1];
tv->players[num].current.angles[2] = tv->controller->ucmds[2].angles[2];
}
else
{ {
tv->players[num].current.angles[0] = tv->proxyplayerangles[0]/360*65535; tv->players[num].current.angles[0] = tv->proxyplayerangles[0]/360*65535;
tv->players[num].current.angles[1] = tv->proxyplayerangles[1]/360*65535; tv->players[num].current.angles[1] = tv->proxyplayerangles[1]/360*65535;
tv->players[num].current.angles[2] = tv->proxyplayerangles[2]/360*65535; tv->players[num].current.angles[2] = tv->proxyplayerangles[2]/360*65535;
} }
}
for (i=0 ; i<3 ; i++) for (i=0 ; i<3 ; i++)
{ {

View file

@ -412,10 +412,12 @@ typedef struct viewer_s {
unsigned int currentstats[MAX_STATS]; unsigned int currentstats[MAX_STATS];
int trackplayer; int trackplayer;
int thisplayer; int thisplayer;
int userid;
packet_entities_t frame[ENTITY_FRAMES]; packet_entities_t frame[ENTITY_FRAMES];
struct viewer_s *next; struct viewer_s *next;
struct viewer_s *commentator;
char name[32]; char name[32];
char userinfo[1024]; char userinfo[1024];
@ -615,6 +617,7 @@ struct cluster_s {
sv_t *servers; sv_t *servers;
int numservers; int numservers;
int nextstreamid; int nextstreamid;
int nextuserid;
//options //options
int qwlistenportnum; int qwlistenportnum;
@ -835,7 +838,7 @@ void WriteData(netmsg_t *b, const char *data, int length);
void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playermask,int suitablefor); void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playermask,int suitablefor);
void Broadcast(cluster_t *cluster, char *buffer, int length, int suitablefor); void Broadcast(cluster_t *cluster, char *buffer, int length, int suitablefor);
void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask); void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask);
void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount, qboolean spectatorflag); 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); void BuildNQServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount);
SOCKET QW_InitUDPSocket(int port); SOCKET QW_InitUDPSocket(int port);
void QW_UpdateUDPStuff(cluster_t *qtv); void QW_UpdateUDPStuff(cluster_t *qtv);

View file

@ -477,6 +477,10 @@ SOURCE=.\parse.c
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=.\pmove.c
# End Source File
# Begin Source File
SOURCE=.\qw.c SOURCE=.\qw.c
!IF "$(CFG)" == "qtvprox - Win32 Release" !IF "$(CFG)" == "qtvprox - Win32 Release"

View file

@ -205,7 +205,7 @@ SOCKET QW_InitUDPSocket(int port)
return sock; return sock;
} }
void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount, qboolean spectatorflag) void BuildServerData(sv_t *tv, netmsg_t *msg, int servercount, viewer_t *viewer)
{ {
movevars_t movevars; movevars_t movevars;
WriteByte(msg, svc_serverdata); WriteByte(msg, svc_serverdata);
@ -217,10 +217,10 @@ void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount, qbo
//dummy connection, for choosing a game to watch. //dummy connection, for choosing a game to watch.
WriteString(msg, "qw"); WriteString(msg, "qw");
if (mvd) if (!viewer)
WriteFloat(msg, 0); WriteFloat(msg, 0);
else else
WriteByte(msg, (MAX_CLIENTS-1) | (spectatorflag?128:0)); WriteByte(msg, (MAX_CLIENTS-1) | (128));
WriteString(msg, "FTEQTV Proxy"); WriteString(msg, "FTEQTV Proxy");
@ -249,10 +249,15 @@ void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount, qbo
{ {
WriteString(msg, tv->gamedir); WriteString(msg, tv->gamedir);
if (mvd) if (!viewer)
WriteFloat(msg, 0); WriteFloat(msg, 0);
else else
WriteByte(msg, tv->thisplayer | (spectatorflag?128:0)); {
if (tv->controller == viewer)
WriteByte(msg, viewer->thisplayer);
else
WriteByte(msg, viewer->thisplayer | 128);
}
WriteString(msg, tv->mapname); WriteString(msg, tv->mapname);
@ -364,7 +369,7 @@ void SendServerData(sv_t *tv, viewer_t *viewer)
if (viewer->netchan.isnqprotocol) if (viewer->netchan.isnqprotocol)
BuildNQServerData(tv, &msg, false, viewer->thisplayer); BuildNQServerData(tv, &msg, false, viewer->thisplayer);
else else
BuildServerData(tv, &msg, false, viewer->servercount, !tv || tv->controller != viewer); BuildServerData(tv, &msg, viewer->servercount, viewer);
SendBufferToViewer(viewer, msg.data, msg.cursize, true); SendBufferToViewer(viewer, msg.data, msg.cursize, true);
@ -646,6 +651,7 @@ qboolean ChallengePasses(netadr_t *addr, int challenge)
void NewClient(cluster_t *cluster, viewer_t *viewer) void NewClient(cluster_t *cluster, viewer_t *viewer)
{ {
viewer->userid = ++cluster->nextuserid;
viewer->timeout = cluster->curtime + 15*1000; viewer->timeout = cluster->curtime + 15*1000;
viewer->trackplayer = -1; viewer->trackplayer = -1;
@ -689,7 +695,7 @@ void ParseUserInfo(cluster_t *cluster, viewer_t *viewer)
strcpy(temp, "unnamed"); strcpy(temp, "unnamed");
if (!*viewer->name) if (!*viewer->name)
Sys_Printf(cluster, "Viewer %s connected\n", temp); Sys_Printf(cluster, "Viewer %s connected\n", temp);
Q_strncpyz(viewer->name, temp, sizeof(temp)); Q_strncpyz(viewer->name, temp, sizeof(viewer->name));
Info_ValueForKey(viewer->userinfo, "rate", temp, sizeof(temp)); Info_ValueForKey(viewer->userinfo, "rate", temp, sizeof(temp));
rate = atof(temp); rate = atof(temp);
@ -815,6 +821,7 @@ void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage)
memset(viewer, 0, sizeof(viewer_t)); memset(viewer, 0, sizeof(viewer_t));
Netchan_Setup (cluster->qwdsocket, &viewer->netchan, *addr, atoi(qport), false); Netchan_Setup (cluster->qwdsocket, &viewer->netchan, *addr, atoi(qport), false);
viewer->netchan.message.maxsize = 1400;
viewer->next = cluster->viewers; viewer->next = cluster->viewers;
cluster->viewers = viewer; cluster->viewers = viewer;
@ -1717,12 +1724,15 @@ void SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg
void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg)
{ {
viewer_t *cv;
packet_entities_t *e; packet_entities_t *e;
int i; int i;
usercmd_t to; usercmd_t to;
unsigned short flags; unsigned short flags;
short interp; short interp;
float lerp; float lerp;
int track;
int runaway = 10;
memset(&to, 0, sizeof(to)); memset(&to, 0, sizeof(to));
@ -1749,6 +1759,21 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg)
if (tv->controller == v) if (tv->controller == v)
lerp = 1; lerp = 1;
track = v->trackplayer;
for (cv = v; cv && runaway-->0; cv = cv->commentator)
{
track = cv->trackplayer;
if (track != MAX_CLIENTS-2)
break;
}
/*
if (v->commentator && track == MAX_CLIENTS-2)
{
track = v->commentator->trackplayer;
if (track < 0)
track = MAX_CLIENTS-2;
}*/
for (i = 0; i < MAX_CLIENTS; i++) for (i = 0; i < MAX_CLIENTS; i++)
{ {
if (i == v->thisplayer) if (i == v->thisplayer)
@ -1756,14 +1781,56 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg)
SendLocalPlayerState(tv, v, i, msg); SendLocalPlayerState(tv, v, i, msg);
continue; continue;
} }
if (v->commentator)// && track == i)
{
if (i == MAX_CLIENTS-2)
{
flags = PF_COMMAND;
WriteByte(msg, svc_playerinfo);
WriteByte(msg, i);
WriteShort(msg, flags);
interp = v->commentator->origin[0]*8;
WriteShort(msg, interp);
interp = v->commentator->origin[1]*8;
WriteShort(msg, interp);
interp = v->commentator->origin[2]*8;
WriteShort(msg, interp);
WriteByte(msg, 0);
if (flags & PF_MSEC)
{
WriteByte(msg, 0);
}
if (flags & PF_COMMAND)
{
to.angles[0] = v->commentator->ucmds[2].angles[0];
to.angles[1] = v->commentator->ucmds[2].angles[1];
to.angles[2] = v->commentator->ucmds[2].angles[2];
WriteDeltaUsercmd(msg, &nullcmd, &to);
}
if (flags & PF_MODEL)
WriteByte(msg, tv->players[i].current.modelindex);
if (flags & PF_WEAPONFRAME)
WriteByte(msg, tv->players[i].current.weaponframe);
continue;
}
if (track == i)
continue;
}
if (!tv->players[i].active) if (!tv->players[i].active)
continue; continue;
if (v->trackplayer != i && !BSP_Visible(tv->bsp, tv->players[i].leafcount, tv->players[i].leafs)) //bsp cull. currently tracked player is always visible
if (track != i && !BSP_Visible(tv->bsp, tv->players[i].leafcount, tv->players[i].leafs))
continue; continue;
flags = PF_COMMAND; flags = PF_COMMAND;
if (v->trackplayer == i && tv->players[i].current.weaponframe) if (track == i && tv->players[i].current.weaponframe)
flags |= PF_WEAPONFRAME; flags |= PF_WEAPONFRAME;
WriteByte(msg, svc_playerinfo); WriteByte(msg, svc_playerinfo);
@ -1860,6 +1927,7 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg)
void UpdateStats(sv_t *qtv, viewer_t *v) void UpdateStats(sv_t *qtv, viewer_t *v)
{ {
viewer_t *cv;
netmsg_t msg; netmsg_t msg;
char buf[6]; char buf[6];
int i; int i;
@ -1869,12 +1937,17 @@ void UpdateStats(sv_t *qtv, viewer_t *v)
InitNetMsg(&msg, buf, sizeof(buf)); InitNetMsg(&msg, buf, sizeof(buf));
if (qtv && qtv->controller == v) if (v->commentator)
cv = v->commentator;
else
cv = v;
if (qtv && qtv->controller == cv)
stats = qtv->players[qtv->thisplayer].stats; stats = qtv->players[qtv->thisplayer].stats;
else if (v->trackplayer == -1 || !qtv) else if (cv->trackplayer == -1 || !qtv)
stats = nullstats; stats = nullstats;
else else
stats = qtv->players[v->trackplayer].stats; stats = qtv->players[cv->trackplayer].stats;
for (i = 0; i < MAX_STATS; i++) for (i = 0; i < MAX_STATS; i++)
{ {
@ -1986,6 +2059,32 @@ void PMove(viewer_t *v, usercmd_t *cmd)
v->velocity[2] = pmove.velocity[2]; v->velocity[2] = pmove.velocity[2];
} }
void QW_SetCommentator(viewer_t *v, viewer_t *commentator)
{
// if (v->commentator == commentator)
// return;
WriteByte(&v->netchan.message, svc_setinfo);
WriteByte(&v->netchan.message, MAX_CLIENTS-2);
WriteString(&v->netchan.message, "name");
if (commentator)
{
WriteString(&v->netchan.message, commentator->name);
QW_StuffcmdToViewer(v, "cmd ptrack %i\n", MAX_CLIENTS-2);
QW_PrintfToViewer(v, "Following commentator %s\n", commentator->name);
if (v->server != commentator->server)
QW_SetViewersServer(v, commentator->server);
}
else
{
WriteString(&v->netchan.message, "");
if (v->commentator )
QW_PrintfToViewer(v, "Commentator disabled\n");
}
v->commentator = commentator;
}
void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean noupwards) void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean noupwards)
{ {
char buf[1024]; char buf[1024];
@ -2378,6 +2477,42 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean
if (remaining) if (remaining)
QW_PrintfToViewer(v, "%i clients not shown\n", remaining); QW_PrintfToViewer(v, "%i clients not shown\n", remaining);
} }
else if (!strncmp(message, ".followid ", 8))
{
int id = atoi(message+8);
viewer_t *cv;
for (cv = cluster->viewers; cv; cv = cv->next)
{
if (cv->userid == id)
{
QW_SetCommentator(v, cv);
return;
}
}
QW_PrintfToViewer(v, "Couldn't find that player\n");
QW_SetCommentator(v, NULL);
}
else if (!strncmp(message, ".follow ", 8))
{
char *id = message+8;
viewer_t *cv;
for (cv = cluster->viewers; cv; cv = cv->next)
{
if (!strcmp(cv->name, id))
{
QW_SetCommentator(v, cv);
return;
}
}
QW_PrintfToViewer(v, "Couldn't find that player\n");
QW_SetCommentator(v, NULL);
}
else if (!strncmp(message, ".follow", 7))
{
QW_SetCommentator(v, NULL);
}
else if (!strncmp(message, "proxy:menu up", 13)) else if (!strncmp(message, "proxy:menu up", 13))
{ {
v->menuop -= 1; v->menuop -= 1;
@ -2426,6 +2561,7 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean
QW_StuffcmdToViewer(v, "bind downarrow +proxback\n"); QW_StuffcmdToViewer(v, "bind downarrow +proxback\n");
QW_StuffcmdToViewer(v, "bind rightarrow +proxright\n"); QW_StuffcmdToViewer(v, "bind rightarrow +proxright\n");
QW_StuffcmdToViewer(v, "bind leftarrow +proxleft\n"); QW_StuffcmdToViewer(v, "bind leftarrow +proxleft\n");
QW_PrintfToViewer(v, "Keys bound not recognised\n");
} }
else if (!strncmp(message, ".menu bind", 10) || !strncmp(message, "proxy:menu bindstd", 18)) else if (!strncmp(message, ".menu bind", 10) || !strncmp(message, "proxy:menu bindstd", 18))
{ {
@ -2440,6 +2576,8 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean
QW_StuffcmdToViewer(v, "bind end \"proxy:menu end\"\n"); QW_StuffcmdToViewer(v, "bind end \"proxy:menu end\"\n");
QW_StuffcmdToViewer(v, "bind pause \"proxy:menu\"\n"); QW_StuffcmdToViewer(v, "bind pause \"proxy:menu\"\n");
QW_StuffcmdToViewer(v, "bind backspace \"proxy:menu back\"\n"); QW_StuffcmdToViewer(v, "bind backspace \"proxy:menu back\"\n");
QW_PrintfToViewer(v, "All keys bound not recognised\n");
} }
else if (!strncmp(message, ".", 1) && strncmp(message, "..", 2)) else if (!strncmp(message, ".", 1) && strncmp(message, "..", 2))
{ {
@ -2860,6 +2998,7 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m)
} }
else if (!strncmp(buf, "begin", 5)) else if (!strncmp(buf, "begin", 5))
{ {
viewer_t *com;
if (atoi(buf+6) != v->servercount) if (atoi(buf+6) != v->servercount)
SendServerData(qtv, v); //this is unfortunate! SendServerData(qtv, v); //this is unfortunate!
else else
@ -2871,6 +3010,10 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m)
SendBufferToViewer(v, msgb, sizeof(msgb), true); SendBufferToViewer(v, msgb, sizeof(msgb), true);
} }
} }
com = v->commentator;
v->commentator = NULL;
QW_SetCommentator(v, com);
} }
else if (!strncmp(buf, "download", 8)) else if (!strncmp(buf, "download", 8))
{ {
@ -2901,9 +3044,16 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m)
QW_PrintfToViewer(v, "%s is not on the proxy, sorry\n", buf+5); //the apology is to make the alternatives distinct. QW_PrintfToViewer(v, "%s is not on the proxy, sorry\n", buf+5); //the apology is to make the alternatives distinct.
} }
else if (!strncmp(buf, "ptrack ", 7)) else if (!strncmp(buf, "ptrack ", 7))
{
v->trackplayer = atoi(buf+7); v->trackplayer = atoi(buf+7);
// if (v->trackplayer != MAX_CLIENTS-2)
// QW_SetCommentator(v, NULL);
}
else if (!strncmp(buf, "ptrack", 6)) else if (!strncmp(buf, "ptrack", 6))
{
v->trackplayer = -1; v->trackplayer = -1;
QW_SetCommentator(v, NULL);
}
else if (!strncmp(buf, "pings", 5)) else if (!strncmp(buf, "pings", 5))
{ {
} }
@ -3364,6 +3514,7 @@ static const char dropcmd[] = {svc_stufftext, 'd', 'i', 's', 'c', 'o', 'n', 'n',
void QW_FreeViewer(cluster_t *cluster, viewer_t *viewer) void QW_FreeViewer(cluster_t *cluster, viewer_t *viewer)
{ {
viewer_t *oview;
int i; int i;
//note: unlink them yourself. //note: unlink them yourself.
@ -3388,6 +3539,12 @@ void QW_FreeViewer(cluster_t *cluster, viewer_t *viewer)
viewer->server->numviewers--; viewer->server->numviewers--;
} }
for (oview = cluster->viewers; oview; oview = oview->next)
{
if (oview->commentator == viewer)
QW_SetCommentator(oview, NULL);
}
free(viewer); free(viewer);
cluster->numviewers--; cluster->numviewers--;
@ -3436,6 +3593,9 @@ void QW_UpdateUDPStuff(cluster_t *cluster)
continue; continue;
} }
// if (rand()&3)
// continue;
m.data = buffer; m.data = buffer;
m.cursize = read; m.cursize = read;
m.maxsize = MAX_MSGLEN; m.maxsize = MAX_MSGLEN;

View file

@ -518,10 +518,17 @@ char *Cmd_Status(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffe
catbuffer(buffer, sizeofbuffer, "Connected\n"); catbuffer(buffer, sizeofbuffer, "Connected\n");
if (qtv->parsingqtvheader || qtv->parsingconnectiondata) if (qtv->parsingqtvheader || qtv->parsingconnectiondata)
catbuffer(buffer, sizeofbuffer, "Waiting for gamestate\n"); catbuffer(buffer, sizeofbuffer, "Waiting for gamestate\n");
if (qtv->usequkeworldprotocols)
{
catbuffer(buffer, sizeofbuffer, "QuakeWorld protocols\n");
if (qtv->controller) if (qtv->controller)
{ {
catbuffer(buffer, sizeofbuffer, "Spectating through %s\n"); catbuffer(buffer, sizeofbuffer, "Controlled by %s\n", qtv->controller->name);
} }
}
else if (qtv->sourcesock == INVALID_SOCKET && !qtv->sourcefile)
catbuffer(buffer, sizeofbuffer, "Connection not established\n");
if (*qtv->modellist[1].name) if (*qtv->modellist[1].name)
{ {
catbuffer(buffer, sizeofbuffer, "Map name %s\n", qtv->modellist[1].name); catbuffer(buffer, sizeofbuffer, "Map name %s\n", qtv->modellist[1].name);

View file

@ -490,7 +490,7 @@ qboolean Net_WriteUpStream(sv_t *qtv)
{ {
int len; int len;
if (qtv->upstreambuffersize) if (qtv->upstreambuffersize && qtv->sourcesock != INVALID_SOCKET)
{ {
len = send(qtv->sourcesock, qtv->upstreambuffer, qtv->upstreambuffersize, 0); len = send(qtv->sourcesock, qtv->upstreambuffer, qtv->upstreambuffersize, 0);
if (len == 0) if (len == 0)
@ -589,6 +589,7 @@ qboolean Net_ReadStream(sv_t *qtv)
Sys_Printf(qtv->cluster, "Error: server %s refused connection\n", qtv->server); Sys_Printf(qtv->cluster, "Error: server %s refused connection\n", qtv->server);
closesocket(qtv->sourcesock); closesocket(qtv->sourcesock);
qtv->sourcesock = INVALID_SOCKET; qtv->sourcesock = INVALID_SOCKET;
qtv->upstreambuffersize = 0; //probably contains initial connection request info
return false; return false;
} }
@ -1049,7 +1050,7 @@ void QTV_ParseQWStream(sv_t *qtv)
strcpy(qtv->status, "Attemping connection\n"); strcpy(qtv->status, "Attemping connection\n");
qtv->challenge = atoi(buffer+5); qtv->challenge = atoi(buffer+5);
if (qtv->controller) if (qtv->controller)
sprintf(buffer, "connect %i %i %i \"%s\\*qtv\\1\\\"", 28, qtv->qport, qtv->challenge, qtv->controller->userinfo); sprintf(buffer, "connect %i %i %i \"%s\\*qtv\\1\"", 28, qtv->qport, qtv->challenge, qtv->controller->userinfo);
else if (qtv->proxyplayer) else if (qtv->proxyplayer)
sprintf(buffer, "connect %i %i %i \"%s\\name\\%s\"", 28, qtv->qport, qtv->challenge, "\\*ver\\fteqtv\\spectator\\0\\rate\\10000", qtv->cluster->hostname); sprintf(buffer, "connect %i %i %i \"%s\\name\\%s\"", 28, qtv->qport, qtv->challenge, "\\*ver\\fteqtv\\spectator\\0\\rate\\10000", qtv->cluster->hostname);
else else
@ -1661,6 +1662,8 @@ void QTV_Run(sv_t *qtv)
Net_ReadStream(qtv); Net_ReadStream(qtv);
qtv->parsetime += packettime; qtv->parsetime += packettime;
qtv->nextconnectattempt = qtv->curtime + RECONNECT_TIME;
} }
else else
break; break;