try to fix entertimes.

try to fix gameclock (when joining mid-game on servers that don't support stat_matchstarttime).
fix iqm loader issue using the same skin for every surface.
modelviewer now allows displaying per-surface info.
fix qcc ptr->foo bug.
console commands with a leading space are considered to be say messages

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4917 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2015-06-24 17:59:57 +00:00
parent 0ced7ec194
commit f35aa1d123
25 changed files with 528 additions and 120 deletions

View file

@ -136,6 +136,20 @@ static float CL_TrackScore(player_info_t *pl, char *rule)
}
return score;
}
static qboolean CL_MayTrack(int seat, int player)
{
if (player < 0)
return false;
if (!cl.players[player].userid || !cl.players[player].name[0] || cl.players[player].spectator)
return false;
for (seat--; seat >= 0; seat--)
{
//not valid if an earlier view is tracking it.
if (Cam_TrackNum(&cl.playerview[seat]) == player)
return false;
}
return true;
}
//returns the player with the highest frags
static int CL_FindHighTrack(int seat, char *rule)
{
@ -146,7 +160,7 @@ static int CL_FindHighTrack(int seat, char *rule)
//set a default to the currently tracked player, to reuse the current player we're tracking if someone lower equalises.
j = cl.playerview[seat].cam_spec_track;
if (j >= 0 && cl.players[j].userid && cl.players[j].name[0] && !cl.players[j].spectator)
if (CL_MayTrack(seat, j))
max = CL_TrackScore(&cl.players[j], rule);
else
{
@ -158,25 +172,18 @@ static int CL_FindHighTrack(int seat, char *rule)
{
s = &cl.players[i];
score = CL_TrackScore(s, rule);
if (s->userid && s->name[0] && !s->spectator && score > max)
if (score > max)
{
if (j == i) //this was our default.
continue;
//skip it if an earlier seat is watching it already
for (k = 0; k < seat; k++)
{
if (Cam_TrackNum(&cl.playerview[k]) == i)
break;
}
if (!CL_MayTrack(seat, i))
continue;
if (cl.teamplay && seat && cl.playerview[0].cam_spec_track >= 0 && strcmp(cl.players[cl.playerview[0].cam_spec_track].team, s->team))
continue; //when using multiview, keep tracking the team
if (k == seat)
{
max = CL_TrackScore(s, rule);
j = i;
}
}
}
return j;
}
@ -224,7 +231,7 @@ qboolean Cam_DrawViewModel(playerview_t *pv)
int Cam_TrackNum(playerview_t *pv)
{
if (pv->cam_state == CAM_EYECAM)
if (cl.spectator && pv->cam_state == CAM_EYECAM)
return pv->cam_spec_track;
return -1;
}

View file

@ -1419,7 +1419,7 @@ void CL_Record_f (void)
{
MSG_WriteByte (&buf, svc_updateentertime);
MSG_WriteByte (&buf, i);
MSG_WriteFloat (&buf, player->entertime);
MSG_WriteFloat (&buf, realtime - player->realentertime); //seconds since
}
if (*player->userinfo)

View file

@ -59,6 +59,14 @@ int CL_TargettedSplit(qboolean nowrap)
char *c;
int pnum;
int mod;
//explicitly targetted at some seat number from the server
if (Cmd_ExecLevel > RESTRICT_SERVER)
return Cmd_ExecLevel - RESTRICT_SERVER-1;
if (Cmd_ExecLevel == RESTRICT_SERVER)
return 0;
//locally executed command.
if (nowrap)
mod = MAX_SPLITS;
else
@ -1579,6 +1587,23 @@ qboolean CLQW_SendCmd (sizebuf_t *buf)
if (cl.sendprespawn)
buf->cursize = st; //don't send movement commands while we're still supposedly downloading. mvdsv does not like that.
else
{
//FIXME: user a timer.
if (!cls.netchan.message.cursize && cl.allocated_client_slots > 1 && cl.splitclients && (cls.fteprotocolextensions & PEXT_SPLITSCREEN))
{
int targ = cl_splitscreen.ival+1;
if (targ > MAX_SPLITS)
targ = MAX_SPLITS;
if (cl.splitclients < targ)
{
char buffer[2048];
CL_SendClientCommand(true, "addseat %i %s", cl.splitclients, COM_QuotedString(cls.userinfo[cl.splitclients], buffer, sizeof(buffer), false));
}
else if (cl.splitclients > targ)
CL_SendClientCommand(true, "addseat %i", targ);
}
}
return dontdrop;
}

View file

@ -6099,6 +6099,24 @@ void CLQW_ParseServerMessage (void)
Cbuf_Execute (); // make sure any stuffed commands are done
CLQW_ParseServerData ();
break;
case svc_signonnum:
cl.splitclients = MSG_ReadByte();
for (i = 0; i < cl.splitclients && i < 4; i++)
{
cl.playerview[i].playernum = MSG_ReadByte();
cl.playerview[i].viewentity = cl.playerview[i].playernum+1;
}
if (i < cl.splitclients)
{
Con_Printf("Server sent us too many seats!\n");
for (; i < cl.splitclients; i++)
{ //svcfte_choosesplitclient has a modulo that is also broken, but at least there's no parse errors this way
MSG_ReadByte();
// CL_SendClientCommand(true, va("%i drop", i+1));
}
cl.splitclients = MAX_SPLITS;
}
break;
#ifdef PEXT_SETVIEW
case svc_setview:
if (!(cls.fteprotocolextensions & PEXT_SETVIEW))
@ -6224,7 +6242,7 @@ void CLQW_ParseServerMessage (void)
i = MSG_ReadByte ();
if (i >= MAX_CLIENTS)
Host_EndGame ("CL_ParseServerMessage: svc_updateentertime > MAX_SCOREBOARD");
cl.players[i].entertime = cl.servertime - MSG_ReadFloat ();
cl.players[i].realentertime = realtime - MSG_ReadFloat ();
break;
case svc_spawnbaseline:

View file

@ -464,7 +464,7 @@ typedef struct {
char name[PLUGMAX_SCOREBOARDNAME];
int ping;
int pl;
int starttime;
float activetime;
int userid;
int spectator;
char userinfo[1024];
@ -501,7 +501,7 @@ static qintptr_t VARGS Plug_GetPlayerInfo(void *offset, quintptr_t mask, const q
Q_strncpyz(out->name, cl.players[i].name, PLUGMAX_SCOREBOARDNAME);
out->ping = cl.players[i].ping;
out->pl = cl.players[i].pl;
out->starttime = cl.players[i].entertime;
out->activetime = realtime - cl.players[i].realentertime;
out->userid = cl.players[i].userid;
out->spectator = cl.players[i].spectator;
Q_strncpyz(out->userinfo, cl.players[i].userinfo, sizeof(out->userinfo));

View file

@ -552,7 +552,7 @@ void CL_CalcClientTime(void)
}
else// if (cls.protocol != CP_QUAKE3)
{
float oldst = realtime;
// float oldst = realtime;
if (cls.demoplayback && cls.timedemo)
{ //more deterministic. one frame is drawn per demo packet parsed. so sync to it as closely as possible.
@ -574,7 +574,7 @@ void CL_CalcClientTime(void)
{
float min, max;
oldst = cl.servertime;
// oldst = cl.servertime;
max = cl.gametime;
min = cl.oldgametime;
@ -624,7 +624,7 @@ void CL_CalcClientTime(void)
}
}
cl.time = cl.servertime;
if (oldst == 0)
/* if (oldst == 0)
{
int i;
for (i = 0; i < cl.allocated_client_slots; i++)
@ -632,9 +632,11 @@ void CL_CalcClientTime(void)
cl.players[i].entertime += cl.servertime;
}
}
*/
return;
}
#if 0
if (cls.protocol == CP_NETQUAKE || (cls.demoplayback && cls.demoplayback != DPB_MVD && cls.demoplayback != DPB_EZTV))
{
float want;
@ -669,6 +671,7 @@ void CL_CalcClientTime(void)
if (cl.time > realtime)
cl.time = realtime;
}
#endif
}
static void CL_DecodeStateSize(unsigned short solid, int modelindex, vec3_t mins, vec3_t maxs)
@ -894,7 +897,10 @@ void CL_PredictMovePNum (int seat)
pv->nolocalplayer = !!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || (cls.protocol != CP_QUAKEWORLD);
if (!cl.spectator) //just in case
{
pv->cam_state = CAM_FREECAM;
pv->cam_spec_track = -1;
}
#ifdef Q2CLIENT
if (cls.protocol == CP_QUAKE2)

View file

@ -159,7 +159,7 @@ typedef struct player_info_s
// scoreboard information
char name[MAX_SCOREBOARDNAME];
char team[MAX_INFO_KEY];
float entertime;
float realentertime; //pegged against realtime, to cope with potentially not knowing the server's time when we first receive this message
int frags;
int ping;
qbyte pl;
@ -826,7 +826,7 @@ typedef struct
double matchgametimestart;
enum {
MATCH_DONTKNOW,
MATCH_DONTKNOW, //assumed to be in progress.
MATCH_COUNTDOWN,
MATCH_STANDBY,
MATCH_INPROGRESS

View file

@ -419,7 +419,7 @@ int Con_ExecuteLine(console_t *con, char *line)
waschat = true;
if (keydown[K_CTRL])
Cbuf_AddText ("say_team ", RESTRICT_LOCAL);
else if (keydown[K_SHIFT])
else if (keydown[K_SHIFT] || *line == ' ')
Cbuf_AddText ("say ", RESTRICT_LOCAL);
else
waschat = false;

View file

@ -2581,6 +2581,7 @@ typedef struct
MV_BONES,
MV_SHADER
} mode;
int surfaceidx;
int skingroup;
int framegroup;
double framechangetime;
@ -2619,9 +2620,7 @@ static unsigned int genhsv(float h_, float s, float v)
((int)(r*255)<<16) |
((int)(g*255)<<8) |
((int)(b*255)<<0);
};
const char *Mod_FrameNameForNum(model_t *model, int num);
const char *Mod_SkinNameForNum(model_t *model, int num);
}
#include "com_mesh.h"
#ifdef SKELETALMODELS
@ -2650,6 +2649,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_
entity_t ent;
vec3_t fwd, rgt, up;
const char *fname;
shader_t *shader;
vec2_t fs = {8,8};
modelview_t *mods = c->dptr;
@ -2713,15 +2713,27 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_
y = 0;
fname = Mod_FrameNameForNum(ent.model, mods->framegroup);
fname = Mod_SurfaceNameForNum(ent.model, mods->surfaceidx);
if (!fname)
fname = "Unknown Frame";
Draw_FunString(0, y, va("%i: %s", mods->framegroup, fname));
fname = "Unknown Surface";
Draw_FunString(0, y, va("Surf %i: %s", mods->surfaceidx, fname));
y+=8;
fname = Mod_SkinNameForNum(ent.model, mods->skingroup);
{
char *fname;
int numframes = 0;
float duration = 0;
qboolean loop = false;
if (!Mod_FrameInfoForNum(ent.model, mods->surfaceidx, mods->framegroup, &fname, &numframes, &duration, &loop))
fname = "Unknown Frame";
Draw_FunString(0, y, va("Frame%i: %s (%i poses, %f secs, %s)", mods->framegroup, fname, numframes, duration, loop?"looped":"unlooped"));
y+=8;
}
fname = Mod_SkinNameForNum(ent.model, mods->surfaceidx, mods->skingroup);
if (!fname)
fname = "Unknown Skin";
Draw_FunString(0, y, va("%i: %s", mods->skingroup, fname));
shader = Mod_ShaderForSkin(ent.model, mods->surfaceidx, mods->skingroup);
Draw_FunString(0, y, va("Skin %i: (%s) %s", mods->skingroup, fname, shader?shader->name:"NO SHADER"));
y+=8;
switch(mods->mode)
@ -2759,7 +2771,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_
{
if (!mods->shadertext)
{
char *body = Shader_GetShaderBody(Mod_ShaderForSkin(ent.model, mods->skingroup));
char *body = Shader_GetShaderBody(Mod_ShaderForSkin(ent.model, mods->surfaceidx, mods->skingroup));
mods->shadertext = Z_StrDup(body);
}
R_DrawTextField(r_refdef.grect.x, r_refdef.grect.y+16, r_refdef.grect.width, r_refdef.grect.height-16, mods->shadertext, CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, fs);
@ -2814,17 +2826,33 @@ static qboolean M_ModelViewerKey(struct menucustom_s *c, struct menu_s *m, int k
{
mods->skingroup += 1;
mods->skinchangetime = realtime;
Z_Free(mods->shadertext);
mods->shadertext = NULL;
}
else if (key == K_PGDN)
{
mods->framegroup = max(0, mods->framegroup-1);
mods->framechangetime = realtime;
Z_Free(mods->shadertext);
mods->shadertext = NULL;
}
else if (key == K_PGUP)
{
mods->framegroup += 1;
mods->framechangetime = realtime;
}
else if (key == K_INS)
{
mods->surfaceidx = max(0, mods->surfaceidx-1);
Z_Free(mods->shadertext);
mods->shadertext = NULL;
}
else if (key == K_DEL)
{
mods->surfaceidx += 1;
Z_Free(mods->shadertext);
mods->shadertext = NULL;
}
else
return false;
return true;

View file

@ -147,9 +147,9 @@ extern const char *Mod_FixName (const char *modname, const char *worldname);
char *Mod_ParseWorldspawnKey (const char *ents, const char *key, char *buffer, size_t sizeofbuffer);
extern void Mod_Think (void);
extern int Mod_SkinNumForName (struct model_s *model, const char *name);
extern int Mod_FrameNumForName (struct model_s *model, const char *name);
extern float Mod_GetFrameDuration (struct model_s *model, int framenum);
extern int Mod_SkinNumForName (struct model_s *model, int surfaceidx, const char *name);
extern int Mod_FrameNumForName (struct model_s *model, int surfaceidx, const char *name);
extern float Mod_GetFrameDuration (struct model_s *model, int surfaceidx, int framenum);
#undef FNC

View file

@ -3090,11 +3090,16 @@ static void QCBUILTIN PF_cs_getplayerkey (pubprogfuncs_t *prinst, struct globalv
ret = buffer;
sprintf(ret, "%i", cl.players[pnum].pl);
}
else if (!strcmp(keyname, "entertime")) //packet loss
else if (!strcmp(keyname, "activetime")) //packet loss
{
ret = buffer;
sprintf(ret, "%i", (int)cl.players[pnum].entertime);
sprintf(ret, "%f", realtime - cl.players[pnum].realentertime);
}
// else if (!strcmp(keyname, "entertime")) //packet loss
// {
// ret = buffer;
// sprintf(ret, "%i", (int)cl.players[pnum].entertime);
// }
else if (!strcmp(keyname, "topcolor_rgb")) //packet loss
{
unsigned int col = cl.players[pnum].ttopcolor;

View file

@ -35,9 +35,6 @@ qc must build the skeletal object still, which fills the skeletal object from th
#include "quakedef.h"
qboolean Mod_FrameInfoForNum(model_t *model, int num, char **name, int *numframes, float *duration, qboolean *loop);
const char *Mod_SkinNameForNum(model_t *model, int num);
#if defined(RAGDOLL) || defined(SKELETALOBJECTS)
#include "pr_common.h"
@ -870,7 +867,7 @@ void skel_generateragdoll_f(void)
int numframes;
float duration;
qboolean loop;
if (!Mod_FrameInfoForNum(mod, i, &fname, &numframes, &duration, &loop))
if (!Mod_FrameInfoForNum(mod, 0, i, &fname, &numframes, &duration, &loop))
break;
VFS_PUTS(f, va("//%i %s (%i frames) (%f secs)%s", i, fname, numframes, duration, loop?" (loop)":""));
}
@ -882,7 +879,7 @@ void skel_generateragdoll_f(void)
for (i = 0; i < 32768; i++)
{
const char *sname;
sname = Mod_SkinNameForNum(mod, i);
sname = Mod_SkinNameForNum(mod, 0, i);
if (!sname)
break;
VFS_PUTS(f, va("//%i %s", i, sname));
@ -2193,17 +2190,15 @@ void QCBUILTIN PF_gettagindex (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
G_FLOAT(OFS_RETURN) = 0;
}
const char *Mod_FrameNameForNum(model_t *model, int num);
const char *Mod_SkinNameForNum(model_t *model, int num);
//string(float modidx, float framenum) frametoname
void QCBUILTIN PF_frametoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
unsigned int modelindex = G_FLOAT(OFS_PARM0);
unsigned int skinnum = G_FLOAT(OFS_PARM1);
int surfaceidx = 0;
model_t *mod = w->Get_CModel(w, modelindex);
const char *n = Mod_FrameNameForNum(mod, skinnum);
const char *n = Mod_FrameNameForNum(mod, surfaceidx, skinnum);
if (n)
RETURN_TSTRING(n);
@ -2215,11 +2210,12 @@ void QCBUILTIN PF_frameforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_
{
world_t *w = prinst->parms->user;
unsigned int modelindex = G_FLOAT(OFS_PARM0);
int surfaceidx = 0;
char *str = PF_VarString(prinst, 1, pr_globals);
model_t *mod = w->Get_CModel(w, modelindex);
if (mod)
G_FLOAT(OFS_RETURN) = Mod_FrameNumForName(mod, str);
G_FLOAT(OFS_RETURN) = Mod_FrameNumForName(mod, surfaceidx, str);
else
G_FLOAT(OFS_RETURN) = -1;
}
@ -2228,10 +2224,11 @@ void QCBUILTIN PF_frameduration (pubprogfuncs_t *prinst, struct globalvars_s *pr
world_t *w = prinst->parms->user;
unsigned int modelindex = G_FLOAT(OFS_PARM0);
unsigned int framenum = G_FLOAT(OFS_PARM1);
int surfaceidx = 0;
model_t *mod = w->Get_CModel(w, modelindex);
if (mod)
G_FLOAT(OFS_RETURN) = Mod_GetFrameDuration(mod, framenum);
G_FLOAT(OFS_RETURN) = Mod_GetFrameDuration(mod, surfaceidx, framenum);
else
G_FLOAT(OFS_RETURN) = 0;
}
@ -2242,8 +2239,9 @@ void QCBUILTIN PF_skintoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl
world_t *w = prinst->parms->user;
unsigned int modelindex = G_FLOAT(OFS_PARM0);
unsigned int skinnum = G_FLOAT(OFS_PARM1);
int surfaceidx = 0;
model_t *mod = w->Get_CModel(w, modelindex);
const char *n = Mod_SkinNameForNum(mod, skinnum);
const char *n = Mod_SkinNameForNum(mod, surfaceidx, skinnum);
if (n)
RETURN_TSTRING(n);
@ -2256,10 +2254,11 @@ void QCBUILTIN PF_skinforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
world_t *w = prinst->parms->user;
unsigned int modelindex = G_FLOAT(OFS_PARM0);
char *str = PF_VarString(prinst, 1, pr_globals);
int surfaceidx = 0;
model_t *mod = w->Get_CModel(w, modelindex);
if (mod)
G_FLOAT(OFS_RETURN) = Mod_SkinNumForName(mod, str);
G_FLOAT(OFS_RETURN) = Mod_SkinNumForName(mod, surfaceidx, str);
else
#endif
G_FLOAT(OFS_RETURN) = -1;

View file

@ -432,9 +432,9 @@ skinfile_t *Mod_LookupSkin(skinid_t id);
void Mod_Init (qboolean initial);
void Mod_Shutdown (qboolean final);
int Mod_TagNumForName(struct model_s *model, const char *name);
int Mod_SkinNumForName(struct model_s *model, const char *name);
int Mod_FrameNumForName(struct model_s *model, const char *name);
float Mod_GetFrameDuration(struct model_s *model, int frameno);
int Mod_SkinNumForName(struct model_s *model, int surfaceidx, const char *name);
int Mod_FrameNumForName(struct model_s *model, int surfaceidx, const char *name);
float Mod_GetFrameDuration(struct model_s *model, int surfaceidx, int frameno);
void Mod_ResortShaders(void);
void Mod_ClearAll (void);

View file

@ -3113,10 +3113,7 @@ ping time frags name
})
#define COLUMN_TIME COLUMN(time, 4*8, \
{ \
if (cl.intermission) \
total = cl.completed_time - s->entertime; \
else \
total = cl.servertime - s->entertime; \
total = realtime - s->realentertime; \
minutes = (int)total/60; \
sprintf (num, "%4i", minutes); \
Draw_FunStringWidth(x, y, num, 4*8, false, false); \

View file

@ -4169,7 +4169,7 @@ int Mod_TagNumForName(model_t *model, const char *name)
return 0;
}
int Mod_FrameNumForName(model_t *model, const char *name)
int Mod_FrameNumForName(model_t *model, int surfaceidx, const char *name)
{
galiasanimation_t *group;
galiasinfo_t *inf;
@ -4186,17 +4186,22 @@ int Mod_FrameNumForName(model_t *model, const char *name)
inf = Mod_Extradata(model);
while(surfaceidx-->0 && inf)
inf = inf->nextsurf;
if (inf)
{
group = inf->ofsanimations;
for (i = 0; i < inf->numanimations; i++, group++)
{
if (!strcmp(group->name, name))
return i;
}
}
return -1;
}
#ifndef SERVERONLY
int Mod_SkinNumForName(model_t *model, const char *name)
int Mod_SkinNumForName(model_t *model, int surfaceidx, const char *name)
{
int i;
galiasinfo_t *inf;
@ -4206,18 +4211,22 @@ int Mod_SkinNumForName(model_t *model, const char *name)
return -1;
inf = Mod_Extradata(model);
while(surfaceidx-->0 && inf)
inf = inf->nextsurf;
if (inf)
{
skin = inf->ofsskins;
for (i = 0; i < inf->numskins; i++, skin++)
{
if (!strcmp(skin->name, name))
return i;
}
}
return -1;
}
#endif
const char *Mod_FrameNameForNum(model_t *model, int num)
const char *Mod_FrameNameForNum(model_t *model, int surfaceidx, int num)
{
galiasanimation_t *group;
galiasinfo_t *inf;
@ -4229,13 +4238,16 @@ const char *Mod_FrameNameForNum(model_t *model, int num)
inf = Mod_Extradata(model);
if (num >= inf->numanimations)
while(surfaceidx-->0 && inf)
inf = inf->nextsurf;
if (!inf || num >= inf->numanimations)
return NULL;
group = inf->ofsanimations;
return group[num].name;
}
qboolean Mod_FrameInfoForNum(model_t *model, int num, char **name, int *numframes, float *duration, qboolean *loop)
qboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **name, int *numframes, float *duration, qboolean *loop)
{
galiasanimation_t *group;
galiasinfo_t *inf;
@ -4247,7 +4259,10 @@ qboolean Mod_FrameInfoForNum(model_t *model, int num, char **name, int *numframe
inf = Mod_Extradata(model);
if (num >= inf->numanimations)
while(surfaceidx-->0 && inf)
inf = inf->nextsurf;
if (!inf || num >= inf->numanimations)
return false;
group = inf->ofsanimations;
@ -4259,7 +4274,7 @@ qboolean Mod_FrameInfoForNum(model_t *model, int num, char **name, int *numframe
}
#ifndef SERVERONLY
shader_t *Mod_ShaderForSkin(model_t *model, int num)
shader_t *Mod_ShaderForSkin(model_t *model, int surfaceidx, int num)
{
galiasinfo_t *inf;
galiasskin_t *skin;
@ -4268,13 +4283,16 @@ shader_t *Mod_ShaderForSkin(model_t *model, int num)
return NULL;
inf = Mod_Extradata(model);
if (num >= inf->numskins)
while(surfaceidx-->0 && inf)
inf = inf->nextsurf;
if (!inf || num >= inf->numskins)
return NULL;
skin = inf->ofsskins;
return skin[num].frame[0].shader;
}
#endif
const char *Mod_SkinNameForNum(model_t *model, int num)
const char *Mod_SkinNameForNum(model_t *model, int surfaceidx, int num)
{
#ifdef SERVERONLY
return NULL;
@ -4286,17 +4304,39 @@ const char *Mod_SkinNameForNum(model_t *model, int num)
return NULL;
inf = Mod_Extradata(model);
if (num >= inf->numskins)
while(surfaceidx-->0 && inf)
inf = inf->nextsurf;
if (!inf || num >= inf->numskins)
return NULL;
skin = inf->ofsskins;
if (!*skin[num].name)
return skin[num].frame[0].shadername;
else
// if (!*skin[num].name)
// return skin[num].frame[0].shadername;
// else
return skin[num].name;
#endif
}
float Mod_GetFrameDuration(model_t *model, int frameno)
const char *Mod_SurfaceNameForNum(model_t *model, int num)
{
#ifdef SERVERONLY
return NULL;
#else
galiasinfo_t *inf;
if (!model || model->type != mod_alias)
return NULL;
inf = Mod_Extradata(model);
while(num-->0 && inf)
inf = inf->nextsurf;
if (inf)
return inf->surfacename;
else
return NULL;
#endif
}
float Mod_GetFrameDuration(model_t *model, int surfaceidx, int frameno)
{
galiasinfo_t *inf;
galiasanimation_t *group;
@ -4305,12 +4345,20 @@ float Mod_GetFrameDuration(model_t *model, int frameno)
return 0;
inf = Mod_Extradata(model);
while(surfaceidx-->0 && inf)
inf = inf->nextsurf;
if (inf)
{
group = inf->ofsanimations;
if (frameno < 0 || frameno >= inf->numanimations)
return 0;
if (frameno >= 0 && frameno < inf->numanimations)
{
group += frameno;
return group->numposes/group->rate;
}
}
return 0;
}
#ifdef MD3MODELS
@ -6476,6 +6524,7 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize)
skin++;
Q_strncpyz(skinframe[j].shadername, strings+mesh[i].material, sizeof(skinframe[j].shadername));
skinframe++;
}
#endif

View file

@ -209,8 +209,12 @@ void Mod_DestroyMesh(galiasinfo_t *galias);
void Alias_FlushCache(void);
void Alias_Shutdown(void);
void Alias_Register(void);
shader_t *Mod_ShaderForSkin(model_t *model, int num);
const char *Mod_SkinNameForNum(model_t *model, int num);
shader_t *Mod_ShaderForSkin(model_t *model, int surfaceidx, int num);
const char *Mod_SkinNameForNum(model_t *model, int surfaceidx, int num);
const char *Mod_SurfaceNameForNum(model_t *model, int num);
const char *Mod_FrameNameForNum(model_t *model, int surfaceidx, int num);
const char *Mod_SkinNameForNum(model_t *model, int surfaceidx, int num);
qboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **name, int *numframes, float *duration, qboolean *loop);
void Mod_DoCRC(model_t *mod, char *buffer, int buffersize);

View file

@ -1226,6 +1226,7 @@ qboolean Cvar_Command (int level)
char *str;
char buffer[65536];
int olev;
int seat;
// check variables
v = Cvar_FindVar (Cmd_Argv(0));
@ -1243,6 +1244,7 @@ qboolean Cvar_Command (int level)
Con_Printf ("Server tried setting %s cvar\n", v->name);
return true;
}
// perform a variable print or set
if (Cmd_Argc() == 1)
{
@ -1291,10 +1293,21 @@ qboolean Cvar_Command (int level)
Con_Printf ("Cvar %s may not be set via the console\n", v->name);
return true;
}
#ifndef SERVERONLY
if (level > RESTRICT_SERVER)
{ //directed at a secondary player.
CL_SendClientCommand(true, "%i setinfo %s %s", level - RESTRICT_SERVER-1, v->name, COM_QuotedString(str, buffer, sizeof(buffer), false));
seat = CL_TargettedSplit(true);
if (v->flags & CVAR_USERINFO)
{
if (Cmd_FromGamecode() && cls.protocol == CP_QUAKEWORLD)
{ //don't bother even changing the cvar locally, just update the server's version.
//fixme: quake2/quake3 latching.
if (seat)
CL_SendClientCommand(true, "%i setinfo %s \"%s\"", seat+1, v->name, str);
else
CL_SendClientCommand(true, "setinfo %s \"%s\"", v->name, str);
}
else
CL_SetInfo(seat, v->name, str);
return true;
}

View file

@ -33,6 +33,7 @@ struct wedict_s
/*the above is shared with qclib*/
link_t area;
pvscache_t pvsinfo;
int lastruntime;
#ifdef USERBE
entityode_t ode;

View file

@ -6008,7 +6008,10 @@ QCC_ref_t *QCC_PR_ParseRefArrayPointer (QCC_ref_t *retbuf, QCC_ref_t *r, pbool a
char *tname;
unsigned int i;
if (!idx.cast && t->type == ev_pointer && !arraysize)
{
t = t->aux_type;
dereference = true;
}
tname = t->name;
if (t->type == ev_struct || t->type == ev_union)
@ -10072,6 +10075,10 @@ pbool QCC_CheckUninitialised(int firststatement, int laststatement)
QCC_type_t *type = pr_scope->type;
int err;
//assume all, because we don't care for optimisations once we know we're not going to compile anything (removes warning about uninitialised unknown variables/typos).
if (pr_error_count)
return true;
for (i = 0; i < type->num_parms; i++)
{
paramend += type->params[i].type->size;

View file

@ -3512,6 +3512,8 @@ static void QCBUILTIN PF_spawnclient (pubprogfuncs_t *prinst, struct globalvars_
svs.clients[i].datagram.allowoverflow = true;
svs.clients[i].datagram.maxsize = 0;
svs.clients[i].edict = EDICT_NUM(prinst, i+1);
SV_SetUpClientEdict (&svs.clients[i], svs.clients[i].edict);
RETURN_EDICT(prinst, svs.clients[i].edict);

View file

@ -1021,6 +1021,7 @@ int SV_ModelIndex (const char *name);
void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg);
void SVQW_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean force, unsigned int protext);
client_t *SV_AddSplit(client_t *controller, char *info, int id);
void SV_GetNewSpawnParms(client_t *cl);
void SV_SaveSpawnparms (void);
void SV_SaveSpawnparmsClient(client_t *client, float *transferparms); //if transferparms, calls SetTransferParms instead, and does not modify the player.

View file

@ -94,6 +94,8 @@ cvar_t allow_download_configs = CVARD("allow_download_configs", "0", "1 allows
cvar_t allow_download_copyrighted = CVARD("allow_download_copyrighted", "0", "0 blocks download of packages that are considered copyrighted. Specifically, this means packages with a leading 'pak' prefix on the filename.\nIf you take your copyrights seriously, you should also set allow_download_pakmaps 0 and allow_download_pakcontents 0.");
cvar_t allow_download_other = CVARD("allow_download_other", "0", "0 blocks downloading of any file that was not covered by any of the directory download blocks.");
extern cvar_t sv_allow_splitscreen;
cvar_t sv_serverip = CVARD("sv_serverip", "", "Set this cvar to the server's public ip address if the server is behind a firewall and cannot detect its own public address. Providing a port is required if the firewall/nat remaps it, but is otherwise optional.");
cvar_t sv_public = CVAR("sv_public", "0");
cvar_t sv_listen_qw = CVARAF("sv_listen_qw", "1", "sv_listen", 0);
@ -1891,6 +1893,101 @@ void SV_UserDNSResolved(void *ctx, void *data, size_t idx, size_t uid)
Z_Free(data);
}
client_t *SV_AddSplit(client_t *controller, char *info, int id)
{
client_t *cl, *prev;
int i;
int curclients;
if (!(controller->fteprotocolextensions & PEXT_SPLITSCREEN))
{
SV_PrintToClient(controller, PRINT_HIGH, "Your client doesn't support splitscreen\n");
return NULL;
}
for (curclients = 0, prev = cl = controller; cl; cl = cl->controlled)
{
prev = cl;
curclients++;
}
if (id && curclients != id)
return NULL; //this would be weird.
if (curclients >= 16)
return NULL; //protocol limit on stats.
if (curclients >= MAX_SPLITS)
return NULL;
//only allow splitscreen if its explicitly allowed. unless its the local client in which case its always allowed.
//wouldn't it be awesome if we could always allow it for spectators? the join command makes that awkward, though I suppose we could just drop the extras in that case.
if (!sv_allow_splitscreen.ival && controller->netchan.remote_address.type != NA_LOOPBACK)
return NULL;
for (i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)
{
if (cl->state == cs_free)
{
break;
}
}
if (i == sv.allocated_client_slots)
{
SV_PrintToClient(controller, PRINT_HIGH, "not enough free player slots\n");
return NULL;
}
cl->spectator = controller->spectator;
cl->netchan.remote_address = controller->netchan.remote_address;
cl->zquake_extensions = controller->zquake_extensions;
cl->fteprotocolextensions = controller->fteprotocolextensions;
cl->fteprotocolextensions2 = controller->fteprotocolextensions2;
cl->penalties = controller->penalties;
cl->protocol = controller->protocol;
Q_strncatz(cl->guid, va("%s:%i", controller->guid, curclients), sizeof(cl->guid));
cl->name = cl->namebuf;
cl->team = cl->teambuf;
nextuserid++; // so every client gets a unique id
cl->userid = nextuserid;
cl->playerclass = 0;
cl->pendingentbits = NULL;
cl->edict = EDICT_NUM(svprogfuncs, i+1);
prev->controlled = cl;
prev = cl;
cl->controller = prev->controller?prev->controller:host_client;
cl->controlled = NULL;
Q_strncpyS (cl->userinfo, info, sizeof(cl->userinfo)-1);
cl->userinfo[sizeof(cl->userinfo)-1] = '\0';
if (controller->spectator)
{
Info_RemoveKey (cl->userinfo, "spectator");
//this is a hint rather than a game breaker should it fail.
Info_SetValueForStarKey (cl->userinfo, "*spectator", "1", sizeof(cl->userinfo));
}
else
Info_RemoveKey (cl->userinfo, "*spectator");
SV_ExtractFromUserinfo (cl, true);
SV_GetNewSpawnParms(cl);
cl->state = controller->state;
if (cl->state >= cs_connected)
{
cl->sendinfo = true;
SV_SetUpClientEdict(cl, cl->edict);
}
if (cl->state >= cs_spawned)
SV_Begin_Core(cl);
return cl;
}
/*
==================
SVC_DirectConnect
@ -2806,7 +2903,13 @@ client_t *SVC_DirectConnect(void)
Info_SetValueForStarKey (cl->userinfo, "*spectator", "1", sizeof(cl->userinfo));
}
//only advertise PEXT_SPLITSCREEN when splitscreen is allowed, to avoid spam. this might mean people need to reconnect after its enabled. oh well.
if (!sv_allow_splitscreen.ival)
newcl->fteprotocolextensions &= ~PEXT_SPLITSCREEN;
for (clients = 1; clients < numssclients; clients++)
SV_AddSplit(newcl, userinfo[clients], clients);
#if 0
for (clients = 1; clients < numssclients; clients++)
{
for (i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)
@ -2819,8 +2922,6 @@ client_t *SVC_DirectConnect(void)
if (i == sv.allocated_client_slots)
break;
newcl->fteprotocolextensions |= PEXT_SPLITSCREEN;
temp.frameunion.frames = cl->frameunion.frames; //don't touch these.
temp.edict = cl->edict;
memcpy(cl, newcl, sizeof(client_t));
@ -2869,6 +2970,7 @@ client_t *SVC_DirectConnect(void)
SV_EvaluatePenalties(cl);
}
#endif
newcl->controller = NULL;
if (!redirect)

View file

@ -2083,9 +2083,10 @@ void WPhys_RunEntity (world_t *w, wedict_t *ent)
else
#endif
{
if ((unsigned int)ent->v->lastruntime == w->framenum)
if (ent->lastruntime == w->framenum)
return;
ent->v->lastruntime = w->framenum;
ent->lastruntime = w->framenum;
ent->v->lastruntime = w->physicstime;
#ifndef CLIENTONLY
svent = NULL;
#endif
@ -2439,7 +2440,7 @@ qboolean SV_Physics (void)
old_bot_time = newbottime;
for (i = 1; i <= sv.allocated_client_slots; i++)
{
if (svs.clients[i-1].state && svs.clients[i-1].protocol == SCP_BAD)
if (svs.clients[i-1].state > cs_zombie && svs.clients[i-1].protocol == SCP_BAD)
{ //then this is a bot
oldhost = host_client;
oldplayer = sv_player;

View file

@ -837,6 +837,9 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int
}
}
if (client->penalties & BAN_BLIND)
continue;
if (pnum >= 0)
{
if (pnum != j)
@ -844,26 +847,26 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int
}
else if (svprogfuncs)
{
if (!((int)client->edict->xv->dimension_see & dimension_mask))
client_t *seat = client;
for(seat = client; seat; seat = seat->controlled)
{
if (!((int)seat->edict->xv->dimension_see & dimension_mask))
continue;
if (!mask) //no pvs? broadcast.
goto inrange;
if (client->penalties & BAN_BLIND)
continue;
if (to == MULTICAST_PHS_R || to == MULTICAST_PHS)
{
vec3_t delta;
VectorSubtract(origin, client->edict->v->origin, delta);
VectorSubtract(origin, seat->edict->v->origin, delta);
if (DotProduct(delta, delta) <= 1024*1024)
goto inrange;
}
{
vec3_t pos;
VectorAdd(client->edict->v->origin, client->edict->v->view_ofs, pos);
VectorAdd(seat->edict->v->origin, seat->edict->v->view_ofs, pos);
cluster = sv.world.worldmodel->funcs.ClusterForPoint (sv.world.worldmodel, pos);
if (cluster>= 0 && !(mask[cluster>>3] & (1<<(cluster&7)) ) )
{
@ -872,6 +875,7 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int
}
}
}
}
inrange:
switch (client->protocol)

View file

@ -3655,6 +3655,48 @@ void SV_Pause_f (void)
}
static void SV_UpdateSeats(client_t *controller)
{
client_t *cl, *prev;
int curclients;
for (curclients = 0, cl = controller; cl; cl = cl->controlled)
{
prev = cl;
curclients++;
}
ClientReliableWrite_Begin(controller, svc_signonnum, 2+curclients);
ClientReliableWrite_Byte(controller, curclients);
for (curclients = 0, cl = controller; cl; cl = cl->controlled, curclients++)
{
ClientReliableWrite_Byte(controller, cl - svs.clients);
}
for (curclients = 0, cl = controller; cl; cl = cl->controlled, curclients++)
{
// send a fixangle over the reliable channel to make sure it gets there
// Never send a roll angle, because savegames can catch the server
// in a state where it is expecting the client to correct the angle
// and it won't happen if the game was just loaded, so you wind up
// with a permanent head tilt
ClientReliableWrite_Begin(controller, svcfte_choosesplitclient, 2+curclients);
ClientReliableWrite_Byte (controller, curclients);
ClientReliableWrite_Byte (controller, svc_setangle);
if (cl->edict->v->fixangle)
{
ClientReliableWrite_Angle(controller, cl->edict->v->angles[0]);
ClientReliableWrite_Angle(controller, cl->edict->v->angles[1]);
ClientReliableWrite_Angle(controller, 0);//cl->edict->v->angles[2]);
}
else
{
ClientReliableWrite_Angle(controller, cl->edict->v->v_angle[0]);
ClientReliableWrite_Angle(controller, cl->edict->v->v_angle[1]);
ClientReliableWrite_Angle(controller, 0);//cl->edict->v->v_angle[2]);
}
}
}
/*
=================
@ -3666,6 +3708,7 @@ The client is going to disconnect, so remove the connection immediately
void SV_Drop_f (void)
{
extern cvar_t sv_fullredirect;
client_t *prev;
SV_EndRedirect ();
if (!host_client->drop)
@ -3681,6 +3724,26 @@ void SV_Drop_f (void)
}
host_client->drop = true;
}
//if splitscreen, orphan the dropper
if (host_client->controller)
{
for (prev = host_client->controller; prev; prev = prev->controlled)
{
if (prev->controlled == host_client)
{
prev->controlled = host_client->controlled;
host_client->netchan.remote_address.type = NA_INVALID; //so the remaining client doesn't get the kick too.
host_client->protocol = SCP_BAD; //make it a bit like a bot, so we don't try sending any datagrams/reliables at someone that isn't able to receive anything.
SV_UpdateSeats(host_client->controller);
host_client->controller->joinobservelockeduntil = realtime + 3;
host_client->controlled = NULL;
host_client->controller = NULL;
break;
}
}
}
}
/*
@ -4494,6 +4557,78 @@ void SV_SetUpClientEdict (client_t *cl, edict_t *ent)
ent->v->frags = 0;
cl->connection_started = realtime;
}
//dynamically add/remove a splitscreen client
static void Cmd_AddSeat_f(void)
{
client_t *cl, *prev;
qboolean changed = false;
//don't allow an altseat to add. paranoia.
if (host_client->controller)
return;
if (host_client->state != cs_spawned)
return;
if (!(host_client->fteprotocolextensions & PEXT_SPLITSCREEN))
return;
if (Cmd_Argc()>1)
{
int num = atoi(Cmd_Argv(1));
int count;
if (!num || host_client->joinobservelockeduntil > realtime)
return;
host_client->joinobservelockeduntil = realtime + 2;
for (count = 1, prev = host_client, cl = host_client->controlled; cl; cl = cl->controlled)
{
if (count >= num)
{
for(; cl; cl = prev->controlled)
{
//unlink it
prev->controlled = cl->controlled;
cl->controller = NULL;
cl->controlled = NULL;
//make it into a pseudo-bot
cl->netchan.remote_address.type = NA_INVALID; //so the remaining client doesn't get the kick too.
cl->protocol = SCP_BAD; //make it a bit like a bot, so we don't try sending any datagrams/reliables at someone that isn't able to receive anything.
//okay, it can get lost now.
cl->drop = true;
}
host_client->controller->joinobservelockeduntil = realtime + 3;
changed = true;
break;
}
prev = cl;
count++;
}
if (!changed && count <= num)
changed = !!SV_AddSplit(host_client, Cmd_Argv(2), num);
}
else
{
cl = NULL;
/* if (host_client->joinobservelockeduntil > realtime)
{
SV_TPrintToClient(host_client, PRINT_HIGH, va("Please wait %.1g more seconds\n", host_client->joinobservelockeduntil-realtime));
return;
}
host_client->joinobservelockeduntil = realtime + 2;
cl = SV_AddSplit(host_client, host_client->userinfo, 0);
*/
}
if (cl || changed)
SV_UpdateSeats(host_client);
}
/*
==================
Cmd_Join_f
@ -4825,6 +4960,9 @@ void Cmd_FPSList_f(void)
void SV_EnableClientsCSQC(void)
{
if (host_client->controller)
return;
#ifdef PEXT_CSQC
// if ((host_client->fteprotocolextensions & PEXT_CSQC) || atoi(Cmd_Argv(1)))
{
@ -5314,6 +5452,7 @@ ucmd_t ucmds[] =
{"nextdl", SV_NextDownload_f, true},
/*quakeworld specific things*/
{"addseat", Cmd_AddSeat_f},
{"join", Cmd_Join_f},
{"observe", Cmd_Observe_f},
{"snap", SV_NoSnap_f},