Added predictable spectator movement. Changed chat to only show to clients on that stream. Added rate limiting. Fixed the stats bug. Added a .clients command.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@2421 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2006-10-20 14:25:20 +00:00
parent 51ba51cb03
commit af5348a8f2
5 changed files with 411 additions and 92 deletions

View File

@ -4,7 +4,7 @@ STRIP=strip
STRIPFLAGS=--strip-unneeded --remove-section=.comment
OBJS = netchan.o parse.o qw.o source.o bsp.o rcon.o mdfour.o crc.o control.o forward.o
OBJS = netchan.o parse.o qw.o source.o bsp.o rcon.o mdfour.o crc.o control.o forward.o pmove.o
qtv: $(OBJS) qtv.h
$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $@.db -lm

161
fteqtv/pmove.c Normal file
View File

@ -0,0 +1,161 @@
#include "qtv.h"
#include <math.h>
#define M_PI 3.1415926535897932384626433832795
void AngleVectors (vec3_t angles, float *forward, float *right, float *up)
{
float angle;
float sr, sp, sy, cr, cp, cy;
angle = angles[1] * (M_PI*2 / 360);
sy = sin(angle);
cy = cos(angle);
angle = angles[0] * (M_PI*2 / 360);
sp = sin(angle);
cp = cos(angle);
angle = angles[2] * (M_PI*2 / 360);
sr = sin(angle);
cr = cos(angle);
forward[0] = cp*cy;
forward[1] = cp*sy;
forward[2] = -sp;
right[0] = (-1*sr*sp*cy+-1*cr*-sy);
right[1] = (-1*sr*sp*sy+-1*cr*cy);
right[2] = -1*sr*cp;
up[0] = (cr*sp*cy+-sr*-sy);
up[1] = (cr*sp*sy+-sr*cy);
up[2] = cr*cp;
}
#define DotProduct(a,b) ((a[0]*b[0]) + (a[1]*b[1]) + (a[2]*b[2]))
#define VectorCopy(a,b) do{b[0]=a[0];b[1]=a[1];b[2]=a[2];}while(0)
#define VectorClear(v) do{v[0]=0;v[1]=0;v[2]=0;}while(0)
#define VectorScale(i,s,o) do{o[0]=i[0]*s;o[1]=i[1]*s;o[2]=i[2]*s;}while(0)
#define Length(v) sqrt(DotProduct(v, v))
#define VectorMA(base,s,m,out) do{out[0]=base[0]+s*m[0];out[1]=base[1]+s*m[1];out[2]=base[2]+s*m[2];}while(0)
#define SHORT2ANGLE(s) ((s*360.0f)/65536)
float VectorNormalize(vec3_t v)
{
float len, ilen;
len = Length(v);
if (len)
{
ilen = 1/len;
v[0] *= ilen;
v[1] *= ilen;
v[2] *= ilen;
}
return len;
}
void PM_SpectatorMove (pmove_t *pmove)
{
float speed, drop, friction, control, newspeed;
float currentspeed, addspeed, accelspeed;
int i;
vec3_t wishvel;
float fmove, smove;
vec3_t wishdir;
float wishspeed;
// friction
speed = Length (pmove->velocity);
if (speed < 1)
{
VectorClear (pmove->velocity);
}
else
{
drop = 0;
friction = pmove->movevars.friction*1.5; // extra friction
control = speed < pmove->movevars.stopspeed ? pmove->movevars.stopspeed : speed;
drop += control*friction*pmove->frametime;
// scale the velocity
newspeed = speed - drop;
if (newspeed < 0)
newspeed = 0;
newspeed /= speed;
VectorScale (pmove->velocity, newspeed, pmove->velocity);
}
// accelerate
fmove = pmove->cmd.forwardmove;
smove = pmove->cmd.sidemove;
VectorNormalize (pmove->forward);
VectorNormalize (pmove->right);
for (i=0 ; i<3 ; i++)
wishvel[i] = pmove->forward[i]*fmove + pmove->right[i]*smove;
wishvel[2] += pmove->cmd.upmove;
VectorCopy (wishvel, wishdir);
wishspeed = VectorNormalize(wishdir);
//
// clamp to server defined max speed
//
if (wishspeed > pmove->movevars.spectatormaxspeed)
{
VectorScale (wishvel, pmove->movevars.spectatormaxspeed/wishspeed, wishvel);
wishspeed = pmove->movevars.spectatormaxspeed;
}
currentspeed = DotProduct(pmove->velocity, wishdir);
addspeed = wishspeed - currentspeed;
// Buggy QW spectator mode, kept for compatibility
// if (pmove->pm_type == PM_OLD_SPECTATOR)
{
if (addspeed <= 0)
return;
}
if (addspeed > 0)
{
accelspeed = pmove->movevars.accelerate*pmove->frametime*wishspeed;
if (accelspeed > addspeed)
accelspeed = addspeed;
for (i=0 ; i<3 ; i++)
pmove->velocity[i] += accelspeed*wishdir[i];
}
// move
VectorMA (pmove->origin, pmove->frametime, pmove->velocity, pmove->origin);
}
void PM_PlayerMove (pmove_t *pmove)
{
pmove->frametime = pmove->cmd.msec * 0.001;
/*
if (pmove.pm_type == PM_NONE || pmove.pm_type == PM_FREEZE)
{
PM_CategorizePosition ();
return;
}
*/
// take angles directly from command
pmove->angles[0] = SHORT2ANGLE(pmove->cmd.angles[0]);
pmove->angles[1] = SHORT2ANGLE(pmove->cmd.angles[1]);
pmove->angles[2] = SHORT2ANGLE(pmove->cmd.angles[2]);
AngleVectors (pmove->angles, pmove->forward, pmove->right, pmove->up);
// if (pmove->pm_type == PM_SPECTATOR || pmove->pm_type == PM_OLD_SPECTATOR)
{
PM_SpectatorMove (pmove);
// pmove->onground = false;
return;
}
}

View File

@ -356,6 +356,36 @@ typedef struct {
} usercmd_t;
extern const usercmd_t nullcmd;
typedef float vec3_t[3];
typedef struct {
float gravity;
float maxspeed;
float spectatormaxspeed;
float accelerate;
float airaccelerate;
float waterfriction;
float entgrav;
float stopspeed;
float wateraccelerate;
float friction;
} movevars_t;
typedef struct {
//in / out
vec3_t origin;
vec3_t velocity;
//in
usercmd_t cmd;
movevars_t movevars;
//internal
vec3_t angles;
float frametime;
vec3_t forward, right, up;
} pmove_t;
#define MAX_BACK_BUFFERS 16
typedef struct sv_s sv_t;
typedef struct cluster_s cluster_t;
@ -384,7 +414,7 @@ typedef struct viewer_s {
struct viewer_s *next;
char name[32];
char userinfo[256];
char userinfo[1024];
int lost; //packets
usercmd_t ucmds[3];
@ -392,7 +422,8 @@ typedef struct viewer_s {
int settime; //the time that we last told the client.
float origin[3];
vec3_t velocity;
vec3_t origin;
int isadmin;
char expectcommand[16];
@ -474,18 +505,7 @@ struct sv_s {
char gamedir[MAX_QPATH];
char mapname[256];
struct {
float gravity;
float maxspeed;
float spectatormaxspeed;
float accelerate;
float airaccelerate;
float waterfriction;
float entgrav;
float stopspeed;
float wateraccelerate;
float friction;
} movevars;
movevars_t movevars;
int cdtrack;
entity_t entity[MAX_ENTITIES];
frame_t frame[MAX_ENTITY_FRAMES];
@ -825,6 +845,8 @@ void SendBufferToViewer(viewer_t *v, const char *buffer, int length, qboolean re
void QW_PrintfToViewer(viewer_t *v, char *format, ...);
void QW_StuffcmdToViewer(viewer_t *v, char *format, ...);
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, ...);
int Netchan_IsLocal (netadr_t adr);

View File

@ -23,6 +23,19 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
static const filename_t ConnectionlessModelList[] = {{""}, {"maps/start.bsp"}, {"progs/player.mdl"}, {""}};
static const filename_t ConnectionlessSoundList[] = {{""}, {""}};
void QTV_DefaultMovevars(movevars_t *vars)
{
vars->gravity = 800;
vars->maxspeed = 320;
vars->spectatormaxspeed = 500;
vars->accelerate = 10;
vars->airaccelerate = 0.7f;
vars->waterfriction = 4;
vars->entgrav = 1;
vars->stopspeed = 10;
vars->wateraccelerate = 10;
vars->friction = 4;
}
void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum);
@ -194,6 +207,7 @@ SOCKET QW_InitUDPSocket(int port)
void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount, qboolean spectatorflag)
{
movevars_t movevars;
WriteByte(msg, svc_serverdata);
WriteLong(msg, PROTOCOL_VERSION);
WriteLong(msg, servercount);
@ -211,16 +225,17 @@ void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount, qbo
// get the movevars
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteFloat(msg, 0);
QTV_DefaultMovevars(&movevars);
WriteFloat(msg, movevars.gravity);
WriteFloat(msg, movevars.stopspeed);
WriteFloat(msg, movevars.maxspeed);
WriteFloat(msg, movevars.spectatormaxspeed);
WriteFloat(msg, movevars.accelerate);
WriteFloat(msg, movevars.airaccelerate);
WriteFloat(msg, movevars.wateraccelerate);
WriteFloat(msg, movevars.friction);
WriteFloat(msg, movevars.waterfriction);
WriteFloat(msg, movevars.entgrav);
@ -663,6 +678,23 @@ void NewClient(cluster_t *cluster, viewer_t *viewer)
QW_PrintfToViewer(viewer, "Type admin for the admin menu\n");
}
void ParseUserInfo(viewer_t *viewer)
{
float rate;
char temp[64];
Info_ValueForKey(viewer->userinfo, "name", viewer->name, sizeof(viewer->name));
Info_ValueForKey(viewer->userinfo, "rate", temp, sizeof(temp));
rate = atof(temp);
if (!rate)
rate = 2500;
if (rate < 250)
rate = 250;
if (rate > 10000)
rate = 10000;
viewer->netchan.rate = 1000.0f / rate;
}
void NewNQClient(cluster_t *cluster, netadr_t *addr)
{
sv_t *initialserver;
@ -734,8 +766,9 @@ void NewNQClient(cluster_t *cluster, netadr_t *addr)
cluster->numviewers++;
strcpy(viewer->name, "unnamed");
sprintf(viewer->userinfo, "\\name\\%s", viewer->name);
sprintf(viewer->userinfo, "\\name\\%s", "unnamed");
ParseUserInfo(viewer);
NewClient(cluster, viewer);
@ -794,8 +827,8 @@ void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage)
cluster->numviewers++;
Info_ValueForKey(infostring, "name", viewer->name, sizeof(viewer->name));
strncpy(viewer->userinfo, infostring, sizeof(viewer->userinfo)-1);
ParseUserInfo(viewer);
Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *addr, "j");
@ -1215,11 +1248,21 @@ void SendLocalPlayerState(sv_t *tv, viewer_t *v, int playernum, netmsg_t *msg)
}
else
{
WriteShort(msg, 0);
flags = 0;
for (j=0 ; j<3 ; j++)
if ((int)v->velocity[j])
flags |= (PF_VELOCITY1<<j);
WriteShort(msg, flags);
WriteShort(msg, v->origin[0]*8);
WriteShort(msg, v->origin[1]*8);
WriteShort(msg, v->origin[2]*8);
WriteByte(msg, 0);
for (j=0 ; j<3 ; j++)
if (flags & (PF_VELOCITY1<<j) )
WriteShort (msg, v->velocity[j]);
}
}
@ -1821,7 +1864,7 @@ void UpdateStats(sv_t *qtv, viewer_t *v)
if (qtv && qtv->controller == v)
stats = qtv->players[qtv->thisplayer].stats;
else if (v->trackplayer != -1 || !qtv)
else if (v->trackplayer == -1 || !qtv)
stats = nullstats;
else
stats = qtv->players[v->trackplayer].stats;
@ -1892,49 +1935,48 @@ int Prespawn(sv_t *qtv, int curmsgsize, netmsg_t *msg, int bufnum, int thisplaye
return r;
}
#define M_PI 3.1415926535897932384626433832795
#include <math.h>
void AngleVectors (short angles[3], float *forward, float *right, float *up)
{
float angle;
float sr, sp, sy, cr, cp, cy;
angle = angles[1] * (M_PI*2 / 65535);
sy = sin(angle);
cy = cos(angle);
angle = angles[0] * (M_PI*2 / 65535);
sp = sin(angle);
cp = cos(angle);
angle = angles[2] * (M_PI*2 / 65535);
sr = sin(angle);
cr = cos(angle);
forward[0] = cp*cy;
forward[1] = cp*sy;
forward[2] = -sp;
right[0] = (-1*sr*sp*cy+-1*cr*-sy);
right[1] = (-1*sr*sp*sy+-1*cr*cy);
right[2] = -1*sr*cp;
up[0] = (cr*sp*cy+-sr*-sy);
up[1] = (cr*sp*sy+-sr*cy);
up[2] = cr*cp;
}
void PMove(viewer_t *v, usercmd_t *cmd)
{
int i;
float fwd[3], rgt[3], up[3];
sv_t *qtv;
pmove_t pmove;
if (v->server && v->server->controller == v)
{
v->origin[0] = v->server->players[v->server->thisplayer].current.origin[0]/8.0f;
v->origin[1] = v->server->players[v->server->thisplayer].current.origin[1]/8.0f;
v->origin[2] = v->server->players[v->server->thisplayer].current.origin[2]/8.0f;
v->velocity[0] = v->server->players[v->server->thisplayer].current.velocity[2]/8.0f;
v->velocity[1] = v->server->players[v->server->thisplayer].current.velocity[2]/8.0f;
v->velocity[2] = v->server->players[v->server->thisplayer].current.velocity[2]/8.0f;
return;
}
AngleVectors(cmd->angles, fwd, rgt, up);
pmove.origin[0] = v->origin[0];
pmove.origin[1] = v->origin[1];
pmove.origin[2] = v->origin[2];
for (i = 0; i < 3; i++)
v->origin[i] += (cmd->forwardmove*fwd[i] + cmd->sidemove*rgt[i] + cmd->upmove*up[i])*(cmd->msec/1000.0f);
pmove.velocity[0] = v->velocity[0];
pmove.velocity[1] = v->velocity[1];
pmove.velocity[2] = v->velocity[2];
pmove.cmd = *cmd;
qtv = v->server;
if (qtv)
{
pmove.movevars = qtv->movevars;
}
else
{
QTV_DefaultMovevars(&pmove.movevars);
}
PM_PlayerMove(&pmove);
v->origin[0] = pmove.origin[0];
v->origin[1] = pmove.origin[1];
v->origin[2] = pmove.origin[2];
v->velocity[0] = pmove.velocity[0];
v->velocity[1] = pmove.velocity[1];
v->velocity[2] = pmove.velocity[2];
}
void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean noupwards)
@ -2177,6 +2219,13 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean
QW_SetViewersServer(v, NULL);
QW_SetMenu(v, MENU_SERVERS);
}
else if (!strncmp(message, ".menu", 5))
{
if (v->menunum)
QW_SetMenu(v, MENU_NONE);
else
QW_SetMenu(v, MENU_SERVERS);
}
else if (!strncmp(message, ".admin", 6))
{
if (!*cluster->adminpassword)
@ -2291,6 +2340,37 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean
{
QW_StuffcmdToViewer(v, "cmd say \".admin\"\n");
}
else if (!strncmp(message, ".clients", 8))
{
viewer_t *ov;
int skipfirst = 0;
int printable = 30;
int remaining = 0;
for (ov = cluster->viewers; ov; ov = ov->next)
{
if (skipfirst > 0)
{
skipfirst--;
}
else if (printable > 0)
{
printable--;
if (ov->server)
{
if (ov->server->controller == ov)
QW_PrintfToViewer(v, "%s: %s\n", ov->name, ov->server->server);
else
QW_PrintfToViewer(v, "%s: %s\n", ov->name, ov->server->server);
}
else
QW_PrintfToViewer(v, "%s: %s\n", ov->name, "None");
}
else
remaining++;
}
if (remaining)
QW_PrintfToViewer(v, "%i clients not shown\n", remaining);
}
else if (!strncmp(message, "proxy:menu up", 13))
{
v->menuop -= 1;
@ -2380,31 +2460,41 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean
}
else
{
viewer_t *ov;
if (cluster->notalking)
return;
InitNetMsg(&msg, buf, sizeof(buf));
for (ov = cluster->viewers; ov; ov = ov->next)
{
if (ov->server != v->server)
continue;
WriteByte(&msg, svc_print);
WriteByte(&msg, 3); //PRINT_CHAT
WriteString2(&msg, v->name);
WriteString2(&msg, "\x8d ");
WriteString2(&msg, message);
WriteString(&msg, "\n");
InitNetMsg(&msg, buf, sizeof(buf));
Broadcast(cluster, msg.data, msg.cursize, QW);
WriteByte(&msg, svc_print);
InitNetMsg(&msg, buf, sizeof(buf));
if (ov->netchan.isnqprotocol)
WriteByte(&msg, 1);
else
{
if (ov->conmenussupported)
{
WriteByte(&msg, 3); //PRINT_CHAT
WriteString2(&msg, "^s^5");
}
else
{
WriteByte(&msg, 3); //PRINT_CHAT
}
}
WriteByte(&msg, svc_print);
WriteByte(&msg, 1);
WriteString2(&msg, v->name);
WriteString2(&msg, "\x8d ");
WriteString2(&msg, message);
WriteString(&msg, "\n");
WriteString2(&msg, v->name);
WriteString2(&msg, "\x8d ");
WriteString2(&msg, message);
WriteString(&msg, "\n");
Broadcast(cluster, msg.data, msg.cursize, NQ);
SendBufferToViewer(ov, msg.data, msg.cursize, true);
}
}
}
}
@ -2493,9 +2583,39 @@ void ParseNQC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m)
WriteByte (&msg, 2);
SendBufferToViewer(v, msg.data, msg.cursize, true);
}
else if (!strncmp(buf, "setinfo", 5))
{
#define TOKENIZE_PUNCTUATION ""
#define MAX_ARGS 3
#define ARG_LEN 256
int i;
char arg[MAX_ARGS][ARG_LEN];
char *argptrs[MAX_ARGS];
char *command = buf;
for (i = 0; i < MAX_ARGS; i++)
{
command = COM_ParseToken(command, arg[i], ARG_LEN, TOKENIZE_PUNCTUATION);
argptrs[i] = arg[i];
}
Info_SetValueForStarKey(v->userinfo, arg[1], arg[2], sizeof(v->userinfo));
ParseUserInfo(v);
// Info_ValueForKey(v->userinfo, "name", v->name, sizeof(v->name));
if (v->server && v->server->controller == v)
SendClientCommand(v->server, "%s", buf);
}
else if (!strncmp(buf, "name ", 5))
{
Q_strncpyz(v->name, buf+5, sizeof(v->name));
Info_SetValueForStarKey(v->userinfo, "name", buf+5, sizeof(v->userinfo));
ParseUserInfo(v);
if (v->server && v->server->controller == v)
SendClientCommand(v->server, "setinfo name \"%s\"", v->name);
}
else if (!strncmp(buf, "color ", 6))
{
@ -2803,7 +2923,8 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m)
}
Info_SetValueForStarKey(v->userinfo, arg[1], arg[2], sizeof(v->userinfo));
Info_ValueForKey(v->userinfo, "name", v->name, sizeof(v->name));
ParseUserInfo(v);
// Info_ValueForKey(v->userinfo, "name", v->name, sizeof(v->name));
if (v->server && v->server->controller == v)
SendClientCommand(v->server, "%s", buf);
@ -3465,6 +3586,8 @@ void QW_UpdateUDPStuff(cluster_t *cluster)
{
v->maysend = (v->nextpacket < cluster->curtime);
}
if (!Netchan_CanPacket(&v->netchan))
continue;
if (v->maysend) //don't send incompleate connection data.
{
v->nextpacket = cluster->curtime + 1000/NQ_PACKETS_PER_SECOND;

View File

@ -410,7 +410,7 @@ qboolean Net_ConnectToServer(sv_t *qtv, char *ip)
{
qtv->usequkeworldprotocols = false;
if (!strncmp(ip, "file:", 5))
if (!strncmp(ip, "file:", 5) || !strncmp(ip, "demo:", 5))
{
qtv->sourcesock = INVALID_SOCKET;
qtv->file = fopen(ip+5, "rb");
@ -1433,6 +1433,13 @@ void QTV_Run(sv_t *qtv)
return;
}
else if (!strcmp(start, "PERROR"))
{
Sys_Printf(qtv->cluster, "\nQTV server error: %s\n\n", colon);
qtv->drop = true;
qtv->buffersize = 0;
return;
}
else if (!strcmp(start, "TERROR"))
{ //we don't support compression, we didn't ask for it.
Sys_Printf(qtv->cluster, "\nQTV server error: %s\n\n", colon);
qtv->drop = true;
@ -1440,9 +1447,12 @@ void QTV_Run(sv_t *qtv)
return;
}
else if (!strcmp(start, "PRINT"))
{ //we don't support compression, we didn't ask for it.
Sys_Printf(qtv->cluster, "QTV server: \n");
return;
{
Sys_Printf(qtv->cluster, "QTV server: %s\n", colon);
}
else if (!strcmp(start, "BEGIN"))
{
qtv->parsingqtvheader = false;
}
else
{
@ -1455,15 +1465,18 @@ void QTV_Run(sv_t *qtv)
qtv->buffersize -= length;
memmove(qtv->buffer, qtv->buffer + length, qtv->buffersize);
if (*authmethod == '\0')
qtv->parsingqtvheader = false;
else
if (*authmethod)
{ //we need to send a challenge response now.
Net_SendQTVConnectionRequest(qtv, authmethod, challenge);
}
if (qtv->parsingqtvheader)
return;
}
else if (qtv->parsingqtvheader)
{
Sys_Printf(qtv->cluster, "QTV server sent no begin command - assuming incompatable\n\n", colon);
qtv->drop = true;
qtv->buffersize = 0;
return;
}
qtv->parsetime = Sys_Milliseconds() + BUFFERTIME*1000;
if (!qtv->usequkeworldprotocols)