attempt to fix autoid revealing spies in TF.

fix issues with converted qizmo demos that appear to contain connectionless packets.
clarify some spam a little.
made demos menu re-open in the same path as before, with the same demo selected. still no mouse.
attempt to cope with gles2's potential lower attribute limit.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4939 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2015-07-09 18:02:49 +00:00
parent b247f3e076
commit 89a33f317a
12 changed files with 186 additions and 99 deletions

View file

@ -862,6 +862,7 @@ readit:
olddemotime = demotime;
net_from.type = NA_INVALID;
return 1;
}

View file

@ -2263,11 +2263,15 @@ void CL_Packet_f (void)
if (Cmd_FromGamecode()) //some mvd servers stuffcmd a packet command which lets them know which ip the client is from.
{ //unfortunatly, 50% of servers are badly configured.
if (cls.demoplayback)
{
Con_DPrintf ("Not sending realip packet from demo\n");
return;
}
if (adr.type == NA_IP)
if (adr.address.ip[0] == 127)
if (adr.address.ip[1] == 0)
if (adr.address.ip[2] == 0)
if (adr.address.ip[3] == 1)
if ((adr.address.ip[0] == 127 && adr.address.ip[1] == 0 && adr.address.ip[2] == 0 && adr.address.ip[3] == 1) ||
(adr.address.ip[0] == 0 && adr.address.ip[1] == 0 && adr.address.ip[2] == 0 && adr.address.ip[3] == 0))
{
adr.address.ip[0] = cls.netchan.remote_address.address.ip[0];
adr.address.ip[1] = cls.netchan.remote_address.address.ip[1];
@ -2613,32 +2617,36 @@ void CL_ConnectionlessPacket (void)
Con_TPrintf ("redirect to %s\n", data);
NET_StringToAdr(data, PORT_QWSERVER, &adr);
data = "\xff\xff\xff\xffgetchallenge\n";
connectinfo.istransfer = true;
connectinfo.adr = adr;
NET_SendPacket (NS_CLIENT, strlen(data), data, &adr);
if (NET_CompareAdr(&connectinfo.adr, &net_from))
{
connectinfo.istransfer = true;
connectinfo.adr = adr;
NET_SendPacket (NS_CLIENT, strlen(data), data, &adr);
}
return;
}
else if (!strcmp(s, "reject"))
{ //generic rejection. stop trying.
char *data = MSG_ReadStringLine();
Con_Printf ("reject\n%s\n", data);
connectinfo.trying = false;
if (NET_CompareAdr(&connectinfo.adr, &net_from))
connectinfo.trying = false;
return;
}
else if (!strcmp(s, "badname"))
{ //rejected purely because of player name
Con_Printf ("f%s\n", s);
connectinfo.trying = false;
return;
if (NET_CompareAdr(&connectinfo.adr, &net_from))
connectinfo.trying = false;
}
else if (!strcmp(s, "badaccount"))
{ //rejected because username or password is wrong
Con_Printf ("f%s\n", s);
connectinfo.trying = false;
return;
if (NET_CompareAdr(&connectinfo.adr, &net_from))
connectinfo.trying = false;
}
else
Con_Printf ("f%s", s);
Con_Printf ("f%s\n", s);
return;
}
if (c == S2C_CHALLENGE)
@ -2916,6 +2924,8 @@ void CL_ConnectionlessPacket (void)
#ifdef Q2CLIENT
client_connect: //fixme: make function
#endif
if (net_from.type == NA_INVALID)
return; //I've found a qizmo demo that contains one of these. its best left ignored.
if (!NET_CompareAdr(&connectinfo.adr, &net_from))
{
@ -2980,7 +2990,7 @@ client_connect: //fixme: make function
{
char cmdtext[2048];
if (net_from.type != net_local_cl_ipadr.type || net_from.type != NA_IP
if (net_from.type == NA_INVALID || net_from.type != net_local_cl_ipadr.type || net_from.type != NA_IP
|| ((*(unsigned *)net_from.address.ip != *(unsigned *)net_local_cl_ipadr.address.ip) && (*(unsigned *)net_from.address.ip != htonl(INADDR_LOOPBACK))))
{
Con_TPrintf ("Command packet from remote host. Ignored.\n");
@ -3047,7 +3057,7 @@ client_connect: //fixme: make function
}
//happens in demos
if (c == svc_disconnect && cls.demoplayback != DPB_NONE)
if (c == svc_disconnect && cls.demoplayback != DPB_NONE && net_from.type == NA_INVALID)
{
Host_EndGame ("End of Demo");
return;

View file

@ -383,9 +383,9 @@ void CL_PredictUsercmd (int pnum, int entnum, player_state_t *from, player_state
VectorCopy (from->velocity, pmove.velocity);
VectorCopy (from->gravitydir, pmove.gravitydir);
if (!(pmove.velocity[0] == 0) && !(pmove.velocity[0] != 0))
if (IS_NAN(pmove.velocity[0]))
{
Con_Printf("nan velocity!\n");
Con_DPrintf("nan velocity!\n");
pmove.velocity[0] = 0;
pmove.velocity[1] = 0;
pmove.velocity[2] = 0;

View file

@ -445,14 +445,19 @@ typedef struct demoitem_s {
char name[1];
} demoitem_t;
typedef struct {
int fsroot; //FS_SYSTEM, FS_GAME, FS_GAMEONLY. if FS_SYSTEM, executed command will have a leading #
char path[MAX_OSPATH];
char selname[MAX_OSPATH];
} demoloc_t;
typedef struct {
menucustom_t *list;
demoitem_t *selected;
demoitem_t *firstitem;
demoloc_t *fs;
int pathlen;
char path[MAX_OSPATH];
int fsroot; //FS_SYSTEM, FS_GAME, FS_GAMEONLY. if FS_SYSTEM, executed command will have a leading #
char *command[64]; //these let the menu be used for nearly any sort of file browser.
char *ext[64];
@ -524,34 +529,34 @@ static qboolean M_DemoKey(menucustom_t *control, menu_t *menu, int key, unsigned
case K_UPARROW:
if (info->selected && info->selected->prev)
info->selected = info->selected->prev;
return true;
break;
case K_MWHEELDOWN:
case K_DOWNARROW:
if (info->selected && info->selected->next)
info->selected = info->selected->next;
return true;
break;
case K_HOME:
info->selected = info->items;
return true;
break;
case K_END:
info->selected = info->items;
while(info->selected->next)
info->selected = info->selected->next;
return true;
break;
case K_PGUP:
for (i = 0; i < 10; i++)
{
if (info->selected && info->selected->prev)
info->selected = info->selected->prev;
}
return true;
break;
case K_PGDN:
for (i = 0; i < 10; i++)
{
if (info->selected && info->selected->next)
info->selected = info->selected->next;
}
return true;
break;
case K_ENTER:
case K_KP_ENTER:
if (info->selected)
@ -568,13 +573,20 @@ static qboolean M_DemoKey(menucustom_t *control, menu_t *menu, int key, unsigned
if (extnum == info->numext) //wasn't on our list of extensions.
extnum = 0;
Cbuf_AddText(va("%s \"%s%s\"\n", info->command[extnum], (info->fsroot==FS_SYSTEM)?"#":"", info->selected->name), RESTRICT_LOCAL);
M_RemoveMenu(menu);
Cbuf_AddText(va("%s \"%s%s\"\n", info->command[extnum], (info->fs->fsroot==FS_SYSTEM)?"#":"", info->selected->name), RESTRICT_LOCAL);
M_RemoveMenu(menu);
return true;
}
}
return true;
break;
default:
return false;
}
return false;
if (info->selected)
Q_strncpyz(info->fs->selname, info->selected->name, sizeof(info->fs->selname));
else
Q_strncpyz(info->fs->selname, "", sizeof(info->fs->selname));
return true;
}
static int QDECL DemoAddItem(const char *filename, qofs_t size, time_t modified, void *parm, searchpathfuncs_t *spath)
@ -730,23 +742,27 @@ static void ShowDemoMenu (menu_t *menu, const char *path)
char *s;
char match[256];
if (*path == '/')
path++;
Q_strncpyz(info->path, path, sizeof(info->path));
if (info->fsroot == FS_GAME)
if (path != info->fs->path)
{
if (*path == '/')
path++;
Q_strncpyz(info->fs->path, path, sizeof(info->fs->path));
}
if (info->fs->fsroot == FS_GAME)
{
if (!strcmp(path, "../"))
{
FS_NativePath("", FS_ROOT, info->path, sizeof(info->path));
info->fsroot = FS_SYSTEM;
while((s = strchr(info->path, '\\')))
FS_NativePath("", FS_ROOT, info->fs->path, sizeof(info->fs->path));
info->fs->fsroot = FS_SYSTEM;
while((s = strchr(info->fs->path, '\\')))
*s = '/';
}
}
while (!strcmp(info->path+strlen(info->path)-3, "../"))
while (!strcmp(info->fs->path+strlen(info->fs->path)-3, "../"))
{
c = 0;
for (s = info->path+strlen(info->path)-3; s >= info->path; s--)
for (s = info->fs->path+strlen(info->fs->path)-3; s >= info->fs->path; s--)
{
if (*s == '/')
{
@ -757,71 +773,85 @@ static void ShowDemoMenu (menu_t *menu, const char *path)
}
}
if (c<2)
*info->path = '\0';
*info->fs->path = '\0';
}
info->selected = NULL;
info->pathlen = strlen(info->path);
info->pathlen = strlen(info->fs->path);
M_Demo_Flush(menu->data);
if (info->fsroot == FS_SYSTEM)
if (info->fs->fsroot == FS_SYSTEM)
{
s = strchr(info->path, '/');
s = strchr(info->fs->path, '/');
if (s && strchr(s+1, '/'))
{
Q_snprintfz(match, sizeof(match), "%s../", info->path);
Q_snprintfz(match, sizeof(match), "%s../", info->fs->path);
DemoAddItem(match, 0, 0, info, NULL);
}
}
else if (*info->path)
else if (*info->fs->path)
{
Q_snprintfz(match, sizeof(match), "%s../", info->path);
Q_snprintfz(match, sizeof(match), "%s../", info->fs->path);
DemoAddItem(match, 0, 0, info, NULL);
}
else if (info->fsroot == FS_GAME)
else if (info->fs->fsroot == FS_GAME)
{
Q_snprintfz(match, sizeof(match), "../");
DemoAddItem(match, 0, 0, info, NULL);
}
if (info->fsroot == FS_SYSTEM)
if (info->fs->fsroot == FS_SYSTEM)
{
if (info->path)
Q_snprintfz(match, sizeof(match), "%s*", info->path);
if (info->fs->path)
Q_snprintfz(match, sizeof(match), "%s*", info->fs->path);
else
Q_snprintfz(match, sizeof(match), "/*");
Sys_EnumerateFiles("", match, DemoAddItem, info, NULL);
}
else
{
Q_snprintfz(match, sizeof(match), "%s*", info->path);
Q_snprintfz(match, sizeof(match), "%s*", info->fs->path);
COM_EnumerateFiles(match, DemoAddItem, info);
}
M_Demo_Flatten(info);
}
void M_Demo_Reselect(demomenu_t *info, const char *name)
{
demoitem_t *item;
for(item = info->items; item; item = item->next)
{
if (!strcmp(item->name, name))
{
info->selected = item;
return;
}
}
}
void M_Menu_Demos_f (void)
{
demomenu_t *info;
menu_t *menu;
menu_t *menu;
static demoloc_t mediareenterloc = {FS_GAME};
Key_Dest_Add(kdm_menu);
Key_Dest_Remove(kdm_console);
m_state = m_complex;
menu = M_CreateMenu(sizeof(demomenu_t));
menu->remove = M_Demo_Remove;
info = menu->data;
info->fsroot = FS_GAME;
info->fs = &mediareenterloc;
info->numext = 0;
info->command[info->numext] = "playdemo";
info->command[info->numext] = "closemenu;playdemo";
info->ext[info->numext++] = ".qwd";
info->command[info->numext] = "playdemo";
info->command[info->numext] = "closemenu;playdemo";
info->ext[info->numext++] = ".dem";
info->command[info->numext] = "playdemo";
info->command[info->numext] = "closemenu;playdemo";
info->ext[info->numext++] = ".dm2";
info->command[info->numext] = "playdemo";
info->command[info->numext] = "closemenu;playdemo";
info->ext[info->numext++] = ".mvd";
info->command[info->numext] = "playdemo";
info->command[info->numext] = "closemenu;playdemo";
info->ext[info->numext++] = ".mvd.gz";
//there are also qizmo demos (.qwz) out there...
//we don't support them, but if we were to ask quizmo to decode them for us, we could do.
@ -845,13 +875,15 @@ void M_Menu_Demos_f (void)
menu->selecteditem = (menuoption_t*)info->list;
ShowDemoMenu(menu, "");
ShowDemoMenu(menu, info->fs->path);
M_Demo_Reselect(info, info->fs->selname);
}
void M_Menu_MediaFiles_f (void)
{
demomenu_t *info;
menu_t *menu;
menu_t *menu;
static demoloc_t mediareenterloc = {FS_GAME};
Key_Dest_Add(kdm_menu);
m_state = m_complex;
@ -860,7 +892,7 @@ void M_Menu_MediaFiles_f (void)
menu->remove = M_Demo_Remove;
info = menu->data;
info->fsroot = FS_GAME;
info->fs = &mediareenterloc;
info->ext[0] = ".m3u";
info->command[0] = "mediaplaylist";
@ -889,6 +921,7 @@ void M_Menu_MediaFiles_f (void)
menu->selecteditem = (menuoption_t*)info->list;
ShowDemoMenu(menu, "");
ShowDemoMenu(menu, info->fs->path);
M_Demo_Reselect(info, info->fs->selname);
}
#endif

View file

@ -80,23 +80,25 @@ typedef unsigned int skinid_t; //skin 0 is 'unused'
struct dlight_s;
typedef struct entity_s
{
//FIXME: instancing somehow. separate visentity+visinstance. only viable with full glsl though.
//will need to generate a vbo somehow for the instances.
int keynum; // for matching entities in different frames
vec3_t origin;
vec3_t angles;
vec3_t angles; // fixme: should be redundant.
vec3_t axis[3];
vec4_t shaderRGBAf; /*colormod+alpha, available for shaders to mix*/
float shaderTime; /*timestamp, for syncing shader times to spawns*/
vec3_t glowmod; /*meant to be a multiplier for the fullbrights*/
int light_known; /*bsp lighting has been caled*/
int light_known; /*bsp lighting has been calced*/
vec3_t light_avg; /*midpoint level*/
vec3_t light_range; /*avg + this = max, avg - this = min*/
vec3_t light_dir;
vec3_t oldorigin;
vec3_t oldangles;
vec3_t oldorigin; /*for q2/q3 beams*/
struct model_s *model; // NULL = no model
int skinnum; // for Alias models
skinid_t customskin; // quake3 style skins

View file

@ -389,6 +389,7 @@ rulesetrule_t rulesetrules_strict[] = {
{"r_shadow_realtime_world", "0"}, /*static lighting can be used to cast shadows around corners*/
{"ruleset_allow_in", "0"},
{"r_projection", "0"},
{"gl_shadeq1_name", "*"},
{NULL}
};
@ -408,6 +409,7 @@ rulesetrule_t rulesetrules_nqr[] = {
{"sbar_teamstatus", "0"},
{"ruleset_allow_in", "0"},
{"r_projection", "0"},
{"gl_shadeq1_name", "*"},
{NULL}
};

View file

@ -1540,6 +1540,7 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam)
qboolean haveinfo;
unsigned int textflags;
int h;
char *pname;
static vec4_t healthcolours[] =
{
@ -1600,6 +1601,7 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam)
health = pl->statsf[STAT_HEALTH];
armour = pl->statsf[STAT_ARMOR];
items = pl->stats[STAT_ITEMS];
pname = pl->name;
haveinfo = true;
}
else
@ -1607,12 +1609,16 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam)
health = pl->tinfo.health;
armour = pl->tinfo.armour;
items = pl->tinfo.items;
pname = ((*pl->tinfo.nick)?pl->tinfo.nick:(cl.teamfortress?"-":pl->name));
haveinfo = pl->tinfo.time > cl.time;
}
y -= 8;
len = COM_ParseFunString(textflags, pl->name, buffer, sizeof(buffer), false) - buffer;
Draw_ExpandedString(x - len*4, y, buffer);
if (strcmp(pname, "-")) //a tinfo nick of - hides names. this can be used for TF to hide names for spies.
{
y -= 8;
len = COM_ParseFunString(textflags, pname, buffer, sizeof(buffer), false) - buffer;
Draw_ExpandedString(x - len*4, y, buffer);
}
if (!haveinfo)
return; //we don't trust the info that we have, so no ids.
@ -1705,6 +1711,7 @@ void R_DrawNameTags(void)
lerpents_t *le;
qboolean isteam;
char *ourteam;
int ourcolour;
extern cvar_t r_showfields, r_projection;
@ -1791,9 +1798,15 @@ void R_DrawNameTags(void)
return;
if (r_refdef.playerview->cam_state != CAM_FREECAM && r_refdef.playerview->cam_spec_track >= 0)
{
ourteam = cl.players[r_refdef.playerview->cam_spec_track].team;
ourcolour = cl.players[r_refdef.playerview->cam_spec_track].rbottomcolor;
}
else
{
ourteam = cl.players[r_refdef.playerview->playernum].team;
ourcolour = cl.players[r_refdef.playerview->playernum].rbottomcolor;
}
for (i = 0; i < cl.allocated_client_slots; i++)
{
@ -1826,14 +1839,16 @@ void R_DrawNameTags(void)
if (i == Cam_TrackNum(r_refdef.playerview)) //no tag for the player that you're tracking, either.
continue;
if (!cl.teamplay || !scr_autoid_team.ival || strcmp(cl.players[i].team, ourteam))
{
if (!cl.teamplay || !scr_autoid_team.ival)
isteam = false;
else if (cl.teamfortress && !cl.spectator) //teamfortress should go by their colours instead, because spies. primarily this is to allow enemy spies to appear through walls as well as your own team (note that the qc will also need tinfo stuff for tf, to avoid issues with just checking player names).
isteam = cl.players[i].rbottomcolor == ourcolour;
else
isteam = !strcmp(cl.players[i].team, ourteam);
if (!isteam)
if (!cl.spectator && !cls.demoplayback || !scr_autoid.ival)
continue; //only show our team when playing, too cheaty otherwise.
isteam = false;
}
else
isteam = true;
SCR_DrawAutoID(nametagorg[i], &cl.players[i], isteam);
}

View file

@ -313,10 +313,10 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b)
}
#endif
Sys_Error("NET_CompareAdr: Bad address type");
Con_Printf("NET_CompareAdr: Bad address type\n");
return false;
}
/*
===================
NET_CompareBaseAdr

View file

@ -932,6 +932,9 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name))
Con_DPrintf("GLSL available\n");
}
if (gl_config.arb_shader_objects)
qglGetIntegerv(GL_MAX_VERTEX_ATTRIBS_ARB, &gl_config.maxattribs);
#endif
qglGetProgramBinary = NULL;
@ -1999,25 +2002,35 @@ GLhandleARB GLSlang_CreateProgramObject (const char *name, GLhandleARB vert, GLh
qglBindAttribLocationARB(program, VATTR_VERTEX1, "v_position1");
qglBindAttribLocationARB(program, VATTR_COLOUR, "v_colour");
#if MAXRLIGHTMAPS > 1
qglBindAttribLocationARB(program, VATTR_COLOUR2, "v_colour2");
qglBindAttribLocationARB(program, VATTR_COLOUR3, "v_colour3");
qglBindAttribLocationARB(program, VATTR_COLOUR4, "v_colour4");
#endif
qglBindAttribLocationARB(program, VATTR_TEXCOORD, "v_texcoord");
qglBindAttribLocationARB(program, VATTR_LMCOORD, "v_lmcoord");
#if MAXRLIGHTMAPS > 1
qglBindAttribLocationARB(program, VATTR_LMCOORD2, "v_lmcoord2");
qglBindAttribLocationARB(program, VATTR_LMCOORD3, "v_lmcoord3");
qglBindAttribLocationARB(program, VATTR_LMCOORD4, "v_lmcoord4");
#endif
qglBindAttribLocationARB(program, VATTR_NORMALS, "v_normal");
qglBindAttribLocationARB(program, VATTR_SNORMALS, "v_svector");
qglBindAttribLocationARB(program, VATTR_TNORMALS, "v_tvector");
qglBindAttribLocationARB(program, VATTR_BONENUMS, "v_bone");
qglBindAttribLocationARB(program, VATTR_BONEWEIGHTS, "v_weight");
qglBindAttribLocationARB(program, VATTR_VERTEX2, "v_position2");
//the following MAY not be valid in gles2.
if (gl_config.maxattribs > VATTR_BONENUMS)
qglBindAttribLocationARB(program, VATTR_BONENUMS, "v_bone");
if (gl_config.maxattribs > VATTR_BONEWEIGHTS)
qglBindAttribLocationARB(program, VATTR_BONEWEIGHTS, "v_weight");
#if MAXRLIGHTMAPS > 1
if (gl_config.maxattribs > VATTR_COLOUR2)
qglBindAttribLocationARB(program, VATTR_COLOUR2, "v_colour2");
if (gl_config.maxattribs > VATTR_COLOUR3)
qglBindAttribLocationARB(program, VATTR_COLOUR3, "v_colour3");
if (gl_config.maxattribs > VATTR_COLOUR4)
qglBindAttribLocationARB(program, VATTR_COLOUR4, "v_colour4");
#endif
#if MAXRLIGHTMAPS > 1
if (gl_config.maxattribs > VATTR_LMCOORD2)
qglBindAttribLocationARB(program, VATTR_LMCOORD2, "v_lmcoord2");
if (gl_config.maxattribs > VATTR_LMCOORD3)
qglBindAttribLocationARB(program, VATTR_LMCOORD3, "v_lmcoord3");
if (gl_config.maxattribs > VATTR_LMCOORD4)
qglBindAttribLocationARB(program, VATTR_LMCOORD4, "v_lmcoord4");
#endif
qglLinkProgramARB(program);
return program;
}
@ -2170,6 +2183,13 @@ qboolean GLSlang_CreateProgramPermu(program_t *prog, const char *name, unsigned
ver = 120;
#endif
}
if ((permu & PERMUTATION_SKELETAL) && gl_config.maxattribs < 10)
return false; //can happen in gles2
#if MAXRLIGHTMAPS > 1
if ((permu & PERMUTATION_LIGHTSTYLES) && gl_config.maxattribs < 16)
return false; //can happen in gles2
#endif
prog->permu[permu].handle = GLSlang_CreateProgram(name, ver, precompilerconstants, vert, tcs, tes, geom, frag, noerrors, blobfile);
if (prog->permu[permu].handle.glsl.handle)
return true;

View file

@ -220,6 +220,7 @@ qboolean GL_CheckExtension(char *extname);
typedef struct {
float glversion;
int maxglslversion;
int maxattribs; //max generic attributes. probably only 16 on nvidia.
qboolean nofixedfunc;
qboolean gles;
qboolean webgl_ie; //workaround ie webgl bugs/omissions.

View file

@ -315,21 +315,24 @@ typedef struct
enum{
PERMUTATION_GENERIC = 0,
PERMUTATION_BUMPMAP = 1,
PERMUTATION_FULLBRIGHT = 2,
PERMUTATION_UPPERLOWER = 4,
PERMUTATION_REFLECTCUBEMASK = 8,
PERMUTATION_BUMPMAP = 1, //FIXME: make argument somehow
PERMUTATION_FULLBRIGHT = 2, //FIXME: make argument somehow
PERMUTATION_UPPERLOWER = 4, //FIXME: make argument somehow
PERMUTATION_REFLECTCUBEMASK = 8, //FIXME: make argument somehow
PERMUTATION_SKELETAL = 16,
PERMUTATION_FOG = 32,
PERMUTATION_FOG = 32, //FIXME: remove.
PERMUTATION_FRAMEBLEND = 64,
#if MAXRLIGHTMAPS > 1
PERMUTATION_LIGHTSTYLES = 128,
PERMUTATION_LIGHTSTYLES = 128, //FIXME: make argument
#endif
PERMUTATIONS = 256
};
enum shaderattribs_e
{
//GLES2 has a limit of 8.
//GL2 has a limit of 16.
//vendors may provide more.
VATTR_VERTEX1=0, //NOTE: read the comment about VATTR_LEG_VERTEX
VATTR_VERTEX2=1,
VATTR_COLOUR=2,

View file

@ -9768,7 +9768,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"con_printf", PF_Fixme, 0, 0, 0, 392, D("void(string conname, string messagefmt, ...)", "Prints onto a named console.")},
{"con_draw", PF_Fixme, 0, 0, 0, 393, D("void(string conname, vector pos, vector size, float fontsize)", "Draws the named console.")},
{"con_input", PF_Fixme, 0, 0, 0, 394, D("float(string conname, float inevtype, float parama, float paramb, float paramc)", "Forwards input events to the named console. Mouse updates should be absolute only.")},
{"cvar_unsaved", PF_Fixme, 0, 0, 0, 0, D("float()", "Returns true if any archived cvar has an unsaved value.")},
{"cvars_haveunsaved",PF_Fixme, 0, 0, 0, 0, D("float()", "Returns true if any archived cvar has an unsaved value.")},
//end fte extras
//DP extras