mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-10 22:51:57 +00:00
mvdplay should be run fine now, once in. You can still hijack scoreboards though.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@832 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
0f0081d8db
commit
8233e58472
7 changed files with 174 additions and 107 deletions
|
@ -174,7 +174,7 @@ CL_GetDemoMessage
|
|||
*/
|
||||
|
||||
float olddemotime = 0;
|
||||
extern float nextdemotime;
|
||||
float nextdemotime = 0;
|
||||
qboolean CL_GetDemoMessage (void)
|
||||
{
|
||||
int r, i, j, tracknum;
|
||||
|
|
|
@ -1886,7 +1886,7 @@ void GL_Set2D (void)
|
|||
qglOrtho (0, vid.width, vid.height, 0, -99999, 99999);
|
||||
|
||||
qglMatrixMode(GL_MODELVIEW);
|
||||
qglLoadIdentity ();
|
||||
qglLoadIdentity ();
|
||||
|
||||
qglDisable (GL_DEPTH_TEST);
|
||||
qglDisable (GL_CULL_FACE);
|
||||
|
|
|
@ -1549,7 +1549,9 @@ void NPP_MVDFlush(void)
|
|||
|
||||
// ents->colormap=playernum+1;
|
||||
|
||||
VectorCopy(ents->origin, oldorg);
|
||||
VectorCopy(ents->origin, sv.recordedplayer[playernum].oldorg);
|
||||
VectorCopy(ents->angles, sv.recordedplayer[playernum].oldang);
|
||||
|
||||
i = 5;
|
||||
for (j=0 ; j<3 ; j++)
|
||||
if (flags & (DF_ORIGIN << j))
|
||||
|
@ -1557,8 +1559,6 @@ void NPP_MVDFlush(void)
|
|||
ents->origin[j] = (signed short)(buffer[i] + (buffer[i+1]<<8))/8.0f;
|
||||
i+=2;
|
||||
}
|
||||
VectorSubtract(ents->origin, oldorg, sv.recordedplayer[playernum].velocity);
|
||||
VectorScale(sv.recordedplayer[playernum].velocity, realtime - sv.recordedplayer[playernum].updatetime, sv.recordedplayer[playernum].velocity);
|
||||
|
||||
VectorCopy(ents->angles, oldang);
|
||||
for (j=0 ; j<3 ; j++)
|
||||
|
@ -1572,9 +1572,6 @@ void NPP_MVDFlush(void)
|
|||
if (flags & (DF_ANGLES << 0)) //'stupid quake bug' I believe is the correct quote...
|
||||
ents->angles[0] = ents->angles[0]*-1/3.0f; //also scale pitch down as well as invert
|
||||
|
||||
VectorSubtract(ents->angles, oldang, sv.recordedplayer[playernum].avelocity);
|
||||
VectorScale(sv.recordedplayer[playernum].avelocity, realtime - sv.recordedplayer[playernum].updatetime, sv.recordedplayer[playernum].avelocity);
|
||||
|
||||
if (flags & DF_MODEL)
|
||||
{
|
||||
ents->modelindex = buffer[i];
|
||||
|
@ -1591,13 +1588,11 @@ void NPP_MVDFlush(void)
|
|||
i+=1;
|
||||
}
|
||||
if (flags & DF_WEAPONFRAME)
|
||||
{
|
||||
wframe = buffer[i];
|
||||
{ //mvds are deltas remember, this is really the only place where that fact is all that important.
|
||||
sv.recordedplayer[playernum].weaponframe = buffer[i];
|
||||
i+=1;
|
||||
}
|
||||
else wframe = 0;
|
||||
|
||||
sv.recordedplayer[playernum].weaponframe = wframe;
|
||||
sv.recordedplayer[playernum].updatetime = realtime;
|
||||
|
||||
ignoreprotocol=true;
|
||||
|
|
|
@ -227,8 +227,8 @@ typedef struct
|
|||
int userid;
|
||||
int weaponframe;
|
||||
char userinfo[MAX_INFO_STRING];
|
||||
vec3_t velocity;
|
||||
vec3_t avelocity;
|
||||
vec3_t oldorg;
|
||||
vec3_t oldang;
|
||||
float updatetime;
|
||||
} recordedplayer[MAX_CLIENTS];
|
||||
|
||||
|
@ -451,6 +451,9 @@ typedef struct client_s
|
|||
int trustlevel;
|
||||
|
||||
qboolean wasrecorded; //this client shouldn't get any net messages sent to them
|
||||
|
||||
vec3_t specorigin; //mvds need to use a different origin from the one QC has.
|
||||
vec3_t specvelocity;
|
||||
|
||||
int language; //the clients language
|
||||
|
||||
|
|
|
@ -204,7 +204,13 @@ qboolean SV_GetPacket (void)
|
|||
//char empty[512];
|
||||
qboolean SV_ReadMVD (void);
|
||||
|
||||
float nextdemotime;
|
||||
#ifdef SERVERONLY
|
||||
float nextdemotime = 0;
|
||||
float olddemotime = 0;
|
||||
#else
|
||||
extern float nextdemotime;
|
||||
extern float olddemotime;
|
||||
#endif
|
||||
void SV_LoadClientDemo_f (void)
|
||||
{
|
||||
int i;
|
||||
|
@ -286,6 +292,7 @@ void SV_LoadClientDemo_f (void)
|
|||
svd.lasttype = dem_read;
|
||||
svd.realtime = realtime;
|
||||
nextdemotime = realtime-0.1; //cause read of the first 0.1 secs to get all spawn info.
|
||||
olddemotime = realtime;
|
||||
while (SV_ReadMVD())
|
||||
{
|
||||
sv.datagram.cursize = 0;
|
||||
|
@ -321,17 +328,17 @@ qboolean SV_RunDemo (void)
|
|||
qbyte c;
|
||||
// usercmd_t *pcmd;
|
||||
// usercmd_t emptycmd;
|
||||
static float prevtime = 0.0;
|
||||
qbyte newtime;
|
||||
|
||||
|
||||
|
||||
readnext:
|
||||
|
||||
// read the time from the packet
|
||||
if (svd.mvdplayback)
|
||||
{
|
||||
fread(&newtime, sizeof(newtime), 1, svd.demofile);
|
||||
nextdemotime = prevtime + newtime * (1/1000.0f);
|
||||
nextdemotime = olddemotime + newtime * (1/1000.0f);
|
||||
demotime = nextdemotime;
|
||||
|
||||
if (nextdemotime > svd.realtime)
|
||||
|
@ -370,7 +377,7 @@ readnext:
|
|||
svd.realtime = demotime; // we're warping
|
||||
}
|
||||
|
||||
prevtime = demotime;
|
||||
olddemotime = demotime;
|
||||
|
||||
// get the msg type
|
||||
if ((r = fread (&c, sizeof(c), 1, svd.demofile)) != 1)
|
||||
|
@ -559,7 +566,7 @@ qboolean SV_ReadMVD (void)
|
|||
if (!cl->spec_track)
|
||||
continue;
|
||||
if (!(cl->spec_track >> 3 & svd.lastto))
|
||||
continue;
|
||||
continue;
|
||||
|
||||
for (c = 0; c < net_message.cursize; c++)
|
||||
NPP_MVDWriteByte(net_message.data[c], cl, false);
|
||||
|
|
|
@ -638,7 +638,7 @@ void SV_WritePlayerToClient(sizebuf_t *msg, clstate_t *ent)
|
|||
}
|
||||
}
|
||||
|
||||
if (ent->spectator == 2 && ent->weaponframe)
|
||||
if (ent->spectator == 2 && ent->weaponframe) //it's not us, but we are spectating, so we need the correct weaponframe
|
||||
pflags |= PF_WEAPONFRAME;
|
||||
|
||||
if (!ent->isself || ent->fteext & PEXT_SPLITSCREEN)
|
||||
|
@ -779,7 +779,7 @@ void SV_WritePlayerToClient(sizebuf_t *msg, clstate_t *ent)
|
|||
if (pflags & (PF_VELOCITY1<<i) )
|
||||
MSG_WriteShort (msg, 0);
|
||||
}
|
||||
|
||||
|
||||
if (pflags & PF_MODEL)
|
||||
{
|
||||
MSG_WriteByte (msg, ent->modelindex);
|
||||
|
@ -1032,16 +1032,13 @@ void SV_WritePlayersToClient (client_t *client, edict_t *clent, qbyte *pvs, size
|
|||
client_t *cl;
|
||||
edict_t *ent, *vent;
|
||||
int pflags;
|
||||
vec3_t lerpedang;
|
||||
|
||||
int splitnum = 0;
|
||||
|
||||
demo_frame_t *demo_frame;
|
||||
demo_client_t *dcl;
|
||||
#define DF_DEAD (1<<8)
|
||||
#define DF_GIB (1<<9)
|
||||
|
||||
if (clent == NULL) //write to demo file. (no pov)
|
||||
if (clent == NULL) //write to demo file. (no PVS)
|
||||
{
|
||||
demo_frame = &demo.frames[demo.parsecount&DEMO_FRAMES_MASK];
|
||||
for (j=0,cl=svs.clients, dcl = demo_frame->clients; j<MAX_CLIENTS ; j++,cl++, dcl++)
|
||||
|
@ -1127,49 +1124,36 @@ void SV_WritePlayersToClient (client_t *client, edict_t *clent, qbyte *pvs, size
|
|||
return;
|
||||
#endif
|
||||
|
||||
for (j=0,cl=svs.clients ; j<sv.allocated_client_slots ; j++,cl++)
|
||||
if (sv.demostatevalid) //this is a demo
|
||||
{
|
||||
if (sv.demostatevalid) //this is a demo
|
||||
usercmd_t cmd;
|
||||
vec3_t ang;
|
||||
vec3_t org;
|
||||
vec3_t vel;
|
||||
float lerp;
|
||||
extern vec3_t player_mins, player_maxs;
|
||||
clstate_t clst;
|
||||
extern float olddemotime, nextdemotime;
|
||||
|
||||
for (i=0 ; i<MAX_CLIENTS ; i++)
|
||||
{
|
||||
// ZOID visibility tracking
|
||||
/* if (ent != clent &&
|
||||
!(client->spec_track && client->spec_track - 1 == j))
|
||||
{
|
||||
if (cl->spectator)
|
||||
continue;
|
||||
//FIXME: Add PVS stuff.
|
||||
|
||||
// ignore if not touching a PV leaf
|
||||
for (i=0 ; i < ent->num_leafs ; i++)
|
||||
if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) ))
|
||||
break;
|
||||
if (i == ent->num_leafs)
|
||||
{
|
||||
continue; // not visible
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (cl->edict == clent && svs.clients[j].spec_track>0)
|
||||
i = svs.clients[j].spec_track-1;
|
||||
else
|
||||
i = j;
|
||||
if (i>=0&&*sv.recordedplayer[i].userinfo)
|
||||
if (*sv.recordedplayer[i].userinfo) //if the client was active
|
||||
{
|
||||
usercmd_t cmd;
|
||||
vec3_t ang;
|
||||
vec3_t org;
|
||||
extern vec3_t player_mins, player_maxs;
|
||||
clstate_t clst;
|
||||
clst.playernum = i;
|
||||
clst.onladder = 0;
|
||||
clst.lastcmd = &cmd;
|
||||
clst.modelindex = sv.demostate[i+1].modelindex;
|
||||
if (!clst.modelindex)
|
||||
continue;
|
||||
clst.modelindex2 = 0;
|
||||
clst.frame = sv.demostate[i+1].frame;
|
||||
clst.weaponframe = sv.recordedplayer[i].weaponframe;
|
||||
clst.angles = ang;
|
||||
clst.origin = org;
|
||||
clst.hull = 1;
|
||||
clst.velocity = sv.recordedplayer[i].velocity;
|
||||
clst.velocity = vel;
|
||||
clst.effects = sv.demostate[i+1].effects;
|
||||
clst.skin = sv.demostate[i+1].skinnum;
|
||||
clst.mins = player_mins;
|
||||
|
@ -1179,17 +1163,26 @@ void SV_WritePlayersToClient (client_t *client, edict_t *clent, qbyte *pvs, size
|
|||
clst.fatness = sv.demostate[i+1].fatness;
|
||||
clst.localtime = sv.time;//sv.recordedplayer[j].updatetime;
|
||||
clst.health = sv.recordedplayer[i].stats[STAT_HEALTH];
|
||||
clst.spectator = 0;
|
||||
clst.spectator = 2; //so that weaponframes work properly.
|
||||
clst.isself = false;
|
||||
clst.fteext = client->fteprotocolextensions;
|
||||
clst.zext = client->zquake_extensions;
|
||||
clst.fteext = 0;//client->fteprotocolextensions;
|
||||
clst.zext = 0;//client->zquake_extensions;
|
||||
clst.cl = NULL;
|
||||
|
||||
ang[0] = sv.demostate[i+1].angles[0]*360.0f/256+ (realtime - sv.recordedplayer[i].updatetime)*sv.recordedplayer[i].avelocity[0];
|
||||
ang[1] = sv.demostate[i+1].angles[1]*360.0f/256+ (realtime - sv.recordedplayer[i].updatetime)*sv.recordedplayer[i].avelocity[1];
|
||||
ang[2] = sv.demostate[i+1].angles[2]*360.0f/256+ (realtime - sv.recordedplayer[i].updatetime)*sv.recordedplayer[i].avelocity[2];
|
||||
lerp = (realtime - olddemotime) / (nextdemotime - olddemotime);
|
||||
if (lerp < 0)
|
||||
lerp = 0;
|
||||
if (lerp > 1)
|
||||
lerp = 1;
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
ang[j] = (360.0f/256)*(sv.recordedplayer[i].oldang[j] + (sv.demostate[i+1].angles[j] - sv.recordedplayer[i].oldang[j])*lerp);
|
||||
|
||||
org[j] = sv.recordedplayer[i].oldorg[j] + (sv.demostate[i+1].origin[j] - sv.recordedplayer[i].oldorg[j])*lerp;
|
||||
|
||||
vel[j] = (-sv.recordedplayer[i].oldorg[j] + sv.demostate[i+1].origin[j])*(nextdemotime - olddemotime);
|
||||
}
|
||||
|
||||
VectorMA(sv.demostate[i+1].origin, (realtime - sv.recordedplayer[i].updatetime), sv.recordedplayer[i].velocity, org);
|
||||
ang[0] *= -3;
|
||||
|
||||
// ang[0] = ang[1] = ang[2] = 0;
|
||||
|
@ -1199,40 +1192,63 @@ void SV_WritePlayersToClient (client_t *client, edict_t *clent, qbyte *pvs, size
|
|||
cmd.angles[1] = ang[1]*65535/360.0f;
|
||||
cmd.angles[2] = ang[2]*65535/360.0f;
|
||||
cmd.msec = 50;
|
||||
{vec3_t f, r, u, v;
|
||||
{vec3_t f, r, u, v;
|
||||
vec_t VectorNormalize2 (vec3_t, vec3_t);
|
||||
AngleVectors(ang, f, r, u);
|
||||
VectorCopy(sv.recordedplayer[i].velocity, v);
|
||||
VectorCopy(vel, v);
|
||||
cmd.forwardmove = DotProduct(f, v);
|
||||
cmd.sidemove = DotProduct(r, v);
|
||||
cmd.upmove = DotProduct(u, v);
|
||||
}
|
||||
}
|
||||
clst.lastcmd=NULL;
|
||||
|
||||
clst.spectator = 1;
|
||||
if (i == j)
|
||||
clst.spectator = 2;
|
||||
|
||||
SV_WritePlayerToClient(msg, &clst);
|
||||
}
|
||||
splitnum = 0;
|
||||
if (cl->controlled)
|
||||
{ //hrm. splitscreen.
|
||||
client_t *s;
|
||||
for (s = cl; s; s = s->controlled, splitnum++)
|
||||
{
|
||||
if (s->edict == cl->edict)
|
||||
break;
|
||||
}
|
||||
if (!s)
|
||||
continue;
|
||||
}
|
||||
else if (cl->edict != clent)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//now build the spectator's thingie
|
||||
|
||||
memset(&clst, 0, sizeof(clst));
|
||||
|
||||
clst.fteext = 0;//client->fteprotocolextensions;
|
||||
clst.zext = 0;//client->zquake_extensions;
|
||||
clst.playernum = MAX_CLIENTS-1;
|
||||
clst.isself = true;
|
||||
clst.modelindex = 0;
|
||||
clst.hull = 1;
|
||||
clst.frame = 0;
|
||||
clst.localtime = sv.time;
|
||||
clst.mins = player_mins;
|
||||
clst.maxs = player_maxs;
|
||||
|
||||
clst.angles = vec3_origin; //not needed, as the client knows better than us anyway.
|
||||
clst.origin = client->specorigin;
|
||||
clst.velocity = client->specvelocity;
|
||||
|
||||
for (client = client; client; client = client->controller)
|
||||
{
|
||||
clst.health = 100;
|
||||
|
||||
if (client->spec_track)
|
||||
{
|
||||
clst.weaponframe = sv.recordedplayer[client->spec_track-1].weaponframe;
|
||||
clst.spectator = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
clst.weaponframe = 0;
|
||||
clst.spectator = 1;
|
||||
}
|
||||
|
||||
SV_WritePlayerToClient(msg, &clst);
|
||||
|
||||
clst.playernum--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (j=0,cl=svs.clients ; j<sv.allocated_client_slots ; j++,cl++)
|
||||
{
|
||||
isbot = !cl->state && cl->name[0];
|
||||
if (cl->state != cs_spawned) //this includes bots
|
||||
if (!isbot || progstype != PROG_NQ) //unless they're NQ bots...
|
||||
|
@ -1341,43 +1357,33 @@ void SV_WritePlayersToClient (client_t *client, edict_t *clent, qbyte *pvs, size
|
|||
if (sv.demostatevalid)
|
||||
clst.health = 100;
|
||||
|
||||
if (sv.demostatevalid)
|
||||
clst.playernum = MAX_CLIENTS-1-splitnum;
|
||||
|
||||
clst.isself = false;
|
||||
if ((cl == client || cl->controller == client) && !sv.demostatevalid)
|
||||
if ((cl == client || cl->controller == client))
|
||||
{
|
||||
clst.isself = true;
|
||||
clst.spectator = 0;
|
||||
if (client->spectator)
|
||||
{
|
||||
clst.spectator = 2;
|
||||
if (client->spec_track)
|
||||
{
|
||||
clst.spectator = 2;
|
||||
clst.mins = svs.clients[client->spec_track-1].edict->v.mins;
|
||||
clst.maxs = svs.clients[client->spec_track-1].edict->v.maxs;
|
||||
clst.health = svs.clients[client->spec_track-1].edict->v.health;
|
||||
clst.weaponframe = svs.clients[client->spec_track-1].edict->v.weaponframe;
|
||||
}
|
||||
else
|
||||
{
|
||||
clst.spectator = 1;
|
||||
clst.health = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (client->spectator || sv.demostatevalid)
|
||||
else if (client->spectator)
|
||||
{
|
||||
clst.health=100;
|
||||
if (client->spec_track && client->spec_track -1 == j)
|
||||
{
|
||||
if (sv.demostatevalid)
|
||||
{
|
||||
clst.origin = sv.demostate[client->spec_track].origin;
|
||||
clst.velocity = sv.recordedplayer[client->spec_track-1].velocity;
|
||||
clst.angles = lerpedang;
|
||||
lerpedang[0] = sv.demostate[client->spec_track].angles[0]*360.0/256;
|
||||
lerpedang[1] = sv.demostate[client->spec_track].angles[1]*360.0/256;
|
||||
lerpedang[2] = sv.demostate[client->spec_track].angles[2]*360.0/256;
|
||||
}
|
||||
if (client->spec_track && ent == clent)
|
||||
clst.spectator = 2;
|
||||
}
|
||||
else
|
||||
clst.spectator = 1;
|
||||
}
|
||||
|
|
|
@ -3747,6 +3747,57 @@ void SV_RunCmd (usercmd_t *ucmd, qboolean recurse)
|
|||
host_frametime = ucmd->msec * 0.001;
|
||||
if (host_frametime > 0.1)
|
||||
host_frametime = 0.1;
|
||||
|
||||
if (sv.demostatevalid)
|
||||
{ //spectators watching MVDs do not affect the running progs.
|
||||
player_mins[0] = -16;
|
||||
player_mins[1] = -16;
|
||||
player_mins[2] = -24;
|
||||
|
||||
player_maxs[0] = 16;
|
||||
player_maxs[1] = 16;
|
||||
player_maxs[2] = 32;
|
||||
|
||||
pmove.angles[0] = SHORT2ANGLE(ucmd->angles[0]);
|
||||
pmove.angles[1] = SHORT2ANGLE(ucmd->angles[1]);
|
||||
pmove.angles[2] = SHORT2ANGLE(ucmd->angles[2]);
|
||||
|
||||
VectorCopy (host_client->specorigin, pmove.origin);
|
||||
VectorCopy (host_client->specvelocity, pmove.velocity);
|
||||
|
||||
if (host_client->zquake_extensions & Z_EXT_PM_TYPE_NEW)
|
||||
pmove.pm_type = PM_SPECTATOR;
|
||||
else
|
||||
pmove.pm_type = PM_OLD_SPECTATOR;
|
||||
pmove.jump_held = host_client->jump_held;
|
||||
pmove.jump_msec = 0;
|
||||
pmove.waterjumptime = 0;
|
||||
pmove.numphysent = 1;
|
||||
pmove.physents[0].model = sv.worldmodel;
|
||||
pmove.cmd = *ucmd;
|
||||
pmove.hullnum = SV_HullNumForPlayer(0, player_mins, player_maxs);
|
||||
|
||||
movevars.entgravity = 0;
|
||||
movevars.maxspeed = 0;
|
||||
movevars.bunnyspeedcap = pm_bunnyspeedcap.value;
|
||||
movevars.ktjump = pm_ktjump.value;
|
||||
movevars.slidefix = (pm_slidefix.value != 0);
|
||||
movevars.airstep = (pm_airstep.value != 0);
|
||||
movevars.walljump = (pm_walljump.value);
|
||||
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
pmove_mins[i] = pmove.origin[i] - 256;
|
||||
pmove_maxs[i] = pmove.origin[i] + 256;
|
||||
}
|
||||
|
||||
PM_PlayerMove ();
|
||||
|
||||
VectorCopy (pmove.origin, host_client->specorigin);
|
||||
VectorCopy (pmove.velocity, host_client->specvelocity);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef SVCHAT
|
||||
if (SV_ChatMove(ucmd->impulse))
|
||||
|
@ -4199,7 +4250,7 @@ haveannothergo:
|
|||
|
||||
cl->lastcmd = newcmd;
|
||||
cl->lastcmd.buttons = 0; // avoid multiple fires on lag
|
||||
|
||||
|
||||
if (msg_badread)
|
||||
{
|
||||
Con_Printf ("SV_ReadClientMessage: badread\n");
|
||||
|
@ -4232,7 +4283,7 @@ haveannothergo:
|
|||
SV_RunCmd (&newcmd, false);
|
||||
|
||||
SV_PostRunCmd();
|
||||
|
||||
|
||||
}
|
||||
|
||||
cl->lastcmd = newcmd;
|
||||
|
@ -4244,7 +4295,7 @@ haveannothergo:
|
|||
Con_Printf ("SV_ReadClientMessage: badread\n");
|
||||
SV_DropClient (cl);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
c = MSG_ReadByte ();
|
||||
if (c != clc_move)
|
||||
|
@ -4272,7 +4323,12 @@ haveannothergo:
|
|||
o[1] = MSG_ReadCoord();
|
||||
o[2] = MSG_ReadCoord();
|
||||
// only allowed by spectators
|
||||
if (host_client->spectator||sv.mvdplayback) {
|
||||
if (sv.mvdplayback)
|
||||
{
|
||||
VectorCopy(o, host_client->specorigin);
|
||||
}
|
||||
else if (host_client->spectator)
|
||||
{
|
||||
VectorCopy(o, sv_player->v.origin);
|
||||
SV_LinkEdict(sv_player, false);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue