From b749d8356aa9a40bd3302f4729e1ae30d0f084bf Mon Sep 17 00:00:00 2001 From: Spoike Date: Thu, 8 Sep 2016 19:04:35 +0000 Subject: [PATCH] remove the old SCVAR macro server: my attempt at mvd recording fixes. there's a couple of other issues, at least with nq mods. client: properly support recording .dems mid-map for 15+666 client: server browser now queries nq servers for players+rules. renderer: add support for single-image dual-layer skies that some other engines use. qcc: add #merge, along with __wrap + __weak keywords. fix a symboldata issue revealed by this. qcc: added a flag to write the sourcecode into the .dat (zip format compatible with any zip program that can deal with 'self extractors'). qccgui: can be told to open a .dat file instead of .src, showing/using any embedded sourcecode. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5017 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_cam.c | 10 +- engine/client/cl_demo.c | 674 +++++++++++++++++++++++-------------- engine/client/cl_input.c | 16 +- engine/client/cl_main.c | 13 +- engine/client/cl_master.h | 2 + engine/client/cl_parse.c | 11 +- engine/client/cl_pred.c | 4 +- engine/client/cl_screen.c | 26 +- engine/client/console.c | 16 +- engine/client/m_download.c | 4 + engine/client/m_master.c | 28 +- engine/client/m_mp3.c | 6 +- engine/client/m_options.c | 1 + engine/client/menu.c | 2 +- engine/client/net_master.c | 437 +++++++++++++++--------- engine/client/pr_menu.c | 4 +- engine/client/r_part.c | 23 +- engine/client/renderer.c | 83 ++--- engine/client/sbar.c | 4 +- engine/client/skin.c | 4 +- engine/client/sys_win.c | 4 +- engine/client/valid.c | 22 +- engine/client/view.c | 98 +++--- engine/common/cmd.c | 44 ++- engine/common/common.c | 4 + engine/common/cvar.h | 2 - engine/common/gl_q2bsp.c | 6 +- engine/common/net_chan.c | 4 +- engine/common/net_wins.c | 13 + engine/common/particles.h | 1 + engine/common/plugin.c | 2 +- engine/gl/gl_backend.c | 2 +- engine/gl/gl_font.c | 4 - engine/gl/gl_rmain.c | 8 +- engine/gl/gl_shader.c | 13 + engine/gl/gl_warp.c | 82 ++++- engine/http/iwebiface.c | 14 +- engine/qclib/cmdlib.h | 2 +- engine/qclib/comprout.c | 2 +- engine/qclib/gui.h | 2 +- engine/qclib/pr_comp.h | 4 +- engine/qclib/progsint.h | 5 + engine/qclib/qcc.h | 43 ++- engine/qclib/qcc_cmdlib.c | 5 +- engine/qclib/qcc_pr_comp.c | 297 ++++++++++------ engine/qclib/qcc_pr_lex.c | 201 ++++++++--- engine/qclib/qccgui.c | 125 ++++++- engine/qclib/qccguistuff.c | 19 +- engine/qclib/qccmain.c | 521 +++++++++++++++++++++++++--- engine/qclib/qcd.h | 5 +- engine/qclib/qcd_main.c | 96 +++++- engine/qclib/qcdecomp.c | 5 +- engine/server/pr_cmds.c | 92 ++--- engine/server/server.h | 5 +- engine/server/sv_ccmds.c | 2 +- engine/server/sv_main.c | 93 ++--- engine/server/sv_mvd.c | 365 ++++---------------- engine/server/sv_phys.c | 22 +- engine/server/sv_send.c | 12 +- engine/server/sv_sql.c | 10 +- engine/server/sv_user.c | 18 +- engine/vk/vk_init.c | 23 +- 62 files changed, 2359 insertions(+), 1306 deletions(-) diff --git a/engine/client/cl_cam.c b/engine/client/cl_cam.c index daf690c0d..4596b16b2 100644 --- a/engine/client/cl_cam.c +++ b/engine/client/cl_cam.c @@ -51,14 +51,14 @@ static void QDECL CL_AutoTrackChanged(cvar_t *v, char *oldval) Cam_AutoTrack_Update(v->string); } // track high fragger -cvar_t cl_autotrack_team = CVARD("cl_autotrack_team", "", "Specifies a team name that should be auto-tracked (players on other teams will not be candidates for autotracking). Accepts * and ? wildcards for awkward chars."); -cvar_t cl_autotrack = CVARCD("cl_autotrack", "auto", CL_AutoTrackChanged, "Specifies the default tracking mode at the start of the map. Use the 'autotrack' command to reset/apply an auto-tracking mode without changing the default.\nValid values are: high, ^hkiller^h, mod, user. Other values are treated as weighting scripts for mvd playback, where available."); -cvar_t cl_hightrack = CVARD("cl_hightrack", "0", "Obsolete. If you want hightrack, use '[cl_]autotrack high' instead."); +cvar_t cl_autotrack_team = CVARD("cl_autotrack_team", "", "Specifies a team name that should be auto-tracked (players on other teams will not be candidates for autotracking). Accepts * and ? wildcards for awkward chars."); +cvar_t cl_autotrack = CVARCD("cl_autotrack", "auto", CL_AutoTrackChanged, "Specifies the default tracking mode at the start of the map. Use the 'autotrack' command to reset/apply an auto-tracking mode without changing the default.\nValid values are: high, ^hkiller^h, mod, user. Other values are treated as weighting scripts for mvd playback, where available."); +cvar_t cl_hightrack = CVARD("cl_hightrack", "0", "Obsolete. If you want hightrack, use '[cl_]autotrack high' instead."); //cvar_t cl_camera_maxpitch = {"cl_camera_maxpitch", "10" }; //cvar_t cl_camera_maxyaw = {"cl_camera_maxyaw", "30" }; -cvar_t cl_chasecam = SCVAR("cl_chasecam", "1"); -cvar_t cl_selfcam = SCVAR("cl_selfcam", "1"); +cvar_t cl_chasecam = CVAR("cl_chasecam", "1"); +cvar_t cl_selfcam = CVAR("cl_selfcam", "1"); void Cam_AutoTrack_Update(const char *mode) { diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 128b60a2a..bae0431f9 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -585,7 +585,7 @@ qboolean CL_GetDemoMessage (void) olddemotime = demtime; - if (msglength > MAX_NQMSGLEN) + if (msglength > net_message.maxsize) { Con_Printf ("Demo message > MAX_MSGLEN"); CL_StopPlayback (); @@ -684,7 +684,7 @@ readnext: cls.td_starttime = Sys_DoubleTime(); demtime = demotime; // warp } - else if (!cl.paused && cls.state >= ca_onserver) + else if (!(cl.paused&~4) && cls.state >= ca_onserver) { // always grab until fully connected if (demtime + 1.0 < demotime) { @@ -993,7 +993,7 @@ void CL_WriteRecordQ2DemoMessage(sizebuf_t *msg) ==================== CL_WriteDemoMessage -Dumps the current net message, prefixed by the length and view angles +Dumps the specified net message as part of initial mid-map demo writing. ==================== */ void CL_WriteRecordDemoMessage (sizebuf_t *msg, int seq) @@ -1008,21 +1008,35 @@ void CL_WriteRecordDemoMessage (sizebuf_t *msg, int seq) if (!cls.demorecording) return; - fl = LittleFloat(Sys_DoubleTime()-recdemostart); - VFS_WRITE (cls.demooutfile, &fl, sizeof(fl)); + if (cls.demorecording == DPB_NETQUAKE) + { + len = LittleLong (msg->cursize); + VFS_WRITE(cls.demooutfile, &len, sizeof(len)); + for (i=0 ; i<3 ; i++) + { + float f = LittleFloat (cl.playerview[0].viewangles[i]); + VFS_WRITE(cls.demooutfile, &f, sizeof(f)); + } + } + else + { + fl = LittleFloat(Sys_DoubleTime()-recdemostart); + VFS_WRITE (cls.demooutfile, &fl, sizeof(fl)); - c = dem_read; - VFS_WRITE (cls.demooutfile, &c, sizeof(c)); + c = dem_read; + VFS_WRITE (cls.demooutfile, &c, sizeof(c)); - len = LittleLong (msg->cursize + 8); - VFS_WRITE (cls.demooutfile, &len, 4); - - i = LittleLong(seq); - VFS_WRITE (cls.demooutfile, &i, 4); - VFS_WRITE (cls.demooutfile, &i, 4); + len = LittleLong (msg->cursize + 8); + VFS_WRITE (cls.demooutfile, &len, 4); + i = LittleLong(seq); + VFS_WRITE (cls.demooutfile, &i, 4); + VFS_WRITE (cls.demooutfile, &i, 4); + } VFS_WRITE (cls.demooutfile, msg->data, msg->cursize); + SZ_Clear(msg); + if (record_flush.ival) VFS_FLUSH (cls.demooutfile); } @@ -1110,6 +1124,353 @@ void CL_RecordMap_f (void) } #endif +//qw-specific serverdata +static void CLQW_RecordServerData(sizebuf_t *buf) +{ + extern char gamedirfile[]; + unsigned int i; + +// send the serverdata + MSG_WriteByte (buf, svc_serverdata); +#ifdef PROTOCOL_VERSION_FTE + if (cls.fteprotocolextensions) //maintain demo compatability + { + MSG_WriteLong (buf, PROTOCOL_VERSION_FTE); + MSG_WriteLong (buf, cls.fteprotocolextensions); + } + if (cls.fteprotocolextensions2) //maintain demo compatability + { + MSG_WriteLong (buf, PROTOCOL_VERSION_FTE2); + MSG_WriteLong (buf, cls.fteprotocolextensions2); + } +#endif + MSG_WriteLong (buf, PROTOCOL_VERSION_QW); + MSG_WriteLong (buf, cl.servercount); + MSG_WriteString (buf, gamedirfile); + + if (cls.fteprotocolextensions2 & PEXT2_MAXPLAYERS) + { + MSG_WriteByte (buf, cl.allocated_client_slots); + MSG_WriteByte (buf, cl.splitclients | (cl.spectator?128:0)); + for (i = 0; i < cl.splitclients; i++) + MSG_WriteByte (buf, cl.playerview[i].playernum); + } + else + { + for (i = 0; i < cl.splitclients; i++) + { + if (cl.spectator) + MSG_WriteByte (buf, cl.playerview[i].playernum | 128); + else + MSG_WriteByte (buf, cl.playerview[i].playernum); + } + if (cls.fteprotocolextensions & PEXT_SPLITSCREEN) + MSG_WriteByte (buf, 128); + } + + // send full levelname + MSG_WriteString (buf, cl.levelname); + + // send the movevars + MSG_WriteFloat(buf, movevars.gravity); + MSG_WriteFloat(buf, movevars.stopspeed); + MSG_WriteFloat(buf, movevars.maxspeed); + MSG_WriteFloat(buf, movevars.spectatormaxspeed); + MSG_WriteFloat(buf, movevars.accelerate); + MSG_WriteFloat(buf, movevars.airaccelerate); + MSG_WriteFloat(buf, movevars.wateraccelerate); + MSG_WriteFloat(buf, movevars.friction); + MSG_WriteFloat(buf, movevars.waterfriction); + MSG_WriteFloat(buf, movevars.entgravity); + + // send server info string + MSG_WriteByte (buf, svc_stufftext); + MSG_WriteString (buf, va("fullserverinfo \"%s\"\n", cl.serverinfo) ); +} + +void CLNQ_WriteServerData(sizebuf_t *buf) +{ + unsigned int protmain; + unsigned int protfl = 0; + unsigned int i; + const char *val; + + val = Info_ValueForKey(cl.serverinfo, "*csprogs"); + if (*val) + { + MSG_WriteByte(buf, svc_stufftext); + MSG_WriteString(buf, va("csqc_progcrc \"%s\"\n", val)); + } + val = Info_ValueForKey(cl.serverinfo, "*csprogssize"); + if (*val) + { + MSG_WriteByte(buf, svc_stufftext); + MSG_WriteString(buf, va("csqc_progsize \"%s\"\n", val)); + } + val = Info_ValueForKey(cl.serverinfo, "*csprogsname"); + if (*val) + { + MSG_WriteByte(buf, svc_stufftext); + MSG_WriteString(buf, va("csqc_progname \"%s\"\n", val)); + } + + MSG_WriteByte(buf, svc_serverdata); + if (cls.fteprotocolextensions) + { + MSG_WriteLong (buf, PROTOCOL_VERSION_FTE); + MSG_WriteLong (buf, cls.fteprotocolextensions); + } + if (cls.fteprotocolextensions2) + { + MSG_WriteLong (buf, PROTOCOL_VERSION_FTE2); + MSG_WriteLong (buf, cls.fteprotocolextensions2); + } + + if (cls.netchan.message.prim.anglesize == 2) + protfl |= RMQFL_SHORTANGLE; + if (cls.netchan.message.prim.anglesize == 4) + protfl |= RMQFL_FLOATANGLE; + if (cls.netchan.message.prim.coordsize == 3) + protfl |= RMQFL_24BITCOORD; + if (cls.netchan.message.prim.coordsize == 4) + protfl |= RMQFL_FLOATCOORD; + switch(cls.protocol_nq) + { + default: + case CPNQ_ID: protmain = PROTOCOL_VERSION_NQ; break; + case CPNQ_BJP1: protmain = PROTOCOL_VERSION_BJP1; break; + case CPNQ_BJP2: protmain = PROTOCOL_VERSION_BJP2; break; + case CPNQ_BJP3: protmain = PROTOCOL_VERSION_BJP3; break; + case CPNQ_FITZ666: protmain = protfl?PROTOCOL_VERSION_RMQ:PROTOCOL_VERSION_FITZ; break; //this might break .scale, fte doesn't care, other engines might. + case CPNQ_DP5: protmain = PROTOCOL_VERSION_DP5; break; + case CPNQ_DP6: protmain = PROTOCOL_VERSION_DP6; break; + case CPNQ_DP7: protmain = PROTOCOL_VERSION_DP7; break; + } + + MSG_WriteLong (buf, protmain); + if (protmain == PROTOCOL_VERSION_RMQ) + MSG_WriteLong (buf, protfl); + + MSG_WriteByte (buf, cl.allocated_client_slots); + MSG_WriteByte (buf, cl.deathmatch?GAME_DEATHMATCH:GAME_COOP); + MSG_WriteString (buf, cl.levelname); + + for (i = 1; *cl.model_name[i] && i < MAX_PRECACHE_MODELS; i++) + MSG_WriteString (buf, cl.model_name[i]); + MSG_WriteByte (buf, 0); + + for (i = 1; *cl.sound_name[i] && i < MAX_PRECACHE_SOUNDS ; i++) + MSG_WriteString (buf, cl.sound_name[i]); + MSG_WriteByte (buf, 0); +} + +void CL_Record_Baseline(sizebuf_t *buf, entity_state_t *state, unsigned int bits) +{ + unsigned int j; + if (bits & FITZ_B_LARGEMODEL) + MSG_WriteShort (buf, state->modelindex); + else + MSG_WriteByte (buf, state->modelindex); + if (bits & FITZ_B_LARGEFRAME) + MSG_WriteShort (buf, state->frame); + else + MSG_WriteByte (buf, state->frame); + MSG_WriteByte (buf, state->colormap); + MSG_WriteByte (buf, state->skinnum); + for (j=0 ; j<3 ; j++) + { + MSG_WriteCoord (buf, state->origin[j]); + MSG_WriteAngle (buf, state->angles[j]); + } + + if (bits & FITZ_B_ALPHA) + MSG_WriteByte(buf, state->trans); + if (bits & RMQFITZ_B_SCALE) + MSG_WriteByte(buf, state->scale); +} + +//nq+qw generic stuff. +static int CL_Record_ParticlesStaticsBaselines(sizebuf_t *buf, int seq) +{ + unsigned int i; + entity_state_t *es; + + //particleeffectnum stuff + for (i = 1; i < MAX_SSPARTICLESPRE; i++) + { + if (!cl.particle_ssname[i]) + break; + MSG_WriteByte(buf, svcfte_precache); + MSG_WriteShort(buf, PC_PARTICLE | i); + MSG_WriteString(buf, cl.particle_ssname[i]); + + if (buf->cursize > buf->maxsize/2) + CL_WriteRecordDemoMessage (buf, seq++); + } + + //custom tents (needed for hexen2, if nothing else) + for (i = 0; ; i++) + { + if (!CL_WriteCustomTEnt(buf, i)) + break; + + if (buf->cursize > buf->maxsize/2) + CL_WriteRecordDemoMessage (buf, seq++); + } + +// spawnstatic + + for (i = 0; i < cl.num_statics; i++) + { + es = &cl_static_entities[i].state; + +#ifndef CLIENTONLY //FIXME + if (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) + { + MSG_WriteByte(buf, svcfte_spawnstatic2); + SVFTE_EmitBaseline(es, false, buf, cls.fteprotocolextensions2); + } + //else if (cls.fteprotocolextensions & PEXT_SPAWNSTATIC2) //qw deltas + else +#endif + { + unsigned int bits = 0; +#ifdef NQPROT + if (es->modelindex > 255) + bits |= FITZ_B_LARGEMODEL; + if (es->frame > 255) + bits |= FITZ_B_LARGEFRAME; + if (es->trans != 255) + bits |= FITZ_B_ALPHA; + if (es->scale != 16) + bits |= RMQFITZ_B_SCALE; + if (cls.protocol == CP_NETQUAKE && CPNQ_IS_BJP) + { + MSG_WriteByte (buf, svc_spawnstatic); + bits = FITZ_B_LARGEMODEL; //bjp always uses shorts for models. + } + else if (cls.protocol == CP_NETQUAKE && cls.protocol_nq == CPNQ_FITZ666 && bits) + { + MSG_WriteByte (buf, svcfitz_spawnstatic2); + MSG_WriteByte (buf, bits); + } +// else if (baselinetype2 >= CPNQ_DP5 && baselinetype2 <= CPNQ_DP7 && (bits & (FITZ_B_LARGEMODEL|FITZ_B_LARGEFRAME))) +// { +// MSG_WriteByte (buf, svcdp_spawnstatic2); +// bits = FITZ_B_LARGEMODEL|FITZ_B_LARGEFRAME; //dp's baseline2 always has these (regular baseline is unmodified) +// } + else +#endif + { + //classic protocol + MSG_WriteByte (buf, svc_spawnstatic); + bits = 0; + } + CL_Record_Baseline(buf, es, bits); + } + + if (buf->cursize > buf->maxsize/2) + CL_WriteRecordDemoMessage (buf, seq++); + } + +// FIXME: static sounds + // static sounds are skipped in demos, life is hard + +// baselines + + for (i = 0; i < cl_baselines_count; i++) + { + es = cl_baselines + i; + + if (memcmp(es, &nullentitystate, sizeof(nullentitystate))) + { +#ifndef CLIENTONLY //FIXME + if (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) + { + MSG_WriteByte(buf, svcfte_spawnbaseline2); + SVFTE_EmitBaseline(es, true, buf, cls.fteprotocolextensions2); + } + //else if (cls.fteprotocolextensions & PEXT_SPAWNSTATIC2) //qw deltas + else +#endif + { + unsigned int bits = 0; +#ifdef NQPROT + if (es->modelindex > 255) + bits |= FITZ_B_LARGEMODEL; + if (es->frame > 255) + bits |= FITZ_B_LARGEFRAME; + if (es->trans != 255) + bits |= FITZ_B_ALPHA; + if (es->scale != 16) + bits |= RMQFITZ_B_SCALE; + if (cls.protocol == CP_NETQUAKE && CPNQ_IS_BJP) + { + MSG_WriteByte (buf, svc_spawnbaseline); + bits = FITZ_B_LARGEMODEL; //bjp always uses shorts for models. + } + else if (cls.protocol == CP_NETQUAKE && cls.protocol_nq == CPNQ_FITZ666 && bits) + { + MSG_WriteByte (buf, svcfitz_spawnbaseline2); + MSG_WriteByte (buf, bits); + } + else if (cls.protocol == CP_NETQUAKE && CPNQ_IS_DP && (bits & (FITZ_B_LARGEMODEL|FITZ_B_LARGEFRAME))) + { + MSG_WriteByte (buf, svcdp_spawnbaseline2); + bits = FITZ_B_LARGEMODEL|FITZ_B_LARGEFRAME; //dp's baseline2 always has these (regular baseline is unmodified) + } + else +#endif + { + MSG_WriteByte (buf,svc_spawnbaseline); + bits = 0; + } + MSG_WriteEntity (buf, i); + + CL_Record_Baseline(buf, es, bits); + } + if (buf->cursize > buf->maxsize/2) + CL_WriteRecordDemoMessage (buf, seq++); + } + } + + return seq; +} +static int CL_Record_Lightstyles(sizebuf_t *buf, int seq) +{ + unsigned int i; +// send all current light styles + for (i=0 ; i= MAX_STANDARDLIGHTSTYLES) + if (!*cl_lightstyle[i].map) + continue; + +#ifdef PEXT_LIGHTSTYLECOL + if ((cls.fteprotocolextensions & PEXT_LIGHTSTYLECOL) && (cl_lightstyle[i].colours[0]!=1||cl_lightstyle[i].colours[1]!=1||cl_lightstyle[i].colours[2]!=1) && *cl_lightstyle[i].map) + { + MSG_WriteByte (buf, svcfte_lightstylecol); + MSG_WriteByte (buf, (unsigned char)i); + MSG_WriteByte (buf, 0x87); + MSG_WriteShort (buf, cl_lightstyle[i].colours[0]*1024); + MSG_WriteShort (buf, cl_lightstyle[i].colours[1]*1024); + MSG_WriteShort (buf, cl_lightstyle[i].colours[2]*1024); + MSG_WriteString (buf, cl_lightstyle[i].map); + } + else +#endif + { + MSG_WriteByte (buf, svc_lightstyle); + MSG_WriteByte (buf, (unsigned char)i); + MSG_WriteString (buf, cl_lightstyle[i].map); + } + + if (buf->cursize > buf->maxsize/2) + CL_WriteRecordDemoMessage (buf, seq++); + } + return seq; +} + const char *Get_Q2ConfigString(int i); /* @@ -1124,11 +1485,9 @@ void CL_Record_f (void) int c; char name[MAX_OSPATH]; sizebuf_t buf; - char buf_data[MAX_QWMSGLEN]; - int n, i, j, seat; + char buf_data[MAX_OVERALLMSGLEN]; + int n, i, seat; char *s, *p, *fname; - entity_t *ent; - entity_state_t *es; player_info_t *player; extern char gamedirfile[]; int seq = 1; @@ -1153,8 +1512,8 @@ void CL_Record_f (void) if (cls.protocol == CP_QUAKE2) defaultext = ".dm2"; -// else if (cls.protocol == CP_NETQUAKE) -// defaultext = ".dem"; + else if (cls.protocol == CP_NETQUAKE && !CPNQ_IS_DP) + defaultext = ".dem"; else if (cls.protocol == CP_QUAKEWORLD) defaultext = ".qwd"; else @@ -1169,6 +1528,9 @@ void CL_Record_f (void) if (c == 2) //user supplied a name { fname = Cmd_Argv(1); + s = strrchr(fname, '.'); + if (!Q_strcasecmp(s, defaultext)) + *s = 0; //hack away that extension that they added. } else { //automagically generate a name @@ -1230,8 +1592,7 @@ void CL_Record_f (void) || c=='<' || c=='>' || c=='"' || c=='.') *p = '_'; } - strncpy(name, fname, sizeof(name)-1-8); - name[sizeof(name)-1-8] = '\0'; + Q_strncpyz(name, fname, sizeof(name)-8); //make a unique name (unless the user specified it). strcat (name, defaultext); //we have the space @@ -1242,9 +1603,11 @@ void CL_Record_f (void) f = FS_OpenVFS (name, "rb", FS_GAME); if (f) { - COM_StripExtension(name, name, sizeof(name)); + //remove the extension again + Q_strncpyz(name, fname, sizeof(name)-8); p = name + strlen(name); - strcat(p, "_XX.qwd"); + strcat(p, "_XX"); + strcat(p, defaultext); p++; i = 0; do @@ -1282,67 +1645,12 @@ void CL_Record_f (void) switch(cls.protocol) { case CP_QUAKEWORLD: + if (!cls.fteprotocolextensions && !cls.fteprotocolextensions2) + buf.maxsize = MAX_QWMSGLEN; //poo compatibility... :P + cls.demorecording = DPB_QUAKEWORLD; - // serverdata - // send the info about the new client to all connected clients - - // send the serverdata - MSG_WriteByte (&buf, svc_serverdata); -#ifdef PROTOCOL_VERSION_FTE - if (cls.fteprotocolextensions) //maintain demo compatability - { - MSG_WriteLong (&buf, PROTOCOL_VERSION_FTE); - MSG_WriteLong (&buf, cls.fteprotocolextensions); - } - if (cls.fteprotocolextensions2) //maintain demo compatability - { - MSG_WriteLong (&buf, PROTOCOL_VERSION_FTE2); - MSG_WriteLong (&buf, cls.fteprotocolextensions2); - } -#endif - MSG_WriteLong (&buf, PROTOCOL_VERSION_QW); - MSG_WriteLong (&buf, cl.servercount); - MSG_WriteString (&buf, gamedirfile); - - if (cls.fteprotocolextensions2 & PEXT2_MAXPLAYERS) - { - MSG_WriteByte (&buf, cl.allocated_client_slots); - MSG_WriteByte (&buf, cl.splitclients | (cl.spectator?128:0)); - for (i = 0; i < cl.splitclients; i++) - MSG_WriteByte (&buf, cl.playerview[i].playernum); - } - else - { - for (i = 0; i < cl.splitclients; i++) - { - if (cl.spectator) - MSG_WriteByte (&buf, cl.playerview[i].playernum | 128); - else - MSG_WriteByte (&buf, cl.playerview[i].playernum); - } - if (cls.fteprotocolextensions & PEXT_SPLITSCREEN) - MSG_WriteByte (&buf, 128); - } - - // send full levelname - MSG_WriteString (&buf, cl.levelname); - - // send the movevars - MSG_WriteFloat(&buf, movevars.gravity); - MSG_WriteFloat(&buf, movevars.stopspeed); - MSG_WriteFloat(&buf, movevars.maxspeed); - MSG_WriteFloat(&buf, movevars.spectatormaxspeed); - MSG_WriteFloat(&buf, movevars.accelerate); - MSG_WriteFloat(&buf, movevars.airaccelerate); - MSG_WriteFloat(&buf, movevars.wateraccelerate); - MSG_WriteFloat(&buf, movevars.friction); - MSG_WriteFloat(&buf, movevars.waterfriction); - MSG_WriteFloat(&buf, movevars.entgravity); - - // send server info string - MSG_WriteByte (&buf, svc_stufftext); - MSG_WriteString (&buf, va("fullserverinfo \"%s\"\n", cl.serverinfo) ); + CLQW_RecordServerData(&buf); // send music Media_WriteCurrentTrack(&buf); @@ -1378,7 +1686,7 @@ void CL_Record_f (void) MSG_WriteByte (&buf, svc_setpause); - MSG_WriteByte (&buf, cl.paused); + MSG_WriteByte (&buf, !!cl.paused); #ifdef PEXT_SETVIEW if (cl.playerview[0].viewentity != cl.playerview[0].playernum+1) //tell the player if we have a different view entity @@ -1389,7 +1697,6 @@ void CL_Record_f (void) #endif // flush packet CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); // soundlist MSG_WriteByte (&buf, svc_soundlist); @@ -1405,7 +1712,7 @@ void CL_Record_f (void) MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, n); CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); + if (n + 1 > 0xff) { MSG_WriteByte (&buf, svcfte_soundlistshort); @@ -1425,7 +1732,6 @@ void CL_Record_f (void) MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, 0); CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); } //FIXME: vweps @@ -1444,7 +1750,7 @@ void CL_Record_f (void) MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, n); CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); + if (n + 1 > 0xff) { MSG_WriteByte (&buf, svcfte_modellistshort); @@ -1464,131 +1770,15 @@ void CL_Record_f (void) MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, 0); CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); } - //particleeffectnum stuff - for (i = 1; i < MAX_SSPARTICLESPRE; i++) - { - if (!cl.particle_ssname[i]) - break; - MSG_WriteByte(&buf, svcfte_precache); - MSG_WriteShort(&buf, PC_PARTICLE | i); - MSG_WriteString(&buf, cl.particle_ssname[i]); - - if (buf.cursize > buf.maxsize/2) - { - CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - } - - //custom tents (needed for hexen2, if nothing else) - for (i = 0; ; i++) - { - if (!CL_WriteCustomTEnt(&buf, i)) - break; - - if (buf.cursize > buf.maxsize/2) - { - CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - } - - // spawnstatic - - for (i = 0; i < cl.num_statics; i++) - { - ent = &cl_static_entities[i].ent; - -#ifndef CLIENTONLY //FIXME - if (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) - { - MSG_WriteByte(&buf, svcfte_spawnstatic2); - SVFTE_EmitBaseline(&cl_static_entities[i].state, false, &buf, cls.fteprotocolextensions2); - } - //else if (cls.fteprotocolextensions & PEXT_SPAWNSTATIC2) //qw deltas - else -#endif - { - MSG_WriteByte (&buf, svc_spawnstatic); - - for (j = 1; j < MAX_PRECACHE_MODELS; j++) - if (ent->model == cl.model_precache[j]) - break; - if (j == MAX_PRECACHE_MODELS) - MSG_WriteByte (&buf, 0); - else - MSG_WriteByte (&buf, j); - - MSG_WriteByte (&buf, ent->framestate.g[FS_REG].frame[0]); - MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, ent->skinnum); - for (j=0 ; j<3 ; j++) - { - MSG_WriteCoord (&buf, ent->origin[j]); - MSG_WriteAngle (&buf, ent->angles[j]); - } - } - - if (buf.cursize > buf.maxsize/2) - { - CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - } - - // FIXME: static sounds - // static sounds are skipped in demos, life is hard - - // baselines - - for (i = 0; i < cl_baselines_count; i++) - { - es = cl_baselines + i; - - if (memcmp(es, &nullentitystate, sizeof(nullentitystate))) - { -#ifndef CLIENTONLY //FIXME - if (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) - { - MSG_WriteByte(&buf, svcfte_spawnbaseline2); - SVFTE_EmitBaseline(es, true, &buf, cls.fteprotocolextensions2); - } - //else if (cls.fteprotocolextensions & PEXT_SPAWNSTATIC2) //qw deltas - else -#endif - { - MSG_WriteByte (&buf,svc_spawnbaseline); - MSG_WriteEntity (&buf, i); - - MSG_WriteByte (&buf, es->modelindex); - MSG_WriteByte (&buf, es->frame); - MSG_WriteByte (&buf, es->colormap); - MSG_WriteByte (&buf, es->skinnum); - for (j=0 ; j<3 ; j++) - { - MSG_WriteCoord(&buf, es->origin[j]); - MSG_WriteAngle(&buf, es->angles[j]); - } - } - if (buf.cursize > buf.maxsize/2) - { - CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - } - } + seq = CL_Record_ParticlesStaticsBaselines(&buf, seq); MSG_WriteByte (&buf, svc_stufftext); MSG_WriteString (&buf, va("cmd spawn %i\n", cl.servercount) ); if (buf.cursize) - { CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } // send current status of all other players @@ -1633,44 +1823,10 @@ void CL_Record_f (void) } if (buf.cursize > buf.maxsize/2) - { CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } } - // send all current light styles - for (i=0 ; i= MAX_STANDARDLIGHTSTYLES) - if (!*cl_lightstyle[i].map) - continue; - -#ifdef PEXT_LIGHTSTYLECOL - if ((cls.fteprotocolextensions & PEXT_LIGHTSTYLECOL) && (cl_lightstyle[i].colours[0]!=1||cl_lightstyle[i].colours[1]!=1||cl_lightstyle[i].colours[2]!=1) && *cl_lightstyle[i].map) - { - MSG_WriteByte (&buf, svcfte_lightstylecol); - MSG_WriteByte (&buf, (unsigned char)i); - MSG_WriteByte (&buf, 0x87); - MSG_WriteShort (&buf, cl_lightstyle[i].colours[0]*1024); - MSG_WriteShort (&buf, cl_lightstyle[i].colours[1]*1024); - MSG_WriteShort (&buf, cl_lightstyle[i].colours[2]*1024); - MSG_WriteString (&buf, cl_lightstyle[i].map); - } - else -#endif - { - MSG_WriteByte (&buf, svc_lightstyle); - MSG_WriteByte (&buf, (unsigned char)i); - MSG_WriteString (&buf, cl_lightstyle[i].map); - } - - if (buf.cursize > buf.maxsize/2) - { - CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - } + seq = CL_Record_Lightstyles(&buf, seq); for (seat = 0; seat < cl.splitclients; seat++) { @@ -1712,10 +1868,7 @@ void CL_Record_f (void) } if (buf.cursize > buf.maxsize/2) - { CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } } } @@ -1775,8 +1928,37 @@ void CL_Record_f (void) CL_WriteRecordQ2DemoMessage (&buf); break; #endif - case CP_NETQUAKE: //FIXME +#ifdef NQPROT + case CP_NETQUAKE: + //csqc stuff + cls.demorecording = DPB_NETQUAKE; + VFS_WRITE(cls.demooutfile, "-1\n", 3); //stupid lame header thing. + + CLNQ_WriteServerData(&buf); + MSG_WriteByte (&buf, svc_setpause); + MSG_WriteByte (&buf, !!cl.paused); + MSG_WriteByte (&buf, svc_setview); + MSG_WriteEntity (&buf, cl.playerview[0].viewentity); + + MSG_WriteByte (&buf, svc_signonnum); + MSG_WriteByte (&buf, 1); + CL_WriteRecordDemoMessage (&buf, seq++); + + seq = CL_Record_ParticlesStaticsBaselines(&buf, seq); + //fixme: brushes... + MSG_WriteByte (&buf, svc_signonnum); + MSG_WriteByte (&buf, 2); + CL_WriteRecordDemoMessage (&buf, seq++); + //fixme: clients + seq = CL_Record_Lightstyles(&buf, seq); + //fixme: stats + MSG_WriteByte (&buf, svc_signonnum); + MSG_WriteByte (&buf, 3); + CL_WriteRecordDemoMessage (&buf, seq++); + break; +#endif default: + //this should have been caught earlier Con_Printf("Unable to begin demo recording with this network protocol\n"); CL_Stop_f(); break; @@ -1820,6 +2002,8 @@ void CL_ReRecord_f (void) Q_snprintfz (name, sizeof(name), "%s", s); + CL_Disconnect(); + // // open the demo file // @@ -1854,7 +2038,9 @@ void CL_ReRecord_f (void) Con_Printf ("recording to %s.\n", name); - CL_Disconnect(); + if (cls.demorecording == DPB_NETQUAKE) //nq demos have some silly header. + VFS_WRITE(cls.demooutfile, "-1\n", 3); + CL_BeginServerReconnect(); } diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index c0992b961..658f1499b 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -33,7 +33,7 @@ cvar_t cl_nodelta = CVAR("cl_nodelta","0"); cvar_t cl_c2sdupe = CVAR("cl_c2sdupe", "0"); cvar_t cl_c2spps = CVAR("cl_c2spps", "0"); -cvar_t cl_c2sImpulseBackup = SCVAR("cl_c2sImpulseBackup","3"); +cvar_t cl_c2sImpulseBackup = CVAR("cl_c2sImpulseBackup","3"); cvar_t cl_netfps = CVAR("cl_netfps", "150"); cvar_t cl_sparemsec = CVARC("cl_sparemsec", "10", CL_SpareMsec_Callback); cvar_t cl_queueimpulses = CVAR("cl_queueimpulses", "0"); @@ -555,17 +555,17 @@ void CL_ProxyMenuHooks(void) //========================================================================== -cvar_t cl_upspeed = SCVARF("cl_upspeed","400", CVAR_ARCHIVE); -cvar_t cl_forwardspeed = SCVARF("cl_forwardspeed","400", CVAR_ARCHIVE); +cvar_t cl_upspeed = CVARF("cl_upspeed","400", CVAR_ARCHIVE); +cvar_t cl_forwardspeed = CVARF("cl_forwardspeed","400", CVAR_ARCHIVE); cvar_t cl_backspeed = CVARFD("cl_backspeed","", CVAR_ARCHIVE, "The base speed that you move backwards at. If empty, uses the value of cl_forwardspeed instead."); -cvar_t cl_sidespeed = SCVARF("cl_sidespeed","400", CVAR_ARCHIVE); +cvar_t cl_sidespeed = CVARF("cl_sidespeed","400", CVAR_ARCHIVE); -cvar_t cl_movespeedkey = SCVAR("cl_movespeedkey","2.0"); +cvar_t cl_movespeedkey = CVAR("cl_movespeedkey","2.0"); -cvar_t cl_yawspeed = SCVAR("cl_yawspeed","140"); -cvar_t cl_pitchspeed = SCVAR("cl_pitchspeed","150"); +cvar_t cl_yawspeed = CVAR("cl_yawspeed","140"); +cvar_t cl_pitchspeed = CVAR("cl_pitchspeed","150"); -cvar_t cl_anglespeedkey = SCVAR("cl_anglespeedkey","1.5"); +cvar_t cl_anglespeedkey = CVAR("cl_anglespeedkey","1.5"); void CL_GatherButtons (usercmd_t *cmd, int pnum) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index e03f1ccc5..7978b4bf3 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -45,11 +45,11 @@ qboolean noclip_anglehack; // remnant from old quake void Host_FinishLoading(void); -cvar_t rcon_password = SCVARF("rcon_password", "", CVAR_NOUNSAFEEXPAND); +cvar_t rcon_password = CVARF("rcon_password", "", CVAR_NOUNSAFEEXPAND); -cvar_t rcon_address = SCVARF("rcon_address", "", CVAR_NOUNSAFEEXPAND); +cvar_t rcon_address = CVARF("rcon_address", "", CVAR_NOUNSAFEEXPAND); -cvar_t cl_timeout = SCVAR("cl_timeout", "60"); +cvar_t cl_timeout = CVAR("cl_timeout", "60"); cvar_t cl_shownet = CVARD("cl_shownet","0", "Debugging var. 0 shows nothing. 1 shows incoming packet sizes. 2 shows individual messages. 3 shows entities too."); // can be 0, 1, or 2 @@ -271,11 +271,11 @@ int host_framecount; qbyte *host_basepal; qbyte *h2playertranslations; -cvar_t host_speeds = SCVAR("host_speeds","0"); // set for running times +cvar_t host_speeds = CVAR("host_speeds","0"); // set for running times #ifdef CRAZYDEBUGGING -cvar_t developer = SCVAR("developer","1"); +cvar_t developer = CVAR("developer","1"); #else -cvar_t developer = SCVAR("developer","0"); +cvar_t developer = CVAR("developer","0"); #endif int fps_count; @@ -5824,6 +5824,7 @@ void Host_Shutdown(void) COM_DestroyWorkerThread(); + P_ShutdownParticleSystem(); Cvar_Shutdown(); Validation_FlushFileList(); diff --git a/engine/client/cl_master.h b/engine/client/cl_master.h index a024ef321..a5c4beaf4 100644 --- a/engine/client/cl_master.h +++ b/engine/client/cl_master.h @@ -173,6 +173,8 @@ extern struct selectedserver_s qboolean inuse; netadr_t adr; float refreshtime; + int lastplayer; + char lastrule[64]; serverdetailedinfo_t *detail; diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 5a6055fa0..669a4ebcb 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -2842,7 +2842,7 @@ void CLQW_ParseServerData (void) CL_DownloadFailed(cls.download->remotename, cls.download); } - Con_DPrintf ("Serverdata packet received.\n"); + Con_DPrintf ("Serverdata packet %s.\n", cls.demoplayback?"read":"received"); // // wipe the client_state_t struct // @@ -3132,7 +3132,7 @@ void CLQ2_ParseServerData (void) cls.fteprotocolextensions2 = 0; cls.demohadkeyframe = true; //assume that it did, so this stuff all gets recorded. - Con_DPrintf ("Serverdata packet received.\n"); + Con_DPrintf ("Serverdata packet %s.\n", cls.demoplayback?"read":"received"); // // wipe the client_state_t struct // @@ -3427,8 +3427,7 @@ void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caution. int nummodels, numsounds; char *str; int gametype; - if (developer.ival) - Con_TPrintf ("Serverdata packet received.\n"); + Con_DPrintf ("Serverdata packet %s.\n", cls.demoplayback?"read":"received"); SCR_SetLoadingStage(LS_CLIENT); CL_ClearState (); Stats_NewMap(); @@ -3436,8 +3435,10 @@ void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caution. CLNQ_ParseProtoVersion(); - if (MSG_ReadByte() > MAX_CLIENTS) + cl.allocated_client_slots = MSG_ReadByte(); + if (cl.allocated_client_slots > MAX_CLIENTS) { + cl.allocated_client_slots = MAX_CLIENTS; Con_Printf ("\nWarning, this server supports more than %i clients, additional clients will do bad things\n", MAX_CLIENTS); } diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index 14417880a..3f00e91de 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -23,8 +23,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. cvar_t cl_predict_extrapolate = CVARD("cl_predict_extrapolate", "", "If 1, enables prediction based upon partial input frames which can change over time resulting in a swimmy feel but does not need to interpolate. If 0, prediction will stay in the past and thus use only completed frames. Interpolation will then be used to smooth movement.\nThis cvar only applies when video and input frames are independant (ie: cl_netfps is set)."); cvar_t cl_predict_timenudge = CVARD("cl_predict_timenudge", "0", "A debug feature. You should normally leave this as 0. Nudges local player prediction into the future if positive (resulting in extrapolation), or into the past if negative (resulting in laggy interpolation). Value is in seconds, so small decimals are required. This cvar applies even if input frames are tied to video frames."); cvar_t cl_predict_smooth = CVARD("cl_lerp_smooth", "2", "If 2, will act as 1 when playing demos and otherwise act as if set to 0.\nIf 1, interpolation will run in the past, resulting in really smooth movement at the cost of latency (even on bunchy german ISDNs).\nIf 0, interpolation will be based upon packet arrival times and may judder due to packet loss."); -cvar_t cl_nopred = SCVAR("cl_nopred","0"); -cvar_t cl_pushlatency = SCVAR("pushlatency","-999"); +cvar_t cl_nopred = CVAR("cl_nopred","0"); +cvar_t cl_pushlatency = CVAR("pushlatency","-999"); extern float pm_airaccelerate; diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index e292e26f3..e1f16b71b 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -224,19 +224,19 @@ float scr_disabled_time; float oldsbar = 0; cvar_t con_stayhidden = CVARFD("con_stayhidden", "0", CVAR_NOTFROMSERVER, "0: allow console to pounce on the user\n1: console stays hidden unless explicitly invoked\n2:toggleconsole command no longer works\n3: shift+escape key no longer works"); -cvar_t show_fps = SCVARF("show_fps", "0", CVAR_ARCHIVE); -cvar_t show_fps_x = SCVAR("show_fps_x", "-1"); -cvar_t show_fps_y = SCVAR("show_fps_y", "-1"); -cvar_t show_clock = SCVAR("cl_clock", "0"); -cvar_t show_clock_x = SCVAR("cl_clock_x", "0"); -cvar_t show_clock_y = SCVAR("cl_clock_y", "-1"); -cvar_t show_gameclock = SCVAR("cl_gameclock", "0"); -cvar_t show_gameclock_x = SCVAR("cl_gameclock_x", "0"); -cvar_t show_gameclock_y = SCVAR("cl_gameclock_y", "-1"); -cvar_t show_speed = SCVAR("show_speed", "0"); -cvar_t show_speed_x = SCVAR("show_speed_x", "-1"); -cvar_t show_speed_y = SCVAR("show_speed_y", "-9"); -cvar_t scr_loadingrefresh = SCVAR("scr_loadingrefresh", "0"); +cvar_t show_fps = CVARF("show_fps", "0", CVAR_ARCHIVE); +cvar_t show_fps_x = CVAR("show_fps_x", "-1"); +cvar_t show_fps_y = CVAR("show_fps_y", "-1"); +cvar_t show_clock = CVAR("cl_clock", "0"); +cvar_t show_clock_x = CVAR("cl_clock_x", "0"); +cvar_t show_clock_y = CVAR("cl_clock_y", "-1"); +cvar_t show_gameclock = CVAR("cl_gameclock", "0"); +cvar_t show_gameclock_x = CVAR("cl_gameclock_x", "0"); +cvar_t show_gameclock_y = CVAR("cl_gameclock_y", "-1"); +cvar_t show_speed = CVAR("show_speed", "0"); +cvar_t show_speed_x = CVAR("show_speed_x", "-1"); +cvar_t show_speed_y = CVAR("show_speed_y", "-9"); +cvar_t scr_loadingrefresh = CVAR("scr_loadingrefresh", "0"); cvar_t scr_showloading = CVAR("scr_showloading", "1"); cvar_t scr_showobituaries = CVAR("scr_showobituaries", "0"); diff --git a/engine/client/console.c b/engine/client/console.c index 7eecd7eaa..bdc829de7 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -58,14 +58,14 @@ qterm_t *activeqterm; float con_cursorspeed = 4; -cvar_t con_numnotifylines = SCVAR("con_notifylines","4"); //max lines to show -cvar_t con_notifytime = SCVAR("con_notifytime","3"); //seconds -cvar_t con_notify_x = SCVAR("con_notify_x","0"); -cvar_t con_notify_y = SCVAR("con_notify_y","0"); -cvar_t con_notify_w = SCVAR("con_notify_w","1"); -cvar_t con_centernotify = SCVAR("con_centernotify", "0"); -cvar_t con_displaypossibilities = SCVAR("con_displaypossibilities", "1"); -cvar_t con_maxlines = SCVAR("con_maxlines", "1024"); +cvar_t con_numnotifylines = CVAR("con_notifylines","4"); //max lines to show +cvar_t con_notifytime = CVAR("con_notifytime","3"); //seconds +cvar_t con_notify_x = CVAR("con_notify_x","0"); +cvar_t con_notify_y = CVAR("con_notify_y","0"); +cvar_t con_notify_w = CVAR("con_notify_w","1"); +cvar_t con_centernotify = CVAR("con_centernotify", "0"); +cvar_t con_displaypossibilities = CVAR("con_displaypossibilities", "1"); +cvar_t con_maxlines = CVAR("con_maxlines", "1024"); cvar_t cl_chatmode = CVARD("cl_chatmode", "2", "0(nq) - everything is assumed to be a console command. prefix with 'say', or just use a messagemode bind\n1(q3) - everything is assumed to be chat, unless its prefixed with a /\n2(qw) - anything explicitly recognised as a command will be used as a command, anything unrecognised will be a chat message.\n/ prefix is supported in all cases.\nctrl held when pressing enter always makes any implicit chat into team chat instead."); cvar_t con_numnotifylines_chat = CVAR("con_numnotifylines_chat", "8"); cvar_t con_notifytime_chat = CVAR("con_notifytime_chat", "8"); diff --git a/engine/client/m_download.c b/engine/client/m_download.c index c95767232..d295acfcf 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -213,6 +213,10 @@ static void PM_FreePackage(package_t *p) for (i = 0; i < countof(p->mirror); i++) Z_Free(p->mirror[i]); + Z_Free(p->description); + Z_Free(p->author); + Z_Free(p->license); + Z_Free(p->previewimage); Z_Free(p->qhash); Z_Free(p->arch); Z_Free(p); diff --git a/engine/client/m_master.c b/engine/client/m_master.c index bb7ec16f9..0c5f0880f 100644 --- a/engine/client/m_master.c +++ b/engine/client/m_master.c @@ -407,6 +407,7 @@ static void SL_PreDraw (menu_t *menu) info->numslots = Master_NumSorted(); snprintf(info->refreshtext, sizeof(info->refreshtext), "Refresh - %u/%u/%u\n", info->numslots, Master_NumAlive(), Master_TotalCount()); } +void NET_SendPollPacket(int len, void *data, netadr_t to); static void SL_PostDraw (menu_t *menu) { static char *helpstrings[] = @@ -436,7 +437,32 @@ static void SL_PostDraw (menu_t *menu) { selectedserver.refreshtime = realtime + 4; server->sends++; - Master_QueryServer(server); +#ifdef NQPROT + //we might have gotten stuck. reset the poll + if ((server->special&SS_PROTOCOLMASK) == SS_NETQUAKE) + { //start spamming the server to get all of its details. silly protocols. + selectedserver.lastplayer = 0; + *selectedserver.lastrule = 0; + + SZ_Clear(&net_message); + net_message.packing = SZ_RAWBYTES; + net_message.currentbit = 0; + MSG_WriteLong(&net_message, 0);// save space for the header, filled in later + MSG_WriteByte(&net_message, CCREQ_PLAYER_INFO); + MSG_WriteByte(&net_message, selectedserver.lastplayer); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + NET_SendPollPacket(net_message.cursize, net_message.data, server->adr); + SZ_Clear(&net_message); + MSG_WriteLong(&net_message, 0);// save space for the header, filled in later + MSG_WriteByte(&net_message, CCREQ_RULE_INFO); + MSG_WriteString(&net_message, selectedserver.lastrule); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + NET_SendPollPacket(net_message.cursize, net_message.data, server->adr); + SZ_Clear(&net_message); + } + else +#endif + Master_QueryServer(server); } R2D_ImageColours(1,1,1,1); if (server && server->moreinfo) diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index b68ebef7e..3abc4463a 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -256,10 +256,10 @@ qboolean Media_EvaluateNextTrack(void); int lasttrackplayed; -cvar_t media_shuffle = SCVAR("media_shuffle", "1"); -cvar_t media_repeat = SCVAR("media_repeat", "1"); +cvar_t media_shuffle = CVAR("media_shuffle", "1"); +cvar_t media_repeat = CVAR("media_repeat", "1"); #ifdef WINAMP -cvar_t media_hijackwinamp = SCVAR("media_hijackwinamp", "0"); +cvar_t media_hijackwinamp = CVAR("media_hijackwinamp", "0"); #endif int selectedoption=-1; diff --git a/engine/client/m_options.c b/engine/client/m_options.c index d4fa0788f..c5cd08f26 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -756,6 +756,7 @@ const char *presetexec[] = "seta r_part_classic_opaque 0;" "seta r_stains 0;" "seta r_drawflat 1;" + "seta r_lightmap 0;" "seta r_nolerp 1;" "seta r_nolightdir 1;" "seta r_dynamic 0;" diff --git a/engine/client/menu.c b/engine/client/menu.c index 631ac5c14..bc4f3a533 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -209,7 +209,7 @@ qboolean m_recursiveDraw; void M_ConfigureNetSubsystem(void); -cvar_t m_helpismedia = SCVAR("m_helpismedia", "0"); +cvar_t m_helpismedia = CVAR("m_helpismedia", "0"); cvar_t m_preset_chosen = CVARF("m_preset_chosen", "0", CVAR_ARCHIVE); //============================================================================= diff --git a/engine/client/net_master.c b/engine/client/net_master.c index 4a70cf109..002dbc292 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -123,7 +123,7 @@ net_masterlist_t net_masterlist[] = { // {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "telefrag.me:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "telefrag.me"}, // {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "master.teamdamage.com:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "master.teamdamage.com"}, - {MP_DPMASTER, CVARFC("net_masterextra1", "ghdigital.com:27950 69.59.212.88:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //69.59.212.88 (admin: LordHavoc) + {MP_DPMASTER, CVARFC("net_masterextra1", "ghdigital.com:27950 207.55.114.154:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //207.55.114.154 (was 69.59.212.88 (admin: LordHavoc) {MP_DPMASTER, CVARFC("net_masterextra2", "dpmaster.deathmask.net:27950 107.161.23.68:27950 [2604:180::4ac:98c1]:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //107.161.23.68 (admin: Willis) {MP_DPMASTER, CVARFC("net_masterextra3", "dpmaster.tchr.no:27950 92.62.40.73:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //92.62.40.73 (admin: tChr) @@ -560,8 +560,8 @@ typedef int SOCKET; //the number of servers should be limited only by memory. -cvar_t slist_cacheinfo = SCVAR("slist_cacheinfo", "0"); //this proves dangerous, memory wise. -cvar_t slist_writeserverstxt = SCVAR("slist_writeservers", "0"); +cvar_t slist_cacheinfo = CVAR("slist_cacheinfo", "0"); //this proves dangerous, memory wise. +cvar_t slist_writeserverstxt = CVAR("slist_writeservers", "0"); void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad); int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favorite); @@ -1904,6 +1904,7 @@ int Master_CheckPollSockets(void) int users, maxusers; int control; + int ccrep; MSG_BeginReading (msg_nullnetprim); control = BigLong(*((int *)net_message.data)); @@ -1915,23 +1916,94 @@ int Master_CheckPollSockets(void) if ((control & NETFLAG_LENGTH_MASK) != ret) continue; - if (MSG_ReadByte() != CCREP_SERVER_INFO) - continue; + ccrep = MSG_ReadByte(); - /*this is an address string sent from the server. its not usable. if its replying to serverinfos, its possible to send it connect requests, while the address that it claims is 50% bugged*/ - MSG_ReadString(); - - Q_strncpyz(name, MSG_ReadString(), sizeof(name)); - Q_strncpyz(map, MSG_ReadString(), sizeof(map)); - users = MSG_ReadByte(); - maxusers = MSG_ReadByte(); - if (MSG_ReadByte() != NQ_NETCHAN_VERSION) + if (ccrep == CCREP_PLAYER_INFO) { -// Q_strcpy(name, "*"); -// Q_strcat(name, name); - } + serverinfo_t *selserver = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr):NULL; + serverinfo_t *info = Master_InfoForServer(&net_from); + info = Master_InfoForServer(&net_from); + if (selserver == info) + { + int playernum = MSG_ReadByte(); + char *playername = MSG_ReadString(); + int playercolor = MSG_ReadLong(); + int playerfrags = MSG_ReadLong(); + int secsonserver = MSG_ReadLong(); + //char *playeraddr = MSG_ReadString(); + if (msg_badread) + continue; - CL_ReadServerInfo(va("\\hostname\\%s\\map\\%s\\maxclients\\%i\\clients\\%i", name, map, maxusers, users), MP_NETQUAKE, false); + selectedserver.lastplayer = playernum+1; + + memset(&info->moreinfo->players[playernum], 0, sizeof(info->moreinfo->players[playernum])); + info->moreinfo->players[playernum].userid = 0; + info->moreinfo->players[playernum].frags = playerfrags; + info->moreinfo->players[playernum].time = secsonserver; + info->moreinfo->players[playernum].ping = 0; //*sigh* + Q_strncpyz(info->moreinfo->players[playernum].name, playername, sizeof(info->moreinfo->players[playernum].name)); + Q_strncpyz(info->moreinfo->players[playernum].skin, "", sizeof(info->moreinfo->players[playernum].skin)); + Q_strncpyz(info->moreinfo->players[playernum].team, "", sizeof(info->moreinfo->players[playernum].team)); + info->moreinfo->players[playernum].topc = playercolor>>4; + info->moreinfo->players[playernum].botc = playercolor&15; + info->moreinfo->players[playernum].isspec = false; + info->moreinfo->numplayers = max(info->moreinfo->numplayers, playernum+1); + + //... and now try to query the next one... because everyone gives up after the first, right?... dude... I hate this shit. + SZ_Clear(&net_message); + MSG_WriteLong(&net_message, 0);// save space for the header, filled in later + MSG_WriteByte(&net_message, CCREQ_PLAYER_INFO); + MSG_WriteByte(&net_message, selectedserver.lastplayer); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + NET_SendPollPacket(net_message.cursize, net_message.data, info->adr); + SZ_Clear(&net_message); + } + } + else if (ccrep == CCREP_RULE_INFO) + { + serverinfo_t *selserver = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr):NULL; + serverinfo_t *info = Master_InfoForServer(&net_from); + char *s, *old; + info = Master_InfoForServer(&net_from); + if (selserver == info) + { + s = MSG_ReadString(); + if (msg_badread) + continue; + Q_strncpyz(selectedserver.lastrule, s, sizeof(selectedserver.lastrule)); + s = MSG_ReadString(); + + old = Info_ValueForKey(info->moreinfo->info, selectedserver.lastrule); + if (strcmp(s, old)) + Info_SetValueForStarKey(info->moreinfo->info, selectedserver.lastrule, s, sizeof(info->moreinfo->info)); + + //... and now try to query the next one... because everyone gives up after the first, right?... dude... I hate this shit. + SZ_Clear(&net_message); + MSG_WriteLong(&net_message, 0);// save space for the header, filled in later + MSG_WriteByte(&net_message, CCREQ_RULE_INFO); + MSG_WriteString(&net_message, selectedserver.lastrule); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + NET_SendPollPacket(net_message.cursize, net_message.data, info->adr); + SZ_Clear(&net_message); + } + } + else if (ccrep == CCREP_SERVER_INFO) + { + /*this is an address string sent from the server. its not usable. if its replying to serverinfos, its possible to send it connect requests, while the address that it claims is 50% bugged*/ + MSG_ReadString(); + + Q_strncpyz(name, MSG_ReadString(), sizeof(name)); + Q_strncpyz(map, MSG_ReadString(), sizeof(map)); + users = MSG_ReadByte(); + maxusers = MSG_ReadByte(); + if (MSG_ReadByte() != NQ_NETCHAN_VERSION) + { +// Q_strcpy(name, "*"); +// Q_strcat(name, name); + } + + CL_ReadServerInfo(va("\\hostname\\%s\\map\\%s\\maxclients\\%i\\clients\\%i", name, map, maxusers, users), MP_NETQUAKE, false); + } } #endif continue; @@ -1994,6 +2066,29 @@ void SListOptionChanged(serverinfo_t *newserver) selectedserver.refreshtime = realtime+4; newserver->sends++; Master_QueryServer(newserver); + +#ifdef NQPROT + selectedserver.lastplayer = 0; + *selectedserver.lastrule = 0; + if ((newserver->special&SS_PROTOCOLMASK) == SS_NETQUAKE) + { //start spamming the server to get all of its details. silly protocols. + SZ_Clear(&net_message); + net_message.packing = SZ_RAWBYTES; + net_message.currentbit = 0; + MSG_WriteLong(&net_message, 0);// save space for the header, filled in later + MSG_WriteByte(&net_message, CCREQ_PLAYER_INFO); + MSG_WriteByte(&net_message, selectedserver.lastplayer); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + NET_SendPollPacket(net_message.cursize, net_message.data, newserver->adr); + SZ_Clear(&net_message); + MSG_WriteLong(&net_message, 0);// save space for the header, filled in later + MSG_WriteByte(&net_message, CCREQ_RULE_INFO); + MSG_WriteString(&net_message, selectedserver.lastrule); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + NET_SendPollPacket(net_message.cursize, net_message.data, newserver->adr); + SZ_Clear(&net_message); + } +#endif } } @@ -2475,7 +2570,10 @@ void Master_QueryServer(serverinfo_t *server) Q_snprintfz(data, sizeof(data), "%c%c%c%cgetstatus", 255, 255, 255, 255); break; case SS_DARKPLACES: - Q_snprintfz(data, sizeof(data), "%c%c%c%cgetinfo", 255, 255, 255, 255); + if (server->moreinfo) + Q_snprintfz(data, sizeof(data), "%c%c%c%cgetstatus", 255, 255, 255, 255); + else + Q_snprintfz(data, sizeof(data), "%c%c%c%cgetinfo", 255, 255, 255, 255); break; #ifdef NQPROT case SS_NETQUAKE: @@ -2841,8 +2939,6 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor } } - MasterInfo_RemovePlayers(&info->adr); - name = Info_ValueForKey(msg, "hostname"); if (!*name) name = Info_ValueForKey(msg, "sv_hostname"); @@ -2939,178 +3035,189 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor strcpy(details.info, msg); msg = msg+strlen(msg)+1; - info->players=details.numplayers = 0; - if (!strchr(msg, '\n')) + //clear player info. unless its an NQ server, which have some really annoying protocol to find out the players. + if ((info->special & SS_PROTOCOLMASK) == SS_NETQUAKE) + { + if (!info->moreinfo && ((slist_cacheinfo.value == 2 || NET_CompareAdr(&info->adr, &selectedserver.adr)) || (info->special & SS_KEEPINFO))) + info->moreinfo = Z_Malloc(sizeof(serverdetailedinfo_t)); info->numhumans = info->players = atoi(Info_ValueForKey(details.info, "clients")); + } else { - int clnum; - - for (clnum=0; clnum < MAX_CLIENTS; clnum++) + MasterInfo_RemovePlayers(&info->adr); + info->players=details.numplayers = 0; + if (!strchr(msg, '\n')) + info->numhumans = info->players = atoi(Info_ValueForKey(details.info, "clients")); + else { - nl = strchr(msg, '\n'); - if (!nl) - break; - *nl = '\0'; + int clnum; - details.players[clnum].isspec = 0; - details.players[clnum].team[0] = 0; - details.players[clnum].skin[0] = 0; - - token = msg; - if (!token) - break; - details.players[clnum].userid = atoi(token); - token = strchr(token+1, ' '); - if (!token) - break; - details.players[clnum].frags = atoi(token); - token = strchr(token+1, ' '); - if (!token) - break; - details.players[clnum].time = atoi(token); - msg = token; - token = strchr(msg+1, ' '); - if (!token) //probably q2 response + for (clnum=0; clnum < MAX_CLIENTS; clnum++) { - //see if this is actually a Quake2 server. - token = strchr(msg+1, '\"'); - if (!token) //it wasn't. + nl = strchr(msg, '\n'); + if (!nl) break; + *nl = '\0'; - details.players[clnum].ping = details.players[clnum].frags; - details.players[clnum].frags = details.players[clnum].userid; + details.players[clnum].isspec = 0; + details.players[clnum].team[0] = 0; + details.players[clnum].skin[0] = 0; - msg = strchr(token+1, '\"'); - if (!msg) - break; - len = msg - token; - if (len >= sizeof(details.players[clnum].name)) - len = sizeof(details.players[clnum].name); - Q_strncpyz(details.players[clnum].name, token+1, len); - - details.players[clnum].skin[0] = '\0'; - - details.players[clnum].topc = 0; - details.players[clnum].botc = 0; - details.players[clnum].time = 0; - } - else //qw response - { - details.players[clnum].ping = atoi(token); - msg = token; - token = strchr(msg+1, ' '); + token = msg; if (!token) break; - - token = strchr(token+1, '\"'); - if (!token) - break; - msg = strchr(token+1, '\"'); - if (!msg) - break; - len = msg - token; - if (len >= sizeof(details.players[clnum].name)) - len = sizeof(details.players[clnum].name); - if (!strncmp(token, "\"\\s\\", 4)) - { - details.players[clnum].isspec |= 1; - Q_strncpyz(details.players[clnum].name, token+4, len-3); - } - else - Q_strncpyz(details.players[clnum].name, token+1, len); - details.players[clnum].name[len] = '\0'; - - token = strchr(msg+1, '\"'); - if (!token) - break; - msg = strchr(token+1, '\"'); - if (!msg) - break; - len = msg - token; - if (len >= sizeof(details.players[clnum].skin)) - len = sizeof(details.players[clnum].skin); - Q_strncpyz(details.players[clnum].skin, token+1, len); - details.players[clnum].skin[len] = '\0'; - - token = strchr(msg+1, ' '); - if (!token) - break; - details.players[clnum].topc = atoi(token); + details.players[clnum].userid = atoi(token); token = strchr(token+1, ' '); if (!token) break; - details.players[clnum].botc = atoi(token); - - token = strchr(msg+1, '\"'); - Q_strncpyz(details.players[clnum].team, "", sizeof(details.players[clnum].team)); - if (token) + details.players[clnum].frags = atoi(token); + token = strchr(token+1, ' '); + if (!token) + break; + details.players[clnum].time = atoi(token); + msg = token; + token = strchr(msg+1, ' '); + if (!token) //probably q2 response { + //see if this is actually a Quake2 server. + token = strchr(msg+1, '\"'); + if (!token) //it wasn't. + break; + + details.players[clnum].ping = details.players[clnum].frags; + details.players[clnum].frags = details.players[clnum].userid; + msg = strchr(token+1, '\"'); - if (msg) + if (!msg) + break; + len = msg - token; + if (len >= sizeof(details.players[clnum].name)) + len = sizeof(details.players[clnum].name); + Q_strncpyz(details.players[clnum].name, token+1, len); + + details.players[clnum].skin[0] = '\0'; + + details.players[clnum].topc = 0; + details.players[clnum].botc = 0; + details.players[clnum].time = 0; + } + else //qw response + { + details.players[clnum].ping = atoi(token); + msg = token; + token = strchr(msg+1, ' '); + if (!token) + break; + + token = strchr(token+1, '\"'); + if (!token) + break; + msg = strchr(token+1, '\"'); + if (!msg) + break; + len = msg - token; + if (len >= sizeof(details.players[clnum].name)) + len = sizeof(details.players[clnum].name); + if (!strncmp(token, "\"\\s\\", 4)) { - len = msg - token; - if (len >= sizeof(details.players[clnum].team)) - len = sizeof(details.players[clnum].team); - Q_strncpyz(details.players[clnum].team, token+1, len); - details.players[clnum].team[len] = '\0'; + details.players[clnum].isspec |= 1; + Q_strncpyz(details.players[clnum].name, token+4, len-3); + } + else + Q_strncpyz(details.players[clnum].name, token+1, len); + details.players[clnum].name[len] = '\0'; + + token = strchr(msg+1, '\"'); + if (!token) + break; + msg = strchr(token+1, '\"'); + if (!msg) + break; + len = msg - token; + if (len >= sizeof(details.players[clnum].skin)) + len = sizeof(details.players[clnum].skin); + Q_strncpyz(details.players[clnum].skin, token+1, len); + details.players[clnum].skin[len] = '\0'; + + token = strchr(msg+1, ' '); + if (!token) + break; + details.players[clnum].topc = atoi(token); + token = strchr(token+1, ' '); + if (!token) + break; + details.players[clnum].botc = atoi(token); + + token = strchr(msg+1, '\"'); + Q_strncpyz(details.players[clnum].team, "", sizeof(details.players[clnum].team)); + if (token) + { + msg = strchr(token+1, '\"'); + if (msg) + { + len = msg - token; + if (len >= sizeof(details.players[clnum].team)) + len = sizeof(details.players[clnum].team); + Q_strncpyz(details.players[clnum].team, token+1, len); + details.players[clnum].team[len] = '\0'; + } } } - } - MasterInfo_AddPlayer(&info->adr, details.players[clnum].name, details.players[clnum].ping, details.players[clnum].frags, details.players[clnum].topc*4 | details.players[clnum].botc, details.players[clnum].skin, details.players[clnum].team); + MasterInfo_AddPlayer(&info->adr, details.players[clnum].name, details.players[clnum].ping, details.players[clnum].frags, details.players[clnum].topc*4 | details.players[clnum].botc, details.players[clnum].skin, details.players[clnum].team); - //WallFly is some q2 bot - //[ServeMe] is some qw bot - if (!strncmp(details.players[clnum].name, "WallFly", 7) || !strcmp(details.players[clnum].name, "[ServeMe]")) - { - //not players nor real people. they don't count towards any metric - details.players[clnum].isspec |= 3; - } - //807 excludes the numerous bot names on some annoying qwtf server - //BOT: excludes fte's botclients (which always have a bot: prefix) - else if (details.players[clnum].ping == 807 || !strncmp(details.players[clnum].name, "BOT:", 4)) - { - info->numbots++; - details.players[clnum].isspec |= 2; - } - else if (details.players[clnum].isspec & 1) - { - info->numspectators++; - } - else - info->numhumans++; - - for (k = clnum, j = clnum-1; j >= 0; j--) - { - if ((details.players[k].isspec != details.players[j].isspec && !details.players[k].isspec) || - details.players[k].frags > details.players[j].frags) + //WallFly is some q2 bot + //[ServeMe] is some qw bot + if (!strncmp(details.players[clnum].name, "WallFly", 7) || !strcmp(details.players[clnum].name, "[ServeMe]")) { - struct serverdetailedplayerinfo_s t = details.players[j]; - details.players[j] = details.players[k]; - details.players[k] = t; - k = j; + //not players nor real people. they don't count towards any metric + details.players[clnum].isspec |= 3; + } + //807 excludes the numerous bot names on some annoying qwtf server + //BOT: excludes fte's botclients (which always have a bot: prefix) + else if (details.players[clnum].ping == 807 || !strncmp(details.players[clnum].name, "BOT:", 4)) + { + info->numbots++; + details.players[clnum].isspec |= 2; + } + else if (details.players[clnum].isspec & 1) + { + info->numspectators++; } else - break; + info->numhumans++; + + for (k = clnum, j = clnum-1; j >= 0; j--) + { + if ((details.players[k].isspec != details.players[j].isspec && !details.players[k].isspec) || + details.players[k].frags > details.players[j].frags) + { + struct serverdetailedplayerinfo_s t = details.players[j]; + details.players[j] = details.players[k]; + details.players[k] = t; + k = j; + } + else + break; + } + details.numplayers++; + + info->players++; + + msg = nl; + if (!msg) + break; //erm... + msg++; } - details.numplayers++; - - info->players++; - - msg = nl; - if (!msg) - break; //erm... - msg++; } - } - if (!info->moreinfo && ((slist_cacheinfo.value == 2 || NET_CompareAdr(&info->adr, &selectedserver.adr)) || (info->special & SS_KEEPINFO))) - info->moreinfo = Z_Malloc(sizeof(serverdetailedinfo_t)); - if (NET_CompareAdr(&info->adr, &selectedserver.adr)) - selectedserver.detail = info->moreinfo; + if (!info->moreinfo && ((slist_cacheinfo.value == 2 || NET_CompareAdr(&info->adr, &selectedserver.adr)) || (info->special & SS_KEEPINFO))) + info->moreinfo = Z_Malloc(sizeof(serverdetailedinfo_t)); + if (NET_CompareAdr(&info->adr, &selectedserver.adr)) + selectedserver.detail = info->moreinfo; - if (info->moreinfo) - memcpy(info->moreinfo, &details, sizeof(serverdetailedinfo_t)); + if (info->moreinfo) + memcpy(info->moreinfo, &details, sizeof(serverdetailedinfo_t)); + } return true; } diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index b9e5cb3a3..598700278 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -1182,8 +1182,8 @@ int menuentsize; // cvars #define MENUPROGSGROUP "Menu progs control" -cvar_t forceqmenu = SCVAR("forceqmenu", "0"); -cvar_t pr_menuqc_coreonerror = SCVAR("pr_menuqc_coreonerror", "1"); +cvar_t forceqmenu = CVAR("forceqmenu", "0"); +cvar_t pr_menuqc_coreonerror = CVAR("pr_menuqc_coreonerror", "1"); //new generic functions. diff --git a/engine/client/r_part.c b/engine/client/r_part.c index c78d194b4..b18dc6cd3 100644 --- a/engine/client/r_part.c +++ b/engine/client/r_part.c @@ -627,6 +627,13 @@ cvar_t r_part_maxdecals = CVAR("r_part_maxdecals", "8192"); particleengine_t *pe; +static struct partalias_s +{ + struct partalias_s *next; + const char *from; + const char *to; +} *partaliaslist; + void P_ParticleEffect_f(void); static void P_ParticleEffectAlias_f(void); @@ -673,12 +680,18 @@ void P_InitParticleSystem(void) R_Clutter_Init(); } -static struct partalias_s +void P_ShutdownParticleSystem(void) { - struct partalias_s *next; - const char *from; - const char *to; -} *partaliaslist; + struct partalias_s *l; + + while (partaliaslist) + { + l = partaliaslist; + partaliaslist = l->next; + Z_Free(l); + } +} + static void P_ParticleEffectAlias_f(void) { struct partalias_s **link, *l; diff --git a/engine/client/renderer.c b/engine/client/renderer.c index ff91e445b..001c58f8b 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -87,46 +87,48 @@ extern cvar_t r_forceprogramify; cvar_t mod_md3flags = CVARD ("mod_md3flags", "1", "The flags field of md3s was never officially defined. If this is set to 1, the flags will be treated identically to mdl files. Otherwise they will be ignored. Naturally, this is required to provide rotating pickups in quake."); cvar_t r_ambient = CVARF ("r_ambient", "0", - CVAR_CHEAT); + CVAR_CHEAT); cvar_t r_bloodstains = CVARF ("r_bloodstains", "1", CVAR_ARCHIVE); cvar_t r_bouncysparks = CVARFD ("r_bouncysparks", "1", - CVAR_ARCHIVE, - "Enables particle interaction with world surfaces, allowing for bouncy particles, stains, and decals."); + CVAR_ARCHIVE, + "Enables particle interaction with world surfaces, allowing for bouncy particles, stains, and decals."); cvar_t r_drawentities = CVAR ("r_drawentities", "1"); cvar_t r_drawflat = CVARAF ("r_drawflat", "0", "gl_textureless", - CVAR_ARCHIVE | CVAR_SEMICHEAT | CVAR_RENDERERCALLBACK | CVAR_SHADERSYSTEM); + CVAR_ARCHIVE | CVAR_SEMICHEAT | CVAR_RENDERERCALLBACK | CVAR_SHADERSYSTEM); +cvar_t r_lightmap = CVARF ("r_lightmap", "0", + CVAR_ARCHIVE | CVAR_SEMICHEAT | CVAR_RENDERERCALLBACK | CVAR_SHADERSYSTEM); cvar_t r_wireframe = CVARFD ("r_wireframe", "0", - CVAR_CHEAT, "Developer feature where everything is drawn with wireframe over the top. Only active where cheats are permitted."); + CVAR_CHEAT, "Developer feature where everything is drawn with wireframe over the top. Only active where cheats are permitted."); cvar_t r_wireframe_smooth = CVAR ("r_wireframe_smooth", "0"); cvar_t r_refract_fbo = CVARD ("r_refract_fbo", "1", "Use an fbo for refraction. If 0, just renders as a portal and uses a copy of the current framebuffer."); cvar_t gl_miptexLevel = CVAR ("gl_miptexLevel", "0"); cvar_t r_drawviewmodel = CVARF ("r_drawviewmodel", "1", CVAR_ARCHIVE); cvar_t r_drawviewmodelinvis = CVAR ("r_drawviewmodelinvis", "0"); cvar_t r_dynamic = CVARF ("r_dynamic", IFMINIMAL("0","1"), - CVAR_ARCHIVE); + CVAR_ARCHIVE); cvar_t r_fastturb = CVARF ("r_fastturb", "0", - CVAR_SHADERSYSTEM); + CVAR_SHADERSYSTEM); cvar_t r_fastsky = CVARF ("r_fastsky", "0", - CVAR_ARCHIVE | CVAR_SHADERSYSTEM); + CVAR_ARCHIVE | CVAR_SHADERSYSTEM); cvar_t r_fastskycolour = CVARF ("r_fastskycolour", "0", - CVAR_RENDERERCALLBACK|CVAR_SHADERSYSTEM); + CVAR_RENDERERCALLBACK|CVAR_SHADERSYSTEM); cvar_t r_fb_bmodels = CVARAF("r_fb_bmodels", "1", "gl_fb_bmodels", CVAR_SEMICHEAT|CVAR_RENDERERLATCH); cvar_t r_fb_models = CVARAFD ("r_fb_models", "1", "gl_fb_models", CVAR_SEMICHEAT, "Force all non-player models to be fullbright in deathmatch. Because if you don't enable these cheats then you'll go splat because everone else uses them. QuakeWorld players suck."); -cvar_t r_skin_overlays = SCVARF ("r_skin_overlays", "1", - CVAR_SEMICHEAT|CVAR_RENDERERLATCH); -cvar_t r_globalskin_first = CVARFD ("r_globalskin_first", "100", CVAR_RENDERERLATCH, "Specifies the first .skin value that is a global skin. Entities within this range will use the shader/image called 'gfx/skinSKIN.lmp' instead of their regular skin. See also: r_globalskin_count."); -cvar_t r_globalskin_count = CVARFD ("r_globalskin_count", "10", CVAR_RENDERERLATCH, "Specifies how many globalskins there are."); +cvar_t r_skin_overlays = CVARF ("r_skin_overlays", "1", + CVAR_SEMICHEAT|CVAR_RENDERERLATCH); +cvar_t r_globalskin_first = CVARFD ("r_globalskin_first", "100", CVAR_RENDERERLATCH, "Specifies the first .skin value that is a global skin. Entities within this range will use the shader/image called 'gfx/skinSKIN.lmp' instead of their regular skin. See also: r_globalskin_count."); +cvar_t r_globalskin_count = CVARFD ("r_globalskin_count", "10", CVAR_RENDERERLATCH, "Specifies how many globalskins there are."); cvar_t r_coronas = CVARFD ("r_coronas", "0", CVAR_ARCHIVE, "Draw coronas on realtime lights. Overrides glquake-esque flashblends."); cvar_t r_coronas_occlusion = CVARFD ("r_coronas_occlusion", "", CVAR_ARCHIVE, "Specifies that coronas should be occluded more carefully.\n0: No occlusion, at all.\n1: BSP occlusion only (simple tracelines).\n2: non-bsp occlusion also (complex tracelines).\n3: Depthbuffer reads (forces synchronisation).\n4: occlusion queries."); cvar_t r_coronas_mindist = CVARFD ("r_coronas_mindist", "128", CVAR_ARCHIVE, "Coronas closer than this will be invisible, preventing near clip plane issues."); cvar_t r_coronas_fadedist = CVARFD ("r_coronas_fadedist", "256", CVAR_ARCHIVE, "Coronas will fade out over this distance."); -cvar_t r_flashblend = SCVARF ("gl_flashblend", "0", - CVAR_ARCHIVE); -cvar_t r_flashblendscale = SCVARF ("gl_flashblendscale", "0.35", - CVAR_ARCHIVE); +cvar_t r_flashblend = CVARF ("gl_flashblend", "0", + CVAR_ARCHIVE); +cvar_t r_flashblendscale = CVARF ("gl_flashblendscale", "0.35", + CVAR_ARCHIVE); cvar_t r_floorcolour = CVARAF ("r_floorcolour", "64 64 128", "r_floorcolor", CVAR_RENDERERCALLBACK|CVAR_SHADERSYSTEM); //cvar_t r_floortexture = SCVARF ("r_floortexture", "", @@ -135,11 +137,11 @@ cvar_t r_fullbright = CVARFD ("r_fullbright", "0", CVAR_CHEAT|CVAR_SHADERSYSTEM, "Ignore world lightmaps, drawing everything fully lit."); cvar_t r_fullbrightSkins = CVARF ("r_fullbrightSkins", "0.8", /*don't default to 1, as it looks a little ugly (too bright), but don't default to 0 either because then you're handicapped in the dark*/ CVAR_SEMICHEAT|CVAR_SHADERSYSTEM); -cvar_t r_lightmap_saturation = SCVAR ("r_lightmap_saturation", "1"); +cvar_t r_lightmap_saturation = CVAR ("r_lightmap_saturation", "1"); cvar_t r_lightstylesmooth = CVARF ("r_lightstylesmooth", "0", CVAR_ARCHIVE); -cvar_t r_lightstylesmooth_limit = SCVAR ("r_lightstylesmooth_limit", "2"); -cvar_t r_lightstylespeed = SCVAR ("r_lightstylespeed", "10"); -cvar_t r_lightstylescale = SCVAR ("r_lightstylescale", "1"); +cvar_t r_lightstylesmooth_limit = CVAR ("r_lightstylesmooth_limit", "2"); +cvar_t r_lightstylespeed = CVAR ("r_lightstylespeed", "10"); +cvar_t r_lightstylescale = CVAR ("r_lightstylescale", "1"); cvar_t r_hdr_irisadaptation = CVARF ("r_hdr_irisadaptation", "0", CVAR_ARCHIVE); cvar_t r_hdr_irisadaptation_multiplier = CVAR ("r_hdr_irisadaptation_multiplier", "2"); cvar_t r_hdr_irisadaptation_minvalue = CVAR ("r_hdr_irisadaptation_minvalue", "0.5"); @@ -162,9 +164,9 @@ cvar_t r_skyboxname = CVARFC ("r_skybox", "", CVAR_RENDERERCALLBACK | CVAR_SHADERSYSTEM, R_SkyBox_Changed); cvar_t r_softwarebanding_cvar = CVARFD ("r_softwarebanding", "0", CVAR_SHADERSYSTEM, "Utilise the Quake colormap in order to emulate 8bit software rendering. This results in banding as well as other artifacts that some believe adds character. Also forces nearest sampling on affected surfaces (palette indicies do not interpolate well)."); qboolean r_softwarebanding; -cvar_t r_speeds = SCVAR ("r_speeds", "0"); -cvar_t r_stainfadeammount = SCVAR ("r_stainfadeammount", "1"); -cvar_t r_stainfadetime = SCVAR ("r_stainfadetime", "1"); +cvar_t r_speeds = CVAR ("r_speeds", "0"); +cvar_t r_stainfadeammount = CVAR ("r_stainfadeammount", "1"); +cvar_t r_stainfadetime = CVAR ("r_stainfadetime", "1"); cvar_t r_stains = CVARFC("r_stains", IFMINIMAL("0","0.75"), CVAR_ARCHIVE, Cvar_Limiter_ZeroToOne_Callback); @@ -207,13 +209,13 @@ cvar_t scr_conspeed = CVAR ("scr_conspeed", "2000"); cvar_t scr_fov = CVARFDC("fov", "90", CVAR_ARCHIVE, "field of vision, 1-170 degrees, standard fov is 90, nquake defaults to 108.", SCR_Fov_Callback); -cvar_t scr_printspeed = SCVAR ("scr_printspeed", "16"); -cvar_t scr_showpause = SCVAR ("showpause", "1"); -cvar_t scr_showturtle = SCVAR ("showturtle", "0"); -cvar_t scr_turtlefps = SCVAR ("scr_turtlefps", "10"); -cvar_t scr_sshot_compression = SCVAR ("scr_sshot_compression", "75"); -cvar_t scr_sshot_type = SCVAR ("scr_sshot_type", "png"); -cvar_t scr_sshot_prefix = SCVAR ("scr_sshot_prefix", "screenshots/fte-"); +cvar_t scr_printspeed = CVAR ("scr_printspeed", "16"); +cvar_t scr_showpause = CVAR ("showpause", "1"); +cvar_t scr_showturtle = CVAR ("showturtle", "0"); +cvar_t scr_turtlefps = CVAR ("scr_turtlefps", "10"); +cvar_t scr_sshot_compression = CVAR ("scr_sshot_compression", "75"); +cvar_t scr_sshot_type = CVAR ("scr_sshot_type", "png"); +cvar_t scr_sshot_prefix = CVAR ("scr_sshot_prefix", "screenshots/fte-"); cvar_t scr_viewsize = CVARFC("viewsize", "100", CVAR_ARCHIVE, SCR_Viewsize_Callback); @@ -278,7 +280,7 @@ extern cvar_t r_drawworld; extern cvar_t r_fullbright; cvar_t r_mirroralpha = CVARFD("r_mirroralpha","1", CVAR_CHEAT|CVAR_SHADERSYSTEM, "Specifies how the default shader is generated for the 'window02_1' texture. Values less than 1 will turn it into a mirror."); extern cvar_t r_netgraph; -cvar_t r_norefresh = SCVAR("r_norefresh","0"); +cvar_t r_norefresh = CVAR("r_norefresh","0"); extern cvar_t r_novis; extern cvar_t r_speeds; extern cvar_t r_waterwarp; @@ -341,7 +343,7 @@ cvar_t gl_lateswap = CVAR ("gl_lateswap", "0"); cvar_t gl_lerpimages = CVARFD ("gl_lerpimages", "1", CVAR_ARCHIVE, "Enables smoother resampling for images which are not power-of-two, when the drivers do not support non-power-of-two textures."); //cvar_t gl_lightmapmode = SCVARF("gl_lightmapmode", "", // CVAR_ARCHIVE); -cvar_t gl_load24bit = SCVARF ("gl_load24bit", "1", +cvar_t gl_load24bit = CVARF ("gl_load24bit", "1", CVAR_ARCHIVE); cvar_t r_clear = CVARAF("r_clear","0", @@ -353,13 +355,13 @@ cvar_t gl_menutint_shader = CVARD ("gl_menutint_shader", "1", "Controls the cvar_t gl_mindist = CVARAD ("gl_mindist", "1", "r_nearclip", "Distance to the near clip plane. Smaller values may damage depth precision, high values can potentialy be used to see through walls..."); -cvar_t gl_motionblur = SCVARF ("gl_motionblur", "0", +cvar_t gl_motionblur = CVARF ("gl_motionblur", "0", CVAR_ARCHIVE); -cvar_t gl_motionblurscale = SCVAR ("gl_motionblurscale", "1"); +cvar_t gl_motionblurscale = CVAR ("gl_motionblurscale", "1"); cvar_t gl_overbright = CVARFC ("gl_overbright", "1", CVAR_ARCHIVE, Surf_RebuildLightmap_Callback); -cvar_t gl_overbright_all = SCVARF ("gl_overbright_all", "0", +cvar_t gl_overbright_all = CVARF ("gl_overbright_all", "0", CVAR_ARCHIVE); cvar_t gl_picmip = CVARFD ("gl_picmip", "0", CVAR_ARCHIVE, "Reduce world/model texture sizes by some exponential factor."); cvar_t gl_picmip2d = CVARFD ("gl_picmip2d", "0", CVAR_ARCHIVE, "Reduce hud/menu texture sizes by some exponential factor."); @@ -367,7 +369,7 @@ cvar_t gl_nohwblend = CVARD ("gl_nohwblend","1", "If 1, don't use hardwar cvar_t gl_savecompressedtex = CVARD ("gl_savecompressedtex", "0", "Write out a copy of textures in a compressed format. The driver will do the compression on the fly, thus this setting is likely inferior to software which does not care so much about compression times."); //cvar_t gl_schematics = CVARD ("gl_schematics", "0", "Gimmick rendering mode that draws the length of various world edges."); cvar_t gl_skyboxdist = CVARD ("gl_skyboxdist", "0", "The distance of the skybox. If 0, the engine will determine it based upon the far clip plane distance."); //0 = guess. -cvar_t gl_smoothcrosshair = SCVAR ("gl_smoothcrosshair", "1"); +cvar_t gl_smoothcrosshair = CVAR ("gl_smoothcrosshair", "1"); cvar_t gl_maxdist = CVARD ("gl_maxdist", "0", "The distance of the far clip plane. If set to 0, some fancy maths will be used to place it at an infinite distance."); #ifdef SPECULAR @@ -395,8 +397,8 @@ cvar_t vid_triplebuffer = CVARAFD ("vid_triplebuffer", "1", "gl_triplebuffe cvar_t r_portalrecursion = CVARD ("r_portalrecursion", "1", "The number of portals the camera is allowed to recurse through."); cvar_t r_portaldrawplanes = CVARD ("r_portaldrawplanes", "0", "Draw front and back planes in portals. Debug feature."); cvar_t r_portalonly = CVARD ("r_portalonly", "0", "Don't draw things which are not portals. Debug feature."); -cvar_t dpcompat_psa_ungroup = SCVAR ("dpcompat_psa_ungroup", "0"); -cvar_t r_noaliasshadows = SCVARF ("r_noaliasshadows", "0", CVAR_ARCHIVE); +cvar_t dpcompat_psa_ungroup = CVAR ("dpcompat_psa_ungroup", "0"); +cvar_t r_noaliasshadows = CVARF ("r_noaliasshadows", "0", CVAR_ARCHIVE); cvar_t r_shadows = CVARFD ("r_shadows", "0", CVAR_ARCHIVE, "Draw basic blob shadows underneath entities without using realtime lighting."); cvar_t r_showbboxes = CVARD("r_showbboxes", "0", "Debugging. Shows bounding boxes. 1=ssqc, 2=csqc. Red=solid, Green=stepping/toss/bounce, Blue=onground."); cvar_t r_showfields = CVARD("r_showfields", "0", "Debugging. Shows entity fields boxes (entity closest to crosshair). 1=ssqc, 2=csqc."); @@ -431,7 +433,7 @@ cvar_t vid_desktopgamma = CVARFD ("vid_desktopgamma", "0", cvar_t r_fog_exp2 = CVARD ("r_fog_exp2", "1", "Expresses how fog fades with distance. 0 (matching DarkPlaces's default) is typically more realistic, while 1 (matching FitzQuake and others) is more common."); extern cvar_t gl_dither; -cvar_t gl_screenangle = SCVAR("gl_screenangle", "0"); +cvar_t gl_screenangle = CVAR("gl_screenangle", "0"); #endif #ifdef VKQUAKE @@ -885,6 +887,7 @@ void Renderer_Init(void) Cvar_Register (&gl_mipcap, GLRENDEREROPTIONS); Cvar_Register (&gl_texture_anisotropic_filtering, GLRENDEREROPTIONS); Cvar_Register (&r_drawflat, GRAPHICALNICETIES); + Cvar_Register (&r_lightmap, GRAPHICALNICETIES); Cvar_Register (&r_menutint, GRAPHICALNICETIES); Cvar_Register (&r_fb_bmodels, GRAPHICALNICETIES); diff --git a/engine/client/sbar.c b/engine/client/sbar.c index 047a6dcf0..138e7bc39 100644 --- a/engine/client/sbar.c +++ b/engine/client/sbar.c @@ -37,7 +37,7 @@ cvar_t scr_scoreboard_showflags = CVARD("scr_scoreboard_showflags", "2", "Displa cvar_t scr_scoreboard_fillalpha = CVARD("scr_scoreboard_fillalpha", "0.7", "Transparency amount for newstyle scoreboard."); cvar_t scr_scoreboard_teamscores = CVARD("scr_scoreboard_teamscores", "1", "Makes +showscores act as +showteamscores. Because reasons."); cvar_t scr_scoreboard_teamsort = CVARD("scr_scoreboard_teamsort", "0", "On the scoreboard, sort players by their team BEFORE their personal score."); -cvar_t scr_scoreboard_titleseperator = SCVAR("scr_scoreboard_titleseperator", "1"); +cvar_t scr_scoreboard_titleseperator = CVAR("scr_scoreboard_titleseperator", "1"); cvar_t sbar_teamstatus = CVARD("sbar_teamstatus", "1", "Display the last team say from each of your team members just above the sbar area."); //=========================================== @@ -3426,7 +3426,7 @@ void Sbar_DeathmatchOverlay (int start) } x = startx; -#define COLUMN(title, width, code, fill) if (showcolumns & (1< #include +//by adding 'extern' to one definition of a function in a translation unit, then the definition in that TU is NOT considered an inline definition. meaning non-inlined references in other TUs can link to it instead of their own if needed. +fte_inlinebody conchar_t *Font_Decode(conchar_t *start, unsigned int *codeflags, unsigned int *codepoint); + + // These 4 libraries required for the version command #if defined(MINGW) diff --git a/engine/common/cvar.h b/engine/common/cvar.h index 67dd55f39..6bb653b1a 100644 --- a/engine/common/cvar.h +++ b/engine/common/cvar.h @@ -103,8 +103,6 @@ typedef struct cvar_s #define CVARD(ConsoleName,Value,Description) CVARAFDC(ConsoleName, Value, NULL, 0, Description, NULL) #define CVAR(ConsoleName,Value) CVARD(ConsoleName, Value, NULL) -#define SCVAR(ConsoleName,Value) CVAR(ConsoleName,Value) -#define SCVARF(ConsoleName,Value,Flags) CVARF(ConsoleName,Value,Flags) #define CVARDP4(Flags,ConsoleName,Value,Description) CVARFD(ConsoleName, Value, Flags,Description) typedef struct cvar_group_s diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index db2debecc..512f27905 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -386,10 +386,10 @@ typedef struct cminfo_s static q2mapsurface_t nullsurface; -cvar_t map_noareas = SCVAR("map_noareas", "0"); //1 for lack of mod support. -cvar_t map_noCurves = SCVARF("map_noCurves", "0", CVAR_CHEAT); +cvar_t map_noareas = CVAR("map_noareas", "0"); //1 for lack of mod support. +cvar_t map_noCurves = CVARF("map_noCurves", "0", CVAR_CHEAT); cvar_t map_autoopenportals = CVARD("map_autoopenportals", "0", "When set to 1, force-opens all area portals. Normally these start closed and are opened by doors when they move, but this requires the gamecode to signal this."); //1 for lack of mod support. -cvar_t r_subdivisions = SCVAR("r_subdivisions", "2"); +cvar_t r_subdivisions = CVAR("r_subdivisions", "2"); static int CM_NumInlineModels (model_t *model); static cmodel_t *CM_InlineModel (model_t *model, char *name); diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 7afef0281..88eba1961 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -81,8 +81,8 @@ fragmentation works like IP, offset and morefrags. offset is *8 (decode: (offset */ int net_drop; -cvar_t showpackets = SCVAR("showpackets", "0"); -cvar_t showdrop = SCVAR("showdrop", "0"); +cvar_t showpackets = CVAR("showpackets", "0"); +cvar_t showdrop = CVAR("showdrop", "0"); cvar_t qport = CVARF("qport_", "0", CVAR_NOSAVE); cvar_t net_mtu = CVARD("net_mtu", "1440", "Specifies a maximum udp payload size, above which packets will be fragmented. If routers all worked properly this could be some massive value, and some massive value may work really nicely for lans. Use smaller values than the default if you're connecting through nested tunnels through routers that fail with IP fragmentation."); cvar_t net_compress = CVARD("net_compress", "0", "Enables huffman compression of network packets."); diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index d9e311cfe..624ab026d 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -2785,6 +2785,19 @@ ftenet_generic_connection_t *FTENET_Generic_EstablishConnection(int adrfamily, i if (ioctlsocket (newsocket, FIONBIO, &_true) == -1) Sys_Error ("UDP_OpenSocket: ioctl FIONBIO: %s", strerror(neterrno())); + //ipv6 sockets need to add themselves to a multicast group, so that we can receive broadcasts on a lan +#if defined(IPPROTO_IPV6) + if (family == AF_INET6 || hybrid || isserver) + { + struct ipv6_mreq req; + memset(&req, 0, sizeof(req)); + req.ipv6mr_multiaddr.s6_addr[0] = 0xff; + req.ipv6mr_multiaddr.s6_addr[1] = 0x02; + req.ipv6mr_multiaddr.s6_addr[15]= 0x01; + req.ipv6mr_interface = 0; + setsockopt(newsocket, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&req, sizeof(req)); + } +#endif // // determine my name & address if we don't already know it diff --git a/engine/common/particles.h b/engine/common/particles.h index b6082d788..48210ea73 100644 --- a/engine/common/particles.h +++ b/engine/common/particles.h @@ -201,6 +201,7 @@ struct model_s; struct msurface_s; void P_InitParticleSystem(void); +void P_ShutdownParticleSystem(void); void P_Shutdown(void); void P_LoadedModel(struct model_s *mod); /*checks a model's various effects*/ void P_DefaultTrail (unsigned int entityeffects, unsigned int modelflags, int *trailid, int *trailpalidx); diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 0931789e9..d2246f935 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -13,7 +13,7 @@ #ifdef PLUGINS cvar_t plug_sbar = CVARD("plug_sbar", "3", "Controls whether plugins are allowed to draw the hud, rather than the engine (when allowed by csqc). This is typically used to permit the ezhud plugin without needing to bother unloading it.\n=0: never use hud plugins.\n&1: Use hud plugins in deathmatch.\n&2: Use hud plugins in singleplayer/coop.\n=3: Always use hud plugins (when loaded)."); -cvar_t plug_loaddefault = SCVAR("plug_loaddefault", "1"); +cvar_t plug_loaddefault = CVAR("plug_loaddefault", "1"); qintptr_t Plug_Bullet_Init(qintptr_t *args); qintptr_t Plug_ODE_Init(qintptr_t *args); diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index da35fa3ad..a96844bfb 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -1337,7 +1337,7 @@ void Shader_LightPass(const char *shortname, shader_t *s, const void *args) { char shadertext[8192*2]; extern cvar_t r_drawflat; - sprintf(shadertext, LIGHTPASS_SHADER, r_drawflat.ival?"#FLAT":""); + sprintf(shadertext, LIGHTPASS_SHADER, (r_lightmap.ival||r_drawflat.ival)?"#FLAT":""); Shader_DefaultScript(shortname, s, shadertext); } diff --git a/engine/gl/gl_font.c b/engine/gl/gl_font.c index 2be52313c..25ec47f59 100644 --- a/engine/gl/gl_font.c +++ b/engine/gl/gl_font.c @@ -31,10 +31,6 @@ struct font_s *font_tiny; static int font_be_flags; extern unsigned int r2d_be_flags; -//by adding 'extern' to one definition of a function in a translation unit, then the definition in that TU is NOT considered an inline definition. meaning non-inlined references in other TUs can link to it instead of their own if needed. -fte_inlinebody conchar_t *Font_Decode(conchar_t *start, unsigned int *codeflags, unsigned int *codepoint); - - #ifdef AVAIL_FREETYPE #include #include FT_FREETYPE_H diff --git a/engine/gl/gl_rmain.c b/engine/gl/gl_rmain.c index 2b8d47022..699fc7414 100644 --- a/engine/gl/gl_rmain.c +++ b/engine/gl/gl_rmain.c @@ -53,9 +53,9 @@ extern cvar_t gl_part_flame; extern cvar_t r_bloom; extern cvar_t r_wireframe_smooth; -cvar_t gl_affinemodels = SCVAR("gl_affinemodels","0"); -cvar_t gl_finish = SCVAR("gl_finish","0"); -cvar_t gl_dither = SCVAR("gl_dither", "1"); +cvar_t gl_affinemodels = CVAR("gl_affinemodels","0"); +cvar_t gl_finish = CVAR("gl_finish","0"); +cvar_t gl_dither = CVAR("gl_dither", "1"); extern cvar_t r_stereo_separation; extern cvar_t r_stereo_convergence; extern cvar_t r_stereo_method; @@ -80,7 +80,7 @@ extern cvar_t r_portaldrawplanes; extern cvar_t r_portalonly; #ifdef R_XFLIP -cvar_t r_xflip = SCVAR("leftisright", "0"); +cvar_t r_xflip = CVAR("leftisright", "0"); #endif extern cvar_t scr_fov; diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 8b93eeecb..28ebe6e61 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -4985,6 +4985,19 @@ void Shader_DefaultBSPLM(const char *shortname, shader_t *s, const void *args) char *builtin = NULL; if (Shader_ParseShader("defaultwall", s)) return; + + if (!builtin && r_lightmap.ival) + builtin = ( + "{\n" + "program drawflat_wall\n" + "{\n" + "map $lightmap\n" + "tcgen lightmap\n" + "rgbgen const 255 255 255\n" + "}\n" + "}\n" + ); + if (!builtin && r_drawflat.ival) builtin = ( "{\n" diff --git a/engine/gl/gl_warp.c b/engine/gl/gl_warp.c index 198defc9f..6de534535 100644 --- a/engine/gl/gl_warp.c +++ b/engine/gl/gl_warp.c @@ -786,17 +786,18 @@ static void GL_DrawSkyBox (texid_t *texnums, batch_t *s) ============= R_InitSky -A sky texture is 256*128, with the right side being a masked overlay +A sky image is 256*128 and comprises two logical textures. +the left is the transparent/blended part. the right is the opaque/background part. ============== */ void R_InitSky (shader_t *shader, const char *skyname, qbyte *src, unsigned int width, unsigned int height) { int i, j, p; - unsigned trans[128*128]; + unsigned *temp; unsigned transpix, alphamask; int r, g, b; unsigned *rgba; - char name[MAX_QPATH]; + char name[MAX_QPATH*2]; unsigned int stride = width; width /= 2; @@ -804,15 +805,64 @@ void R_InitSky (shader_t *shader, const char *skyname, qbyte *src, unsigned int if (width < 1 || height < 1 || stride != width*2 || !src) return; - if (width*height > countof(trans)) + //try to load dual-layer-single-image skies. + //this is always going to be lame special case crap { - unsigned int wibuf[16] = {0}; - shader->defaulttextures->base = R_LoadTexture("$blackimage", 4, 4, TF_RGBA32, wibuf, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA); - shader->defaulttextures->base = R_LoadReplacementTexture(skyname, NULL, 0, src, stride, height, TF_SOLID8); - shader->defaulttextures->fullbright = shader->defaulttextures->base; - return; + size_t filesize = 0; + qbyte *filedata = NULL; + if (!filedata) + { + Q_snprintfz(name, sizeof(name), "textures/%s.tga", skyname); + filedata = FS_LoadMallocFile(name, &filesize); + } + if (!filedata) + { + Q_snprintfz(name, sizeof(name), "textures/%s.png", skyname); + filedata = FS_LoadMallocFile(name, &filesize); + } + + if (filedata) + { + int imagewidth, imageheight; + qboolean hasalpha; //fixme, if this is false, is it worth all this code? + unsigned int *imagedata = (unsigned int*)Read32BitImageFile(filedata, filesize, &imagewidth, &imageheight, &hasalpha, name); + Z_Free(filedata); + + if (imagedata && !(imagewidth&1)) + { + imagewidth>>=1; + + temp = BZF_Malloc(imagewidth*imageheight*sizeof(*temp)); + if (temp) + { + for (i=0 ; idefaulttextures->base = R_LoadReplacementTexture(name, NULL, IF_NOALPHA, temp, imagewidth, imageheight, TF_RGBX32); + + for (i=0 ; idefaulttextures->fullbright = R_LoadReplacementTexture(name, NULL, 0, temp, imagewidth, imageheight, TF_RGBA32); + BZ_Free(temp); + return; + } + } + BZ_Free(imagedata); + } } + temp = BZ_Malloc(width*height*sizeof(*temp)); + // make an average value for the back to avoid // a fringe on the top level @@ -822,7 +872,7 @@ void R_InitSky (shader_t *shader, const char *skyname, qbyte *src, unsigned int { p = src[i*stride + j + width]; rgba = &d_8to24rgbtable[p]; - trans[(i*width) + j] = *rgba; + temp[(i*width) + j] = *rgba; r += ((qbyte *)rgba)[0]; g += ((qbyte *)rgba)[1]; b += ((qbyte *)rgba)[2]; @@ -832,11 +882,12 @@ void R_InitSky (shader_t *shader, const char *skyname, qbyte *src, unsigned int { Q_snprintfz(name, sizeof(name), "%s_solid", skyname); Q_strlwr(name); - shader->defaulttextures->base = R_LoadReplacementTexture(name, NULL, IF_NOALPHA, trans, width, height, TF_RGBX32); + shader->defaulttextures->base = R_LoadReplacementTexture(name, NULL, IF_NOALPHA, temp, width, height, TF_RGBX32); } if (!shader->defaulttextures->fullbright) { + //fixme: use premultiplied alpha here. ((qbyte *)&transpix)[0] = r/(width*height); ((qbyte *)&transpix)[1] = g/(width*height); ((qbyte *)&transpix)[2] = b/(width*height); @@ -847,15 +898,16 @@ void R_InitSky (shader_t *shader, const char *skyname, qbyte *src, unsigned int { p = src[i*stride + j]; if (p == 0) - trans[(i*width) + j] = transpix; + temp[(i*width) + j] = transpix; else - trans[(i*width) + j] = d_8to24rgbtable[p] & alphamask; + temp[(i*width) + j] = d_8to24rgbtable[p] & alphamask; } //FIXME: support _trans - Q_snprintfz(name, sizeof(name), "%s_alpha", skyname); + Q_snprintfz(name, sizeof(name), "%s_alpha:%s_trans", skyname, skyname); Q_strlwr(name); - shader->defaulttextures->fullbright = R_LoadReplacementTexture(name, NULL, 0, trans, width, height, TF_RGBA32); + shader->defaulttextures->fullbright = R_LoadReplacementTexture(name, NULL, 0, temp, width, height, TF_RGBA32); } + BZ_Free(temp); } #endif diff --git a/engine/http/iwebiface.c b/engine/http/iwebiface.c index 5961f0e79..8b6b21923 100644 --- a/engine/http/iwebiface.c +++ b/engine/http/iwebiface.c @@ -373,13 +373,13 @@ IWEBFILE *IWebFOpenRead(char *name) //fread(name, "rb"); #else #ifndef CLIENTONLY -cvar_t ftpserver = SCVAR("sv_ftp", "0"); -cvar_t ftpserver_port = SCVAR("sv_ftp_port", "21"); -cvar_t httpserver = SCVAR("sv_http", "0"); -cvar_t httpserver_port = SCVAR("sv_http_port", "80"); -cvar_t sv_readlevel = SCVAR("sv_readlevel", "0"); //default to allow anyone -cvar_t sv_writelevel = SCVAR("sv_writelevel", "35"); //allowed to write to uploads/uname -cvar_t sv_fulllevel = SCVAR("sv_fulllevel", "51"); //allowed to write anywhere, replace any file... +cvar_t ftpserver = CVAR("sv_ftp", "0"); +cvar_t ftpserver_port = CVAR("sv_ftp_port", "21"); +cvar_t httpserver = CVAR("sv_http", "0"); +cvar_t httpserver_port = CVAR("sv_http_port", "80"); +cvar_t sv_readlevel = CVAR("sv_readlevel", "0"); //default to allow anyone +cvar_t sv_writelevel = CVAR("sv_writelevel", "35"); //allowed to write to uploads/uname +cvar_t sv_fulllevel = CVAR("sv_fulllevel", "51"); //allowed to write anywhere, replace any file... #endif //this file contains functions called from each side. diff --git a/engine/qclib/cmdlib.h b/engine/qclib/cmdlib.h index 317507fd3..d57bdbe53 100644 --- a/engine/qclib/cmdlib.h +++ b/engine/qclib/cmdlib.h @@ -76,7 +76,7 @@ int CheckParm (char *check); int SafeOpenWrite (char *filename, int maxsize); int SafeOpenRead (char *filename); void SafeRead (int handle, void *buffer, long count); -void SafeWrite (int handle, void *buffer, long count); +void SafeWrite (int handle, const void *buffer, long count); pbool SafeClose(int hand); int SafeSeek(int hand, int ofs, int mode); void *SafeMalloc (long size); diff --git a/engine/qclib/comprout.c b/engine/qclib/comprout.c index bc7aceae6..16ddb7482 100644 --- a/engine/qclib/comprout.c +++ b/engine/qclib/comprout.c @@ -242,7 +242,7 @@ int QC_strncasecmp(const char *s1, const char *s2, int n) return -1; } -void editbadfile(char *fname, int line) +void editbadfile(const char *fname, int line) { if (!*errorfile) { diff --git a/engine/qclib/gui.h b/engine/qclib/gui.h index 9824a7603..8b7abf6e7 100644 --- a/engine/qclib/gui.h +++ b/engine/qclib/gui.h @@ -1,6 +1,6 @@ void GoToDefinition(char *name); int Grep(char *filename, char *string); -void EditFile(char *name, int line, pbool setcontrol); +void EditFile(const char *name, int line, pbool setcontrol); void GUI_SetDefaultOpts(void); int GUI_BuildParms(char *args, char **argv, pbool quick); diff --git a/engine/qclib/pr_comp.h b/engine/qclib/pr_comp.h index f4d93348b..b95749e74 100644 --- a/engine/qclib/pr_comp.h +++ b/engine/qclib/pr_comp.h @@ -594,8 +594,6 @@ typedef struct } dprograms_t; #define standard_dprograms_t_size ((size_t)&((dprograms_t*)NULL)->ofsfiles) -#endif - @@ -623,3 +621,5 @@ typedef struct typeinfo_s int size; string_t name; } typeinfo_t; + +#endif diff --git a/engine/qclib/progsint.h b/engine/qclib/progsint.h index 2747664b4..e74b0f35a 100644 --- a/engine/qclib/progsint.h +++ b/engine/qclib/progsint.h @@ -1,3 +1,6 @@ +#ifndef PROGSINT_H_INCLUDED +#define PROGSINT_H_INCLUDED + #ifdef _WIN32 #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS @@ -518,3 +521,5 @@ char *QCC_COM_Parse (const char *data); extern char qcc_token[1024]; extern char *basictypenames[]; #endif + +#endif diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index 694805861..5c8d8efe5 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -377,17 +377,18 @@ typedef struct QCC_def_s struct QCC_function_s *scope; // function the var was defined in, or NULL struct QCC_def_s *deftail; // arrays and structs create multiple globaldef objects providing different types at the different parts of the single object (struct), or alternative names (vectors). this allows us to correctly set the const type based upon how its initialised. struct QCC_def_s *generatedfor; - int initialized; // 1 when a declaration included "= immediate". 2 = extern. 3 = don't warn (unless actually called) + int initialized; // 1 when a declaration included "= immediate". 2 = extern. 3 = don't warn (unless actually used) int constant; // 1 says we can use the value over and over again struct QCC_def_s *symbolheader; //this is the original symbol within which the def is stored. - union QCC_eval_s *symboldata; //null if uninitialised. + union QCC_eval_s *symboldata; //null if uninitialised. use sym->symboldata[sym->ofs] to index. unsigned int symbolsize; //total byte size of symbol int refcount; //if 0, temp can be reused. tracked on globals too in order to catch bugs that would otherwise be a little too obscure. int timescalled; //part of the opt_stripfunctions optimisation. - int s_file; + const char *filen; + int s_filed; int s_line; int arraysize; @@ -406,6 +407,8 @@ typedef struct QCC_def_s pbool used:1; //if it remains 0, it may be stripped. this is forced for functions and fields. commonly 0 on fields. pbool localscope:1; //is a local, as opposed to a static (which is only visible within its scope) pbool arraylengthprefix:1; //hexen2 style arrays have a length prefixed to them for auto bounds checks. this can only work reliably for simple non-struct arrays. + pbool assumedtype:1; //#merged. the type is not reliable. + pbool weak:1; //ignore any initialiser value (only permitted on functions) int fromstatement; //statement that it is valid from. temp_t *temp; @@ -462,8 +465,9 @@ struct QCC_function_s { int builtin; // the builtin number. >= 0 int code; // first statement. if -1, is a builtin. - string_t s_file; // source file with definition - const char *file; + dfunction_t *merged; // this function was merged. this is the index to use to ensure that the parms are sized correctly.. + string_t s_filed; // source file with definition + const char *filen; int line; char *name; //internal name of function struct QCC_function_s *parentscope; //for nested functions @@ -568,6 +572,8 @@ extern pbool keyword_nosave; //don't write the def to the output. extern pbool keyword_inline; //don't write the def to the output. extern pbool keyword_strip; //don't write the def to the output. extern pbool keyword_union; //you surly know what a union is! +extern pbool keyword_wrap; +extern pbool keyword_weak; extern pbool keyword_unused; extern pbool keyword_used; @@ -885,6 +891,8 @@ extern compiler_flag_t compiler_flag[]; extern unsigned char qccwarningaction[WARN_MAX]; extern jmp_buf pr_parse_abort; // longjump with this on parse error +extern const char *s_filen; //name of the file we're currently compiling. +extern QCC_string_t s_filed; //name of the file we're currently compiling, as seen by whoever reads the .dat extern int pr_source_line; extern char *pr_file_p; @@ -906,16 +914,18 @@ extern int pr_error_count, pr_warning_count; void QCC_PR_NewLine (pbool incomment); #define GDF_NONE 0 -#define GDF_SAVED 1 -#define GDF_STATIC 2 -#define GDF_CONST 4 -#define GDF_STRIP 8 //always stripped, regardless of optimisations. used for class member fields -#define GDF_SILENT 16 //used by the gui, to suppress ALL warnings associated with querying the def. -#define GDF_INLINE 32 //attempt to inline calls to this function -#define GDF_USED 64 //don't strip this, ever. +#define GDF_SAVED 1 +#define GDF_STATIC 2 +#define GDF_CONST 4 +#define GDF_STRIP 8 //always stripped, regardless of optimisations. used for class member fields +#define GDF_SILENT 16 //used by the gui, to suppress ALL warnings associated with querying the def. +#define GDF_INLINE 32 //attempt to inline calls to this function +#define GDF_USED 64 //don't strip this, ever. +#define GDF_BASICTYPE 128 //don't care about #merge types not being known correctly. QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, struct QCC_function_s *scope, pbool allocate, int arraysize, unsigned int flags); QCC_sref_t QCC_PR_GetSRef (QCC_type_t *type, char *name, struct QCC_function_s *scope, pbool allocate, int arraysize, unsigned int flags); void QCC_FreeTemp(QCC_sref_t t); +void QCC_FreeDef(QCC_def_t *def); char *QCC_PR_CheckCompConstTooltip(char *word, char *outstart, char *outend); void QCC_PR_PrintDefs (void); @@ -940,8 +950,6 @@ void QCC_PR_ResetErrorScope(void); extern pbool pr_dumpasm; -extern QCC_string_t s_file; // filename for function definition - extern QCC_def_t def_ret, def_parms[MAX_PARMS]; void QCC_PR_EmitArrayGetFunction(QCC_def_t *defscope, QCC_def_t *thearray, char *arrayname); @@ -1006,7 +1014,7 @@ typedef struct int block; int used; int fileline; - char *filename; + const char *filename; } precache_t; extern precache_t *precache_sound; extern int numsounds; @@ -1060,7 +1068,7 @@ static bool inline QCC_PR_CheckToken (char *string) return true; } -static void inline QCC_PR_Expect (char *string) +static void inline QCC_PR_Expect (const char *string) { if (strcmp (string, pr_token)) QCC_PR_ParseError ("expected %s, found %s",string, pr_token); @@ -1068,12 +1076,13 @@ static void inline QCC_PR_Expect (char *string) } #endif -void editbadfile(char *fname, int line); +void editbadfile(const char *fname, int line); char *TypeName(QCC_type_t *type, char *buffer, int buffersize); void QCC_PR_AddIncludePath(const char *newinc); void QCC_PR_IncludeChunk (char *data, pbool duplicate, char *filename); void QCC_PR_IncludeChunkEx(char *data, pbool duplicate, char *filename, CompilerConstant_t *cnst); void QCC_PR_CloseProcessor(void); +void QCC_FindBestInclude(char *newfile, char *currentfile, pbool verbose); pbool QCC_PR_UnInclude(void); extern void *(*pHash_Get)(hashtable_t *table, const char *name); extern void *(*pHash_GetNext)(hashtable_t *table, const char *name, void *old); diff --git a/engine/qclib/qcc_cmdlib.c b/engine/qclib/qcc_cmdlib.c index f08ad2f38..e5ebed367 100644 --- a/engine/qclib/qcc_cmdlib.c +++ b/engine/qclib/qcc_cmdlib.c @@ -568,7 +568,7 @@ void VARGS QCC_Error (int errortype, const char *error, ...) printf ("\n************ ERROR ************\n%s\n", msg); - editbadfile(strings+s_file, pr_source_line); + editbadfile(s_filen, pr_source_line); numsourcefiles = 0; @@ -890,7 +890,7 @@ void ResizeBuf(int hand, int newsize) qccfile[hand].buff = nb; qccfile[hand].buffsize = newsize; } -void SafeWrite(int hand, void *buf, long count) +void SafeWrite(int hand, const void *buf, long count) { if (qccfile[hand].ofs +count >= qccfile[hand].buffsize) ResizeBuf(hand, qccfile[hand].ofs + count+(64*1024)); @@ -1242,6 +1242,7 @@ long QCC_LoadFile (char *filename, void **bufferptr) mem += sizeof(qcc_cachedsourcefile_t); externs->ReadFile(filename, mem, len+2, NULL); + mem[len] = 0; mem = QCC_SanitizeCharSet(mem, &len, NULL, &orig); diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index b1fd5c1cb..b70d009c1 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -78,6 +78,8 @@ pbool keyword_inline; pbool keyword_strip; pbool keyword_ignore; pbool keyword_union; //you surly know what a union is! +pbool keyword_weak; +pbool keyword_wrap; #define keyword_not 1 //hexenc support needs this, and fteqcc can optimise without it, but it adds an extra token after the if, so it can cause no namespace conflicts @@ -174,7 +176,7 @@ QCC_sref_t QCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign, p void QCC_Marshal_Locals(int firststatement, int laststatement); QCC_sref_t QCC_PR_ParseArrayPointer (QCC_sref_t d, pbool allowarrayassign, pbool makestructpointers); QCC_sref_t QCC_LoadFromArray(QCC_sref_t base, QCC_sref_t index, QCC_type_t *t, pbool preserve); -void QCC_PR_ParseInitializerDef(QCC_def_t *def); +void QCC_PR_ParseInitializerDef(QCC_def_t *def, unsigned int flags); QCC_ref_t *QCC_DefToRef(QCC_ref_t *ref, QCC_sref_t def); //ref is a buffer to write into, to avoid excessive allocs QCC_sref_t QCC_RefToDef(QCC_ref_t *ref, pbool freetemps); @@ -186,7 +188,7 @@ QCC_ref_t *QCC_PR_BuildAccessorRef(QCC_ref_t *retbuf, QCC_sref_t base, QCC_sref_ QCC_sref_t QCC_StoreSRefToRef(QCC_ref_t *dest, QCC_sref_t source, pbool readable, pbool preservedest); QCC_sref_t QCC_StoreRefToRef(QCC_ref_t *dest, QCC_ref_t *source, pbool readable, pbool preservedest); void QCC_PR_DiscardRef(QCC_ref_t *ref); -QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *type); +QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *type, pbool dowrap); const char *QCC_VarAtOffset(QCC_sref_t ref, unsigned int size); QCC_sref_t QCC_EvaluateCast(QCC_sref_t src, QCC_type_t *cast, pbool implicit); @@ -194,7 +196,7 @@ QCC_statement_t *QCC_Generate_OP_IFNOT(QCC_sref_t e, pbool preserve); QCC_statement_t *QCC_Generate_OP_IF(QCC_sref_t e, pbool preserve); QCC_statement_t *QCC_Generate_OP_GOTO(void); QCC_sref_t QCC_PR_GenerateLogicalNot(QCC_sref_t e, const char *errormessage); -QCC_function_t *QCC_PR_GenerateQCFunction (QCC_def_t *def, QCC_type_t *type); +QCC_function_t *QCC_PR_GenerateQCFunction (QCC_def_t *def, QCC_type_t *type, pbool dowrap); //NOTE: prints may use from the func argument's symbol, which can be awkward if its a temp. QCC_sref_t QCC_PR_GenerateFunctionCallSref (QCC_sref_t newself, QCC_sref_t func, QCC_sref_t *arglist, int argcount); @@ -232,7 +234,8 @@ QCC_type_t *pr_assumetermtype; //undefined things get this time, with no warnin QCC_function_t *pr_assumetermscope; unsigned int pr_assumetermflags; //GDF_ pbool pr_dumpasm; -QCC_string_t s_file, s_file2; // filename for function definition +const char *s_filen; +QCC_string_t s_filed; // filename for function definition unsigned int locals_marshalled; // largest local block size that needs to be allocated for locals overlapping. @@ -1482,7 +1485,7 @@ pbool QCC_StatementIsAJump(int stnum, int notifdest); //typically used for debugging. Also used to determine function names for intrinsics. const char *QCC_GetSRefName(QCC_sref_t ref) { - if (ref.sym && ref.sym->name && !ref.ofs) + if (ref.sym && ref.sym->name/* && !ref.ofs*/) { if (ref.sym->temp) return ref.cast->name; @@ -4259,7 +4262,7 @@ void QCC_PrecacheSound (const char *n, int ch) // QCC_Error ("PrecacheSound: numsounds == MAX_SOUNDS"); strcpy (precache_sound[i].name, n); precache_sound[i].block = ch; - precache_sound[i].filename = strings+s_file; + precache_sound[i].filename = s_filen; precache_sound[i].fileline = pr_source_line; numsounds++; } @@ -4290,7 +4293,7 @@ void QCC_PrecacheModel (const char *n, int ch) precache_model[i].block = ch - '0'; else precache_model[i].block = 1; - precache_model[i].filename = strings+s_file; + precache_model[i].filename = s_filen; precache_model[i].fileline = pr_source_line; nummodels++; } @@ -4313,7 +4316,7 @@ void QCC_SetModel (const char *n) precache_model[i].block = 0; precache_model[i].used=1; - precache_model[i].filename = strings+s_file; + precache_model[i].filename = s_filen; precache_model[i].fileline = pr_source_line; nummodels++; } @@ -4335,7 +4338,7 @@ void QCC_SoundUsed (const char *n) precache_sound[i].block = 0; precache_sound[i].used=1; - precache_sound[i].filename = strings+s_file; + precache_sound[i].filename = s_filen; precache_sound[i].fileline = pr_source_line; numsounds++; } @@ -6485,7 +6488,7 @@ void QCC_PR_EmitClassFromFunction(QCC_def_t *scope, QCC_type_t *basetype) pr_source_line = pr_token_line_last = scope->s_line; - pr_scope = QCC_PR_GenerateQCFunction(scope, scope->type); + pr_scope = QCC_PR_GenerateQCFunction(scope, scope->type, false); //reset the locals chain pr.local_head.nextlocal = NULL; pr.local_tail = &pr.local_head; @@ -7282,7 +7285,7 @@ QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbo { QCC_PR_ParseWarning (ERR_UNKNOWNVALUE, "Class \"%s\" is not defined, cannot access memeber \"%s\"", assumeclass->name, name); if (!autoprototype && !autoprototyped) - QCC_PR_Note(ERR_UNKNOWNVALUE, strings+s_file, pr_source_line, "Consider using #pragma autoproto"); + QCC_PR_Note(ERR_UNKNOWNVALUE, s_filen, pr_source_line, "Consider using #pragma autoproto"); } else { @@ -7515,6 +7518,8 @@ QCC_sref_t QCC_EvaluateCast(QCC_sref_t src, QCC_type_t *cast, pbool implicit) } else if (!implicit && !typecmp_lax(src.cast, cast)) src.cast = cast; + else if (!implicit && cast->type == ev_void) + src.cast = type_void; //anything can be cast to void, but only do it explicitly. else { char typea[256]; @@ -7704,7 +7709,7 @@ QCC_ref_t *QCC_PR_RefTerm (QCC_ref_t *retbuf, unsigned int exprflags) e = nullsref; e.cast = type_float; patch = QCC_Generate_OP_GOTO(); - e = QCC_MakeIntConst(QCC_PR_ParseImmediateStatements (NULL, newtype) - functions); + e = QCC_MakeIntConst(QCC_PR_ParseImmediateStatements (NULL, newtype, false) - functions); e.cast = newtype; patch->a.ofs = &statements[numstatements] - patch; @@ -9590,7 +9595,7 @@ void QCC_PR_ParseStatement_For(void) { d = QCC_PR_GetDef (type, QCC_PR_ParseName(), pr_scope, true, 0, 0); QCC_PR_Expect("="); - QCC_PR_ParseInitializerDef(d); + QCC_PR_ParseInitializerDef(d, 0); QCC_FreeDef(d); QCC_FreeDef(d); } @@ -11114,7 +11119,7 @@ void QCC_CheckForDeadAndMissingReturns(int first, int last, int rettype) } if (st2 == last) { - QCC_PR_Warning(WARN_UNREACHABLECODE, pr_scope->file, statements[st].linenum, "%s: contains unreachable code (line %i)", pr_scope->name, statements[st].linenum); + QCC_PR_Warning(WARN_UNREACHABLECODE, pr_scope->filen, statements[st].linenum, "%s: contains unreachable code (line %i)", pr_scope->name, statements[st].linenum); } continue; } @@ -11287,12 +11292,12 @@ int QCC_CheckOneUninitialised(int firststatement, int laststatement, QCC_def_t * if (st->op == OP_DONE || st->op == OP_RETURN) { - if (st->a.sym == def && st->a.ofs >= min && st->a.ofs < max) + if (st->a.sym && st->a.sym->symbolheader == def && st->a.ofs >= min && st->a.ofs < max) return i; return -2; } - if (st->op == OP_GLOBALADDRESS && (st->a.sym == def || (st->a.sym == def->symbolheader))) + if (st->op == OP_GLOBALADDRESS && (st->a.sym->symbolheader == def || (st->a.sym->symbolheader == def))) return -1; //assume taking a pointer to it is an initialisation. // this code catches gotos, but can cause issues with while statements. @@ -11301,7 +11306,7 @@ int QCC_CheckOneUninitialised(int firststatement, int laststatement, QCC_def_t * if (pr_opcodes[st->op].type_a) { - if (st->a.sym == def && st->a.ofs >= min && st->a.ofs < max) + if (st->a.sym && st->a.sym->symbolheader == def && st->a.ofs >= min && st->a.ofs < max) { if (OpAssignsToA(st->op)) return -1; @@ -11320,7 +11325,7 @@ int QCC_CheckOneUninitialised(int firststatement, int laststatement, QCC_def_t * if (pr_opcodes[st->op].type_b) { - if (st->b.sym == def && st->b.ofs >= min && st->b.ofs < max) + if (st->b.sym && st->b.sym->symbolheader == def && st->b.ofs >= min && st->b.ofs < max) { if (OpAssignsToB(st->op)) return -1; @@ -11356,7 +11361,7 @@ int QCC_CheckOneUninitialised(int firststatement, int laststatement, QCC_def_t * continue; } - if (pr_opcodes[st->op].type_c && st->c.sym == def && st->c.ofs >= min && st->c.ofs < max) + if (pr_opcodes[st->op].type_c && st->c.sym && st->c.sym->symbolheader == def && st->c.ofs >= min && st->c.ofs < max) { if (OpAssignsToC(st->op)) return -1; @@ -11407,7 +11412,7 @@ pbool QCC_CheckUninitialised(int firststatement, int laststatement) err = QCC_CheckOneUninitialised(firststatement, laststatement, local, local->ofs, local->ofs + local->type->size * (local->arraysize?local->arraysize:1)); if (err > 0) { - QCC_PR_Warning(WARN_UNINITIALIZED, strings+s_file, statements[err].linenum, "Potentially uninitialised variable %s", local->name); + QCC_PR_Warning(WARN_UNINITIALIZED, s_filen, statements[err].linenum, "Potentially uninitialised variable %s", local->name); result = true; // break; } @@ -11448,13 +11453,13 @@ void QCC_Marshal_Locals(int firststatement, int laststatement) //these matter when the function goes recursive (and locals marshalling counts as recursive every time). if (local->symboldata[local->ofs]._int) { - QCC_PR_Note(ERR_INTERNAL, strings+local->s_file, local->s_line, "Marshaling non-const initialised %s", local->name); + QCC_PR_Note(ERR_INTERNAL, local->filen, local->s_line, "Marshaling non-const initialised %s", local->name); error = true; } if (local->constant) { - QCC_PR_Note(ERR_INTERNAL, strings+local->s_file, local->s_line, "Marshaling const %s", local->name); + QCC_PR_Note(ERR_INTERNAL, local->filen, local->s_line, "Marshaling const %s", local->name); error = true; } } @@ -11551,9 +11556,9 @@ void QCC_WriteGUIAsmFunction(QCC_function_t *sc, unsigned int firststatement) } } if (currentsourcefile) - printf("code: %s:%i: %i:%s;\n", sc->file, statements[i].linenum, currentsourcefile, line); + printf("code: %s:%i: %i:%s;\n", sc->filen, statements[i].linenum, currentsourcefile, line); else - printf("code: %s:%i: %s;\n", sc->file, statements[i].linenum, line); + printf("code: %s:%i: %s;\n", sc->filen, statements[i].linenum, line); } } @@ -11649,8 +11654,8 @@ QCC_function_t *QCC_PR_GenerateBuiltinFunction (QCC_def_t *def, int builtinnum) if (numfunctions >= MAX_FUNCTIONS) QCC_PR_ParseError(ERR_INTERNAL, "Too many functions - %i\nAdd \"MAX_FUNCTIONS\" \"%i\" to qcc.cfg", numfunctions, (numfunctions+4096)&~4095); func = &functions[numfunctions++]; - func->s_file = s_file2; - func->file = strings+s_file; + func->filen = s_filen; + func->s_filed = s_filed; func->line = def->s_line; //FIXME func->name = def->name; func->builtin = builtinnum; @@ -11660,14 +11665,32 @@ QCC_function_t *QCC_PR_GenerateBuiltinFunction (QCC_def_t *def, int builtinnum) func->def = def; return func; } -QCC_function_t *QCC_PR_GenerateQCFunction (QCC_def_t *def, QCC_type_t *type) +QCC_function_t *QCC_PR_GenerateQCFunction (QCC_def_t *def, QCC_type_t *type, pbool dowrap) { QCC_function_t *func; if (numfunctions >= MAX_FUNCTIONS) QCC_PR_ParseError(ERR_INTERNAL, "Too many functions - %i\nAdd \"MAX_FUNCTIONS\" \"%i\" to qcc.cfg", numfunctions, (numfunctions+4096)&~4095); - func = &functions[numfunctions++]; - func->s_file = s_file2; - func->file = strings+s_file; + if (dowrap && def->symboldata[0].function) + { + QCC_def_t *locals; + QCC_function_t *prior = &functions[numfunctions++]; + func = &functions[def->symboldata[0].function]; + memcpy(prior, func, sizeof(*prior)); + memset(func, 0, sizeof(*func)); + + for (locals = prior->firstlocal; locals; locals = locals->nextlocal) + { + if (locals->scope != func) + QCC_PR_ParseError(ERR_INTERNAL, "internal consistency check failed while wrapping %s", def->name); + locals->scope = prior; + } + } + else if (dowrap) + QCC_PR_ParseError(ERR_INTERNAL, "cannot wrap bodyless function %s", def->name); + else + func = &functions[numfunctions++]; + func->filen = s_filen; + func->s_filed = s_filed; func->line = pr_source_line;//def?def->s_line:0; //FIXME func->name = def?def->name:""; func->builtin = 0; @@ -11688,13 +11711,15 @@ Parse a function body If def is set, allows stuff to refer back to a def for the function. ============ */ -QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *type) +QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *type, pbool dowrap) { unsigned int u, p; QCC_function_t *f; QCC_sref_t parm; pbool needsdone=false; + QCC_def_t *prior = NULL; + conditional = 0; expandedemptymacro = false; @@ -11735,7 +11760,7 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *typ // if (type->vargs) // QCC_PR_ParseError (ERR_FUNCTIONWITHVARGS, "QC function with variable arguments and function body"); - pr_scope = f = QCC_PR_GenerateQCFunction(def, type); + pr_scope = f = QCC_PR_GenerateQCFunction(def, type, dowrap); //reset the locals chain pr.local_head.nextlocal = NULL; @@ -11799,6 +11824,20 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *typ QCC_FreeTemp(parm); } + if (dowrap) + { //if we're wrapping, then we moved the old function entry to the end and reused it for our function. + //so we need to define some local that refers to the prior def. + QCC_sref_t priorim = QCC_MakeIntConst(numfunctions-1); + prior; + prior = QCC_PR_DummyDef(f->type, "prior", f, 0, priorim.sym, 0, true, GDF_CONST); //create a union into it + prior->initialized = true; + prior->filen = functions[numfunctions-1].filen; + prior->s_filed = functions[numfunctions-1].s_filed; + prior->s_line = functions[numfunctions-1].line; + + QCC_FreeTemp(priorim); + } + if (type->vargcount) { if (!pr_parm_argcount_name) @@ -11899,6 +11938,9 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *typ } QCC_FreeTemps(); + if (prior && !prior->referenced) + QCC_PR_ParseError(ERR_REDECLARATION, "Wrapper function does not refer to its prior function"); + pr_token_line_last = pr_token_line; // this is cheap @@ -12089,7 +12131,8 @@ QCC_def_t *QCC_PR_EmitArrayGetVector(QCC_sref_t array) numslots = array.sym->arraysize*array.cast->size; numslots = (numslots+2)/3; - s_file = array.sym->s_file; + s_filen = array.sym->filen; + s_filed = array.sym->s_filed; func = QCC_PR_GetDef(ftype, qcva("ArrayGetVec*%s", array.sym->name), NULL, true, 0, false); pr_source_line = pr_token_line_last = array.sym->s_line; //thankfully these functions are emitted after compilation. @@ -12097,9 +12140,10 @@ QCC_def_t *QCC_PR_EmitArrayGetVector(QCC_sref_t array) if (numfunctions >= MAX_FUNCTIONS) QCC_Error(ERR_INTERNAL, "Too many function defs"); - pr_scope = QCC_PR_GenerateQCFunction(func, ftype); + pr_scope = QCC_PR_GenerateQCFunction(func, ftype, false); pr_source_line = pr_token_line_last = pr_scope->line = array.sym->s_line; //thankfully these functions are emitted after compilation. - pr_scope->s_file = array.sym->s_file; + pr_scope->filen = array.sym->filen; + pr_scope->s_filed = array.sym->s_filed; func->symboldata[func->ofs]._int = pr_scope - functions; index = QCC_PR_GetSRef(type_float, "index___", pr_scope, true, 0, false); @@ -12143,7 +12187,8 @@ void QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, QCC_def_t *arraydef, char *ar // if (flag_fasttrackarrays && numslots > 6) // fasttrackpossible = QCC_PR_GetSRef(type_float, "__ext__fasttrackarrays", NULL, true, 0, false); - s_file = scope->s_file; + s_filen = scope->filen; + s_filed = scope->s_filed; vectortrick = nullsref; // if (numslots >= 15 && thearray.cast->type != ev_vector) @@ -12152,9 +12197,10 @@ void QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, QCC_def_t *arraydef, char *ar // vectortrick.cast = vectortrick.sym->type; // } - pr_scope = QCC_PR_GenerateQCFunction(scope, scope->type); + pr_scope = QCC_PR_GenerateQCFunction(scope, scope->type, false); pr_source_line = pr_token_line_last = pr_scope->line = thearray.sym->s_line; //thankfully these functions are emitted after compilation. - pr_scope->s_file = thearray.sym->s_file; + pr_scope->filen = thearray.sym->filen; + pr_scope->s_filed = thearray.sym->s_filed; index = QCC_PR_GetSRef(type_float, "__indexg", pr_scope, true, 0, false); @@ -12334,10 +12380,12 @@ void QCC_PR_EmitArraySetFunction(QCC_def_t *scope, QCC_def_t *arraydef, char *ar if (numfunctions >= MAX_FUNCTIONS) QCC_Error(ERR_INTERNAL, "Too many function defs"); - s_file = arraydef->s_file; - pr_scope = QCC_PR_GenerateQCFunction(scope, scope->type); + s_filen = arraydef->filen; + s_filed = arraydef->s_filed; + pr_scope = QCC_PR_GenerateQCFunction(scope, scope->type, false); pr_source_line = pr_token_line_last = pr_scope->line = thearray.sym->s_line; //thankfully these functions are emitted after compilation. - pr_scope->s_file = thearray.sym->s_file; + pr_scope->filen = thearray.sym->filen; + pr_scope->s_filed = thearray.sym->s_filed; index = QCC_PR_GetSRef(type_float, "indexs___", pr_scope, true, 0, false); value = QCC_PR_GetSRef(thearray.cast, "value___", pr_scope, true, 0, false); @@ -12467,7 +12515,8 @@ QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_function_t *scope, } def->s_line = pr_source_line; - def->s_file = s_file; + def->filen = s_filen; + def->s_filed = s_filed; if (a>=0) def->initialized = 1; @@ -12517,7 +12566,7 @@ QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_function_t *scope, } def->symbolheader = rootsymbol; - def->symboldata = rootsymbol->symboldata; + def->symboldata = rootsymbol->symboldata + def->ofs; def->symbolsize = (def->arraysize?def->arraysize:1) * type->size; if (type->type == ev_struct && (!arraysize || a>=0)) @@ -12732,7 +12781,7 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, struct QCC_function_s *s } //ignore it if its static in some other file. - if (def->isstatic && strcmp(strings+def->s_file, strings+s_file)) + if (def->isstatic && strcmp(def->filen, s_filen)) { if (!foundstatic) foundstatic = def; //save it off purely as a warning. @@ -12740,24 +12789,56 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, struct QCC_function_s *s continue; // in a different function } + if (def->assumedtype && !(flags & GDF_BASICTYPE)) + { + if (allocate) + { //if we're asserting a type for it in some def then it'll no longer be assumed. + if (def->type->type != type->type) + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, "Type mismatch on redeclaration of %s. Basic types are different.",name); + def->type = type; + def->assumedtype = false; + def->filen = s_filen; + def->s_line = pr_source_line; + if (flags & GDF_CONST) + def->constant = true; + } + else + { //we know enough of its type to write it out, but not enough to actually use it safely, so pretend this def isn't defined yet. + def = pHash_GetNext(&globalstable, name, def); + continue; + } + } + if (type && typecmp(def->type, type)) { if (pr_scope || typecmp_lax(def->type, type)) { - if (!strcmp("droptofloor", def->name) || //vanilla + if (!pr_scope && ( + !strcmp("droptofloor", def->name) || //vanilla !strcmp("callfunction", def->name) || //should be (..., string name) but dpextensions gets this wrong. !strcmp("trailparticles", def->name) //dp got the two arguments the wrong way. fteqw doesn't care any more, but dp is still wrong. - ) + )) { //this is a hack. droptofloor was wrongly declared in vanilla qc, which causes problems with replacement extensions.qc. //yes, this is a selfish lazy hack for this, there's probably a better way, but at least we spit out a warning still. - QCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, "%s builtin was wrongly defined as %s. ignoring invalid dupe definition",name, TypeName(type, typebuf1, sizeof(typebuf1))); + QCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, "%s builtin was wrongly defined as %s. ignoring later definition",name, TypeName(type, typebuf1, sizeof(typebuf1))); QCC_PR_ParsePrintDef(WARN_COMPATIBILITYHACK, def); } else { - //unequal even when we're lax - QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, "Type mismatch on redeclaration of %s. %s, should be %s",name, TypeName(type, typebuf1, sizeof(typebuf1)), TypeName(def->type, typebuf2, sizeof(typebuf2))); + int flen = strlen(s_filen); + if (!pr_scope && flen >= 13 && !QC_strcasecmp(s_filen+flen-13, "extensions.qc") && def->type->type == ev_function) + { + //this is a hack. droptofloor was wrongly declared in vanilla qc, which causes problems with replacement extensions.qc. + //yes, this is a selfish lazy hack for this, there's probably a better way, but at least we spit out a warning still. + QCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, "%s builtin was redefined as %s. ignoring alternative definition",name, TypeName(type, typebuf1, sizeof(typebuf1))); + QCC_PR_ParsePrintDef(WARN_COMPATIBILITYHACK, def); + } + else + { + //unequal even when we're lax + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, "Type mismatch on redeclaration of %s. %s, should be %s",name, TypeName(type, typebuf1, sizeof(typebuf1)), TypeName(def->type, typebuf2, sizeof(typebuf2))); + } } } else @@ -12919,7 +13000,7 @@ QCC_def_t *QCC_PR_DummyFieldDef(QCC_type_t *type, QCC_function_t *scope, int arr def = QCC_PR_GetDef(ftype, newname, scope, true, 0, saved); if (parttype->type == ev_function) def->initialized = true; - def->symboldata->_int = *fieldofs; + def->symboldata[def->ofs]._int = *fieldofs; *fieldofs += parttype->size; } else @@ -12954,7 +13035,9 @@ void QCC_PR_ExpandUnionToFields(QCC_type_t *type, unsigned int *fields) QCC_PR_DummyFieldDef(pass, pr_scope, 1, fields, GDF_SAVED|GDF_CONST); } -void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t def) +#define PIF_WRAP 1 //new initialisation is meant to wrap an existing one. +#define PIF_STRONGER 2 //previous initialisation was weak. +void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t def, unsigned int flags) { QCC_sref_t tmp; int i; @@ -12967,7 +13050,7 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d { for (i = 0; i < arraysize; i++) { - QCC_PR_ParseInitializerType(0, basedef, def); + QCC_PR_ParseInitializerType(0, basedef, def, flags); def.ofs += def.cast->size; if (!QCC_PR_CheckToken(",")) { @@ -13020,10 +13103,18 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d { for (i = 1; i < numfunctions; i++) { - if (!strcmp(functions[i].name, fname) && functions[i].code == -1 && functions[i].builtin == binum) + if (functions[i].code == -1 && functions[i].builtin == binum) { - tmp = QCC_MakeIntConst(i); - break; + if (!*functions[i].name) + { + functions[i].name = qccHunkAlloc(strlen(fname)+1); + strcpy(functions[i].name, fname); + } + if (!strcmp(functions[i].name, fname)) + { + tmp = QCC_MakeIntConst(i); + break; + } } } } @@ -13031,10 +13122,15 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d { for (i = 1; i < numfunctions; i++) { - if (!strcmp(functions[i].name, defname) && functions[i].code == -1 && functions[i].builtin == binum) + if (functions[i].code == -1 && functions[i].builtin == binum) { - tmp = QCC_MakeIntConst(i); - break; + if (!*functions[i].name) + functions[i].name = (char*)defname; + if (!strcmp(functions[i].name, defname)) + { + tmp = QCC_MakeIntConst(i); + break; + } } } } @@ -13047,14 +13143,19 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d } else { - if (basedef->initialized == 1) + if (flags&PIF_WRAP) + { + if (!basedef->initialized || !def.sym->symboldata[def.ofs]._int) + QCC_PR_ParseErrorPrintSRef (ERR_REDECLARATION, def, "wrapper function does not wrap anything"); + } + else if (basedef->initialized == 1 && !(flags & PIF_STRONGER)) { //normally this is an error, but to aid supporting new stuff with old, we convert it into a warning if a vanilla(ish) qc function replaces extension builtins. //the qc function is the one that is used, but there is a warning so you know how to gain efficiency. int bi = -1; if (def.cast->type == ev_function && !arraysize) { - if (!strcmp(defname, "anglemod")) + if (!strcmp(defname, "anglemod") || !strcmp(defname, "crossproduct")) bi = def.sym->symboldata[def.ofs]._int; } if (bi <= 0 || bi >= numfunctions) @@ -13063,12 +13164,13 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d bi = functions[bi].code; if (bi < 0) { - QCC_PR_ParseWarning(WARN_NOTSTANDARDBEHAVIOUR, "%s already declared as builtin", defname); + QCC_PR_ParseWarning(WARN_NOTSTANDARDBEHAVIOUR, "%s already declared as a builtin", defname); QCC_PR_ParsePrintSRef(WARN_NOTSTANDARDBEHAVIOUR, def); basedef->initialized = 3; } else QCC_PR_ParseErrorPrintSRef (ERR_REDECLARATION, def, "redeclaration of function body"); + } if (pr_scope) @@ -13084,7 +13186,7 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d //generate a goto statement around the nested function, so that nothing is hurt. patch = QCC_Generate_OP_GOTO(); - f = QCC_PR_ParseImmediateStatements (NULL, type); + f = QCC_PR_ParseImmediateStatements (NULL, type, flags&PIF_WRAP); patch->a.ofs = &statements[numstatements] - patch; //make sure parent state is restored properly. @@ -13093,7 +13195,7 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d pr_scope = parent; } else - f = QCC_PR_ParseImmediateStatements (def.sym, type); + f = QCC_PR_ParseImmediateStatements (def.sym, type, flags&PIF_WRAP); //allow dupes if its a builtin if (!f->code && basedef->initialized) @@ -13158,7 +13260,7 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d def.cast = (type)->params[partnum].type; def.ofs = offset + (type)->params[partnum].ofs; - QCC_PR_ParseInitializerType((type)->params[partnum].arraysize, basedef, def); + QCC_PR_ParseInitializerType((type)->params[partnum].arraysize, basedef, def, flags); if (isunion || !QCC_PR_CheckToken(",")) { QCC_PR_Expect("}"); @@ -13189,7 +13291,7 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d QCC_PR_ParsePrintSRef(WARN_UNINITIALIZED, tmp); } - if (basedef->initialized && basedef->initialized != 3) + if (basedef->initialized && basedef->initialized != 3 && !(flags & PIF_STRONGER)) { for (i = 0; (unsigned)i < type->size; i++) if (def.sym->symboldata[def.ofs+i]._int != tmp.sym->symboldata[tmp.ofs+i]._int) @@ -13201,6 +13303,11 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d QCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, "incompatible redeclaration. Please validate builtin numbers. parseentitydata is #613"); QCC_PR_ParsePrintSRef(WARN_COMPATIBILITYHACK, tmp); } + else if (!def.sym->arraysize && def.cast->type == ev_function && functions[def.sym->symboldata[def.ofs+i]._int].code>-1 && functions[tmp.sym->symboldata[tmp.ofs+i]._int].code==-1) + { + QCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, "incompatible redeclaration. Ignoring replacement of qc function with builtin."); + QCC_PR_ParsePrintSRef(WARN_COMPATIBILITYHACK, tmp); + } else QCC_PR_ParseErrorPrintSRef (ERR_REDECLARATION, def, "incompatible redeclaration"); } @@ -13300,9 +13407,9 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d } } -void QCC_PR_ParseInitializerDef(QCC_def_t *def) +void QCC_PR_ParseInitializerDef(QCC_def_t *def, unsigned int flags) { - QCC_PR_ParseInitializerType(def->arraysize, def, QCC_MakeSRef(def, def->ofs, def->type)); + QCC_PR_ParseInitializerType(def->arraysize, def, QCC_MakeSRef(def, 0, def->type), flags); if (!def->initialized || def->initialized == 3) def->initialized = 1; } @@ -13431,6 +13538,8 @@ void QCC_PR_ParseDefs (char *classname) pbool inlinefunction = false; pbool allowinline = false; pbool dostrip = false; + pbool dowrap = false; + pbool doweak = false; pbool forceused = false; int arraysize; unsigned int gd_flags; @@ -13666,6 +13775,10 @@ void QCC_PR_ParseDefs (char *classname) allowinline = true; else if (QCC_PR_CheckKeyword(keyword_ignore, "ignore")) dostrip = true; + else if (QCC_PR_CheckKeyword(keyword_wrap, "wrap")) + dowrap = true; + else if (QCC_PR_CheckKeyword(keyword_weak, "weak")) + doweak = true; else break; } @@ -13732,7 +13845,7 @@ void QCC_PR_ParseDefs (char *classname) { def->referenced = true; - f = QCC_PR_ParseImmediateStatements (def, type); + f = QCC_PR_ParseImmediateStatements (def, type, false); def->initialized = 1; def->isstatic = isstatic; @@ -13943,7 +14056,7 @@ void QCC_PR_ParseDefs (char *classname) if (isstatic) { - if (!strcmp(strings+def->s_file, strings+s_file)) + if (!strcmp(def->filen, s_filen)) def->isstatic = isstatic; else //if (type->type != ev_function && defaultstatic) //functions don't quite consitiute a definition QCC_PR_ParseErrorPrintDef (ERR_REDECLARATION, def, "can't redefine non-static as static"); @@ -13978,21 +14091,11 @@ void QCC_PR_ParseDefs (char *classname) QCC_type_t *parentclass; if (def->shared) QCC_PR_ParseError (ERR_SHAREDINITIALISED, "shared values may not be assigned an initial value", name); - if (def->initialized == 1) - { -// if (def->type->type == ev_function) -// { -// i = G_FUNCTION(def->ofs); -// df = &functions[i]; -// QCC_PR_ParseErrorPrintDef (ERR_REDECLARATION, def, "%s redeclared, prev instance is in %s", name, strings+df->s_file); -// } -// else -// QCC_PR_ParseErrorPrintDef(ERR_REDECLARATION, def, "%s redeclared", name); - } - if (autoprototype || dostrip) + //if weak, only use the first non-weak version of the function + if (autoprototype || dostrip || (def->initialized && doweak) || (!def->initialized && doweak && dowrap)) { //ignore the code and stuff - if (dostrip && !def->initialized) + if ((dostrip || doweak && dowrap) && !def->initialized) def->initialized = 3; if (dostrip) def->referenced = true; @@ -14045,7 +14148,11 @@ void QCC_PR_ParseDefs (char *classname) parentclass = pr_classtype; pr_classtype = defclass?defclass:pr_classtype; def->constant = (isconstant || (!isvar && !pr_scope)); - QCC_PR_ParseInitializerDef(def); + QCC_PR_ParseInitializerDef(def, (dowrap?PIF_WRAP:0)|(def->weak?PIF_STRONGER:0)); + if (doweak) + def->weak = true; + else + def->weak = false; QCC_FreeDef(def); pr_classtype = parentclass; } @@ -14141,6 +14248,7 @@ compiles the 0 terminated text, adding defintions to the pr structure */ pbool QCC_PR_CompileFile (char *string, char *filename) { + char *tmp; jmp_buf oldjb; if (!pr.memory) QCC_Error (ERR_INTERNAL, "PR_CompileFile: Didn't clear"); @@ -14149,18 +14257,16 @@ pbool QCC_PR_CompileFile (char *string, char *filename) compilingfile = filename; + s_filen = tmp = qccHunkAlloc(strlen(filename)+1); + strcpy(tmp, filename); if (opt_filenames) { optres_filenames += strlen(filename); - pr_file_p = qccHunkAlloc(strlen(filename)+1); - strcpy(pr_file_p, filename); - s_file = pr_file_p - strings; - s_file2 = 0; + s_filed = 0; } else - { - s_file = s_file2 = QCC_CopyString (filename); - } + s_filed = QCC_CopyString (filename); + pr_file_p = string; pr_assumetermtype = NULL; @@ -14257,14 +14363,15 @@ pbool QCC_Include(char *filename) char *newfile; char fname[512]; char *opr_file_p; - QCC_string_t os_file, os_file2; + const char *os_filen; + QCC_string_t os_filed; int opr_source_line; char *ocompilingfile; struct qcc_includechunk_s *oldcurrentchunk; ocompilingfile = compilingfile; - os_file = s_file; - os_file2 = s_file2; + os_filen = s_filen; + os_filed = s_filed; opr_source_line = pr_source_line; opr_file_p = pr_file_p; oldcurrentchunk = currentchunk; @@ -14277,8 +14384,8 @@ pbool QCC_Include(char *filename) currentchunk = oldcurrentchunk; compilingfile = ocompilingfile; - s_file = os_file; - s_file2 = os_file2; + s_filen = os_filen; + s_filed = os_filed; pr_source_line = opr_source_line; pr_file_p = opr_file_p; diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index d791d1de6..91ddb4aa2 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -113,12 +113,21 @@ void QCC_PR_CloseProcessor(void) void QCC_PR_AddIncludePath(const char *newinc) { int i; + + if (!*newinc) + { + QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Invalid include path."); + return; + } for (i = 0; i < MAXINCLUDEDIRS; i++) { if (!*qccincludedir[i]) { + const char *e = newinc + strlen(newinc)-1; QC_strlcpy(qccincludedir[i], newinc, sizeof(qccincludedir)); + if (*e != '/' && *e != '\\') + QC_strlcat(qccincludedir[i], "/", sizeof(qccincludedir)); break; } if (!strcmp(qccincludedir[i], newinc)) @@ -126,13 +135,10 @@ void QCC_PR_AddIncludePath(const char *newinc) } if (i == MAXINCLUDEDIRS) { - if (!s_file) - QCC_PR_Warning(WARN_STRINGTOOLONG, "cmdline", 0, "Too many include dirs. Ignoring and hoping the stars align."); - else - QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Too many include dirs. Ignoring and hoping the stars align."); + QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Too many include dirs. Ignoring and hoping the stars align."); } } -void QCC_PR_IncludeChunkEx (char *data, pbool duplicate, char *filename, CompilerConstant_t *cnst) +static void QCC_PR_IncludeChunkEx (char *data, pbool duplicate, char *filename, CompilerConstant_t *cnst) { qcc_includechunk_t *chunk = qccHunkAlloc(sizeof(qcc_includechunk_t)); chunk->prev = currentchunk; @@ -181,7 +187,7 @@ pbool QCC_PR_UnInclude(void) PR_PrintNextLine ============== */ -void QCC_PR_PrintNextLine (void) +static void QCC_PR_PrintNextLine (void) { char *t; @@ -191,7 +197,7 @@ void QCC_PR_PrintNextLine (void) printf ("\n"); } -void QCC_Canonicalize(char *fullname, size_t fullnamesize, char *newfile, char *base) +static void QCC_Canonicalize(char *fullname, size_t fullnamesize, char *newfile, char *base) { int doubledots; char *end = fullname; @@ -269,10 +275,20 @@ void QCC_FindBestInclude(char *newfile, char *currentfile, pbool verbose) if (verbose) { - if (autoprototype) - printf("prototyping include %s\n", fullname); + if (verbose == 2) + { + if (autoprototype) + printf("prototyping %s\n", fullname); + else + printf("compiling %s\n", fullname); + } else - printf("including %s\n", fullname); + { + if (autoprototype) + printf("prototyping include %s\n", fullname); + else + printf("including %s\n", fullname); + } } QCC_Include(fullname); } @@ -285,6 +301,7 @@ int QCC_PR_LexInteger (void); void QCC_AddFile (char *filename); void QCC_PR_LexString (void); pbool QCC_PR_SimpleGetToken (void); +pbool QCC_PR_SimpleGetString(void); #define PPI_VALUE 0 #define PPI_NOT 1 @@ -800,9 +817,9 @@ pbool QCC_PR_Precompiler(void) msg[a] = '\0'; if (flag_msvcstyle) - printf ("%s(%i) : #message: %s\n", strings + s_file, pr_source_line, msg); + printf ("%s(%i) : #message: %s\n", s_filen, pr_source_line, msg); else - printf ("%s:%i: #message: %s\n", strings + s_file, pr_source_line, msg); + printf ("%s:%i: #message: %s\n", s_filen, pr_source_line, msg); QCC_PR_SkipToEndOfLine(false); } else if (!strncmp(directive, "copyright", 9)) @@ -852,6 +869,25 @@ pbool QCC_PR_Precompiler(void) QCC_PR_SkipToEndOfLine(true); } + else if (!strncmp(directive, "merge", 5)) + { + extern char destfile[1024]; + pr_file_p=directive+5; + + while(qcc_iswhitesameline(*pr_file_p)) + pr_file_p++; + + QCC_PR_SimpleGetString(); + printf("Merging to %s\n", pr_token); + QCC_ImportProgs(pr_token); + if (!*destfile) + { + QCC_Canonicalize(destfile, sizeof(destfile), pr_token, compilingfile); + printf("Outputfile: %s\n", destfile); + } + + QCC_PR_SkipToEndOfLine(true); + } else if (!strncmp(directive, "includelist", 11)) { int defines=0; @@ -946,7 +982,7 @@ pbool QCC_PR_Precompiler(void) while(qcc_iswhitesameline(*pr_file_p)) pr_file_p++; - QCC_PR_LexString(); + QCC_PR_SimpleGetString(); printf("Including datafile: %s\n", pr_token); QCC_AddFile(pr_token); @@ -970,21 +1006,11 @@ pbool QCC_PR_Precompiler(void) while(qcc_iswhitesameline(*pr_file_p)) pr_file_p++; - QCC_PR_LexString(); - strcpy(destfile, pr_token); - printf("Outputfile: %s\n", destfile); + QCC_PR_SimpleGetString(); + QCC_Canonicalize(destfile, sizeof(destfile), pr_token, compilingfile); + printf("Outputfile: %s\n", pr_token); - pr_file_p++; - - for (a = 0; a < sizeof(msg)-1 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) - msg[a] = pr_file_p[a]; - - msg[a-1] = '\0'; - - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } + QCC_PR_SkipToEndOfLine(true); } else if (!strncmp(directive, "pragma", 6)) { @@ -2327,6 +2353,83 @@ void QCC_PR_ExpandMacro(void) pr_immediate._float = (float)i; } +pbool QCC_PR_SimpleGetString(void) +{ + int c; + int i = 0; + char *f; + + pr_token[0] = 0; + +// skip whitespace + while ((c = *pr_file_p) && qcc_iswhite(c)) + { + if (c=='\n') + return false; + pr_file_p++; + } + if (c == 0) //eof + return false; +//abort if there's a comment. + if (pr_file_p[0] == '/') + { + if (pr_file_p[1] == '/') + { //comment alert + while(*pr_file_p && *pr_file_p != '\n') + pr_file_p++; + return false; + } + if (pr_file_p[1] == '*') + return false; + } + + if (*pr_file_p != '\"') + return false; //nope, not a string. + f = pr_file_p+1; + while (*f) + { + if (*f == '\n' || !*f) + { //bad string + QCC_Error (ERR_INTERNAL, "new line inside string"); + pr_token[0] = 0; + return false; + } + if (*f == '\"') + { //end-of-string + pr_token[i] = 0; + pr_file_p = f+1; + return false; + } + if (i == sizeof(qcc_token)-1) + QCC_Error (ERR_INTERNAL, "token exceeds %i chars", i); + if (*f == '\\') + { + f++; + if (!*f) + f = ""; + else if (*f == 'n') + { + pr_token[i++] = '\n'; + f++; + } + else if (*f == 'r') + { + pr_token[i++] = '\r'; + f++; + } + else if (*f == 't') + { + pr_token[i++] = '\t'; + f++; + } + else + pr_token[i++] = *f++; + } + else + pr_token[i++] = *f++; + } + return true; +} // just parses text, returning false if an eol is reached pbool QCC_PR_SimpleGetToken (void) { @@ -2872,7 +2975,7 @@ static char *QCC_PR_CheckBuiltinCompConst(char *constname, char *retbuf, size_t } if (!strcmp(constname, "__FILE__")) { - QC_snprintfz(retbuf, retbufsize, "\"%s\"", strings + s_file); + QC_snprintfz(retbuf, retbufsize, "\"%s\"", s_filen); return retbuf; } if (!strcmp(constname, "__LINE__")) @@ -3165,9 +3268,9 @@ int QCC_PR_CheckCompConst(void) if (flag_debugmacros) { if (flag_msvcstyle) - printf ("%s(%i) : macro %s: %s\n", strings+s_file, pr_source_line, c->name, pr_file_p); + printf ("%s(%i) : macro %s: %s\n", s_filen, pr_source_line, c->name, pr_file_p); else - printf ("%s:%i: macro %s: %s\n", strings+s_file, pr_source_line, c->name, pr_file_p); + printf ("%s:%i: macro %s: %s\n", s_filen, pr_source_line, c->name, pr_file_p); } } else @@ -3399,14 +3502,14 @@ void QCC_PR_ParsePrintDef (int type, QCC_def_t *def) { if (!qccwarningaction[type]) return; - if (def->s_file) + if (def->filen) { char tybuffer[512]; char tmbuffer[512]; char *modifiers; if (QCC_Temp_Describe(def, tmbuffer, sizeof(tmbuffer))) { - printf ("%s:%i: (%s)(%s)\n", strings + def->s_file, def->s_line, TypeName(def->type, tybuffer, sizeof(tybuffer)), tmbuffer); + printf ("%s:%i: (%s)(%s)\n", def->filen, def->s_line, TypeName(def->type, tybuffer, sizeof(tybuffer)), tmbuffer); } else { @@ -3416,9 +3519,9 @@ void QCC_PR_ParsePrintDef (int type, QCC_def_t *def) else if (def->isstatic) modifiers = "static "; if (flag_msvcstyle) - printf ("%s(%i) : %s%s %s is defined here\n", strings + def->s_file, def->s_line, modifiers, TypeName(def->type, tybuffer, sizeof(tybuffer)), def->name); + printf ("%s(%i) : %s%s %s is defined here\n", def->filen, def->s_line, modifiers, TypeName(def->type, tybuffer, sizeof(tybuffer)), def->name); else - printf ("%s:%i: %s%s %s is defined here\n", strings + def->s_file, def->s_line, modifiers, TypeName(def->type, tybuffer, sizeof(tybuffer)), def->name); + printf ("%s:%i: %s%s %s is defined here\n", def->filen, def->s_line, modifiers, TypeName(def->type, tybuffer, sizeof(tybuffer)), def->name); } } } @@ -3455,7 +3558,7 @@ Aborts the current file load ============ */ #ifndef QCC -void editbadfile(char *file, int line); +void editbadfile(const char *file, int line); #endif //will abort. void VARGS QCC_PR_ParseError (int errortype, const char *error, ...) @@ -3468,14 +3571,14 @@ void VARGS QCC_PR_ParseError (int errortype, const char *error, ...) va_end (argptr); #ifndef QCC - editbadfile(strings+s_file, pr_source_line); + editbadfile(s_filen, pr_source_line); #endif QCC_PR_PrintScope(); if (flag_msvcstyle) - printf ("%s(%i) : error: %s\n", strings + s_file, pr_source_line, string); + printf ("%s(%i) : error: %s\n", s_filen, pr_source_line, string); else - printf ("%s:%i: error: %s\n", strings + s_file, pr_source_line, string); + printf ("%s:%i: error: %s\n", s_filen, pr_source_line, string); longjmp (pr_parse_abort, 1); } @@ -3490,13 +3593,13 @@ void VARGS QCC_PR_ParseErrorPrintDef (int errortype, QCC_def_t *def, const char va_end (argptr); #ifndef QCC - editbadfile(strings+s_file, pr_source_line); + editbadfile(s_filen, pr_source_line); #endif QCC_PR_PrintScope(); if (flag_msvcstyle) - printf ("%s(%i) : error: %s\n", strings + s_file, pr_source_line, string); + printf ("%s(%i) : error: %s\n", s_filen, pr_source_line, string); else - printf ("%s:%i: error: %s\n", strings + s_file, pr_source_line, string); + printf ("%s:%i: error: %s\n", s_filen, pr_source_line, string); QCC_PR_ParsePrintDef(WARN_ERROR, def); @@ -3513,13 +3616,13 @@ void VARGS QCC_PR_ParseErrorPrintSRef (int errortype, QCC_sref_t def, const char va_end (argptr); #ifndef QCC - editbadfile(strings+s_file, pr_source_line); + editbadfile(s_filen, pr_source_line); #endif QCC_PR_PrintScope(); if (flag_msvcstyle) - printf ("%s(%i) : error: %s\n", strings + s_file, pr_source_line, string); + printf ("%s(%i) : error: %s\n", s_filen, pr_source_line, string); else - printf ("%s:%i: error: %s\n", strings + s_file, pr_source_line, string); + printf ("%s:%i: error: %s\n", s_filen, pr_source_line, string); QCC_PR_ParsePrintSRef(WARN_ERROR, def); @@ -3552,7 +3655,7 @@ pbool VARGS QCC_PR_PrintWarning (int type, const char *file, int line, const cha if (!string) ; else if (!file || !*file) - printf (": werror%s: %s\n", wnam, string); + printf (":: werror%s: %s\n", wnam, string); else if (flag_msvcstyle) printf ("%s(%i) : werror%s: %s\n", file, line, wnam, string); else @@ -3564,7 +3667,7 @@ pbool VARGS QCC_PR_PrintWarning (int type, const char *file, int line, const cha if (!string) ; else if (!file || !*file) - printf (": warning%s: %s\n", wnam, string); + printf (":: warning%s: %s\n", wnam, string); else if (flag_msvcstyle) printf ("%s(%i) : warning%s: %s\n", file, line, wnam, string); else @@ -3604,7 +3707,7 @@ pbool VARGS QCC_PR_ParseWarning (int type, const char *error, ...) QC_vsnprintf (string,sizeof(string)-1, error,argptr); va_end (argptr); - return QCC_PR_PrintWarning(type, strings + s_file, pr_source_line, string); + return QCC_PR_PrintWarning(type, s_filen, pr_source_line, string); } void VARGS QCC_PR_Note (int type, const char *file, int line, const char *error, ...) @@ -4607,8 +4710,10 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) { if (!qcc_typeinfo[i].typedefed) continue; - if (STRCMP(qcc_typeinfo[i].name, accessorname) == 0 && qcc_typeinfo[i].type == ev_accessor) + if (STRCMP(qcc_typeinfo[i].name, accessorname) == 0) { + if (qcc_typeinfo[i].type != ev_accessor) + QCC_PR_ParseError(ERR_NOTANAME, "Type %s cannot be redefined as an accessor", accessorname); newt = &qcc_typeinfo[i]; break; } diff --git a/engine/qclib/qccgui.c b/engine/qclib/qccgui.c index f3e0204f9..38b3c1b15 100644 --- a/engine/qclib/qccgui.c +++ b/engine/qclib/qccgui.c @@ -321,6 +321,57 @@ static pbool QCC_RegSetValue(HKEY base, char *keyname, char *valuename, int type } */ +typedef struct vfile_s +{ //when originally running from a .dat, we load up all the functions and work from those rather than actual files. + //(these get re-written into the resulting .dat) + struct vfile_s *next; + void *fdata; + size_t fsize; + char name[1]; +} vfile_t; +static vfile_t *qcc_vfiles; +vfile_t *QCC_FindVFile(const char *name) +{ + vfile_t *f; + for (f = qcc_vfiles; f; f = f->next) + { + if (!strcmp(f->name, name)) + return f; + } + //give it another go, for case + for (f = qcc_vfiles; f; f = f->next) + { + if (!QC_strcasecmp(f->name, name)) + return f; + } + return NULL; +} +pbool QCC_AddVFile(const char *name, void *data, size_t size) +{ + vfile_t *f = QCC_FindVFile(name); + if (!f) + { + f = malloc(sizeof(vfile_t) + strlen(name)); + f->next = qcc_vfiles; + strcpy(f->name, name); + qcc_vfiles = f; + } + else + free(f->fdata); + f->fdata = malloc(size); + memcpy(f->fdata, data, size); + f->fsize = size; + return true; +} + +void QCC_EnumerateFilesResult(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize) +{ + void *buffer = malloc(plainsize); + if (QC_decode(NULL, compsize, plainsize, method, compdata, buffer)) + QCC_AddVFile(name, buffer, plainsize); + free(buffer); +} + /* ============== LoadFile @@ -330,6 +381,17 @@ unsigned char *PDECL QCC_ReadFile (const char *fname, void *buffer, int len, siz { long length; FILE *f; + vfile_t *v = QCC_FindVFile(fname); + if (v) + { + if (len > v->fsize) + len = v->fsize; + memcpy(buffer, v->fdata, len); + if (sz) + *sz = len; + return buffer; + } + f = fopen(fname, "rb"); if (!f) return NULL; @@ -347,6 +409,11 @@ int PDECL QCC_RawFileSize (const char *fname) { long length; FILE *f; + + vfile_t *v = QCC_FindVFile(fname); + if (v) + return v->fsize; + f = fopen(fname, "rb"); if (!f) return -1; @@ -499,7 +566,10 @@ pbool PDECL QCC_WriteFile (const char *name, void *data, int len) return false; #endif } - + + if (QCC_FindVFile(name)) + return QCC_AddVFile(name, data, len); + f = fopen(name, "wb"); if (!f) return false; @@ -1241,7 +1311,7 @@ enum { static void EditorReload(editor_t *editor); int EditorSave(editor_t *edit); -void EditFile(char *name, int line, pbool setcontrol); +void EditFile(const char *name, int line, pbool setcontrol); pbool EditorModified(editor_t *e); void QueryOpenFile(void) @@ -1786,7 +1856,7 @@ char *GetTooltipText(editor_t *editor, int pos, pbool dwell) { if (line > functions[fno].line && bestline < functions[fno].line) { - if (!strcmp(editor->filename, functions[fno].file)) + if (!strcmp(editor->filename, functions[fno].filen)) { best = fno; bestline = functions[fno].line; @@ -2046,7 +2116,9 @@ static void UpdateEditorTitle(editor_t *editor) encoding = "unknown"; break; } - if (editor->modified) + if (QCC_FindVFile(editor->filename)) + sprintf(title, "%s:%i - Virtual", editor->filename, 1+editor->curline); + else if (editor->modified) sprintf(title, "*%s:%i - %s", editor->filename, 1+editor->curline, encoding); else sprintf(title, "%s:%i - %s", editor->filename, 1+editor->curline, encoding); @@ -2386,7 +2458,7 @@ static void EditorReload(editor_t *editor) } //line is 0-based. use -1 for no reselection -void EditFile(char *name, int line, pbool setcontrol) +void EditFile(const char *name, int line, pbool setcontrol) { char title[1024]; editor_t *neweditor; @@ -2714,7 +2786,7 @@ unsigned char *GUIReadFile(const char *fname, void *buffer, int blen, size_t *sz //our qcc itself is fine with utf-16, so long as it has a BOM. if (e->scintilla) { - SendMessage(e->editpane, SCI_GETTEXT, blen, (LPARAM)buffer); + blen = SendMessage(e->editpane, SCI_GETTEXT, blen, (LPARAM)buffer); } else if (e->savefmt == UTF_ANSI) { @@ -2738,7 +2810,9 @@ unsigned char *GUIReadFile(const char *fname, void *buffer, int blen, size_t *sz SendMessage(e->editpane, SCI_SETSAVEPOINT, 0, 0); //tell the control that it was saved. } else + { QCC_WriteFileW(e->filename, (wchar_t*)buffer+1, blen); + } } } } @@ -5323,6 +5397,8 @@ void UpdateFileList(void) int size; char *buffer; + AddSourceFile(NULL, progssrcname); + f = fopen (progssrcname, "rb"); if (!f) return; @@ -5343,14 +5419,15 @@ void UpdateFileList(void) if (*qcc_token == '#') { //aaaahhh! newstyle! - AddSourceFile(NULL, progssrcname); } else { pr_file_p = QCC_COM_Parse(pr_file_p); //we dont care about the produced progs.dat - AddSourceFile(NULL, progssrcname); while(pr_file_p) { + if (*qcc_token == '#') //panic if there's preprocessor in there. + break; + AddSourceFile(progssrcname, qcc_token); pr_file_p = QCC_COM_Parse(pr_file_p); //we dont care about the produced progs.dat } @@ -5552,6 +5629,38 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin ShowWindow(mainwindow, SW_SHOWDEFAULT); + { + char *ext = strrchr(progssrcname, '.'); + if (ext && !QC_strcasecmp(ext, ".dat")) + { + FILE *f = fopen(progssrcname, "rb"); + if (f) + { + char *buf; + size_t size; + + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + buf = malloc(size); + fread(buf, 1, size, f); + fclose(f); + QC_EnumerateFilesFromBlob(buf, size, QCC_EnumerateFilesResult); + free(buf); + } + strcpy(progssrcname, "progs.src"); + + for (i = 0; ; i++) + { + if (!strcmp("embedsrc", compiler_flag[i].abbrev)) + { + compiler_flag[i].flags |= FLAG_SETINGUI; + break; + } + } + } + } + if (fl_compileonstart) { CreateOutputWindow(false); diff --git a/engine/qclib/qccguistuff.c b/engine/qclib/qccguistuff.c index ab4975a19..f517867f9 100644 --- a/engine/qclib/qccguistuff.c +++ b/engine/qclib/qccguistuff.c @@ -99,21 +99,21 @@ void GoToDefinition(char *name) if (fnum > 0 && fnum < numfunctions) { fnc = &functions[fnum]; - if (fnc->code>=0 && fnc->s_file) + if (fnc->code>=0 && fnc->filen) { - EditFile(strings+fnc->s_file, statements[fnc->code].linenum-1, false); + EditFile(fnc->filen, statements[fnc->code].linenum-1, false); return; } } } - if (!def->s_file) + if (!def->filen) { char msgbuffer[2048]; QC_snprintfz(msgbuffer, sizeof(msgbuffer), "Global definition of \"%s\" was not specified.", name); GUI_DialogPrint("Not found", msgbuffer); } else - EditFile(def->s_file+strings, def->s_line-1, false); + EditFile(def->filen, def->s_line-1, false); } else { @@ -413,6 +413,9 @@ void GUI_ParseCommandLine(char *args) GUI_LoadConfig(); + paramlen = strlen(parameters); + if (paramlen) + parameters[paramlen++] = ' '; while(*args) { while (*args == ' ' || *args == '\t') @@ -686,7 +689,7 @@ void GUI_RevealOptions(void) -int GUI_BuildParms(char *args, char **argv, pbool quick) +int GUI_BuildParms(char *args, char **argv, pbool quick)//, char *forceoutputfile) { static char param[2048]; int paramlen = 0; @@ -770,6 +773,12 @@ int GUI_BuildParms(char *args, char **argv, pbool quick) args=next; }*/ +// if (*forceoutputfile) +// { +// argv[argc++] = "-destfile"; +// argv[argc++] = forceoutputfile; +// } + if (*progssrcname) { argv[argc++] = "-srcfile"; diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 96c391585..4b1019785 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -80,7 +80,7 @@ int numstatements; QCC_function_t *functions; -dfunction_t *dfunctions; +//dfunction_t *dfunctions; int numfunctions; QCC_ddef_t *qcc_globals; @@ -200,6 +200,8 @@ struct { {" F312", WARN_OVERFLOW}, {" F313", WARN_DENORMAL}, {" F314", WARN_LAXCAST}, + {" F315", WARN_DUPLICATEPRECOMPILER}, + {" F316", WARN_IDENTICALPRECOMPILER}, {" F208", WARN_NOTREFERENCEDCONST}, {" F209", WARN_EXTRAPRECACHE}, @@ -319,7 +321,8 @@ compiler_flag_t compiler_flag[] = { {&keyword_union, defaultkeyword, "union", "Keyword: union", "Disables the 'union' keyword."}, //you surly know what a union is! {&keyword_var, defaultkeyword, "var", "Keyword: var", "Disables the 'var' keyword."}, {&keyword_vector, defaultkeyword, "vector", "Keyword: vector", "Disables the 'vector' keyword."}, - + {&keyword_wrap, defaultkeyword, "wrap", "Keyword: wrap", "Disables the 'wrap' keyword."}, + {&keyword_weak, defaultkeyword, "weak", "Keyword: weak", "Disables the 'weak' keyword."}, //options {&keywords_coexist, FLAG_ASDEFAULT, "kce", "Keywords Coexist", "If you want keywords to NOT be disabled when they a variable by the same name is defined, check here."}, @@ -767,7 +770,7 @@ int WriteSourceFiles(int h, pbool sourceaswell, pbool legacyembed) #ifdef AVAIL_ZLIB idf[num].compmethod = 2; #else - idf[num].compmethod = 1; + idf[num].compmethod = 0; #endif idf[num].ofs = SafeSeek(h, 0, SEEK_CUR); idf[num].compsize = QC_encode(progfuncs, f->size, idf[num].compmethod, f->file, h); @@ -879,13 +882,21 @@ int WriteBodylessFuncs (int handle) int ret=0; for (d=pr.def_head.next ; d ; d=d->next) { + if (!d->used || !d->constant) + continue; + if (d->type->type == ev_function && !d->scope)// function parms are ok { - if ((d->initialized == 2) && d->referenced) + if (d->initialized == 2) { SafeWrite(handle, d->name, strlen(d->name)+1); ret++; } + if (d->initialized == 0) + { + QCC_PR_Warning(ERR_NOFUNC, d->filen, d->s_line, "function %s has no body", d->name); + QCC_PR_ParsePrintDef(ERR_NOFUNC, d); + } } } @@ -1043,9 +1054,9 @@ void QCC_UnmarshalLocals(void) if (verbose >= 3) { if (onum == numpr_globals) - printf("code: %s:%i: function %s no private locals\n", functions[i].file, functions[i].line, functions[i].name); + printf("code: %s:%i: function %s no private locals\n", functions[i].filen, functions[i].line, functions[i].name); else - printf("code: %s:%i: function %s private locals %i-%i\n", functions[i].file, functions[i].line, functions[i].name, onum, numpr_globals); + printf("code: %s:%i: function %s private locals %i-%i\n", functions[i].filen, functions[i].line, functions[i].name, onum, numpr_globals); } } } @@ -1065,14 +1076,14 @@ void QCC_UnmarshalLocals(void) if (verbose >= 3) { if (onum == numpr_globals) - printf("code: %s:%i: function %s no locals\n", functions[i].file, functions[i].line, functions[i].name); + printf("code: %s:%i: function %s no locals\n", functions[i].filen, functions[i].line, functions[i].name); else { - printf("code: %s:%i: function %s overlapped locals %i-%i\n", functions[i].file, functions[i].line, functions[i].name, onum, numpr_globals); + printf("code: %s:%i: function %s overlapped locals %i-%i\n", functions[i].filen, functions[i].line, functions[i].name, onum, numpr_globals); for (d = functions[i].firstlocal; d; d = d->nextlocal) { - printf("code: %s:%i: %s @%i\n", functions[i].file, functions[i].line, d->name, d->ofs); + printf("code: %s:%i: %s @%i\n", functions[i].filen, functions[i].line, d->name, d->ofs); } } } @@ -1296,7 +1307,7 @@ pbool QCC_WriteData (int crc) funcs[i].first_statement = PRLittleLong (functions[i].code); funcs[i].parm_start = 0;//PRLittleLong (functions[i].parm_start); funcs[i].s_name = PRLittleLong (QCC_CopyString(functions[i].name)); - funcs[i].s_file = PRLittleLong (functions[i].s_file); + funcs[i].s_file = PRLittleLong (functions[i].s_filed); funcs[i].numparms = 0;//PRLittleLong ((functions[i].numparms>MAX_PARMS)?MAX_PARMS:functions[i].numparms); funcs[i].locals = 0;//PRLittleLong (functions[i].locals); for (j = 0; j < MAX_PARMS; j++) @@ -1327,9 +1338,17 @@ pbool QCC_WriteData (int crc) } else funcs[i].s_name = PRLittleLong (QCC_CopyString(functions[i].name)); - funcs[i].s_file = PRLittleLong (functions[i].s_file); + funcs[i].s_file = PRLittleLong (functions[i].s_filed); - if (functions[i].code == -1) + if (functions[i].merged) + { + funcs[i].parm_start = functions[i].merged->parm_start; + funcs[i].locals = functions[i].merged->locals; + funcs[i].numparms = functions[i].merged->numparms; + for(p = 0; p < funcs[i].numparms; p++) + funcs[i].parm_size[p] = functions[i].merged->parm_size[p]; + } + else if (functions[i].code == -1) { funcs[i].parm_start = 0; funcs[i].locals = 0; @@ -1346,7 +1365,7 @@ pbool QCC_WriteData (int crc) { if (!local->used) { //all params should have been assigned space. logically we could have safely omitted the last ones, but blurgh. - QCC_PR_Warning(ERR_INTERNAL, strings + local->s_file, local->s_line, "Argument %s was not marked used.\n", local->name); + QCC_PR_Warning(ERR_INTERNAL, local->filen, local->s_line, "Argument %s was not marked used.\n", local->name); continue; } @@ -1394,10 +1413,10 @@ pbool QCC_WriteData (int crc) funcs[i].numparms = PRLittleLong(funcs[i].numparms); if (funcs[i].locals && !funcs[i].parm_start) - QCC_PR_Warning(0, strings + funcs[i].s_file, functions[i].line, "%s:%i: func %s @%i locals@%i+%i, %i parms\n", functions[i].file, functions[i].line, strings+funcs[i].s_name, funcs[i].first_statement, funcs[i].parm_start, funcs[i].locals, funcs[i].numparms); + QCC_PR_Warning(0, strings + funcs[i].s_file, functions[i].line, "%s:%i: func %s @%i locals@%i+%i, %i parms\n", functions[i].filen, functions[i].line, strings+funcs[i].s_name, funcs[i].first_statement, funcs[i].parm_start, funcs[i].locals, funcs[i].numparms); #ifdef DEBUG_DUMP - printf("code: %s:%i: func %s @%i locals@%i+%i, %i parms\n", functions[i].file, functions[i].line, strings+funcs[i].s_name, funcs[i].first_statement, funcs[i].parm_start, funcs[i].locals, funcs[i].numparms); + printf("code: %s:%i: func %s @%i locals@%i+%i, %i parms\n", functions[i].file, functions[i].line, strings+funcs[i].s_named, funcs[i].first_statement, funcs[i].parm_start, funcs[i].locals, funcs[i].numparms); #endif } funcdata = funcs; @@ -1454,15 +1473,15 @@ pbool QCC_WriteData (int crc) else if (strcmp(def->name, "IMMEDIATE") && qccwarningaction[wt] && !(def->type->type == ev_function && def->symbolheader->timescalled) && !def->symbolheader->used) { char typestr[256]; - if (QC_strcasestr(strings + def->s_file, "extensions") && !verbose) + if (QC_strcasestr(def->filen, "extensions") && !verbose) { //try to avoid annoying warnings from dpextensions.qc extwarncount++; - QCC_PR_Warning(wt, strings + def->s_file, def->s_line, NULL); + QCC_PR_Warning(wt, def->filen, def->s_line, NULL); } else if (def->arraysize) - QCC_PR_Warning(wt, strings + def->s_file, def->s_line, (dupewarncount++ >= 10 && !verbose)?NULL:"%s %s[%i] no references.", TypeName(def->type, typestr, sizeof(typestr)), def->name, def->arraysize); + QCC_PR_Warning(wt, def->filen, def->s_line, (dupewarncount++ >= 10 && !verbose)?NULL:"%s %s[%i] no references.", TypeName(def->type, typestr, sizeof(typestr)), def->name, def->arraysize); else - QCC_PR_Warning(wt, strings + def->s_file, def->s_line, (dupewarncount++ >= 10 && !verbose)?NULL:"%s %s no references.", TypeName(def->type, typestr, sizeof(typestr)), def->name); + QCC_PR_Warning(wt, def->filen, def->s_line, (dupewarncount++ >= 10 && !verbose)?NULL:"%s %s no references.", TypeName(def->type, typestr, sizeof(typestr)), def->name); } pr_scope = NULL; @@ -1470,7 +1489,7 @@ pbool QCC_WriteData (int crc) { optres_unreferenced++; #ifdef DEBUG_DUMP - printf("code: %s:%i: strip noref %s %s@%i;\n", strings+def->s_file, def->s_line, def->type->name, def->name, def->ofs); + printf("code: %s:%i: strip noref %s %s@%i;\n", def->filen, def->s_line, def->type->name, def->name, def->ofs); #endif continue; } @@ -1478,7 +1497,7 @@ pbool QCC_WriteData (int crc) if ((def->type->type == ev_struct || def->type->type == ev_union || def->arraysize) && def->deftail) { #ifdef DEBUG_DUMP - printf("code: %s:%i: strip struct %s %s@%i;\n", strings+def->s_file, def->s_line, def->type->name, def->name, def->ofs); + printf("code: %s:%i: strip struct %s %s@%i;\n", def->filen, def->s_line, def->type->name, def->name, def->ofs); #endif //the head of an array/struct is never written. only, its member fields are. continue; @@ -2157,7 +2176,7 @@ strofs = (strofs+3)&~3; printf("unable to write value for 'entity progs'\n"); //would not work anyway else { - QCC_PR_Warning(WARN_DENORMAL, strings + def->s_file, def->s_line, "'entity progs' is non-portable and will not work across engines nor cpus."); + QCC_PR_Warning(WARN_DENORMAL, def->filen, def->s_line, "'entity progs' is non-portable and will not work across engines nor cpus."); if (def->initialized) i = PRLittleLong(qcc_pr_globals[def->ofs]._int); @@ -2264,6 +2283,289 @@ strofs = (strofs+3)&~3; return true; } +/* +#merge "oldprogs" +wrap void() worldspawn = +{ + print("hello world\n"); + prior(); +}; + +Progs merging is done by loading in an existing progs.dat and essentially appending new stuff on the end. +The resulting output should be the same, other than wraps (which replaces the previous function global with the new one). +*/ +static void QCC_MergeStrings(char *in, unsigned int num) +{ + memcpy(strings, in, num); + strofs = num; +} +int QCC_MergeValidateString(int str) +{ + if (str < 0 || str >= strofs) + str = 0; + return str; +} +static void QCC_MergeFunctions(dfunction_t *in, unsigned int num) +{ + numfunctions = 0; + while(num --> 0) + { + if (in->first_statement <= 0) + { + functions[numfunctions].builtin = -in->first_statement; + functions[numfunctions].code = -1; + } + else + { + functions[numfunctions].builtin = 0; + functions[numfunctions].code = in->first_statement; + } + functions[numfunctions].s_filed = QCC_MergeValidateString(in->s_file); + functions[numfunctions].filen = strings+functions[numfunctions].s_filed; + functions[numfunctions].line = 0; + functions[numfunctions].name = strings+QCC_MergeValidateString(in->s_name); + functions[numfunctions].parentscope = NULL; + functions[numfunctions].type = NULL; + functions[numfunctions].def = NULL; + functions[numfunctions].firstlocal = NULL; + functions[numfunctions].privatelocals = true; + functions[numfunctions].merged = in; + numfunctions++; + in++; + } +} +static void QCC_MergeStatements16(dstatement16_t *in, unsigned int num) +{ + QCC_statement_t *out = statements; + numstatements = num; + for (; num --> 0; out++, in++) + { + out->op = in->op; + out->a.sym = NULL; + out->a.cast = NULL; + out->a.ofs = in->a; + out->b.sym = NULL; + out->b.cast = NULL; + out->b.ofs = in->b; + out->c.sym = NULL; + out->c.cast = NULL; + out->c.ofs = in->c; + out->linenum = 0; + } + + out->op = OP_DONE; + out->a.ofs = 0; + out->b.ofs = 0; + out->c.ofs = 0; + out->linenum = 0; + numstatements++; +} +QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_function_t *scope, int arraysize, QCC_def_t *rootsymbol, unsigned int ofs, int referable, unsigned int flags); +static etype_t QCC_MergeFindFieldType(unsigned int ofs, const char *fldname, ddef16_t *fields, size_t numfields) +{ + size_t i; + etype_t best = ev_void; + for (i = 0; i < numfields; i++) + { + if (fields[i].ofs == ofs) + { //sometimes we have field unions. go for the exact name match if we can so we don't get confused over vectors/floats. otherwise just go with the first (and hope they're correctly ordered) + char *name = strings+QCC_MergeValidateString(fields[i].s_name); + if (!strcmp(name, fldname)) + return fields[i].type; + if (best == ev_void) + best = fields[i].type; + } + } + return best; +} +static void QCC_MergeUnstrip(dfunction_t *in, unsigned int num) +{ + size_t i; + char *name; + QCC_def_t *def; + + //functions may have been stripped. this results in an annoying lack of errors, and will likely confuse function wrapping... + //generate a new def for each function, if it doesn't already exist. + //these are probably going to be wasteful dupes, but they'll just get stripped again if they're still not used. + for (i = 0; i < num; i++) + { + if (!in[i].s_name) + continue; + + name = strings+QCC_MergeValidateString(in[i].s_name); + + def = QCC_PR_GetDef(NULL, name, NULL, false, 0, GDF_BASICTYPE); + if (!def) + { + def = QCC_PR_GetDef(type_function, name, NULL, true, 0, GDF_BASICTYPE); + def->symboldata[def->ofs].function = i; + def->initialized = true; + def->referenced = true; + def->assumedtype = true; + } + QCC_FreeDef(def); + } +} +QCC_type_t *QCC_PR_FieldType (QCC_type_t *pointsto); +static void QCC_MergeGlobalDefs16(ddef16_t *in, size_t num, void *values, size_t defscount, ddef16_t *fields, size_t numfields) +{ + QCC_def_t *root, *def; + QCC_type_t *type; + etype_t evt; + + char *name; + unsigned int flags; + pbool referrable; + + numpr_globals = 0; //that root object will replace the normal reserved globals. + root = QCC_PR_GetDef(type_void, "", NULL, true, 0, GDF_USED); + root->symboldata = values; + root->symbolsize = defscount; + + for (; num --> 0; in++) + { + name = strings+QCC_MergeValidateString(in->s_name); + + flags = GDF_USED; + if (in->type & DEF_SAVEGLOBAL) + flags |= GDF_SAVED; + + evt = in->type&~DEF_SAVEGLOBAL; + if (evt == ev_field) + evt = QCC_MergeFindFieldType(root->symboldata[in->ofs]._int, name, fields, numfields); + switch(evt) + { + case ev_void: + type = type_void; + break; + case ev_vector: + type = type_vector; + break; + case ev_float: + type = type_float; + break; + case ev_string: + type = type_string; + break; + case ev_entity: + type = type_entity; + break; + case ev_integer: + type = type_integer; + break; + case ev_function: + type = type_function; + break; + default: + type = type_variant; + break; + } + if ((in->type&~DEF_SAVEGLOBAL) == ev_field) + { + type = QCC_PR_FieldType(type); + flags |= GDF_CONST; + } + + referrable = true; //fixme: disable if this appears to be within a function's local storage + + def = QCC_PR_DummyDef(type, name, NULL, 0, root, in->ofs, referrable, flags); + def->initialized = 1; + def->referenced = true; + def->assumedtype = true; + + if (evt == ev_vector) + { + int j = 3; + if ((in->type&~DEF_SAVEGLOBAL) == ev_field) + { + for (j = 0; j < 3; j++) + { + if (in[j+1].ofs == in->ofs+j && (in[j+1].type&~DEF_SAVEGLOBAL) == ev_field && QCC_MergeFindFieldType(root->symboldata[in[j+1].ofs]._int, strings+QCC_MergeValidateString(in[j+1].s_name), fields, numfields) == ev_float) + continue; + break; + } + } + else + { + for (j = 0; j < 3; j++) + { + if (in[j+1].ofs == in->ofs+j && (in[j+1].type&~DEF_SAVEGLOBAL) == ev_float) + continue; + break; + } + } + in += j; + num -= j; + } + } + QCC_FreeDef(root); +} + +/*load a progs into the current compile state.*/ +void QCC_ImportProgs(const char *filename) +{ + int flen; + dprograms_t *prog; + + //these keywords are implicitly enabled by #merge + keyword_weak = true; + keyword_wrap = true; + +// if (strofs != 0) //could be fixed with relocs +// QCC_Error(ERR_BADEXTENSION, "#merge used too late. It must be used before any other definitions."); + if (numstatements != 1) //should be easy to deal with. + QCC_Error(ERR_BADEXTENSION, "#merge used too late. It must be used before any other definitions."); + if (numfunctions != 1) //could be fixed with relocs + QCC_Error(ERR_BADEXTENSION, "#merge used too late. It must be used before any other definitions."); + if (numglobaldefs != 1) //could be fixed by inserting it properly. any already-defined defs must have their parentdef changed to union them with imported ones. + QCC_Error(ERR_BADEXTENSION, "#merge used too late. It must be used before any other definitions (globals)."); + if (numfielddefs != 1) //could be fixed with relocs + QCC_Error(ERR_BADEXTENSION, "#merge used too late. It must be used before any other definitions (fields)."); + if (numpr_globals != RESERVED_OFS) //not normally changed until after compiling + QCC_Error(ERR_BADEXTENSION, "#merge used too late. It must be used before any other definitions (regs)."); + + flen = externs->FileSize(filename); + if (flen < 0) + { + QCC_Error(ERR_COULDNTOPENFILE, "Couldn't open file %s", filename); + return; + } + + printf ("\nnote: The #merge feature is still experimental\n\n"); + //FIXME: find overlapped locals. strip them. merge with new ones. + //FIXME: find temps. strip them. you get the idea. + //FIXME: find immediates. set up hash tables for them for reuse. HAH! + + prog = qccHunkAlloc(flen); + + externs->ReadFile(filename, prog, flen, NULL); + + if (prog->version == 7 && prog->secondaryversion == PROG_SECONDARYVERSION16 && !prog->blockscompressed && !prog->numtypes) + ; + else if (prog->version == 7 && prog->secondaryversion == PROG_SECONDARYVERSION32 && !prog->blockscompressed && !prog->numtypes) + ; + else if (prog->version != 6) + { + QCC_Error(ERR_COULDNTOPENFILE, "Unsupported version: %s", filename); + return; + } + + QCC_MergeStrings(((char*)prog+prog->ofs_strings), prog->numstrings); + QCC_MergeFunctions((dfunction_t*)((char*)prog+prog->ofs_functions), prog->numfunctions); + pr.size_fields = prog->entityfields; + if (prog->version == 7 && prog->secondaryversion == PROG_SECONDARYVERSION32) + { +// QCC_MergeStatements32((dstatement32_t*)((char*)prog+prog->ofs_statements), prog->numstatements); +// QCC_MergeGlobalDefs32((ddef32_t*)((char*)prog+prog->ofs_globaldefs), prog->numglobaldefs, ((char*)prog)+prog->ofs_globals, prog->numglobals, (ddef16_t*)((char*)prog+prog->ofs_fielddefs), prog->numfielddefs); + QCC_Error(ERR_COULDNTOPENFILE, "32bit versions not supported: %s", filename); + } + else + { + QCC_MergeStatements16((dstatement16_t*)((char*)prog+prog->ofs_statements), prog->numstatements); + QCC_MergeGlobalDefs16((ddef16_t*)((char*)prog+prog->ofs_globaldefs), prog->numglobaldefs, ((char*)prog)+prog->ofs_globals, prog->numglobals, (ddef16_t*)((char*)prog+prog->ofs_fielddefs), prog->numfielddefs); + } + QCC_MergeUnstrip((dfunction_t*)((char*)prog+prog->ofs_functions), prog->numfunctions); +} /* @@ -2618,8 +2920,13 @@ int QCC_PR_FinishCompilation (void) QCC_type_t *t; int errors; + pbool externokay = false; + errors = false; + if (qcc_targetformat == QCF_FTE || qcc_targetformat == QCF_FTEDEBUG || qcc_targetformat == QCF_FTEH2) + externokay = true; + // check to make sure all functions prototyped have code for (d=pr.def_head.next ; d ; d=d->next) { @@ -2652,13 +2959,21 @@ int QCC_PR_FinishCompilation (void) continue; } } - QCC_PR_Warning(ERR_NOFUNC, strings + d->s_file, d->s_line, "function %s has no body",d->name); + QCC_PR_Warning(ERR_NOFUNC, d->filen, d->s_line, "function %s has no body",d->name); QCC_PR_ParsePrintDef(ERR_NOFUNC, d); bodylessfuncs = true; errors = true; } else if (d->initialized==2) + { + if (!externokay) + { + QCC_PR_Warning(ERR_NOFUNC, d->filen, d->s_line, "extern is not supported with this target format",d->name); + QCC_PR_ParsePrintDef(ERR_NOFUNC, d); + errors = true; + } bodylessfuncs = true; + } } } pr_scope = NULL; @@ -2819,7 +3134,9 @@ unsigned short QCC_PR_WriteProgdefs (char *filename) { if (!strcmp (d->name, "end_sys_globals")) break; -// if (d->ofsname) + continue; +// if (d->symbolheader->ofstype->type) @@ -3389,6 +3706,11 @@ void QCC_CopyFiles (void) //============================================================================ +#ifdef _WIN32 +#define WINDOWSARG(x) x +#else +#define WINDOWSARG(x) false +#endif void QCC_PR_CommandLinePrecompilerOptions (void) { @@ -3400,6 +3722,47 @@ void QCC_PR_CommandLinePrecompilerOptions (void) for (i = 1;i= argc-1 || argv[p+1][0] == '-') - p = QCC_CheckParm ("-srcfile"); if (p && p < argc-1 ) sprintf (qccmprogsdat, "%s", argv[p+1]); else @@ -4335,6 +4694,13 @@ void QCC_ContinueCompile(void) return; } + pr_file_p = qccmsrc; + s_filen = ""; + s_filed = 0; + pr_source_line = 0; + QCC_PR_LexWhitespace(false); + qccmsrc = pr_file_p; + qccmsrc = QCC_COM_Parse(qccmsrc); if (!qccmsrc) { @@ -4371,18 +4737,51 @@ void QCC_ContinueCompile(void) } return; } - QCC_GenerateRelativePath(qccmfilename, sizeof(qccmfilename), compilingrootfile, qcc_token); + QCC_FindBestInclude(qcc_token, compilingrootfile, 2); +/* + { + int includepath = 0; + while(1) + { + if (includepath) + { + if (includepath > MAXINCLUDEDIRS || !*qccincludedir[includepath-1]) + { + QCC_GenerateRelativePath(qccmfilename, sizeof(qccmfilename), compilingrootfile, qcc_token); + break; + } + + currentfile = qccincludedir[includepath-1]; + } + + QCC_Canonicalize(qccmfilename, sizeof(fullname), qcc_token, compilingrootfile); + + { + extern progfuncs_t *qccprogfuncs; + if (qccprogfuncs->funcs.parms->FileSize(qccmfilename) == -1) + { + includepath++; + continue; + } + } + break; + } + } + + QCC_GenerateRelativePath(qccmfilename, sizeof(qccmfilename), compilingrootfile, qcc_token); if (autoprototype) printf ("prototyping %s\n", qccmfilename); else { printf ("compiling %s\n", qccmfilename); } + QCC_LoadFile (qccmfilename, (void *)&qccmsrc2); if (!QCC_PR_CompileFile (qccmsrc2, qccmfilename) ) QCC_Error (ERR_PARSEERRORS, "Errors have occured\n"); + */ } void QCC_FinishCompile(void) { @@ -4394,7 +4793,8 @@ void QCC_FinishCompile(void) if (setjmp(pr_parse_abort)) QCC_Error(ERR_INTERNAL, ""); - s_file = 0; + s_filen = ""; + s_filed = 0; pr_source_line = 0; if (!QCC_PR_FinishCompilation ()) @@ -4510,7 +4910,6 @@ void QCC_FinishCompile(void) -extern QCC_string_t s_file, s_file2; extern char *pr_file_p; extern int pr_source_line; void QCC_PR_ParseDefs (char *classname); @@ -4521,6 +4920,7 @@ void QCC_PR_ParseDefs (char *classname); void StartNewStyleCompile(void) { + char *tmp; if (setjmp(pr_parse_abort)) { if (++pr_error_count > MAX_ERRORS) @@ -4535,8 +4935,17 @@ void StartNewStyleCompile(void) compilingfile = qccmprogsdat; + s_filen = tmp = qccHunkAlloc(strlen(compilingfile)+1); + strcpy(tmp, compilingfile); + if (opt_filenames) + { + optres_filenames += strlen(compilingfile)+1; + s_filed = 0; + } + else + s_filed = QCC_CopyString (compilingfile); + pr_file_p = qccmsrc; - s_file = s_file2 = QCC_CopyString (compilingfile); pr_source_line = 0; @@ -4565,9 +4974,19 @@ void new_QCC_ContinueCompile(void) if (autoprototype && !parseonly) { + char *tmp; qccmsrc = originalqccmsrc; + + s_filen = tmp = qccHunkAlloc(strlen(compilingfile)+1); + strcpy(tmp, compilingfile); + if (opt_filenames) + { + optres_filenames += strlen(compilingfile)+1; + s_filed = 0; + } + else + s_filed = QCC_CopyString (compilingfile); pr_file_p = qccmsrc; - s_file = s_file2 = QCC_CopyString (compilingfile); autoprototyped = autoprototype; QCC_SetDefaultProperties(); diff --git a/engine/qclib/qcd.h b/engine/qclib/qcd.h index 671d546d4..50fac10d1 100644 --- a/engine/qclib/qcd.h +++ b/engine/qclib/qcd.h @@ -1,6 +1,7 @@ pbool QC_decodeMethodSupported(int method); -char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, char *info, char *buffer); -int QC_encode(progfuncs_t *progfuncs, int len, int method, char *in, int handle); +char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const char *info, char *buffer); +int QC_encode(progfuncs_t *progfuncs, int len, int method, const char *in, int handle); +pbool QC_EnumerateFilesFromBlob(const void *blob, size_t blobsize, void (*cb)(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize)); int QC_encodecrc(int len, char *in); char *PDECL filefromprogs(pubprogfuncs_t *progfuncs, progsnum_t prnum, char *fname, size_t *size, char *buffer); diff --git a/engine/qclib/qcd_main.c b/engine/qclib/qcd_main.c index bf521ae75..11f75546a 100644 --- a/engine/qclib/qcd_main.c +++ b/engine/qclib/qcd_main.c @@ -1,5 +1,5 @@ #include "progsint.h" -//#include "qcc.h" +#include "qcc.h" #ifndef NO_ZLIB #define AVAIL_ZLIB @@ -35,7 +35,7 @@ pbool QC_decodeMethodSupported(int method) return false; } -char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, char *info, char *buffer) +char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const char *info, char *buffer) { int i; if (method == 0) //copy @@ -49,11 +49,11 @@ char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, char * for (i = 0; i < len; i++) buffer[i] = info[i] ^ 0xA5; } - else if (method == 2) //compression (ZLIB) - { #ifdef AVAIL_ZLIB + else if (method == 2 || method == 8) //compression (ZLIB) + { z_stream strm = { - info, + (char*)info, complen, 0, @@ -73,12 +73,15 @@ char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, char * 0 }; - inflateInit(&strm); + if (method == 8) + inflateInit2(&strm, -MAX_WBITS); + else + inflateInit(&strm); if (Z_STREAM_END != inflate(&strm, Z_FINISH)) //decompress it in one go. Sys_Error("Failed block decompression\n"); inflateEnd(&strm); -#endif } +#endif //add your decryption/decompression routine here. else Sys_Error("Bad file encryption routine\n"); @@ -96,10 +99,10 @@ int QC_encodecrc(int len, char *in) return 0; #endif } -void SafeWrite(int hand, void *buf, long count); +void SafeWrite(int hand, const void *buf, long count); int SafeSeek(int hand, int ofs, int mode); //we are allowed to trash our input here. -int QC_encode(progfuncs_t *progfuncs, int len, int method, char *in, int handle) +int QC_encode(progfuncs_t *progfuncs, int len, int method, const char *in, int handle) { int i; if (method == 0) //copy, allows a lame pass-through. @@ -107,20 +110,20 @@ int QC_encode(progfuncs_t *progfuncs, int len, int method, char *in, int handle) SafeWrite(handle, in, len); return len; } - else if (method == 1) //xor encryption, not secure. maybe useful for the string table. + /*else if (method == 1) //xor encryption, not secure. maybe useful for the string table. { for (i = 0; i < len; i++) in[i] = in[i] ^ 0xA5; SafeWrite(handle, in, len); return len; - } + }*/ else if (method == 2 || method == 8) //compression (ZLIB) { #ifdef AVAIL_ZLIB char out[8192]; z_stream strm = { - in, + (char *)in, len, 0, @@ -169,6 +172,75 @@ int QC_encode(progfuncs_t *progfuncs, int len, int method, char *in, int handle) } #endif +static int QC_ReadRawInt(const unsigned char *blob) +{ + return (blob[0]<<0) | (blob[1]<<8) | (blob[2]<<16) | (blob[3]<<24); +} +static int QC_ReadRawShort(const unsigned char *blob) +{ + return (blob[0]<<0) | (blob[1]<<8); +} +pbool QC_EnumerateFilesFromBlob(const void *blob, size_t blobsize, void (*cb)(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize)) +{ + unsigned int cdentries; + unsigned int cdlen; + const unsigned char *eocd; + const unsigned char *cd; + int nl,el,cl; + if (blobsize < 22) + return false; + eocd = blob; + eocd += blobsize-22; + if (QC_ReadRawInt(eocd+0) != 0x06054b50) + return false; + if (QC_ReadRawShort(eocd+4) || QC_ReadRawShort(eocd+6) || QC_ReadRawShort(eocd+20) || QC_ReadRawShort(eocd+8) != QC_ReadRawShort(eocd+10)) + return false; + cd = blob; + cd += QC_ReadRawInt(eocd+16); + cdlen = QC_ReadRawInt(eocd+12); + cdentries = QC_ReadRawInt(eocd+10); + if (cd+cdlen>=(const unsigned char*)blob+blobsize) + return false; + + + for(; cdentries --> 0; cd += 46 + nl+el+cl) + { + if (QC_ReadRawInt(cd+0) != 0x02014b50) + break; + nl = QC_ReadRawShort(cd+28); + el = QC_ReadRawShort(cd+30); + cl = QC_ReadRawShort(cd+32); + + if (QC_ReadRawShort(cd+8) != 0) + continue; + + { + const unsigned char *le = (const unsigned char*)blob + QC_ReadRawInt(cd+42); + unsigned int csize, usize, method; + char name[256]; + + if (QC_ReadRawInt(le+0) != 0x04034b50) + continue; + if (QC_ReadRawShort(le+6) != 0) //general purpose flags + continue; + method = QC_ReadRawShort(le+8); + if (method != 0 && method != 8) + continue; + if (nl != QC_ReadRawShort(le+26)) + continue; //name is weird... + if (el != QC_ReadRawShort(le+28)) + continue; //name is weird... + + csize = QC_ReadRawInt(le+18); + usize = QC_ReadRawInt(le+22); + QC_strlcpy(name, cd+46, (nl+1=0) - { - if ((qbyte *)svprogfuncs->filefromprogs(svprogfuncs, prnumforfile, filename, &sz, NULL)==(qbyte *)-1) - return sz; - prnumforfile--; - } - return -1; -} -qbyte *PR_OpenFile(char *filename, qbyte *buffer) -{ - return svprogfuncs->filefromprogs(svprogfuncs, prnumforfile, filename, NULL, buffer); -} - - //#define RETURN_EDICT(pf, e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(pf, e)) #define RETURN_SSTRING(s) (((int *)pr_globals)[OFS_RETURN] = PR_SetString(prinst, s)) //static - exe will not change it. @@ -4314,7 +4292,7 @@ vector aim(entity, missilespeed) ============= */ //cvar_t sv_aim = {"sv_aim", "0.93"}; -cvar_t sv_aim = SCVAR("sv_aim", "2"); +cvar_t sv_aim = CVAR("sv_aim", "2"); static void QCBUILTIN PF_aim (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { edict_t *ent, *check, *bestent; @@ -10951,6 +10929,26 @@ void PR_DumpPlatform_f(void) #else //eg: pr_dumpplatform -FFTE -TCS -O csplat + const char *keywords[] = + { + "ignore" //0 + "qwqc", //qw + "nqqc", //nq + "ssqc" //qw|nq + "csqc" //cs + "csqwqc", //cs|qw + "csnqqc", //cs|nq + "gameqc" //cs|nq|qw + "menuonly" //mn + "mnqwqc", //mn|qw + "mnnqqc", //mn|nq + "mnssqc" //mn|qw|nq + "mncsqc" //mn|cs + "mncsqwqc", //mn|cs|qw + "mncsnqqc", //mn|cs|nq + "" //mn|cs|nq|qw + }; + int idx; int i, j; int d = 0, nd, k; @@ -11345,11 +11343,11 @@ void PR_DumpPlatform_f(void) {"CONTENTBIT_MONSTERCLIP", "const int", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_MONSTERCLIP)}, {"CONTENTBIT_BODY", "const int", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_BODY)}, {"CONTENTBIT_CORPSE", "const int", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_CORPSE)}, - {"CONTENTBIT_Q2LADDER", "const int", QW|NQ|CS, NULL, 0,STRINGIFY(Q2CONTENTS_LADDER)}, - {"CONTENTBIT_SKY", "const int", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_SKY)}, - {"CONTENTBITS_POINTSOLID", "const int", QW|NQ|CS, NULL, 0,STRINGIFY(MASK_POINTSOLID)}, - {"CONTENTBITS_BOXSOLID", "const int", QW|NQ|CS, NULL, 0,STRINGIFY(MASK_BOXSOLID)}, - {"CONTENTBITS_FLUID", "const int", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_FLUID)}, + {"CONTENTBIT_Q2LADDER", "const int", QW|NQ|CS, "Content bit specific to q2bsp", 0,STRINGIFY(Q2CONTENTS_LADDER)}, + {"CONTENTBIT_SKY", "const int", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_SKY)"i"}, + {"CONTENTBITS_POINTSOLID", "const int", QW|NQ|CS, "Bits that traceline would normally consider solid", 0,"CONTENTBIT_SOLID|"STRINGIFY(Q2CONTENTS_WINDOW)"|CONTENTBIT_BODY"}, + {"CONTENTBITS_BOXSOLID", "const int", QW|NQ|CS, "Bits that tracebox would normally consider solid", 0,"CONTENTBIT_SOLID|"STRINGIFY(Q2CONTENTS_WINDOW)"|CONTENTBIT_BODY|CONTENTBIT_PLAYERCLIP"}, + {"CONTENTBITS_FLUID", "const int", QW|NQ|CS, NULL, 0,"CONTENTBIT_WATER|CONTENTBIT_SLIME|CONTENTBIT_LAVA|CONTENTBIT_SKY"}, {"CHAN_AUTO", "const float", QW|NQ|CS, "The automatic channel, play as many sounds on this channel as you want, and they'll all play, however the other channels will replace each other.", CHAN_AUTO}, {"CHAN_WEAPON", "const float", QW|NQ|CS, NULL, CHAN_WEAPON}, @@ -11889,22 +11887,28 @@ void PR_DumpPlatform_f(void) VFS_PRINTF(f, "#define %s\n", QSG_Extensions[i].name); } + VFS_PRINTF(f, "\n"); + if (accessors) - { - VFS_PRINTF(f, "accessor strbuf : float;\n"); - VFS_PRINTF(f, "accessor searchhandle : float;\n"); - VFS_PRINTF(f, "accessor hashtable : float;\n"); - VFS_PRINTF(f, "accessor infostring : string;\n"); - VFS_PRINTF(f, "accessor filestream : float;\n"); - } - else - { - VFS_PRINTF(f, "#define strbuf float\n"); - VFS_PRINTF(f, "#define searchhandle float\n"); - VFS_PRINTF(f, "#define hashtable float\n"); - VFS_PRINTF(f, "#define infostring string\n"); - VFS_PRINTF(f, "#define filestream float\n"); - } + VFS_PRINTF(f, "#define _ACCESSORS;\n"); + + VFS_PRINTF(f, + "#ifdef _ACCESSORS\n" + "accessor strbuf : float;\n" + "accessor searchhandle : float;\n" + "accessor hashtable : float;\n" + "accessor infostring : string;\n" + "accessor filestream : float;\n" + "accessor filestream : float;\n" + "#else\n" + "#define strbuf float\n" + "#define searchhandle float\n" + "#define hashtable float\n" + "#define infostring string\n" + "#define filestream float\n" + "#endif\n" + ); + VFS_PRINTF(f, "\n"); for (i = 0; knowndefs[i].name; i++) @@ -12238,6 +12242,7 @@ void PR_DumpPlatform_f(void) if (accessors) { + VFS_PRINTF(f, "#ifdef _ACCESSORS\n"); VFS_PRINTF(f, "accessor strbuf : float\n{\n" "\tinline get float asfloat[float idx] = {return stof(bufstr_get(this, idx));};\n" @@ -12274,6 +12279,7 @@ void PR_DumpPlatform_f(void) "\tget string = fgets;\n" "\tinline set string = {fputs(this,value);};\n" "};\n"); + VFS_PRINTF(f, "#endif\n"); } VFS_PRINTF(f, "#pragma noref 0\n"); diff --git a/engine/server/server.h b/engine/server/server.h index f8ef81d9f..74a9ce0c3 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -738,7 +738,7 @@ typedef struct { int maxsize; } dbuffer_t; -#define DEMO_FRAMES 64 +#define DEMO_FRAMES 64 //why is this not just 2? #define DEMO_FRAMES_MASK (DEMO_FRAMES - 1) typedef struct @@ -758,6 +758,7 @@ typedef struct qboolean fixangle[MAX_CLIENTS]; float fixangletime[MAX_CLIENTS]; vec3_t angles[MAX_CLIENTS]; + qboolean resetdeltas; int parsecount; int lastwritten; demo_frame_t frames[DEMO_FRAMES]; @@ -1486,7 +1487,7 @@ char *SV_Demo_CurrentOutput(void); void SV_MVDInit(void); char *SV_MVDNum(char *buffer, int bufferlen, int num); void SV_SendMVDMessage(void); -void SV_MVD_WriteReliables(void); +void SV_MVD_WriteReliables(qboolean writebroadcasts); qboolean SV_ReadMVD (void); void SV_FlushDemoSignon (void); void DestFlush(qboolean compleate); diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 828085ef2..06453407c 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -37,7 +37,7 @@ qboolean SV_MayCheat(void) } extern cvar_t cl_warncmd; -cvar_t sv_cheats = SCVARF("sv_cheats", "0", CVAR_LATCH); +cvar_t sv_cheats = CVARF("sv_cheats", "0", CVAR_LATCH); extern redirect_t sv_redirected; extern cvar_t sv_public; diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index e1af576ff..bff3d5ef9 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -46,32 +46,32 @@ client_t *host_client; // current client // bound the size of the physics time tic #ifdef SERVERONLY -cvar_t sv_mintic = CVARD("sv_mintic","0.013", "The minimum interval between running physics frames."); +cvar_t sv_mintic = CVARD("sv_mintic","0.013", "The minimum interval between running physics frames."); #else -cvar_t sv_mintic = CVARD("sv_mintic","0", "The minimum interval between running physics frames."); //client builds can think as often as they want. +cvar_t sv_mintic = CVARD("sv_mintic","0", "The minimum interval between running physics frames."); //client builds can think as often as they want. #endif -cvar_t sv_maxtic = CVARD("sv_maxtic","0.1", "The maximum interval between running physics frames. If the value is too low, multiple physics interations might be run at a time (based upon sv_limittics). Set identical to sv_mintic for fixed-interval ticks, which may be required if ODE is used.");//never run a tick slower than this -cvar_t sv_limittics = CVARD("sv_limittics","3", "The maximum number of ticks that may be run within a frame, to allow the server to catch up if it stalled or if sv_maxtic is too low.");// +cvar_t sv_maxtic = CVARD("sv_maxtic","0.1", "The maximum interval between running physics frames. If the value is too low, multiple physics interations might be run at a time (based upon sv_limittics). Set identical to sv_mintic for fixed-interval ticks, which may be required if ODE is used.");//never run a tick slower than this +cvar_t sv_limittics = CVARD("sv_limittics","3", "The maximum number of ticks that may be run within a frame, to allow the server to catch up if it stalled or if sv_maxtic is too low.");// -cvar_t sv_nailhack = CVARD("sv_nailhack","0", "If set to 1, disables the nail entity networking optimisation. This hack was popularised by qizmo which recommends it for better compression. Also allows clients to interplate nail positions and add trails."); -cvar_t sv_nopvs = CVARD("sv_nopvs", "0", "Set to 1 to ignore pvs on the server. This can make wallhacks more dangerous, so should only be used for debugging."); -cvar_t fraglog_public = CVARD("fraglog_public", "1", "Enables support for connectionless fraglog requests"); -cvar_t fraglog_details = CVARD("fraglog_details", "1", "Bitmask\n1: killer+killee names.\n2: killer+killee teams\n4:timestamp.\n8:killer weapon\n16:killer+killee guid.\nFor compatibility, use 1(vanilla) or 7(mvdsv)."); +cvar_t sv_nailhack = CVARD("sv_nailhack","0", "If set to 1, disables the nail entity networking optimisation. This hack was popularised by qizmo which recommends it for better compression. Also allows clients to interplate nail positions and add trails."); +cvar_t sv_nopvs = CVARD("sv_nopvs", "0", "Set to 1 to ignore pvs on the server. This can make wallhacks more dangerous, so should only be used for debugging."); +cvar_t fraglog_public = CVARD("fraglog_public", "1", "Enables support for connectionless fraglog requests"); +cvar_t fraglog_details = CVARD("fraglog_details", "1", "Bitmask\n1: killer+killee names.\n2: killer+killee teams\n4:timestamp.\n8:killer weapon\n16:killer+killee guid.\nFor compatibility, use 1(vanilla) or 7(mvdsv)."); -cvar_t timeout = SCVAR("timeout","65"); // seconds without any message -cvar_t zombietime = SCVAR("zombietime", "2"); // seconds to sink messages +cvar_t timeout = CVAR("timeout","65"); // seconds without any message +cvar_t zombietime = CVAR("zombietime", "2"); // seconds to sink messages // after disconnect #ifdef SERVERONLY -cvar_t developer = SCVAR("developer","0"); // show extra messages +cvar_t developer = CVAR("developer","0"); // show extra messages -cvar_t rcon_password = SCVARF("rcon_password", "", CVAR_NOUNSAFEEXPAND); // password for remote server commands -cvar_t password = SCVARF("password", "", CVAR_NOUNSAFEEXPAND); // password for entering the game +cvar_t rcon_password = CVARF("rcon_password", "", CVAR_NOUNSAFEEXPAND); // password for remote server commands +cvar_t password = CVARF("password", "", CVAR_NOUNSAFEEXPAND); // password for entering the game #else extern cvar_t developer; extern cvar_t rcon_password; extern cvar_t password; #endif -cvar_t spectator_password = CVARF("spectator_password", "", CVAR_NOUNSAFEEXPAND); // password for entering as a sepctator +cvar_t spectator_password = CVARF("spectator_password", "", CVAR_NOUNSAFEEXPAND); // password for entering as a sepctator cvar_t allow_download = CVARD("allow_download", "1", "If 1, permits downloading. Set to 0 to unconditionally block *ALL* downloads."); cvar_t allow_download_skins = CVARD("allow_download_skins", "1", "0 blocks downloading of any file in the skins/ directory"); @@ -88,46 +88,46 @@ cvar_t allow_download_packages = CVARD("allow_download_packages", "1", "if 1, p cvar_t allow_download_refpackages = CVARD("allow_download_refpackages", "1", "If set to 1, packages that contain files needed during spawn functions will be become 'referenced' and automatically downloaded to clients.\nThis cvar should probably not be set if you have large packages that provide replacement pickup models on public servers.\nThe path command will show a '(ref)' tag next to packages which clients will automatically attempt to download."); cvar_t allow_download_wads = CVARD("allow_download_wads", "1", "0 blocks downloading of any file in the wads/ directory, or is in the root directory with the extension .wad"); cvar_t allow_download_configs = CVARD("allow_download_configs", "0", "1 allows downloading of config files, either with the extension .cfg or in the subdir configs/.\n"CON_ERROR"THIS IS DANGEROUS AS IT CAN ALLOW PEOPLE TO READ YOUR RCON PASSWORD."); -cvar_t allow_download_locs = CVARD("allow_download_locs", "1", "0 blocks downloading of any file in the locs/ directory"); +cvar_t allow_download_locs = CVARD("allow_download_locs", "1", "0 blocks downloading of any file in the locs/ directory"); 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); -cvar_t sv_listen_nq = CVARD("sv_listen_nq", "2", "Allow new (net)quake clients to connect to the server.\n0 = don't let them in.\n1 = allow them in (WARNING: this allows 'qsmurf' DOS attacks).\n2 = accept (net)quake clients by emulating a challenge (as secure as QW/Q2 but does not fully conform to the NQ protocol)."); -cvar_t sv_listen_dp = CVARD("sv_listen_dp", "0", "Allows the server to respond with the DP-specific handshake protocol.\nWarning: this can potentially get confused with quake2, and results in race conditions with both vanilla netquake and quakeworld protocols.\nOn the plus side, DP clients can usually be identified correctly, enabling a model+sound limit boost."); -cvar_t sv_listen_q3 = CVAR("sv_listen_q3", "0"); -cvar_t sv_reportheartbeats = CVAR("sv_reportheartbeats", "1"); -cvar_t sv_highchars = CVAR("sv_highchars", "1"); -cvar_t sv_maxrate = CVAR("sv_maxrate", "30000"); -cvar_t sv_maxdrate = CVARAF("sv_maxdrate", "500000", - "sv_maxdownloadrate", 0); -cvar_t sv_minping = CVARF("sv_minping", "", CVAR_SERVERINFO); +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); +cvar_t sv_listen_nq = CVARD("sv_listen_nq", "2", "Allow new (net)quake clients to connect to the server.\n0 = don't let them in.\n1 = allow them in (WARNING: this allows 'qsmurf' DOS attacks).\n2 = accept (net)quake clients by emulating a challenge (as secure as QW/Q2 but does not fully conform to the NQ protocol)."); +cvar_t sv_listen_dp = CVARD("sv_listen_dp", "0", "Allows the server to respond with the DP-specific handshake protocol.\nWarning: this can potentially get confused with quake2, and results in race conditions with both vanilla netquake and quakeworld protocols.\nOn the plus side, DP clients can usually be identified correctly, enabling a model+sound limit boost."); +cvar_t sv_listen_q3 = CVAR("sv_listen_q3", "0"); +cvar_t sv_reportheartbeats = CVAR("sv_reportheartbeats", "1"); +cvar_t sv_highchars = CVAR("sv_highchars", "1"); +cvar_t sv_maxrate = CVAR("sv_maxrate", "30000"); +cvar_t sv_maxdrate = CVARAF("sv_maxdrate", "500000", + "sv_maxdownloadrate", 0); +cvar_t sv_minping = CVARF("sv_minping", "", CVAR_SERVERINFO); -cvar_t sv_bigcoords = CVARFD("sv_bigcoords", "1", 0, "Uses floats for coordinates instead of 16bit values.\nAlso boosts angle precision, so can be useful even on small maps.\nAffects clients thusly:\nQW: enforces a mandatory protocol extension\nDP: enables DPP7 protocol support\nNQ: uses RMQ protocol (protocol 999)."); -cvar_t sv_calcphs = CVARFD("sv_calcphs", "2", CVAR_LATCH, "Enables culling of sound effects. 0=always skip phs. Sounds are globally broadcast. 1=always generate phs. Sounds are always culled. On large maps the phs will be dumped to disk. 2=On large single-player maps, generation of phs is skipped. Otherwise like option 1."); +cvar_t sv_bigcoords = CVARFD("sv_bigcoords", "1", 0, "Uses floats for coordinates instead of 16bit values.\nAlso boosts angle precision, so can be useful even on small maps.\nAffects clients thusly:\nQW: enforces a mandatory protocol extension\nDP: enables DPP7 protocol support\nNQ: uses RMQ protocol (protocol 999)."); +cvar_t sv_calcphs = CVARFD("sv_calcphs", "2", CVAR_LATCH, "Enables culling of sound effects. 0=always skip phs. Sounds are globally broadcast. 1=always generate phs. Sounds are always culled. On large maps the phs will be dumped to disk. 2=On large single-player maps, generation of phs is skipped. Otherwise like option 1."); -cvar_t sv_showconnectionlessmessages = CVARD("sv_showconnectionlessmessages", "0", "Display a line describing each connectionless message that arrives on the server. Primarily a debugging feature, but also potentially useful to admins."); -cvar_t sv_cullplayers_trace = CVARFD("sv_cullplayers_trace", "", CVAR_SERVERINFO, "Attempt to cull player entities using tracelines as an anti-wallhack."); -cvar_t sv_cullentities_trace = CVARFD("sv_cullentities_trace", "", CVAR_SERVERINFO, "Attempt to cull non-player entities using tracelines as an extreeme anti-wallhack."); -cvar_t sv_phs = CVARD("sv_phs", "1", "If 1, do not use the phs. It is generally better to use sv_calcphs instead, and leave this as 1."); -cvar_t sv_resetparms = CVAR("sv_resetparms", "0"); -cvar_t sv_pupglow = CVARFD("sv_pupglow", "", CVAR_SERVERINFO, "Instructs clients to enable hexen2-style powerup pulsing."); +cvar_t sv_showconnectionlessmessages = CVARD("sv_showconnectionlessmessages", "0", "Display a line describing each connectionless message that arrives on the server. Primarily a debugging feature, but also potentially useful to admins."); +cvar_t sv_cullplayers_trace = CVARFD("sv_cullplayers_trace", "", CVAR_SERVERINFO, "Attempt to cull player entities using tracelines as an anti-wallhack."); +cvar_t sv_cullentities_trace = CVARFD("sv_cullentities_trace", "", CVAR_SERVERINFO, "Attempt to cull non-player entities using tracelines as an extreeme anti-wallhack."); +cvar_t sv_phs = CVARD("sv_phs", "1", "If 1, do not use the phs. It is generally better to use sv_calcphs instead, and leave this as 1."); +cvar_t sv_resetparms = CVAR("sv_resetparms", "0"); +cvar_t sv_pupglow = CVARFD("sv_pupglow", "", CVAR_SERVERINFO, "Instructs clients to enable hexen2-style powerup pulsing."); -cvar_t sv_master = CVAR("sv_master", "0"); -cvar_t sv_masterport = CVAR("sv_masterport", "0"); +cvar_t sv_master = CVAR("sv_master", "0"); +cvar_t sv_masterport = CVAR("sv_masterport", "0"); -cvar_t pext_ezquake_nochunks = CVARD("pext_ezquake_nochunks", "0", "Prevents ezquake clients from being able to use the chunked download extension. This sidesteps numerous ezquake issues, and will make downloads slower but more robust."); +cvar_t pext_ezquake_nochunks = CVARD("pext_ezquake_nochunks", "0", "Prevents ezquake clients from being able to use the chunked download extension. This sidesteps numerous ezquake issues, and will make downloads slower but more robust."); -cvar_t sv_gamespeed = CVARAF("sv_gamespeed", "1", "slowmo", 0); -cvar_t sv_csqcdebug = CVAR("sv_csqcdebug", "0"); -cvar_t sv_csqc_progname = CVAR("sv_csqc_progname", "csprogs.dat"); -cvar_t pausable = CVAR("pausable", "1"); -cvar_t sv_banproxies = CVARD("sv_banproxies", "0", "If enabled, anyone connecting via known proxy software will be refused entry. This should aid with blocking aimbots, but is only reliable for certain public proxies."); -cvar_t sv_specprint = CVARD("sv_specprint", "3", "Bitfield that controls which player events spectators see when tracking that player.\n&1: spectators will see centerprints.\n&2: spectators will see sprints (pickup messages etc).\n&4: spectators will receive console commands, this is potentially risky.\nIndividual spectators can use 'setinfo sp foo' to limit this setting."); +cvar_t sv_gamespeed = CVARAF("sv_gamespeed", "1", "slowmo", 0); +cvar_t sv_csqcdebug = CVAR("sv_csqcdebug", "0"); +cvar_t sv_csqc_progname = CVAR("sv_csqc_progname", "csprogs.dat"); +cvar_t pausable = CVAR("pausable", "1"); +cvar_t sv_banproxies = CVARD("sv_banproxies", "0", "If enabled, anyone connecting via known proxy software will be refused entry. This should aid with blocking aimbots, but is only reliable for certain public proxies."); +cvar_t sv_specprint = CVARD("sv_specprint", "3", "Bitfield that controls which player events spectators see when tracking that player.\n&1: spectators will see centerprints.\n&2: spectators will see sprints (pickup messages etc).\n&4: spectators will receive console commands, this is potentially risky.\nIndividual spectators can use 'setinfo sp foo' to limit this setting."); // @@ -4415,6 +4415,11 @@ static void SV_PauseChanged(void) ClientReliableWrite_Byte (cl, sv.paused!=0); } } + if (sv.mvdrecording) + { + ClientReliableWrite_Begin (&demo.recorder, svc_setpause, 2); + ClientReliableWrite_Byte (&demo.recorder, sv.paused!=0); + } } /* diff --git a/engine/server/sv_mvd.c b/engine/server/sv_mvd.c index f614b5fc3..250e06bab 100644 --- a/engine/server/sv_mvd.c +++ b/engine/server/sv_mvd.c @@ -40,7 +40,7 @@ cvar_t sv_demofps = CVAR("sv_demofps", "30"); cvar_t sv_demoPings = CVARD("sv_demoPings", "10", "Interval between ping updates in mvds"); cvar_t sv_demoMaxSize = CVARD("sv_demoMaxSize", "", "Demos will be truncated to be no larger than this size."); cvar_t sv_demoExtraNames = CVAR("sv_demoExtraNames", ""); -cvar_t sv_demoExtensions = CVARD("sv_demoExtensions", "0", "Enables protocol extensions within MVDs. This will cause older/non-fte clients to error upon playback"); +cvar_t sv_demoExtensions = CVARD("sv_demoExtensions", "", "Enables protocol extensions within MVDs. This will cause older/non-fte clients to error upon playback.\n0: off.\n1: all extensions.\n2: extensions also supported by a certain other engine."); cvar_t qtv_password = CVAR( "qtv_password", ""); cvar_t qtv_streamport = CVARAF( "qtv_streamport", "0", @@ -105,7 +105,7 @@ static void DestClose(mvddest_t *d, enum mvdclosereason_e reason) else if (d->desttype != DEST_STREAM) { char buf[512]; - SV_BroadcastPrintf (PRINT_CHAT, "Server recording complete\n/download demos/%s", COM_QuotedString(d->name, buf, sizeof(buf), false)); + SV_BroadcastPrintf (PRINT_CHAT, "Server recording complete\n^[/download %s^]\n", COM_QuotedString(va("demos/%s",d->name), buf, sizeof(buf), false)); } Z_Free(d); @@ -134,8 +134,11 @@ void DestFlush(qboolean compleate) int len; mvddest_t *d, *t; - //make sure everything is flushed. - MVDWrite_Begin(255, -1, 0); + if (compleate) + { + //make sure everything is flushed. + MVDWrite_Begin(255, -1, 0); + } if (!demo.dest) return; @@ -380,7 +383,7 @@ void SV_MVD_RunPendingConnections(void) start = start+1; while(*start == ' ' || *start == '\t') start++; - Con_Printf("qtv, got (%s) (%s)\n", com_token, start); + Con_DPrintf("qtv, got (%s) (%s)\n", com_token, start); if (!strcmp(com_token, "VERSION")) { start = COM_ParseToken(start, NULL); @@ -429,6 +432,14 @@ void SV_MVD_RunPendingConnections(void) { //compression not supported yet } + else if (!strcmp(com_token, "QTV_EZQUAKE_EXT")) + { + //if we were treating this as a regular client over tcp (qizmo...) + } + else if (!strcmp(com_token, "USERINFO")) + { + //if we were treating this as a regular client over tcp (qizmo...) + } else { //not recognised. @@ -865,77 +876,9 @@ void SV_MVD_FullClientUpdate(sizebuf_t *msg, client_t *player) MSG_WriteString (msg, info); } -#if 0 -/* -============== -DemoWriteToDisk - -Writes to disk a message meant for specifc client -or all messages if type == 0 -Message is cleared from demobuf after that -============== -*/ - -static void SV_MVDWriteToDisk(int type, int to, float time) -{ - int pos = 0, oldm, oldd; - header_t *p; - int size; - sizebuf_t msg; - - p = (header_t *)demo.dbuf->sb.data; - demo.dbuf->h = NULL; - - oldm = demo.dbuf->bufsize; - oldd = demobuffer->start; - while (pos < demo.dbuf->bufsize) - { - size = p->size; - pos += header + size; - - // no type means we are writing to disk everything - if (!type || (p->type == type && p->to == to)) - { - if (size) - { - msg.data = p->data; - msg.cursize = size; - - SV_WriteMVDMessage(&msg, p->type, p->to, time); - } - - // data is written so it need to be cleard from demobuf - if (demo.dbuf->sb.data != (qbyte*)p) - memmove(demo.dbuf->sb.data + size + header, demo.dbuf->sb.data, (qbyte*)p - demo.dbuf->sb.data); - - demo.dbuf->bufsize -= size + header; - demo.dbuf->sb.data += size + header; - pos -= size + header; - demo.dbuf->sb.maxsize -= size + header; - demobuffer->start += size + header; - } - // move along - p = (header_t *)(p->data + size); - } - - if (demobuffer->start == demobuffer->last) - { - if (demobuffer->start == demobuffer->end) - { - demobuffer->end = 0; // demobuffer is empty - demo.dbuf->sb.data = demobuffer->data; - } - - // go back to begining of the buffer - demobuffer->last = demobuffer->end; - demobuffer->start = 0; - } -} -#endif - sizebuf_t *MVDWrite_Begin(qbyte type, int to, int size) { - if (demomsg.cursize && demomsgtype != type && demomsgto != to && demomsg.cursize+size > sizeof(demomsgbuf)) + if (demomsg.cursize && (demomsgtype != type || demomsgto != to || demomsg.cursize+size > sizeof(demomsgbuf))) { SV_WriteMVDMessage(&demomsg, demomsgtype, demomsgto, demo_prevtime); demomsg.cursize = 0; @@ -945,51 +888,9 @@ sizebuf_t *MVDWrite_Begin(qbyte type, int to, int size) demomsgto = to; demomsg.maxsize = demomsg.cursize+size; - demomsg.cursize = 0; demomsg.data = demomsgbuf; demomsg.prim = demo.recorder.netchan.netprim; return &demomsg; -#if 0 - qbyte *p; - qboolean move = false; - - // will it fit? - while (demo.dbuf->bufsize + size + header > demo.dbuf->sb.maxsize) - { - // if we reached the end of buffer move msgbuf to the begining - if (!move && demobuffer->end > demobuffer->start) - move = true; - - if (!SV_MVDWritePackets(1)) - return false; - - if (move && demobuffer->start > demo.dbuf->bufsize + header + size) - MVDMoveBuf(); - } - - if (demo.dbuf->h == NULL || demo.dbuf->h->type != type || demo.dbuf->h->to != to || demo.dbuf->h->full) { - MVDSetBuf(type, to); - } - - if (demo.dbuf->h->size + size > MAX_QWMSGLEN) - { - demo.dbuf->h->full = 1; - MVDSetBuf(type, to); - } - - // we have to make room for new data - if (demo.dbuf->sb.cursize != demo.dbuf->bufsize) { - p = demo.dbuf->sb.data + demo.dbuf->sb.cursize; - memmove(p+size, p, demo.dbuf->bufsize - demo.dbuf->sb.cursize); - } - - demo.dbuf->bufsize += size; - demo.dbuf->h->size += size; - if ((demobuffer->end += size) > demobuffer->last) - demobuffer->last = demobuffer->end; - - return true; -#endif } /* @@ -1007,19 +908,30 @@ void SV_WriteMVDMessage (sizebuf_t *msg, int type, int to, float time) if (!sv.mvdrecording) return; + if (msg->overflowed) + { + msg->overflowed = false; + Con_Printf("SV_WriteMVDMessage: message overflowed\n"); + return; + } + msec = (time - demo_prevtime)*1000; if (abs(msec) > 1000) { //catastoptic slip. debugging? reset any sync - msec = 0; + msec = 1; demo_prevtime = time; } - else - { - if (msec > 255) msec = 255; - if (msec < 2) msec = 0; + else if (msec > 0) + { //if there was any progress, make sure we write msecs >0 + if (msec > 255) + msec = 255; + if (msec < 1) + msec = 1; demo_prevtime += msec*0.001; } + else + msec = 0; c = msec; DemoWrite(&c, sizeof(c)); @@ -1065,20 +977,23 @@ void SV_WriteMVDMessage (sizebuf_t *msg, int type, int to, float time) } //if you use ClientReliable to write to demo.recorder's message buffer (for code reuse) call this function to ensure its flushed. -void SV_MVD_WriteReliables(void) +void SV_MVD_WriteReliables(qboolean writebroadcasts) { int i; - //chuck in the broadcast reliables - ClientReliableCheckBlock(&demo.recorder, sv.reliable_datagram.cursize); - ClientReliableWrite_SZ(&demo.recorder, sv.reliable_datagram.data, sv.reliable_datagram.cursize); - //and the broadcast unreliables. everything is reliables when it comes to mvds - ClientReliableCheckBlock(&demo.recorder, sv.datagram.cursize); - ClientReliableWrite_SZ(&demo.recorder, sv.datagram.data, sv.datagram.cursize); + if (writebroadcasts) + { + //chuck in the broadcast reliables + ClientReliableCheckBlock(&demo.recorder, sv.reliable_datagram.cursize); + ClientReliableWrite_SZ(&demo.recorder, sv.reliable_datagram.data, sv.reliable_datagram.cursize); + //and the broadcast unreliables. everything is reliables when it comes to mvds + ClientReliableCheckBlock(&demo.recorder, sv.datagram.cursize); + ClientReliableWrite_SZ(&demo.recorder, sv.datagram.data, sv.datagram.cursize); + } if (demo.recorder.netchan.message.cursize) { - SV_WriteMVDMessage(&demo.recorder.netchan.message, dem_all, 0, sv.time); + SV_WriteMVDMessage(&demo.recorder.netchan.message, dem_all, 0, demo_prevtime); demo.recorder.netchan.message.cursize = 0; } for (i = 0; i < demo.recorder.num_backbuf; i++) @@ -1086,7 +1001,7 @@ void SV_MVD_WriteReliables(void) demo.recorder.backbuf.data = demo.recorder.backbuf_data[i]; demo.recorder.backbuf.cursize = demo.recorder.backbuf_size[i]; if (demo.recorder.backbuf.cursize) - SV_WriteMVDMessage(&demo.recorder.backbuf, dem_all, 0, sv.time); + SV_WriteMVDMessage(&demo.recorder.backbuf, dem_all, 0, demo_prevtime); demo.recorder.backbuf_size[i] = 0; } demo.recorder.num_backbuf = 0; @@ -1143,6 +1058,8 @@ qboolean SV_MVDWritePackets (int num) //flush any intermediate data MVDWrite_Begin(255, -1, 0); + msg.allowoverflow = true; //fixme + msg.overflowed = false; msg.prim = svs.netprim; msg.data = msg_buf; msg.maxsize = sizeof(msg_buf); @@ -1714,6 +1631,7 @@ qboolean SV_MVD_Record (mvddest_t *dest) if (!dest) return false; + SV_MVD_WriteReliables(false); DestFlush(true); if (!sv.mvdrecording) @@ -1726,7 +1644,7 @@ qboolean SV_MVD_Record (mvddest_t *dest) demo.datagram.data = demo.datagram_data; demo.datagram.prim = demo.recorder.netchan.netprim; - if (sv_demoExtensions.ival == 2) + if (sv_demoExtensions.ival == 2 || !*sv_demoExtensions.string) { /*more limited subset supported by ezquake*/ demo.recorder.fteprotocolextensions = PEXT_CHUNKEDDOWNLOADS|PEXT_256PACKETENTITIES|PEXT_FLOATCOORDS|PEXT_MODELDBL|PEXT_ENTITYDBL|PEXT_ENTITYDBL2|PEXT_SPAWNSTATIC2; // demo.recorder.fteprotocolextensions |= PEXT_HLBSP; /*ezquake DOES have this, but it is pointless and should have been in some feature mask rather than protocol extensions*/ @@ -1773,6 +1691,7 @@ qboolean SV_MVD_Record (mvddest_t *dest) SV_MVD_SendInitialGamestate(dest); return true; } + void SV_EnableClientsCSQC(void); void SV_MVD_SendInitialGamestate(mvddest_t *dest) { @@ -1788,7 +1707,10 @@ void SV_MVD_SendInitialGamestate(mvddest_t *dest) if (!demo.dest) return; + SV_MVD_WriteReliables(false); + sv.mvdrecording = true; + demo.resetdeltas = true; host_client = &demo.recorder; if (host_client->fteprotocolextensions & PEXT_CSQC) @@ -1860,7 +1782,6 @@ void SV_MVD_SendInitialGamestate(mvddest_t *dest) SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); -#if 1 demo.recorder.prespawn_stage = PRESPAWN_SERVERINFO; demo.recorder.prespawn_idx = 0; demo.recorder.netchan.message = buf; @@ -1875,181 +1796,10 @@ void SV_MVD_SendInitialGamestate(mvddest_t *dest) demo.recorder.prespawn_allow_modellist = true; //normally set for the server to wait for ack. we don't want to wait. SV_SendClientPrespawnInfo(&demo.recorder); - SV_WriteRecordMVDMessage (&demo.recorder.netchan.message); - SZ_Clear (&demo.recorder.netchan.message); + SV_MVD_WriteReliables(false); } memset(&demo.recorder.netchan.message, 0, sizeof(demo.recorder.netchan.message)); -#else - // send music - MSG_WriteByte (&buf, svc_cdtrack); - MSG_WriteByte (&buf, 0); // none in demos - // send server info string - MSG_WriteByte (&buf, svc_stufftext); - MSG_WriteString (&buf, va("fullserverinfo \"%s\"\n", svs.info) ); - - // flush packet - SV_WriteRecordMVDMessage (&buf); - SZ_Clear (&buf); - -// soundlist - MSG_WriteByte (&buf, svc_soundlist); /*FIXME: soundlist2*/ - MSG_WriteByte (&buf, 0); - - n = 0; - s = sv.strings.sound_precache[n+1]; - while (*s) - { - MSG_WriteString (&buf, s); - if (buf.cursize > MAX_QWMSGLEN/2) - { - MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, n); - SV_WriteRecordMVDMessage (&buf); - SZ_Clear (&buf); - MSG_WriteByte (&buf, svc_soundlist); - MSG_WriteByte (&buf, n + 1); - } - n++; - s = sv.strings.sound_precache[n+1]; - } - - if (buf.cursize) - { - MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, 0); - SV_WriteRecordMVDMessage (&buf); - SZ_Clear (&buf); - } - -// modellist - MSG_WriteByte (&buf, svc_modellist); /*FIXME: modellist2*/ - MSG_WriteByte (&buf, 0); - - n = 0; - s = sv.strings.model_precache[n+1]; - while (s) - { - MSG_WriteString (&buf, s); - if (buf.cursize > MAX_QWMSGLEN/2) - { - MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, n); - SV_WriteRecordMVDMessage (&buf); - SZ_Clear (&buf); - MSG_WriteByte (&buf, svc_modellist); - MSG_WriteByte (&buf, n + 1); - } - n++; - s = sv.strings.model_precache[n+1]; - } - if (buf.cursize) - { - MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, 0); - SV_WriteRecordMVDMessage (&buf); - SZ_Clear (&buf); - } - -// baselines - { - entity_state_t from; - edict_t *ent; - entity_state_t *state; - - memset(&from, 0, sizeof(from)); - - for (n = 0; n < sv.world.num_edicts; n++) - { - ent = EDICT_NUM(svprogfuncs, n); - state = &ent->baseline; - - if (!state->number || !state->modelindex) - { //ent doesn't have a baseline - continue; - } - - if (demo.recorder.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) - { - MSG_WriteByte(&buf, svcfte_spawnbaseline2); - SVFTE_EmitBaseline(state, true, &buf); - } - else if (!ent) - { - MSG_WriteByte(&buf, svc_spawnbaseline); - - MSG_WriteShort (&buf, n); - - MSG_WriteByte (&buf, 0); - - MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, 0); - for (i=0 ; i<3 ; i++) - { - MSG_WriteCoord(&buf, 0); - MSG_WriteAngle(&buf, 0); - } - } - else if (demo.recorder.fteprotocolextensions & PEXT_SPAWNSTATIC2) - { - MSG_WriteByte(&buf, svcfte_spawnbaseline2); - SVQW_WriteDelta(&from, state, &buf, true, demo.recorder.fteprotocolextensions); - } - else - { - MSG_WriteByte(&buf, svc_spawnbaseline); - - MSG_WriteShort (&buf, n); - - MSG_WriteByte (&buf, state->modelindex&255); - - MSG_WriteByte (&buf, state->frame); - MSG_WriteByte (&buf, (int)state->colormap); - MSG_WriteByte (&buf, (int)state->skinnum); - for (i=0 ; i<3 ; i++) - { - MSG_WriteCoord(&buf, state->origin[i]); - MSG_WriteAngle(&buf, state->angles[i]); - } - } - if (buf.cursize > MAX_QWMSGLEN/2) - { - SV_WriteRecordMVDMessage (&buf); - SZ_Clear (&buf); - } - } - } - - //prespawn - - for (n = 0; n < sv.num_signon_buffers; n++) - { - if (buf.cursize+sv.signon_buffer_size[n] > MAX_QWMSGLEN/2) - { - SV_WriteRecordMVDMessage (&buf); - SZ_Clear (&buf); - } - SZ_Write (&buf, - sv.signon_buffers[n], - sv.signon_buffer_size[n]); - } - - if (buf.cursize > MAX_QWMSGLEN/2) - { - SV_WriteRecordMVDMessage (&buf); - SZ_Clear (&buf); - } - - MSG_WriteByte (&buf, svc_stufftext); - MSG_WriteString (&buf, va("cmd spawn %i\n",svs.spawncount) ); - - if (buf.cursize) - { - SV_WriteRecordMVDMessage (&buf); - SZ_Clear (&buf); - } -#endif // send current status of all other players for (i = 0; i < demo.recorder.max_net_clients && i < svs.allocated_client_slots; i++) @@ -2060,6 +1810,7 @@ void SV_MVD_SendInitialGamestate(mvddest_t *dest) if (buf.cursize > MAX_QWMSGLEN/2) { + //flush backbuffer SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); } @@ -2096,8 +1847,8 @@ void SV_MVD_SendInitialGamestate(mvddest_t *dest) { for (j = 0; j < MAX_CL_STATS; j++) { - demo.statsi[i][j] ^= -1; - demo.statsf[i][j] *= -0.41426712; //randomish value + demo.statsi[i][j] = 0x7fffffff; + demo.statsf[i][j] = -0x7fffffff; } demo.playerreset[i] = true; } @@ -2108,7 +1859,7 @@ void SV_MVD_SendInitialGamestate(mvddest_t *dest) MSG_WriteString (&buf, "skins\n"); SV_WriteRecordMVDMessage (&buf); - + SV_MVD_WriteReliables(false); SV_WriteSetMVDMessage(); singledest = NULL; diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 327268525..c92ab6cb5 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -42,18 +42,18 @@ solid_edge items only clip against bsp models. */ -cvar_t sv_maxvelocity = SCVAR("sv_maxvelocity","10000"); +cvar_t sv_maxvelocity = CVAR("sv_maxvelocity","10000"); -cvar_t sv_gravity = SCVAR( "sv_gravity", "800"); -cvar_t sv_stopspeed = SCVAR( "sv_stopspeed", "100"); -cvar_t sv_maxspeed = SCVAR( "sv_maxspeed", "320"); -cvar_t sv_spectatormaxspeed = SCVAR( "sv_spectatormaxspeed", "500"); -cvar_t sv_accelerate = SCVAR( "sv_accelerate", "10"); -cvar_t sv_airaccelerate = SCVAR( "sv_airaccelerate", "0.7"); -cvar_t sv_wateraccelerate = SCVAR( "sv_wateraccelerate", "10"); -cvar_t sv_friction = SCVAR( "sv_friction", "4"); -cvar_t sv_waterfriction = SCVAR( "sv_waterfriction", "4"); -cvar_t sv_gameplayfix_noairborncorpse = SCVAR( "sv_gameplayfix_noairborncorpse", "0"); +cvar_t sv_gravity = CVAR( "sv_gravity", "800"); +cvar_t sv_stopspeed = CVAR( "sv_stopspeed", "100"); +cvar_t sv_maxspeed = CVAR( "sv_maxspeed", "320"); +cvar_t sv_spectatormaxspeed = CVAR( "sv_spectatormaxspeed", "500"); +cvar_t sv_accelerate = CVAR( "sv_accelerate", "10"); +cvar_t sv_airaccelerate = CVAR( "sv_airaccelerate", "0.7"); +cvar_t sv_wateraccelerate = CVAR( "sv_wateraccelerate", "10"); +cvar_t sv_friction = CVAR( "sv_friction", "4"); +cvar_t sv_waterfriction = CVAR( "sv_waterfriction", "4"); +cvar_t sv_gameplayfix_noairborncorpse = CVAR( "sv_gameplayfix_noairborncorpse", "0"); cvar_t sv_gameplayfix_multiplethinks = CVARD( "sv_gameplayfix_multiplethinks", "1", "Enables multiple thinks per entity per frame so small nextthink times are accurate. QuakeWorld mods expect a value of 1."); cvar_t sv_gameplayfix_stepdown = CVARD( "sv_gameplayfix_stepdown", "0", "Attempt to step down steps, instead of only up them. Affects non-predicted movetype_walk."); #if !defined(CLIENTONLY) && defined(NQPROT) && !defined(NOLEGACY) diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 69a841c76..7a7128dbf 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -2634,7 +2634,7 @@ void SV_FlushBroadcasts (void) } } - SV_MVD_WriteReliables(); + SV_MVD_WriteReliables(true); SZ_Clear (&sv.reliable_datagram); SZ_Clear (&sv.datagram); @@ -3322,8 +3322,6 @@ void SV_SendMVDMessage(void) // possibly a nails update msg.cursize = 0; msg.prim = demo.recorder.netchan.netprim; - if (!demo.recorder.delta_sequence) - demo.recorder.delta_sequence = -1; // copy the accumulated multicast datagram // for this client out to the message @@ -3339,7 +3337,13 @@ void SV_SendMVDMessage(void) SV_MVDWritePackets(1); } - demo.recorder.delta_sequence = demo.recorder.netchan.incoming_sequence&255; + if (demo.resetdeltas) + { + demo.resetdeltas = false; + demo.recorder.delta_sequence = -1; + } + else + demo.recorder.delta_sequence = demo.recorder.netchan.incoming_sequence&255; demo.recorder.netchan.incoming_sequence++; demo.frames[demo.parsecount&DEMO_FRAMES_MASK].time = demo.time = sv.time; diff --git a/engine/server/sv_sql.c b/engine/server/sv_sql.c index f30c929b9..2c86e9d34 100644 --- a/engine/server/sv_sql.c +++ b/engine/server/sv_sql.c @@ -102,11 +102,11 @@ static dllfunction_t sqlitefuncs[] = dllhandle_t *sqlitehandle; #endif -cvar_t sql_driver = SCVARF("sv_sql_driver", "", CVAR_NOUNSAFEEXPAND); -cvar_t sql_host = SCVARF("sv_sql_host", "127.0.0.1", CVAR_NOUNSAFEEXPAND); -cvar_t sql_username = SCVARF("sv_sql_username", "", CVAR_NOUNSAFEEXPAND); -cvar_t sql_password = SCVARF("sv_sql_password", "", CVAR_NOUNSAFEEXPAND); -cvar_t sql_defaultdb = SCVARF("sv_sql_defaultdb", "", CVAR_NOUNSAFEEXPAND); +cvar_t sql_driver = CVARF("sv_sql_driver", "", CVAR_NOUNSAFEEXPAND); +cvar_t sql_host = CVARF("sv_sql_host", "127.0.0.1", CVAR_NOUNSAFEEXPAND); +cvar_t sql_username = CVARF("sv_sql_username", "", CVAR_NOUNSAFEEXPAND); +cvar_t sql_password = CVARF("sv_sql_password", "", CVAR_NOUNSAFEEXPAND); +cvar_t sql_defaultdb = CVARF("sv_sql_defaultdb", "", CVAR_NOUNSAFEEXPAND); void SQL_PushResult(sqlserver_t *server, queryresult_t *qres) { diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index e264dbb15..916e0bdce 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -42,9 +42,9 @@ cvar_t cl_rollangle = SCVAR("cl_rollangle", "2.0"); extern cvar_t cl_rollspeed; extern cvar_t cl_rollangle; #endif -cvar_t sv_spectalk = SCVAR("sv_spectalk", "1"); +cvar_t sv_spectalk = CVAR("sv_spectalk", "1"); -cvar_t sv_mapcheck = SCVAR("sv_mapcheck", "1"); +cvar_t sv_mapcheck = CVAR("sv_mapcheck", "1"); cvar_t sv_fullredirect = CVARD("sv_fullredirect", "", "This is the ip:port to redirect players to when the server is full"); cvar_t sv_antilag = CVARFD("sv_antilag", "", CVAR_SERVERINFO, "Attempt to backdate impacts to compensate for lag. 0=completely off. 1=mod-controlled. 2=forced, which might break certain uses of traceline."); @@ -60,9 +60,9 @@ cvar_t sv_protocol_nq = CVARD("sv_protocol_nq", "", "Specifies the default prot cvar_t sv_minpitch = CVARAFD("minpitch", "", "sv_minpitch", CVAR_SERVERINFO, "Assumed to be -70"); cvar_t sv_maxpitch = CVARAFD("maxpitch", "", "sv_maxpitch", CVAR_SERVERINFO, "Assumed to be 80"); -cvar_t sv_cmdlikercon = SCVAR("sv_cmdlikercon", "0"); //set to 1 to allow a password of username:password instead of the correct rcon password. -cvar_t cmd_allowaccess = SCVAR("cmd_allowaccess", "0"); //set to 1 to allow cmd to execute console commands on the server. -cvar_t cmd_gamecodelevel = SCVAR("cmd_gamecodelevel", STRINGIFY(RESTRICT_LOCAL)); //execution level which gamecode is told about (for unrecognised commands) +cvar_t sv_cmdlikercon = CVAR("sv_cmdlikercon", "0"); //set to 1 to allow a password of username:password instead of the correct rcon password. +cvar_t cmd_allowaccess = CVAR("cmd_allowaccess", "0"); //set to 1 to allow cmd to execute console commands on the server. +cvar_t cmd_gamecodelevel = CVAR("cmd_gamecodelevel", STRINGIFY(RESTRICT_LOCAL)); //execution level which gamecode is told about (for unrecognised commands) cvar_t sv_pure = CVARFD("sv_pure", "", CVAR_SERVERINFO, "The most evil cvar in the world, many clients will ignore this.\n0=standard quake rules.\n1=clients should prefer files within packages present on the server.\n2=clients should use *only* files within packages present on the server.\nDue to quake 1.01/1.06 differences, a setting of 2 only works in total conversions."); cvar_t sv_nqplayerphysics = CVARAD("sv_nqplayerphysics", "0", "sv_nomsec", "Disable player prediction and run NQ-style player physics instead. This can be used for compatibility with mods that expect exact behaviour."); @@ -98,14 +98,14 @@ extern cvar_t pm_airstep; extern cvar_t pm_walljump; extern cvar_t pm_watersinkspeed; extern cvar_t pm_flyfriction; -cvar_t sv_pushplayers = SCVAR("sv_pushplayers", "0"); +cvar_t sv_pushplayers = CVAR("sv_pushplayers", "0"); //yes, realip cvars need to be fully initialised or realip will be disabled cvar_t sv_getrealip = CVARD("sv_getrealip", "0", "Attempt to obtain a more reliable IP for clients, rather than just their proxy."); -cvar_t sv_realip_kick = SCVAR("sv_realip_kick", "0"); +cvar_t sv_realip_kick = CVAR("sv_realip_kick", "0"); cvar_t sv_realiphostname_ipv4 = CVARD("sv_realiphostname_ipv4", "", "This is the server's public ip:port. This is needed for realip to work when the autodetected/local ip is not globally routable"); cvar_t sv_realiphostname_ipv6 = CVARD("sv_realiphostname_ipv6", "", "This is the server's public ip:port. This is needed for realip to work when the autodetected/local ip is not globally routable"); -cvar_t sv_realip_timeout = SCVAR("sv_realip_timeout", "10"); +cvar_t sv_realip_timeout = CVAR("sv_realip_timeout", "10"); #ifdef VOICECHAT cvar_t sv_voip = CVARD("sv_voip", "1", "Enable reception of voice packets."); @@ -1015,7 +1015,7 @@ void SV_SendClientPrespawnInfo(client_t *client) else if (client->prespawn_idx == 4) { ClientReliableWrite_Begin(client, svc_setpause, 2); - ClientReliableWrite_Byte (client, sv.paused); + ClientReliableWrite_Byte (client, sv.paused!=0); } else { diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c index beb3f2d2e..a6d028fef 100644 --- a/engine/vk/vk_init.c +++ b/engine/vk/vk_init.c @@ -2847,6 +2847,9 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat } } + //create the platform-specific surface + createSurface(); + //figure out which gpu we're going to use { uint32_t gpucount = 0, i; @@ -2863,7 +2866,24 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat for (i = 0; i < gpucount; i++) { VkPhysicalDeviceProperties props; + uint32_t j, queue_count; vkGetPhysicalDeviceProperties(devs[i], &props); + vkGetPhysicalDeviceQueueFamilyProperties(devs[i], &queue_count, NULL); + + for (j = 0; j < queue_count; j++) + { + VkBool32 supportsPresent; + VkAssert(vkGetPhysicalDeviceSurfaceSupportKHR(devs[i], j, vk.surface, &supportsPresent)); + if (supportsPresent) + break; //okay, this one should be usable + } + if (j == queue_count) + { + //no queues can present to that surface, so I guess we can't use that device + Con_DPrintf("vulkan: ignoring device %s as it can't present to window\n", props.deviceName); + continue; + } + if (!vk.gpu) vk.gpu = devs[i]; switch(props.deviceType) @@ -2954,9 +2974,6 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat ); } - //create the platform-specific surface - createSurface(); - //figure out which of the device's queue's we're going to use { uint32_t queue_count, i;