diff --git a/engine/Makefile b/engine/Makefile index 2a4b8b5c8..9f5b5c669 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -34,6 +34,13 @@ WHOAMI:=$(shell whoami) ifeq ($(FTE_TARGET),win32) ifeq ($(shell $(CC) -v 2>&1 | grep mingw),) #CC didn't state that it was mingw... so try fixing that up + ifneq ($(shell which i686-w64-mingw32-gcc 2> /dev/null),) + #yup, the alternative exists (this matches the one debian has) + CC=i686-w64-mingw32-gcc + WINDRES=i686-w64-mingw32-windres + STRIP=i686-w64-mingw32-strip +# BITS?=32 + endif ifneq ($(shell which i586-mingw32msvc-gcc 2> /dev/null),) #yup, the alternative exists (this matches the one debian has) CC=i586-mingw32msvc-gcc diff --git a/engine/client/cl_cam.c b/engine/client/cl_cam.c index 3bd23b65e..9107cfe77 100644 --- a/engine/client/cl_cam.c +++ b/engine/client/cl_cam.c @@ -35,10 +35,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define BUTTON_ATTACK 1 #define MAX_ANGLE_TURN 10 -vec3_t desired_position[MAX_SPLITS]; // where the camera wants to be -static qboolean locked[MAX_SPLITS]; -static int oldbuttons[MAX_SPLITS]; - char cl_spectatorgroup[] = "Spectator Tracking"; // track high fragger @@ -50,11 +46,6 @@ cvar_t cl_selfcam = SCVAR("cl_selfcam", "1"); //cvar_t cl_camera_maxpitch = {"cl_camera_maxpitch", "10" }; //cvar_t cl_camera_maxyaw = {"cl_camera_maxyaw", "30" }; -vec3_t cam_viewangles[MAX_SPLITS]; -double cam_lastviewtime[MAX_SPLITS]; - -int spec_track[MAX_SPLITS]; // player# of who we are tracking -int autocam[MAX_SPLITS]; int selfcam=1; @@ -64,11 +55,11 @@ static float vlen(vec3_t v) } // returns true if weapon model should be drawn in camera mode -qboolean Cam_DrawViewModel(int pnum) +qboolean Cam_DrawViewModel(playerview_t *pv) { if (cl.spectator) { - if (autocam[pnum] && locked[pnum] && cl_chasecam.ival) + if (pv->cam_auto && pv->cam_locked && cl_chasecam.ival) return true; return false; } @@ -81,60 +72,66 @@ qboolean Cam_DrawViewModel(int pnum) } // returns true if we should draw this player, we don't if we are chase camming -qboolean Cam_DrawPlayer(int pnum, int playernum) +qboolean Cam_DrawEntity(playerview_t *pv, int entitykey) { +// if (!entitykey) + return true; // if (playernum == cl.playernum[pnum]) // return false; if (cl.spectator) { - if (autocam[pnum] && locked[pnum] && (cl_chasecam.value||scr_chatmode==2) && - spec_track[pnum] == playernum && r_secondaryview != 2) + if (pv->cam_auto && pv->cam_locked && (cl_chasecam.value||scr_chatmode==2) && + pv->cam_spec_track+1 == entitykey && r_secondaryview != 2) return false; } else { if (selfcam == 1 && !r_refdef.externalview) - if (playernum == (cl.viewentity[pnum]?cl.viewentity[pnum]-1:(cl.playernum[pnum]))) + if (entitykey == pv->viewentity) return false; } return true; } -int Cam_TrackNum(int pnum) +int Cam_TrackNum(playerview_t *pv) { - if (!autocam[pnum]) + if (!pv->cam_auto) return -1; - return spec_track[pnum]; + return pv->cam_spec_track; } -void Cam_Unlock(int pnum) +void Cam_Unlock(playerview_t *pv) { - if (autocam[pnum]) + if (pv->cam_auto) { CL_SendClientCommand(true, "ptrack"); - autocam[pnum] = CAM_NONE; - locked[pnum] = false; + pv->cam_auto = CAM_NONE; + pv->cam_locked = false; + pv->viewentity = (cls.demoplayback)?0:(pv->playernum+1); //free floating Sbar_Changed(); } } -void Cam_Lock(int pnum, int playernum) +void Cam_Lock(playerview_t *pv, int playernum) { int i; - cam_lastviewtime[pnum] = -1000; + pv->cam_lastviewtime = -1000; CL_SendClientCommand(true, "ptrack %i", playernum); - spec_track[pnum] = playernum; - locked[pnum] = false; + pv->cam_spec_track = playernum; + pv->cam_locked = false; + pv->viewentity = (cls.demoplayback)?0:(pv->playernum+1); //free floating + Skin_FlushPlayers(); if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) { - memcpy(&cl.playerview[pnum].stats, cl.players[playernum].stats, sizeof(cl.playerview[pnum].stats)); - locked[pnum] = true; //instantly lock if the player is valid. + memcpy(&pv->stats, cl.players[playernum].stats, sizeof(pv->stats)); + pv->cam_locked = true; //instantly lock if the player is valid. + pv->viewentity = playernum+1; } Sbar_Changed(); @@ -218,7 +215,7 @@ static qboolean Cam_IsVisible(vec3_t playerorigin, vec3_t vec) return true; } -static qboolean InitFlyby(int pnum, vec3_t selforigin, vec3_t playerorigin, vec3_t playerviewangles, int checkvis) +static qboolean InitFlyby(playerview_t *pv, vec3_t selforigin, vec3_t playerorigin, vec3_t playerviewangles, int checkvis) { float f, max; vec3_t vec, vec2; @@ -317,16 +314,16 @@ static qboolean InitFlyby(int pnum, vec3_t selforigin, vec3_t playerorigin, vec3 return false; } - locked[pnum] = true; - VectorCopy(vec, desired_position[pnum]); + pv->cam_locked = true; + VectorCopy(vec, pv->cam_desired_position); return true; } -static void Cam_CheckHighTarget(int pnum) +static void Cam_CheckHighTarget(playerview_t *pv) { int i, j, max; player_info_t *s; - int sp; + playerview_t *spv; j = -1; for (i = 0, max = -9999; i < MAX_CLIENTS; i++) @@ -334,12 +331,13 @@ static void Cam_CheckHighTarget(int pnum) s = &cl.players[i]; if (s->name[0] && !s->spectator && s->frags > max) { - for (sp = pnum-1; sp >= 0; sp--) + //skip it if an earlier seat is watching it already + for (spv = pv-1; spv >= cl.playerview && spv < &cl.playerview[cl.splitclients]; spv--) { - if (Cam_TrackNum(sp) == i) + if (Cam_TrackNum(spv) == i) break; } - if (sp == -1) + if (!(spv >= cl.playerview && spv < &cl.playerview[cl.splitclients])) { max = s->frags; j = i; @@ -348,21 +346,22 @@ static void Cam_CheckHighTarget(int pnum) } if (j >= 0) { - if (!locked[pnum] || cl.players[j].frags > cl.players[spec_track[pnum]].frags) + if (!pv->cam_locked || cl.players[j].frags > cl.players[pv->cam_spec_track].frags) { - Cam_Lock(pnum, j); - for (sp = pnum+1; sp < cl.splitclients; sp++) + Cam_Lock(pv, j); + //un-lock any higher seats watching our new target. this keeps things ordered. + for (spv = pv+1; spv >= cl.playerview && spv < &cl.playerview[cl.splitclients]; spv++) { - if (Cam_TrackNum(sp) == j) - locked[sp] = false; + if (Cam_TrackNum(spv) == j) + spv->cam_locked = false; } } } else - Cam_Unlock(pnum); + Cam_Unlock(pv); } -void Cam_SelfTrack(int pnum) +void Cam_SelfTrack(playerview_t *pv) { vec3_t vec; if (!cl.worldmodel || cl.worldmodel->needload) @@ -378,36 +377,36 @@ void Cam_SelfTrack(int pnum) vec3_t forward, right, up; trace_t tr; AngleVectors(r_refdef.viewangles, forward, right, up); - VectorMA(cl.playerview[pnum].simorg, -128, forward, desired_position[pnum]); - tr = Cam_DoTrace(cl.playerview[pnum].simorg, desired_position[pnum]); - VectorCopy(tr.endpos, desired_position[pnum]); + VectorMA(pv->simorg, -128, forward, pv->cam_desired_position); + tr = Cam_DoTrace(pv->simorg, pv->cam_desired_position); + VectorCopy(tr.endpos, pv->cam_desired_position); } else { //view from a random wall - if (!locked[pnum] || !Cam_IsVisible(cl.playerview[pnum].simorg, desired_position[pnum])) + if (!pv->cam_locked || !Cam_IsVisible(pv->simorg, pv->cam_desired_position)) { - if (!locked[pnum] || realtime - cam_lastviewtime[pnum] > 0.1) + if (!pv->cam_locked || realtime - pv->cam_lastviewtime > 0.1) { - if (!InitFlyby(pnum, desired_position[pnum], cl.playerview[pnum].simorg, cl.playerview[pnum].simangles, true)) - InitFlyby(pnum, desired_position[pnum], cl.playerview[pnum].simorg, cl.playerview[pnum].simangles, false); - cam_lastviewtime[pnum] = realtime; + if (!InitFlyby(pv, pv->cam_desired_position, pv->simorg, pv->simangles, true)) + InitFlyby(pv, pv->cam_desired_position, pv->simorg, pv->simangles, false); + pv->cam_lastviewtime = realtime; } } else { - cam_lastviewtime[pnum] = realtime; + pv->cam_lastviewtime = realtime; } //tracking failed. - if (!locked[pnum]) + if (!pv->cam_locked) return; } // move there locally immediately - VectorCopy(desired_position[pnum], r_refdef.vieworg); + VectorCopy(pv->cam_desired_position, r_refdef.vieworg); - VectorSubtract(cl.playerview[pnum].simorg, desired_position[pnum], vec); + VectorSubtract(pv->simorg, pv->cam_desired_position, vec); VectorAngles(vec, NULL, r_refdef.viewangles); r_refdef.viewangles[0] = -r_refdef.viewangles[0]; } @@ -417,7 +416,7 @@ void Cam_SelfTrack(int pnum) // // Take over the user controls and track a player. // We find a nice position to watch the player and move there -void Cam_Track(int pnum, usercmd_t *cmd) +void Cam_Track(playerview_t *pv, usercmd_t *cmd) { player_state_t *player, *self; inframe_t *frame; @@ -427,51 +426,51 @@ void Cam_Track(int pnum, usercmd_t *cmd) if (!cl.spectator || !cl.worldmodel) //can happen when the server changes level return; - if (cl_hightrack.value && !locked[pnum]) - Cam_CheckHighTarget(pnum); + if (cl_hightrack.value && !pv->cam_locked) + Cam_CheckHighTarget(pv); - if (!autocam[pnum] || cls.state != ca_active) + if (!pv->cam_auto || cls.state != ca_active) return; - if (locked[pnum] && (!cl.players[spec_track[pnum]].name[0] || cl.players[spec_track[pnum]].spectator)) + if (pv->cam_locked && (!cl.players[pv->cam_spec_track].name[0] || cl.players[pv->cam_spec_track].spectator)) { - locked[pnum] = false; + pv->cam_locked = false; if (cl_hightrack.value) - Cam_CheckHighTarget(pnum); + Cam_CheckHighTarget(pv); else - Cam_Unlock(pnum); + Cam_Unlock(pv); return; } frame = &cl.inframes[cl.validsequence & UPDATE_MASK]; - player = frame->playerstate + spec_track[pnum]; - self = frame->playerstate + cl.playernum[pnum]; + player = frame->playerstate + pv->cam_spec_track; + self = frame->playerstate + pv->playernum; - if (!locked[pnum] || !Cam_IsVisible(player->origin, desired_position[pnum])) + if (!pv->cam_locked || !Cam_IsVisible(player->origin, pv->cam_desired_position)) { - if (!locked[pnum] || realtime - cam_lastviewtime[pnum] > 0.1) + if (!pv->cam_locked || realtime - pv->cam_lastviewtime > 0.1) { - if (!InitFlyby(pnum, self->origin, player->origin, player->viewangles, true)) - InitFlyby(pnum, self->origin, player->origin, player->viewangles, false); - cam_lastviewtime[pnum] = realtime; + if (!InitFlyby(pv, self->origin, player->origin, player->viewangles, true)) + InitFlyby(pv, self->origin, player->origin, player->viewangles, false); + pv->cam_lastviewtime = realtime; } } else { - cam_lastviewtime[pnum] = realtime; + pv->cam_lastviewtime = realtime; } //tracking failed. - if (!locked[pnum] || !autocam[pnum]) + if (!pv->cam_locked || !pv->cam_auto) return; if (cl_chasecam.value || scr_chatmode == 2) { if (scr_chatmode != 2) - cam_lastviewtime[pnum] = realtime; + pv->cam_lastviewtime = realtime; - VectorCopy(player->viewangles, cl.playerview[pnum].viewangles); + VectorCopy(player->viewangles, pv->viewangles); if (memcmp(player->origin, &self->origin, sizeof(player->origin)) != 0) { if (!cls.demoplayback) @@ -491,23 +490,23 @@ void Cam_Track(int pnum, usercmd_t *cmd) // Ok, move to our desired position and set our angles to view // the player - VectorSubtract(desired_position[pnum], self->origin, vec); + VectorSubtract(pv->cam_desired_position, self->origin, vec); len = vlen(vec); cmd->forwardmove = cmd->sidemove = cmd->upmove = 0; if (len > 16) { // close enough? MSG_WriteByte (&cls.netchan.message, clc_tmove); - MSG_WriteCoord (&cls.netchan.message, desired_position[pnum][0]); - MSG_WriteCoord (&cls.netchan.message, desired_position[pnum][1]); - MSG_WriteCoord (&cls.netchan.message, desired_position[pnum][2]); + MSG_WriteCoord (&cls.netchan.message, pv->cam_desired_position[0]); + MSG_WriteCoord (&cls.netchan.message, pv->cam_desired_position[1]); + MSG_WriteCoord (&cls.netchan.message, pv->cam_desired_position[2]); } // move there locally immediately - VectorCopy(desired_position[pnum], self->origin); + VectorCopy(pv->cam_desired_position, self->origin); - VectorSubtract(player->origin, desired_position[pnum], vec); - VectorAngles(vec, NULL, cl.playerview[pnum].viewangles); - cl.playerview[pnum].viewangles[0] = -cl.playerview[pnum].viewangles[0]; + VectorSubtract(player->origin, pv->cam_desired_position, vec); + VectorAngles(vec, NULL, pv->viewangles); + pv->viewangles[0] = -pv->viewangles[0]; } void Cam_SetAutoTrack(int userid) @@ -515,7 +514,7 @@ void Cam_SetAutoTrack(int userid) } -void Cam_TrackCrosshairedPlayer(int pnum) +void Cam_TrackCrosshairedPlayer(playerview_t *pv) { inframe_t *frame; player_state_t *player; @@ -526,7 +525,7 @@ void Cam_TrackCrosshairedPlayer(int pnum) vec3_t dir; frame = &cl.inframes[cl.validsequence & UPDATE_MASK]; - player = frame->playerstate + cl.playernum[pnum]; + player = frame->playerstate + pv->playernum; VectorCopy(player->origin, selforg); for (i = 0; i < MAX_CLIENTS; i++) @@ -544,12 +543,12 @@ void Cam_TrackCrosshairedPlayer(int pnum) Con_Printf("Track %i? %f\n", best, bestdot); if (best != -1) //did we actually get someone? { - autocam[pnum]++; - Cam_Lock(pnum, best); + pv->cam_auto++; + Cam_Lock(pv, best); } } -void Cam_FinishMove(int pnum, usercmd_t *cmd) +void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd) { int i; player_info_t *s; @@ -571,26 +570,26 @@ void Cam_FinishMove(int pnum, usercmd_t *cmd) nb |= (cmd->forwardmove>0)?32:0; nb |= (cmd->upmove<0)?64:0; nb |= (cmd->upmove>0)?128:0; - if (Cam_TrackNum(pnum) >= 0) + if (Cam_TrackNum(pv) >= 0) { - if (nb & (nb ^ oldbuttons[pnum]) & 4) + if (nb & (nb ^ pv->cam_oldbuttons) & 4) Cvar_SetValue(&cl_demospeed, max(cl_demospeed.value - 0.1, 0)); - if (nb & (nb ^ oldbuttons[pnum]) & 8) + if (nb & (nb ^ pv->cam_oldbuttons) & 8) Cvar_SetValue(&cl_demospeed, min(cl_demospeed.value + 0.1, 10)); - if (nb & (nb ^ oldbuttons[pnum]) & (4|8)) + if (nb & (nb ^ pv->cam_oldbuttons) & (4|8)) Con_Printf("playback speed: %g%%\n", cl_demospeed.value*100); - if (nb & (nb ^ oldbuttons[pnum]) & 16) + if (nb & (nb ^ pv->cam_oldbuttons) & 16) Cbuf_AddText("demo_jump +10", RESTRICT_LOCAL); - if (nb & (nb ^ oldbuttons[pnum]) & 32) + if (nb & (nb ^ pv->cam_oldbuttons) & 32) Cbuf_AddText("demo_jump -10", RESTRICT_LOCAL); - if (nb & (nb ^ oldbuttons[pnum]) & (4|8)) + if (nb & (nb ^ pv->cam_oldbuttons) & (4|8)) Con_Printf("playback speed: %g%%\n", cl_demospeed.value*100); - if (nb & (nb ^ oldbuttons[pnum]) & 64) + if (nb & (nb ^ pv->cam_oldbuttons) & 64) Cvar_SetValue(&cl_splitscreen, max(cl_splitscreen.ival - 1, 0)); - if (nb & (nb ^ oldbuttons[pnum]) & 128) + if (nb & (nb ^ pv->cam_oldbuttons) & 128) Cvar_SetValue(&cl_splitscreen, min(cl_splitscreen.ival + 1, MAX_SPLITS-1)); } - oldbuttons[pnum] = (oldbuttons[pnum] & 3) | (nb & ~3); + pv->cam_oldbuttons = (pv->cam_oldbuttons & 3) | (nb & ~3); if (cmd->impulse) { int pl = cmd->impulse; @@ -609,11 +608,12 @@ void Cam_FinishMove(int pnum, usercmd_t *cmd) pl--; if (!pl) { - Cam_Lock(pnum, i); - - pnum++; - if (pnum < cl.splitclients) + Cam_Lock(pv, i); + if (pv >= &cl.playerview[0] && pv < &cl.playerview[cl.splitclients]) + { pl = 1; + pv++; + } else break; } @@ -625,16 +625,15 @@ void Cam_FinishMove(int pnum, usercmd_t *cmd) if (cmd->buttons & BUTTON_ATTACK) { - if (!(oldbuttons[pnum] & BUTTON_ATTACK)) + if (!(pv->cam_oldbuttons & BUTTON_ATTACK)) { + pv->cam_oldbuttons |= BUTTON_ATTACK; + pv->cam_auto++; - oldbuttons[pnum] |= BUTTON_ATTACK; - autocam[pnum]++; - - if (autocam[pnum] > CAM_TRACK) + if (pv->cam_auto > CAM_TRACK) { - Cam_Unlock(pnum); - VectorCopy(cl.playerview[pnum].viewangles, cmd->angles); + Cam_Unlock(pv); + VectorCopy(pv->viewangles, cmd->angles); return; } } @@ -643,62 +642,62 @@ void Cam_FinishMove(int pnum, usercmd_t *cmd) } else { - oldbuttons[pnum] &= ~BUTTON_ATTACK; - if (!autocam[pnum]) + pv->cam_oldbuttons &= ~BUTTON_ATTACK; + if (!pv->cam_auto) { - if ((cmd->buttons & BUTTON_JUMP) && !(oldbuttons[pnum] & BUTTON_JUMP)) - Cam_TrackCrosshairedPlayer(pnum); - oldbuttons[pnum] = (oldbuttons[pnum]&~BUTTON_JUMP) | (cmd->buttons & BUTTON_JUMP); + if ((cmd->buttons & BUTTON_JUMP) && !(pv->cam_oldbuttons & BUTTON_JUMP)) + Cam_TrackCrosshairedPlayer(pv); + pv->cam_oldbuttons = (pv->cam_oldbuttons&~BUTTON_JUMP) | (cmd->buttons & BUTTON_JUMP); return; } } - if (autocam[pnum] && cl_hightrack.ival) + if (pv->cam_auto && cl_hightrack.ival) { - Cam_CheckHighTarget(pnum); + Cam_CheckHighTarget(pv); return; } - if (locked[pnum]) + if (pv->cam_locked) { - if ((cmd->buttons & BUTTON_JUMP) && (oldbuttons[pnum] & BUTTON_JUMP)) + if ((cmd->buttons & BUTTON_JUMP) && (pv->cam_oldbuttons & BUTTON_JUMP)) return; // don't pogo stick if (!(cmd->buttons & BUTTON_JUMP)) { - oldbuttons[pnum] &= ~BUTTON_JUMP; + pv->cam_oldbuttons &= ~BUTTON_JUMP; return; } - oldbuttons[pnum] |= BUTTON_JUMP; // don't jump again until released + pv->cam_oldbuttons |= BUTTON_JUMP; // don't jump again until released } // Con_Printf("Selecting track target...\n"); - if (locked[pnum] && autocam[pnum]) - end = (spec_track[pnum] + 1) % MAX_CLIENTS; + if (pv->cam_locked && pv->cam_auto) + end = (pv->cam_spec_track + 1) % MAX_CLIENTS; else - end = spec_track[pnum]; + end = pv->cam_spec_track; i = end; do { s = &cl.players[i]; if (s->name[0] && !s->spectator) { - Cam_Lock(pnum, i); + Cam_Lock(pv, i); return; } i = (i + 1) % MAX_CLIENTS; } while (i != end); // stay on same guy? - i = spec_track[pnum]; + i = pv->cam_spec_track; s = &cl.players[i]; if (s->name[0] && !s->spectator) { - Cam_Lock(pnum, i); + Cam_Lock(pv, i); return; } Con_Printf("No target found ...\n"); - autocam[pnum] = locked[pnum] = false; + pv->cam_auto = pv->cam_locked = false; } void Cam_Reset(void) @@ -706,21 +705,20 @@ void Cam_Reset(void) int pnum; for (pnum = 0; pnum < MAX_SPLITS; pnum++) { - autocam[pnum] = CAM_NONE; - spec_track[pnum] = 0; + playerview_t *pv = &cl.playerview[pnum]; + pv->cam_auto = CAM_NONE; + pv->cam_spec_track = 0; } } -void Cam_TrackPlayer(int pnum, char *cmdname, char *plrarg) +void Cam_TrackPlayer(int seat, char *cmdname, char *plrarg) { + playerview_t *pv = &cl.playerview[seat]; int slot; player_info_t *s; - if (pnum >= MAX_SPLITS) - { - Con_Printf("This command is unavailable in this compilation.\n"); + if (seat >= MAX_SPLITS) return; - } if (cls.state <= ca_connected) { @@ -736,7 +734,7 @@ void Cam_TrackPlayer(int pnum, char *cmdname, char *plrarg) if (!Q_strcasecmp(plrarg, "off")) { - Cam_Unlock(pnum); + Cam_Unlock(pv); return; } @@ -782,9 +780,9 @@ void Cam_TrackPlayer(int pnum, char *cmdname, char *plrarg) } } - autocam[pnum] = CAM_TRACK; - Cam_Lock(pnum, slot); - locked[pnum] = true; + pv->cam_auto = CAM_TRACK; + Cam_Lock(pv, slot); + pv->cam_locked = true; } void Cam_Track_f(void) diff --git a/engine/client/cl_cg.c b/engine/client/cl_cg.c index 3d10e5234..6f38bcda8 100644 --- a/engine/client/cl_cg.c +++ b/engine/client/cl_cg.c @@ -19,11 +19,6 @@ void CG_Command_f(void); #define CGTAGNUM 5423 -#define VM_TOSTRCACHE(a) VMQ3_StringToHandle(VM_POINTER(a)) -#define VM_FROMSTRCACHE(a) VMQ3_StringFromHandle(a) -char *VMQ3_StringFromHandle(int handle); -int VMQ3_StringToHandle(char *str); - extern model_t mod_known[]; extern int mod_numknown; #define VM_FROMMHANDLE(a) ((a&&((unsigned int)a)<=mod_numknown)?mod_known+a-1:NULL) @@ -331,10 +326,10 @@ qboolean CGQ3_GetUserCmd(int cmdNumber, q3usercmd_t *ucmd) usercmd_t *cmd; cmdNumber--; - if (cmdNumber > ccs.currentUserCmdNumber) + if (cmdNumber > cl.movesequence) Host_EndGame("CL_GetUserCmd: cmdNumber > ccs.currentUserCmdNumber"); - if (ccs.currentUserCmdNumber - cmdNumber > CMD_MASK) + if (cl.movesequence - cmdNumber > CMD_MASK) return false; // too old cmd = &cl.outframes[(cmdNumber) & CMD_MASK].cmd[0]; @@ -929,7 +924,7 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con break; case CG_GETCURRENTCMDNUMBER: - VM_LONG(ret) = ccs.currentUserCmdNumber; + VM_LONG(ret) = cl.movesequence; break; case CG_GETUSERCMD: VALIDATEPOINTER(arg[1], sizeof(q3usercmd_t)); @@ -1140,7 +1135,7 @@ void CG_Start (void) if (cgvm) { //hu... cgame doesn't appear to have a query version call! SCR_EndLoadingPlaque(); - VM_Call(cgvm, CG_INIT, ccs.serverMessageNum, ccs.lastServerCommandNum, cl.playernum[0]); + VM_Call(cgvm, CG_INIT, ccs.serverMessageNum, ccs.lastServerCommandNum, cl.playerview[0].playernum); } else { diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 4119d4993..03bac1fce 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -134,6 +134,7 @@ Dumps the current net message, prefixed by the length and view angles void CL_WriteDemoMessage (sizebuf_t *msg) { int len; + int i; float fl; qbyte c; @@ -148,9 +149,27 @@ void CL_WriteDemoMessage (sizebuf_t *msg) c = dem_read; VFS_WRITE (cls.demooutfile, &c, sizeof(c)); - len = LittleLong (msg->cursize); - VFS_WRITE (cls.demooutfile, &len, 4); - VFS_WRITE (cls.demooutfile, msg->data, msg->cursize); + if (*(int*)msg->data == -1) + { + //connectionless packet. + len = LittleLong (msg->cursize); + VFS_WRITE (cls.demooutfile, &len, 4); + VFS_WRITE (cls.demooutfile, msg->data + msg_readcount, msg->cursize - msg_readcount); + } + else + { + //regenerate a legacy netchan. no fragmentation support, but whatever. this ain't udp. + //the length + len = LittleLong (msg->cursize - msg_readcount + 8); + VFS_WRITE (cls.demooutfile, &len, 4); + //hack the netchan here. + i = cls.netchan.incoming_sequence; + VFS_WRITE (cls.demooutfile, &i, 4); + i = cls.netchan.incoming_acknowledged; + VFS_WRITE (cls.demooutfile, &i, 4); + //and the data + VFS_WRITE (cls.demooutfile, msg->data + msg_readcount, msg->cursize - msg_readcount); + } VFS_FLUSH (cls.demooutfile); } @@ -729,23 +748,34 @@ readit: if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) { + int seat; switch(cls_lasttype) { case dem_multiple: - tracknum = spec_track[0]; - if (!autocam[0]) - tracknum = -1; - if (tracknum == -1 || !(cls_lastto & (1 << tracknum))) + for (seat = 0; seat < cl.splitclients; seat++) + { + tracknum = cl.playerview[seat].cam_spec_track; + if (!cl.playerview[seat].cam_auto) + tracknum = -1; + if (tracknum == -1 || !(cls_lastto & (1 << tracknum))) + continue; + } + if (seat == cl.splitclients) { olddemotime = demotime; goto readnext; } break; case dem_single: - tracknum = spec_track[0]; - if (!autocam[0]) - tracknum = -1; - if (tracknum == -1 || cls_lastto != tracknum) + for (seat = 0; seat < cl.splitclients; seat++) + { + tracknum = cl.playerview[seat].cam_spec_track; + if (!cl.playerview[seat].cam_auto) + tracknum = -1; + if (tracknum == -1 || !(cls_lastto != tracknum)) + continue; + } + if (seat == cl.splitclients) { olddemotime = demotime; goto readnext; @@ -834,8 +864,6 @@ qboolean CL_GetMessage (void) if (NET_GetPacket (NS_CLIENT, 0) < 0) return false; - CL_WriteDemoMessage (&net_message); - return true; } @@ -1107,15 +1135,27 @@ void CL_Record_f (void) MSG_WriteLong (&buf, cl.servercount); MSG_WriteString (&buf, gamedirfile); - for (i = 0; i < cl.splitclients; i++) + if (cls.fteprotocolextensions2 & PEXT2_MAXPLAYERS) { - if (cl.spectator) - MSG_WriteByte (&buf, cl.playernum[i] | 128); - else - MSG_WriteByte (&buf, cl.playernum[i]); + 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); } - if (cls.fteprotocolextensions & PEXT_SPLITSCREEN) - MSG_WriteByte (&buf, 128); // send full levelname MSG_WriteString (&buf, cl.levelname); @@ -1141,10 +1181,10 @@ void CL_Record_f (void) MSG_WriteByte (&buf, 0); // none in demos #ifdef PEXT_SETVIEW - if (cl.viewentity[0]) //tell the player if we have a different view entity + if (cl.playerview[0].viewentity != cl.playerview[0].playernum+1) //tell the player if we have a different view entity { MSG_WriteByte (&buf, svc_setview); - MSG_WriteByte (&buf, cl.viewentity[0]); + MSG_WriteEntity (&buf, cl.playerview[0].viewentity); } #endif // flush packet @@ -1284,30 +1324,45 @@ void CL_Record_f (void) // send current status of all other players - for (i = 0; i < MAX_CLIENTS; i++) + for (i = 0; i < cl.allocated_client_slots; i++) { player = cl.players + i; - MSG_WriteByte (&buf, svc_updatefrags); - MSG_WriteByte (&buf, i); - MSG_WriteShort (&buf, player->frags); + if (player->frags != 0) + { + MSG_WriteByte (&buf, svc_updatefrags); + MSG_WriteByte (&buf, i); + MSG_WriteShort (&buf, player->frags); + } - MSG_WriteByte (&buf, svc_updateping); - MSG_WriteByte (&buf, i); - MSG_WriteShort (&buf, player->ping); + if (player->ping != 0) + { + MSG_WriteByte (&buf, svc_updateping); + MSG_WriteByte (&buf, i); + MSG_WriteShort (&buf, player->ping); + } - MSG_WriteByte (&buf, svc_updatepl); - MSG_WriteByte (&buf, i); - MSG_WriteByte (&buf, player->pl); + if (player->pl != 0) + { + MSG_WriteByte (&buf, svc_updatepl); + MSG_WriteByte (&buf, i); + MSG_WriteByte (&buf, player->pl); + } - MSG_WriteByte (&buf, svc_updateentertime); - MSG_WriteByte (&buf, i); - MSG_WriteFloat (&buf, player->entertime); + if (*player->userinfo) + { + MSG_WriteByte (&buf, svc_updateentertime); + MSG_WriteByte (&buf, i); + MSG_WriteFloat (&buf, player->entertime); + } - MSG_WriteByte (&buf, svc_updateuserinfo); - MSG_WriteByte (&buf, i); - MSG_WriteLong (&buf, player->userid); - MSG_WriteString (&buf, player->userinfo); + if (*player->userinfo) + { + MSG_WriteByte (&buf, svc_updateuserinfo); + MSG_WriteByte (&buf, i); + MSG_WriteLong (&buf, player->userid); + MSG_WriteString (&buf, player->userinfo); + } if (buf.cursize > MAX_QWMSGLEN/2) { @@ -1443,6 +1498,7 @@ play [demoname] */ void CL_PlayDemo_f (void) { + char *demoname; if (Cmd_Argc() != 2) { Con_Printf ("playdemo : plays a demo\n"); @@ -1463,7 +1519,10 @@ void CL_PlayDemo_f (void) #endif #endif - CL_PlayDemo(Cmd_Argv(1)); + demoname = Cmd_Argv(1); + if (*demoname == '#' && Cmd_FromGamecode()) + return; + CL_PlayDemo(demoname); } void CL_PlayDemo(char *demoname) diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index e35991363..f89602f89 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -233,9 +233,10 @@ void CL_DecayLights (void) { int i; dlight_t *dl; + float frametime = host_frametime; if (cl.paused) //DON'T DO IT!!! - return; + frametime = 0; dl = cl_dlights+rtlights_first; for (i=rtlights_first ; iradius -= host_frametime*dl->decay; + dl->radius -= frametime*dl->decay; if (dl->radius < 0) { if (i==rtlights_first) @@ -269,21 +270,21 @@ void CL_DecayLights (void) if (dl->channelfade[0]) { - dl->color[0] -= host_frametime*dl->channelfade[0]; + dl->color[0] -= frametime*dl->channelfade[0]; if (dl->color[0] < 0) dl->color[0] = 0; } if (dl->channelfade[1]) { - dl->color[1] -= host_frametime*dl->channelfade[1]; + dl->color[1] -= frametime*dl->channelfade[1]; if (dl->color[1] < 0) dl->color[1] = 0; } if (dl->channelfade[2]) { - dl->color[2] -= host_frametime*dl->channelfade[2]; + dl->color[2] -= frametime*dl->channelfade[2]; if (dl->color[2] < 0) dl->color[2] = 0; } @@ -1614,9 +1615,9 @@ void CL_RotateAroundTag(entity_t *ent, int entnum, int parenttagent, int parentt { extern int parsecountmod; // Con_Printf("tagent %i\n", tagent); - if (parenttagent <= MAX_CLIENTS && parenttagent > 0) + if (parenttagent <= cl.allocated_client_slots && parenttagent > 0) { - if (parenttagent == cl.playernum[0]+1) + if (parenttagent == cl.playerview[0].playernum+1) { org = cl.playerview[0].simorg; ang = cl.playerview[0].simangles; @@ -3221,7 +3222,6 @@ void CL_LinkPacketEntities (void) cl_numvisedicts++; - ent->externalmodelview = 0; ent->forcedshader = NULL; ent->keynum = state->number; @@ -3234,7 +3234,7 @@ void CL_LinkPacketEntities (void) ent->flags = 0; if (state->dpflags & RENDER_VIEWMODEL) ent->flags |= Q2RF_WEAPONMODEL|Q2RF_MINLIGHT|Q2RF_DEPTHHACK; - if (state->dpflags & RENDER_EXTERIORMODEL) + if ((state->dpflags & RENDER_EXTERIORMODEL) || r_refdef.playerview->viewentity == state->number) ent->flags |= Q2RF_EXTERNALMODEL; if (state->effects & NQEF_ADDITIVE) ent->flags |= Q2RF_ADDITIVE; @@ -3245,10 +3245,6 @@ void CL_LinkPacketEntities (void) if (state->trans != 0xff) ent->flags |= Q2RF_TRANSLUCENT; - /*FIXME: pay attention to tags instead, so nexuiz can work with splitscreen*/ - if (ent->flags & Q2RF_EXTERNALMODEL) - ent->externalmodelview = ~0; - /* if (le->origin[2] < r_refdef.waterheight != le->lastorigin[2] < r_refdef.waterheight) { P_RunParticleEffectTypeString(le->origin, NULL, 1, "te_watertransition"); @@ -3316,7 +3312,7 @@ void CL_LinkPacketEntities (void) /*if this entity is in a player's slot...*/ if (ent->keynum <= cl.allocated_client_slots) { - if (!cl.nolocalplayer[0]) + if (!cl.playerview[0].nolocalplayer) ent->keynum += MAX_EDICTS; } @@ -3657,10 +3653,8 @@ void CL_ParsePlayerinfo (void) if (cls.findtrack && info->stats[STAT_HEALTH] > 0) { -// extern int ideal_track; - autocam[0] = CAM_TRACK; - Cam_Lock(0, num); -// ideal_track = num; + cl.playerview[0].cam_auto = CAM_TRACK; + Cam_Lock(&cl.playerview[0], num); cls.findtrack = false; } @@ -3730,28 +3724,33 @@ void CL_ParsePlayerinfo (void) cl.players[num].statsf[STAT_WEAPONFRAME] = state->weaponframe; for (i = 0; i < cl.splitclients; i++) { - if (spec_track[i] == num) + playerview_t *pv = &cl.playerview[i]; + if (pv->cam_spec_track == num) { - cl.playerview[i].stats[STAT_WEAPONFRAME] = state->weaponframe; - cl.playerview[i].statsf[STAT_WEAPONFRAME] = state->weaponframe; + pv->stats[STAT_WEAPONFRAME] = state->weaponframe; + pv->statsf[STAT_WEAPONFRAME] = state->weaponframe; } } + //add a new splitscreen autotrack view if we can if (cl.splitclients < MAX_SPLITS && !cl.players[num].spectator) { extern cvar_t cl_splitscreen; if (cl.splitclients < cl_splitscreen.value+1) { for (i = 0; i < cl.splitclients; i++) - if (autocam[i] && spec_track[i] == num) + { + playerview_t *pv = &cl.playerview[i]; + if (pv->cam_auto && pv->cam_spec_track == num) return; + } if (i == cl.splitclients) { - autocam[cl.splitclients] = CAM_TRACK; - spec_track[cl.splitclients] = num; - cl.splitclients++; - Cam_Lock(i, num); + playerview_t *pv = &cl.playerview[cl.splitclients++]; + pv->cam_auto = CAM_TRACK; + pv->cam_spec_track = num; + Cam_Lock(pv, num); } } } @@ -3959,10 +3958,11 @@ guess_pm_type: //can't CL_SetStatInt as we don't know if its actually us or not for (i = 0; i < cl.splitclients; i++) { - if ((cl.spectator?spec_track[i]:cl.playernum[i]) == num) + playerview_t *pv = &cl.playerview[i]; + if ((cl.spectator?pv->cam_spec_track:pv->playernum) == num) { - cl.playerview[i].stats[STAT_WEAPONFRAME] = state->weaponframe; - cl.playerview[i].statsf[STAT_WEAPONFRAME] = state->weaponframe; + pv->stats[STAT_WEAPONFRAME] = state->weaponframe; + pv->statsf[STAT_WEAPONFRAME] = state->weaponframe; } } @@ -4048,6 +4048,8 @@ void CL_AddFlagModels (entity_t *ent, int team) newent = CL_NewTempEntity (); newent->model = cl.model_precache[cl_flagindex]; newent->skinnum = team; + newent->keynum = ent->keynum; + newent->flags |= ent->flags; AngleVectors (ent->angles, v_forward, v_right, v_up); v_forward[2] = -v_forward[2]; // reverse z component @@ -4073,6 +4075,7 @@ void CL_AddVWeapModel(entity_t *player, model_t *model) newent = CL_NewTempEntity (); newent->keynum = player->keynum; + newent->flags |= player->flags; VectorCopy(player->origin, newent->origin); VectorCopy(player->angles, newent->angles); @@ -4165,7 +4168,7 @@ void CL_LinkPlayers (void) } // spawn light flashes, even ones coming from invisible objects - if (r_powerupglow.value && !(r_powerupglow.value == 2 && j == cl.playernum[0]) + if (r_powerupglow.value && !(r_powerupglow.value == 2 && j == cl.playerview[0].playernum) && (state->effects & (EF_BLUE|EF_RED|EF_BRIGHTLIGHT|EF_DIMLIGHT))) { vec3_t colour; @@ -4210,7 +4213,7 @@ void CL_LinkPlayers (void) VectorCopy(state->origin, org); //make the light appear at the predicted position rather than anywhere else. for (pnum = 0; pnum < cl.splitclients; pnum++) - if (cl.playernum[pnum] == j) + if (cl.playerview[pnum].playernum == j) VectorCopy(cl.playerview[pnum].simorg, org); if (model) { @@ -4280,29 +4283,25 @@ void CL_LinkPlayers (void) angles[ROLL] = 0; angles[ROLL] = V_CalcRoll (angles, state->velocity)*4; - ent->externalmodelview = 0; + if (j+1 == r_refdef.playerview->viewentity || (cl.spectator && r_refdef.playerview->cam_locked && r_refdef.playerview->cam_spec_track == j)) + ent->flags |= Q2RF_EXTERNALMODEL; // the player object gets added with flags | 2 for (pnum = 0; pnum < cl.splitclients; pnum++) { - if (j == (cl.viewentity[pnum]?cl.viewentity[pnum]:cl.playernum[pnum])) - { - ent->flags |= Q2RF_EXTERNALMODEL; - ent->externalmodelview |= (1<playernum) { /* if (cl.spectator) { cl_numvisedicts--; continue; } -*/ angles[0] = -1*cl.playerview[pnum].viewangles[0] / 3; - angles[1] = cl.playerview[pnum].viewangles[1]; - angles[2] = cl.playerview[pnum].viewangles[2]; - ent->origin[0] = cl.playerview[pnum].simorg[0]; - ent->origin[1] = cl.playerview[pnum].simorg[1]; - ent->origin[2] = cl.playerview[pnum].simorg[2]+cl.crouch[pnum]; - break; +*/ angles[0] = -1*pv->viewangles[0] / 3; + angles[1] = pv->viewangles[1]; + angles[2] = pv->viewangles[2]; + ent->origin[0] = pv->simorg[0]; + ent->origin[1] = pv->simorg[1]; + ent->origin[2] = pv->simorg[2]+pv->crouch; } } @@ -4380,12 +4379,8 @@ void CL_LinkViewModel(void) unsigned int plnum; player_state_t *plstate; - static struct model_s *oldmodel[MAX_SPLITS]; - static float lerptime[MAX_SPLITS]; - static float frameduration[MAX_SPLITS]; - static int prevframe[MAX_SPLITS]; - static int oldframe[MAX_SPLITS]; float alpha; + playerview_t *pv = r_refdef.playerview; extern cvar_t cl_gunx, cl_guny, cl_gunz; extern cvar_t cl_gunanglex, cl_gunangley, cl_gunanglez; @@ -4396,7 +4391,7 @@ void CL_LinkViewModel(void) return; #endif - if (r_drawviewmodel.value <= 0 || !Cam_DrawViewModel(r_refdef.currentplayernum)) + if (r_drawviewmodel.value <= 0 || !Cam_DrawViewModel(r_refdef.playerview)) return; #ifdef Q2CLIENT @@ -4407,10 +4402,10 @@ void CL_LinkViewModel(void) if (!r_drawentities.ival) return; - if ((cl.playerview[r_refdef.currentplayernum].stats[STAT_ITEMS] & IT_INVISIBILITY) && r_drawviewmodelinvis.value <= 0) + if ((r_refdef.playerview->stats[STAT_ITEMS] & IT_INVISIBILITY) && r_drawviewmodelinvis.value <= 0) return; - if (cl.playerview[r_refdef.currentplayernum].stats[STAT_HEALTH] <= 0) + if (r_refdef.playerview->stats[STAT_HEALTH] <= 0) return; if (r_drawviewmodel.value > 0 && r_drawviewmodel.value < 1) @@ -4418,7 +4413,7 @@ void CL_LinkViewModel(void) else alpha = 1; - if ((cl.playerview[r_refdef.currentplayernum].stats[STAT_ITEMS] & IT_INVISIBILITY) + if ((r_refdef.playerview->stats[STAT_ITEMS] & IT_INVISIBILITY) && r_drawviewmodelinvis.value > 0 && r_drawviewmodelinvis.value < 1) alpha *= r_drawviewmodelinvis.value; @@ -4428,7 +4423,7 @@ void CL_LinkViewModel(void) V_ClearEntity(&ent); - ent.model = cl.viewent[r_refdef.currentplayernum].model; + ent.model = r_refdef.playerview->viewent.model; if (!ent.model) return; @@ -4457,30 +4452,30 @@ void CL_LinkViewModel(void) if (!CLHL_AnimateViewEntity(&ent)) #endif { - ent.framestate.g[FS_REG].frame[0] = cl.viewent[r_refdef.currentplayernum].framestate.g[FS_REG].frame[0]; - ent.framestate.g[FS_REG].frame[1] = oldframe[r_refdef.currentplayernum]; + ent.framestate.g[FS_REG].frame[0] = pv->viewent.framestate.g[FS_REG].frame[0]; + ent.framestate.g[FS_REG].frame[1] = pv->oldframe; - if (ent.framestate.g[FS_REG].frame[0] != prevframe[r_refdef.currentplayernum]) + if (ent.framestate.g[FS_REG].frame[0] != pv->prevframe) { - oldframe[r_refdef.currentplayernum] = ent.framestate.g[FS_REG].frame[1] = prevframe[r_refdef.currentplayernum]; + pv->oldframe = ent.framestate.g[FS_REG].frame[1] = pv->prevframe; - frameduration[r_refdef.currentplayernum] = (realtime - lerptime[r_refdef.currentplayernum]); - if (frameduration[r_refdef.currentplayernum] < 0.01)//no faster than 100 times a second... to avoid divide by zero - frameduration[r_refdef.currentplayernum] = 0.01; - if (frameduration[r_refdef.currentplayernum] > 0.2) //no slower than 5 times a second - frameduration[r_refdef.currentplayernum] = 0.2; - lerptime[r_refdef.currentplayernum] = realtime; + pv->frameduration = (realtime - pv->lerptime); + if (pv->frameduration < 0.01)//no faster than 100 times a second... to avoid divide by zero + pv->frameduration = 0.01; + if (pv->frameduration > 0.2) //no slower than 5 times a second + pv->frameduration = 0.2; + pv->lerptime = realtime; } - prevframe[r_refdef.currentplayernum] = ent.framestate.g[FS_REG].frame[0]; + pv->prevframe = ent.framestate.g[FS_REG].frame[0]; - if (ent.model != oldmodel[r_refdef.currentplayernum]) + if (ent.model != pv->oldmodel) { - oldmodel[r_refdef.currentplayernum] = ent.model; - oldframe[r_refdef.currentplayernum] = ent.framestate.g[FS_REG].frame[1] = ent.framestate.g[FS_REG].frame[0]; - frameduration[r_refdef.currentplayernum] = 0.1; - lerptime[r_refdef.currentplayernum] = realtime; + pv->oldmodel = ent.model; + pv->oldframe = ent.framestate.g[FS_REG].frame[1] = ent.framestate.g[FS_REG].frame[0]; + pv->frameduration = 0.1; + pv->lerptime = realtime; } - ent.framestate.g[FS_REG].lerpfrac = 1-(realtime-lerptime[r_refdef.currentplayernum])/frameduration[r_refdef.currentplayernum]; + ent.framestate.g[FS_REG].lerpfrac = 1-(realtime-pv->lerptime)/pv->frameduration; ent.framestate.g[FS_REG].lerpfrac = bound(0, ent.framestate.g[FS_REG].lerpfrac, 1); } @@ -4488,9 +4483,9 @@ void CL_LinkViewModel(void) plnum = -1; if (cl.spectator) - plnum = Cam_TrackNum(r_refdef.currentplayernum); + plnum = Cam_TrackNum(pv); if (plnum == -1) - plnum = cl.playernum[r_refdef.currentplayernum]; + plnum = r_refdef.playerview->playernum; plstate = &cl.inframes[parsecountmod].playerstate[plnum]; CLQ1_AddPowerupShell(V_AddEntity(&ent), true, plstate?plstate->effects:0); @@ -4663,14 +4658,13 @@ void CL_SetUpPlayerPrediction(qboolean dopred) pplayer->active = true; pplayer->flags = state->flags; - // note that the local player is special, since he moves locally - // we use his last predicted postition + // note that the local players are special, since they move locally + // we use their last predicted postition for (s = 0; s < cl.splitclients; s++) { - if (j == cl.playernum[s]) + if (j == cl.playerview[s].playernum) { - VectorCopy(cl.inframes[cls.netchan.outgoing_sequence&UPDATE_MASK].playerstate[cl.playernum[s]].origin, - pplayer->origin); + VectorCopy(cl.inframes[cls.netchan.outgoing_sequence&UPDATE_MASK].playerstate[cl.playerview[s].playernum].origin, pplayer->origin); break; } } diff --git a/engine/client/cl_ignore.c b/engine/client/cl_ignore.c index 5f9c7be97..b56c57b8d 100644 --- a/engine/client/cl_ignore.c +++ b/engine/client/cl_ignore.c @@ -85,7 +85,7 @@ int Player_SlottoId (int slot) char *Player_MyName (void) { - return cl.players[cl.playernum[0]].name; + return cl.players[cl.playerview[0].playernum].name; } @@ -599,8 +599,8 @@ qboolean Ignore_Message(char *s, int flags, int offset) (int) ignore_opponents.ival == 1 || (cls.state >= ca_connected && /*!cl.standby &&*/ !cls.demoplayback && !cl.spectator) // match? ) && - flags == 1 && !cl.spectator && slot != cl.playernum[0] && - (!cl.teamplay || strcmp(cl.players[slot].team, cl.players[cl.playernum[0]].team)) + flags == 1 && !cl.spectator && slot != cl.playerview[0].playernum && + (!cl.teamplay || strcmp(cl.players[slot].team, cl.players[cl.playerview[0].playernum].team)) ) { return true; diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index 80c3e3de2..e18c0a2ff 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -228,7 +228,7 @@ void IN_MLookUp (void) int pnum = CL_TargettedSplit(false); KeyUp(&in_mlook); if ( !(in_mlook.state[pnum]&1) && lookspring.ival) - V_StartPitchDrift(pnum); + V_StartPitchDrift(&cl.playerview[pnum]); } void IN_UpDown(void) {KeyDown(&in_up);} void IN_UpUp(void) {KeyUp(&in_up);} @@ -277,10 +277,10 @@ void IN_JumpDown (void) else #endif if (condition && cl.playerview[pnum].stats[STAT_HEALTH] > 0 && !cls.demoplayback && !cl.spectator && - cl.inframes[cl.validsequence&UPDATE_MASK].playerstate[cl.playernum[pnum]].messagenum == cl.validsequence && cl.waterlevel[pnum] >= 2 && (!cl.teamfortress || !(in_forward.state[pnum] & 1)) + cl.inframes[cl.validsequence&UPDATE_MASK].playerstate[cl.playerview[pnum].playernum].messagenum == cl.validsequence && cl.playerview[pnum].waterlevel >= 2 && (!cl.teamfortress || !(in_forward.state[pnum] & 1)) ) KeyDown(&in_up); - else if (condition && cl.spectator && Cam_TrackNum(pnum) == -1) + else if (condition && cl.spectator && Cam_TrackNum(&cl.playerview[pnum]) == -1) KeyDown(&in_up); else KeyDown(&in_jump); @@ -535,7 +535,7 @@ void CL_AdjustAngles (int pnum, double frametime) } if (in_klook.state[pnum] & 1) { - V_StopPitchDrift (pnum); + V_StopPitchDrift (&cl.playerview[pnum]); quant = cl_pitchspeed.ival; if (cl.fpd & FPD_LIMIT_PITCH || !ruleset_allow_frj.ival) quant = bound(-700, quant, 700); @@ -553,7 +553,7 @@ void CL_AdjustAngles (int pnum, double frametime) cl.playerview[pnum].viewanglechange[PITCH] += speed*cl_pitchspeed.ival * down; if (up || down) - V_StopPitchDrift (pnum); + V_StopPitchDrift (&cl.playerview[pnum]); } /* @@ -1561,11 +1561,11 @@ void CL_SendCmd (double frametime, qboolean mainloop) // if we are spectator, try autocam if (cl.spectator) - Cam_Track(plnum, cmd); + Cam_Track(&cl.playerview[plnum], cmd); CL_FinishMove(cmd, cmd->msec, plnum); - Cam_FinishMove(plnum, cmd); + Cam_FinishMove(&cl.playerview[plnum], cmd); } while (clientcmdlist) @@ -1674,8 +1674,8 @@ void CL_SendCmd (double frametime, qboolean mainloop) // if we are spectator, try autocam // if (cl.spectator) - Cam_Track(plnum, &independantphysics[plnum]); - Cam_FinishMove(plnum, &independantphysics[plnum]); + Cam_Track(&cl.playerview[plnum], &independantphysics[plnum]); + Cam_FinishMove(&cl.playerview[plnum], &independantphysics[plnum]); independantphysics[plnum].msec = msecstouse; } diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 16e82d047..8adc916ed 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -36,6 +36,8 @@ void Name_Callback(struct cvar_s *var, char *oldvalue); qboolean noclip_anglehack; // remnant from old quake +void Host_FinishLoading(void); + cvar_t rcon_password = SCVARF("rcon_password", "", CVAR_NOUNSAFEEXPAND); @@ -103,75 +105,75 @@ extern int total_loading_size, current_loading_size, loading_stage; // // info mirrors // -cvar_t password = CVARAF("password", "", "pq_password", CVAR_USERINFO | CVAR_NOUNSAFEEXPAND); //this is parhaps slightly dodgy... added pq_password alias because baker seems to be using this for user accounts. -cvar_t spectator = CVARF("spectator", "", CVAR_USERINFO); -cvar_t name = CVARFC("name", "unnamed", CVAR_ARCHIVE | CVAR_USERINFO, Name_Callback); -cvar_t team = CVARF("team", "", CVAR_ARCHIVE | CVAR_USERINFO); -cvar_t skin = CVARF("skin", "", CVAR_ARCHIVE | CVAR_USERINFO); -cvar_t model = CVARF("model", "", CVAR_ARCHIVE | CVAR_USERINFO); -cvar_t topcolor = CVARF("topcolor", "", CVAR_ARCHIVE | CVAR_USERINFO); -cvar_t bottomcolor = CVARF("bottomcolor", "", CVAR_ARCHIVE | CVAR_USERINFO); -cvar_t rate = CVARFD("rate", "10000"/*"6480"*/, CVAR_ARCHIVE | CVAR_USERINFO, "A rough measure of the bandwidth to try to use while playing. Too high a value may result in 'buffer bloat'."); -cvar_t drate = CVARFD("drate", "100000", CVAR_ARCHIVE | CVAR_USERINFO, "A rough measure of the bandwidth to try to use while downloading."); // :) -cvar_t noaim = CVARF("noaim", "", CVAR_ARCHIVE | CVAR_USERINFO); -cvar_t msg = CVARFD("msg", "1", CVAR_ARCHIVE | CVAR_USERINFO, "Filter console prints/messages. Only functions on QuakeWorld servers. 0=pickup messages. 1=death messages. 2=critical messages. 3=chat."); -cvar_t b_switch = CVARF("b_switch", "", CVAR_ARCHIVE | CVAR_USERINFO); -cvar_t w_switch = CVARF("w_switch", "", CVAR_ARCHIVE | CVAR_USERINFO); -cvar_t cl_nofake = CVARD("cl_nofake", "2", "value 0: permits \\r chars in chat messages\nvalue 1: blocks all \\r chars\nvalue 2: allows \\r chars, but only from teammates"); -cvar_t cl_chatsound = CVAR("cl_chatsound","1"); -cvar_t cl_enemychatsound = CVAR("cl_enemychatsound", "misc/talk.wav"); -cvar_t cl_teamchatsound = CVAR("cl_teamchatsound", "misc/talk.wav"); +cvar_t password = CVARAF("password", "", "pq_password", CVAR_USERINFO | CVAR_NOUNSAFEEXPAND); //this is parhaps slightly dodgy... added pq_password alias because baker seems to be using this for user accounts. +cvar_t spectator = CVARF("spectator", "", CVAR_USERINFO); +cvar_t name = CVARFC("name", "unnamed", CVAR_ARCHIVE | CVAR_USERINFO, Name_Callback); +cvar_t team = CVARF("team", "", CVAR_ARCHIVE | CVAR_USERINFO); +cvar_t skin = CVARF("skin", "", CVAR_ARCHIVE | CVAR_USERINFO); +cvar_t model = CVARF("model", "", CVAR_ARCHIVE | CVAR_USERINFO); +cvar_t topcolor = CVARF("topcolor", "", CVAR_ARCHIVE | CVAR_USERINFO); +cvar_t bottomcolor = CVARF("bottomcolor", "", CVAR_ARCHIVE | CVAR_USERINFO); +cvar_t rate = CVARFD("rate", "10000"/*"6480"*/, CVAR_ARCHIVE | CVAR_USERINFO, "A rough measure of the bandwidth to try to use while playing. Too high a value may result in 'buffer bloat'."); +cvar_t drate = CVARFD("drate", "100000", CVAR_ARCHIVE | CVAR_USERINFO, "A rough measure of the bandwidth to try to use while downloading."); // :) +cvar_t noaim = CVARF("noaim", "", CVAR_ARCHIVE | CVAR_USERINFO); +cvar_t msg = CVARFD("msg", "1", CVAR_ARCHIVE | CVAR_USERINFO, "Filter console prints/messages. Only functions on QuakeWorld servers. 0=pickup messages. 1=death messages. 2=critical messages. 3=chat."); +cvar_t b_switch = CVARF("b_switch", "", CVAR_ARCHIVE | CVAR_USERINFO); +cvar_t w_switch = CVARF("w_switch", "", CVAR_ARCHIVE | CVAR_USERINFO); +cvar_t cl_nofake = CVARD("cl_nofake", "2", "value 0: permits \\r chars in chat messages\nvalue 1: blocks all \\r chars\nvalue 2: allows \\r chars, but only from teammates"); +cvar_t cl_chatsound = CVAR("cl_chatsound","1"); +cvar_t cl_enemychatsound = CVAR("cl_enemychatsound", "misc/talk.wav"); +cvar_t cl_teamchatsound = CVAR("cl_teamchatsound", "misc/talk.wav"); -cvar_t r_torch = CVARF("r_torch", "0", CVAR_CHEAT); -cvar_t r_rocketlight = CVARFC("r_rocketlight", "1", CVAR_ARCHIVE, Cvar_Limiter_ZeroToOne_Callback); -cvar_t r_lightflicker = CVAR("r_lightflicker", "1"); -cvar_t cl_r2g = CVARF("cl_r2g", "0", CVAR_ARCHIVE); -cvar_t r_powerupglow = CVAR("r_powerupglow", "1"); -cvar_t v_powerupshell = CVARF("v_powerupshell", "0", CVAR_ARCHIVE); -cvar_t cl_gibfilter = CVARF("cl_gibfilter", "0", CVAR_ARCHIVE); -cvar_t cl_deadbodyfilter = CVAR("cl_deadbodyfilter", "0"); +cvar_t r_torch = CVARF("r_torch", "0", CVAR_CHEAT); +cvar_t r_rocketlight = CVARFC("r_rocketlight", "1", CVAR_ARCHIVE, Cvar_Limiter_ZeroToOne_Callback); +cvar_t r_lightflicker = CVAR("r_lightflicker", "1"); +cvar_t cl_r2g = CVARF("cl_r2g", "0", CVAR_ARCHIVE); +cvar_t r_powerupglow = CVAR("r_powerupglow", "1"); +cvar_t v_powerupshell = CVARF("v_powerupshell", "0", CVAR_ARCHIVE); +cvar_t cl_gibfilter = CVARF("cl_gibfilter", "0", CVAR_ARCHIVE); +cvar_t cl_deadbodyfilter = CVAR("cl_deadbodyfilter", "0"); -cvar_t cl_gunx = SCVAR("cl_gunx", "0"); -cvar_t cl_guny = SCVAR("cl_guny", "0"); -cvar_t cl_gunz = SCVAR("cl_gunz", "0"); +cvar_t cl_gunx = CVAR("cl_gunx", "0"); +cvar_t cl_guny = CVAR("cl_guny", "0"); +cvar_t cl_gunz = CVAR("cl_gunz", "0"); -cvar_t cl_gunanglex = SCVAR("cl_gunanglex", "0"); -cvar_t cl_gunangley = SCVAR("cl_gunangley", "0"); -cvar_t cl_gunanglez = SCVAR("cl_gunanglez", "0"); +cvar_t cl_gunanglex = CVAR("cl_gunanglex", "0"); +cvar_t cl_gunangley = CVAR("cl_gunangley", "0"); +cvar_t cl_gunanglez = CVAR("cl_gunanglez", "0"); -cvar_t cl_download_csprogs = CVARFD("cl_download_csprogs", "1", CVAR_NOTFROMSERVER, "Download updated client gamecode if available."); -cvar_t cl_download_redirection = CVARFD("cl_download_redirection", "2", CVAR_NOTFROMSERVER, "Follow download redirection to download packages instead of individual files. 2 allows redirection only to named packages files. Also allows the server to send nearly arbitary download commands."); -cvar_t cl_download_mapsrc = CVARD("cl_download_mapsrc", "", "Specifies an http location prefix for map downloads. EG: \"http://bigfoot.morphos-team.net/misc/quakemaps/\""); -cvar_t cl_download_packages = CVARFD("cl_download_packages", "1", CVAR_NOTFROMSERVER, "0=Do not download packages simply because the server is using them. 1=Download and load packages as needed (does not affect games which do not use this package). 2=Do download and install permanently (use with caution!)"); -cvar_t requiredownloads = CVARFD("requiredownloads","1", CVAR_ARCHIVE, "0=join the game before downloads have even finished (might be laggy). 1=wait for all downloads to complete before joining."); +cvar_t cl_download_csprogs = CVARFD("cl_download_csprogs", "1", CVAR_NOTFROMSERVER, "Download updated client gamecode if available. Warning: If you clear this to avoid downloading vm code, you should also clear cl_download_packages."); +cvar_t cl_download_redirection = CVARFD("cl_download_redirection", "2", CVAR_NOTFROMSERVER, "Follow download redirection to download packages instead of individual files. 2 allows redirection only to named packages files. Also allows the server to send nearly arbitary download commands."); +cvar_t cl_download_mapsrc = CVARD("cl_download_mapsrc", "", "Specifies an http location prefix for map downloads. EG: \"http://bigfoot.morphos-team.net/misc/quakemaps/\""); +cvar_t cl_download_packages = CVARFD("cl_download_packages", "1", CVAR_NOTFROMSERVER, "0=Do not download packages simply because the server is using them. 1=Download and load packages as needed (does not affect games which do not use this package). 2=Do download and install permanently (use with caution!)"); +cvar_t requiredownloads = CVARFD("requiredownloads","1", CVAR_ARCHIVE, "0=join the game before downloads have even finished (might be laggy). 1=wait for all downloads to complete before joining."); -cvar_t cl_muzzleflash = SCVAR("cl_muzzleflash", "1"); +cvar_t cl_muzzleflash = CVAR("cl_muzzleflash", "1"); -cvar_t cl_item_bobbing = CVARF("cl_model_bobbing", "0", CVAR_ARCHIVE); -cvar_t cl_countpendingpl = SCVAR("cl_countpendingpl", "0"); +cvar_t cl_item_bobbing = CVARF("cl_model_bobbing", "0", CVAR_ARCHIVE); +cvar_t cl_countpendingpl = CVARD("cl_countpendingpl", "0", "If set to 1, packet loss percentages will show packets still in transit as lost, even if they might still be received."); -cvar_t cl_standardchat = CVARFD("cl_standardchat", "0", CVAR_ARCHIVE, "Disables auto colour coding in chat messages."); -cvar_t msg_filter = CVARD("msg_filter", "0", "Filter out chat messages: 0=neither. 1=broadcast chat. 2=team chat. 3=all chat."); -cvar_t cl_standardmsg = CVARFD("cl_standardmsg", "0", CVAR_ARCHIVE, "Disables auto colour coding in console prints."); -cvar_t cl_parsewhitetext = CVARD("cl_parsewhitetext", "1", "When parsing chat messages, enable support for messages like: red{white}red"); +cvar_t cl_standardchat = CVARFD("cl_standardchat", "0", CVAR_ARCHIVE, "Disables auto colour coding in chat messages."); +cvar_t msg_filter = CVARD("msg_filter", "0", "Filter out chat messages: 0=neither. 1=broadcast chat. 2=team chat. 3=all chat."); +cvar_t cl_standardmsg = CVARFD("cl_standardmsg", "0", CVAR_ARCHIVE, "Disables auto colour coding in console prints."); +cvar_t cl_parsewhitetext = CVARD("cl_parsewhitetext", "1", "When parsing chat messages, enable support for messages like: red{white}red"); -cvar_t cl_dlemptyterminate = CVAR("cl_dlemptyterminate", "1"); +cvar_t cl_dlemptyterminate = CVAR("cl_dlemptyterminate", "1"); -cvar_t host_mapname = CVARAF("mapname", "", - "host_mapname", 0); +cvar_t host_mapname = CVARAF("mapname", "", + "host_mapname", 0); -cvar_t ruleset_allow_playercount = SCVAR("ruleset_allow_playercount", "1"); -cvar_t ruleset_allow_frj = SCVAR("ruleset_allow_frj", "1"); -cvar_t ruleset_allow_semicheats = SCVAR("ruleset_allow_semicheats", "1"); -cvar_t ruleset_allow_packet = SCVAR("ruleset_allow_packet", "1"); -cvar_t ruleset_allow_particle_lightning = SCVAR("ruleset_allow_particle_lightning", "1"); -cvar_t ruleset_allow_overlongsounds = SCVAR("ruleset_allow_overlong_sounds", "1"); -cvar_t ruleset_allow_larger_models = SCVAR("ruleset_allow_larger_models", "1"); -cvar_t ruleset_allow_modified_eyes = SCVAR("ruleset_allow_modified_eyes", "0"); -cvar_t ruleset_allow_sensative_texture_replacements = SCVAR("ruleset_allow_sensative_texture_replacements", "1"); -cvar_t ruleset_allow_localvolume = SCVAR("ruleset_allow_localvolume", "1"); -cvar_t ruleset_allow_shaders = SCVARF("ruleset_allow_shaders", "1", CVAR_SHADERSYSTEM); -cvar_t ruleset_allow_fbmodels = SCVARF("ruleset_allow_fbmodels", "1", CVAR_SHADERSYSTEM); +cvar_t ruleset_allow_playercount = CVAR("ruleset_allow_playercount", "1"); +cvar_t ruleset_allow_frj = CVAR("ruleset_allow_frj", "1"); +cvar_t ruleset_allow_semicheats = CVAR("ruleset_allow_semicheats", "1"); +cvar_t ruleset_allow_packet = CVAR("ruleset_allow_packet", "1"); +cvar_t ruleset_allow_particle_lightning = CVAR("ruleset_allow_particle_lightning", "1"); +cvar_t ruleset_allow_overlongsounds = CVAR("ruleset_allow_overlong_sounds", "1"); +cvar_t ruleset_allow_larger_models = CVAR("ruleset_allow_larger_models", "1"); +cvar_t ruleset_allow_modified_eyes = CVAR("ruleset_allow_modified_eyes", "0"); +cvar_t ruleset_allow_sensative_texture_replacements = CVAR("ruleset_allow_sensative_texture_replacements", "1"); +cvar_t ruleset_allow_localvolume = CVAR("ruleset_allow_localvolume", "1"); +cvar_t ruleset_allow_shaders = CVARF("ruleset_allow_shaders", "1", CVAR_SHADERSYSTEM); +cvar_t ruleset_allow_fbmodels = CVARF("ruleset_allow_fbmodels", "1", CVAR_SHADERSYSTEM); extern cvar_t cl_hightrack; extern cvar_t vid_renderer; @@ -274,13 +276,13 @@ void CL_UpdateWindowTitle(void) default: #ifndef CLIENTONLY if (sv.state) - VID_SetWindowCaption(va("%s %s: %s", DISTRIBUTION, fs_gamename.string, sv.name)); + VID_SetWindowCaption(va("%s", fs_gamename.string, sv.name)); else #endif - VID_SetWindowCaption(va("%s %s: %s", DISTRIBUTION, fs_gamename.string, cls.servername)); + VID_SetWindowCaption(va("%s: %s", fs_gamename.string, cls.servername)); break; case ca_disconnected: - VID_SetWindowCaption(va("%s %s: disconnected", DISTRIBUTION, fs_gamename.string)); + VID_SetWindowCaption(va("%s: disconnected", fs_gamename.string)); break; } } @@ -775,6 +777,8 @@ void CL_CheckForResend (void) return; } + CL_FlushClientCommands(); + t2 = Sys_DoubleTime (); connect_time = realtime+t2-t1; // for retransmit requests @@ -1109,7 +1113,7 @@ void CL_ClearState (void) T_FreeInfoStrings(); SCR_ShowPic_Clear(); - if (cl.playernum[0] == -1) + if (cl.playerview[0].playernum == -1) { //left over from q2 connect. Media_StopFilm(true); } @@ -1172,7 +1176,7 @@ void CL_ClearState (void) for (i = 0; i < MAX_SPLITS; i++) { VectorSet(cl.playerview[i].gravitydir, 0, 0, -1); - cl.viewheight[i] = DEFAULT_VIEWHEIGHT; + cl.playerview[i].viewheight = DEFAULT_VIEWHEIGHT; } cl.minpitch = -70; cl.maxpitch = 80; @@ -1313,6 +1317,8 @@ void CL_Disconnect (void) CL_ClearState(); + FS_PureMode(0, NULL, NULL, 0); + //now start up the csqc/menu module again. CSQC_UnconnectedInit(); } @@ -1373,7 +1379,7 @@ void CL_User_f (void) if (cls.protocol == CP_NETQUAKE) Con_Printf("name: %s\ncolour %i %i\nping: %i\n", cl.players[i].name, cl.players[i].rbottomcolor, cl.players[i].rtopcolor, cl.players[i].ping); else - Info_Print (cl.players[i].userinfo); + Info_Print (cl.players[i].userinfo, ""); found = true; } } @@ -1455,7 +1461,7 @@ void CL_Color_f (void) bottom = CL_ParseColour(Cmd_Argv(2)); } - Q_snprintfz (num, sizeof(num), (top&0xff000000)?"%#08x":"%i", top & 0xffffff); + Q_snprintfz (num, sizeof(num), (top&0xff000000)?"0x%06x":"%i", top & 0xffffff); if (top == 0) *num = '\0'; if (Cmd_ExecLevel>RESTRICT_SERVER) //colour command came from server for a split client @@ -1464,7 +1470,7 @@ void CL_Color_f (void) // Cvar_LockFromServer(&topcolor, num); else Cvar_Set (&topcolor, num); - Q_snprintfz (num, sizeof(num), (bottom&0xff000000)?"%#08x":"%i", bottom & 0xffffff); + Q_snprintfz (num, sizeof(num), (bottom&0xff000000)?"0x%06x":"%i", bottom & 0xffffff); if (bottom == 0) *num = '\0'; if (Cmd_ExecLevel>RESTRICT_SERVER) //colour command came from server for a split client @@ -1479,7 +1485,6 @@ void CL_Color_f (void) #endif } -void FS_GenCachedPakName(char *pname, char *crc, char *local, int llen); qboolean CL_CheckDLFile(char *filename); void CL_PakDownloads(int mode) { @@ -1513,7 +1518,8 @@ void CL_PakDownloads(int mode) /*if we already have such a file, this is a no-op*/ if (CL_CheckDLFile(va("package/%s", pname))) continue; - FS_GenCachedPakName(pname, com_token, local, sizeof(local)); + if (!FS_GenCachedPakName(pname, com_token, local, sizeof(local))) + continue; } else Q_strncpyz(local, pname, sizeof(local)); @@ -1523,26 +1529,27 @@ void CL_PakDownloads(int mode) void CL_CheckServerPacks(void) { - static qboolean oldpure; - qboolean pure = cl_pure.ival || atoi(Info_ValueForKey(cl.serverinfo, "sv_pure")); + static int oldpure; + int pure = atof(Info_ValueForKey(cl.serverinfo, "sv_pure"))*2; + if (pure < cl_pure.ival) + pure = cl_pure.ival; + pure = bound(0, pure, 2); + + if (!*cl.serverpakcrcs || cls.demoplayback) + pure = 0; if (pure != oldpure || cl.serverpakschanged) { + CL_PakDownloads((pure && !cl_download_packages.ival)?1:cl_download_packages.ival); + FS_PureMode(pure, cl.serverpaknames, cl.serverpakcrcs, cls.challenge); + if (pure) { - CL_PakDownloads((!cl_download_packages.ival)?1:cl_download_packages.ival); - FS_ForceToPure(cl.serverpaknames, cl.serverpakcrcs, cls.challenge); - /*when enabling pure, kill cached models/sounds/etc*/ Cache_Flush(); /*make sure cheating lamas can't use old shaders from a different srver*/ Shader_NeedReload(true); } - else - { - CL_PakDownloads(cl_download_packages.ival); - FS_ImpurePacks(cl.serverpaknames, cl.serverpakcrcs); - } } oldpure = pure; cl.serverpakschanged = false; @@ -1836,7 +1843,7 @@ void CL_SetInfo_f (void) int pnum = CL_TargettedSplit(true); if (Cmd_Argc() == 1) { - Info_Print (cls.userinfo[pnum]); + Info_Print (cls.userinfo[pnum], ""); return; } if (Cmd_Argc() != 3) @@ -2347,12 +2354,14 @@ void CL_ConnectionlessPacket (void) #ifdef Q3CLIENT else if (!strcmp(com_token, "onnectResponse")) { + cls.protocol = CP_QUAKE3; goto client_connect; } #endif #ifdef Q2CLIENT else if (!strcmp(com_token, "lient_connect")) { + cls.protocol = CP_QUAKE2; goto client_connect; } #endif @@ -2429,6 +2438,7 @@ void CL_ConnectionlessPacket (void) } else if (!strcmp(s, "client_connect")) { + cls.protocol = CP_QUAKE2; goto client_connect; } else if (!strcmp(s, "disconnect")) @@ -2497,6 +2507,7 @@ void CL_ConnectionlessPacket (void) { int compress; int mtu; + cls.protocol = CP_QUAKEWORLD; #ifdef Q2CLIENT client_connect: //fixme: make function #endif @@ -2684,7 +2695,7 @@ void CLNQ_ConnectionlessPacket(void) #endif void CL_MVDUpdateSpectator (void); - +void CL_WriteDemoMessage (sizebuf_t *msg); /* ================= CL_ReadPackets @@ -2741,6 +2752,8 @@ void CL_ReadPackets (void) if (cls.state == ca_disconnected) { //connect to nq servers, but don't get confused with sequenced packets. + if (NET_WasSpecialPacket(NS_CLIENT)) + continue; #ifdef NQPROT CLNQ_ConnectionlessPacket (); #endif @@ -2799,6 +2812,8 @@ void CL_ReadPackets (void) else if (!Netchan_Process(&cls.netchan)) continue; // wasn't accepted for some reason + CL_WriteDemoMessage (&net_message); + if (cls.netchan.incoming_sequence > cls.netchan.outgoing_sequence) { //server should not be responding to packets we have not sent yet Con_DPrintf("Server is from the future! (%i packets)\n", cls.netchan.incoming_sequence - cls.netchan.outgoing_sequence); @@ -3061,7 +3076,7 @@ void CL_ServerInfo_f(void) { if (cls.demoplayback || cls.protocol != CP_QUAKEWORLD) { - Info_Print (cl.serverinfo); + Info_Print (cl.serverinfo, ""); } else Cmd_ForwardToServer (); @@ -3164,7 +3179,6 @@ void CL_Init (void) Cvar_Register (&cl_servername, cl_controlgroup); Cvar_Register (&cl_serveraddress, cl_controlgroup); Cvar_Register (&cl_demospeed, "Demo playback"); - Cvar_Register (&cl_warncmd, "Warnings"); Cvar_Register (&cl_upspeed, cl_inputgroup); Cvar_Register (&cl_forwardspeed, cl_inputgroup); Cvar_Register (&cl_backspeed, cl_inputgroup); @@ -3526,6 +3540,395 @@ qboolean Host_SimulationTime(float time) } #endif +void Host_RunFileNotify(struct dl_download *dl) +{ + if (dl->file) + { + Host_RunFile(dl->url, strlen(dl->url), dl->file); + dl->file = NULL; + } +} + +#include "fs.h" +#define HRF_OVERWRITE 1 +#define HRF_NOOVERWRITE 2 +#define HRF_ABORT 4 + +#define HRF_OPENED 8 + +#define HRF_ACTION (HRF_OVERWRITE|HRF_NOOVERWRITE|HRF_ABORT) +typedef struct { + char ext[4]; //FIXME: override by mime types + unsigned int flags; + vfsfile_t *srcfile; + vfsfile_t *dstfile; + char fname[1]; //system path or url. +} hrf_t; + +void Host_DoRunFile(hrf_t *f); + + +void Host_RunFileDownloaded(struct dl_download *dl) +{ + hrf_t *f = dl->user_ctx; + f->srcfile = dl->file; + dl->file = NULL; + + Host_DoRunFile(f); +} +void Host_RunFilePrompted(void *ctx, int button) +{ + hrf_t *f = ctx; + switch(button) + { + case 0: + f->flags |= HRF_OVERWRITE; + break; + case 1: + f->flags |= HRF_NOOVERWRITE; + break; + default: + f->flags |= HRF_ABORT; + break; + } + Host_DoRunFile(f); +} + +qboolean waitingformanifest; +void Host_DoRunFile(hrf_t *f) +{ + char qname[MAX_QPATH]; + char displayname[MAX_QPATH]; + char loadcommand[MAX_OSPATH]; + qboolean isnew = false; + qboolean haschanged = false; + + if (f->flags & HRF_ABORT) + { + if (f->srcfile) + VFS_CLOSE(f->srcfile); + if (f->dstfile) + VFS_CLOSE(f->dstfile); + Z_Free(f); + return; + } + + if (!strcmp(f->ext, "qwd") || !strcmp(f->ext, "dem") || !strcmp(f->ext, "mvd")) + { + //play directly via system path, no prompts needed + Cbuf_AddText(va("playdemo \"#%s\"\n", f->fname), RESTRICT_LOCAL); + + f->flags |= HRF_ABORT; + Host_DoRunFile(f); + return; + } + else if (!strcmp(f->ext, "qtv")) + { + //play directly via url/system path, no prompts needed + Cbuf_AddText(va("qtvplay \"#%s\"\n", f->fname), RESTRICT_LOCAL); + + f->flags |= HRF_ABORT; + Host_DoRunFile(f); + return; + } + else if (!strcmp(f->ext, "bsp")) + { + char shortname[MAX_QPATH]; + COM_StripExtension(COM_SkipPath(f->fname), shortname, sizeof(shortname)); + snprintf(qname, sizeof(qname), "maps/%s.bsp", shortname); + snprintf(loadcommand, sizeof(loadcommand), "map \"%s\"\n", shortname); + snprintf(displayname, sizeof(displayname), "map: %s", shortname); + } + else if (!strcmp(f->ext, "pak") || !strcmp(f->ext, "pk3")) + { + char *shortname; + shortname = COM_SkipPath(f->fname); + snprintf(qname, sizeof(qname), "%s", shortname); + snprintf(loadcommand, sizeof(loadcommand), "fs_restart\n"); + snprintf(displayname, sizeof(displayname), "package: %s", shortname); + } + else if (!strcmp(f->ext, "fmf")) + { + if (f->flags & HRF_OPENED) + { + waitingformanifest--; + if (f->srcfile) + { + ftemanifest_t *man; + int len = VFS_GETLEN(f->srcfile); + char *fdata = BZ_Malloc(len+1); + VFS_READ(f->srcfile, fdata, len); + VFS_CLOSE(f->srcfile); + fdata[len] = 0; + man = FS_Manifest_Parse(fdata); + if (!man->updateurl) + man->updateurl = Z_StrDup(f->fname); + BZ_Free(fdata); + FS_ChangeGame(man, true); + return; + } + } + } + else + { + f->flags |= HRF_ABORT; + Host_DoRunFile(f); + return; + } + + if (!(f->flags & HRF_OPENED)) + { + f->flags |= HRF_OPENED; + if (!f->srcfile) + { +#ifdef WEBCLIENT + if (!strncmp(f->fname, "http://", 7)) + { + struct dl_download *dl = HTTP_CL_Get(f->fname, NULL, Host_RunFileDownloaded); + dl->user_ctx = f; + return; + } +#endif + f->srcfile = VFSOS_Open(f->fname, "rb"); //input file is a system path, or something. + } + } + + if (!f->srcfile) + { + f->flags |= HRF_ABORT; + Host_DoRunFile(f); + return; + } + + VFS_SEEK(f->srcfile, 0); + + COM_StripExtension(COM_SkipPath(f->fname), qname, sizeof(qname)); + + f->dstfile = FS_OpenVFS(qname, "rb", FS_GAME); + if (f->dstfile) + { + //do a real diff. + if (VFS_GETLEN(f->srcfile) != VFS_GETLEN(f->dstfile)) + haschanged = true; + else + { + int len = VFS_GETLEN(f->srcfile); + char sbuf[8192], dbuf[8192]; + if (len > sizeof(sbuf)) + len = sizeof(sbuf); + VFS_READ(f->srcfile, sbuf, len); + VFS_READ(f->dstfile, dbuf, len); + haschanged = memcmp(sbuf, dbuf, len); + } + VFS_SEEK(f->srcfile, 0); + VFS_CLOSE(f->dstfile); + f->dstfile = NULL; + } + else + isnew = true; + + if (haschanged) + { + if (!(f->flags & HRF_ACTION)) + { + M_Menu_Prompt(Host_RunFilePrompted, f, "File already exists.", "What would you like to do?", displayname, "Overwrite", "Run old", "Cancel"); + return; + } + } + else if (isnew) + { + if (!(f->flags & HRF_ACTION)) + { + M_Menu_Prompt(Host_RunFilePrompted, f, "File appears new.", "Would you like to install", displayname, "Install!", "", "Cancel"); + return; + } + } + + if (f->flags & HRF_OVERWRITE) + { + char buffer[8192]; + int len; + f->dstfile = FS_OpenVFS(qname, "wb", FS_GAMEONLY); + if (f->dstfile) + { + while(1) + { + len = VFS_READ(f->srcfile, buffer, sizeof(buffer)); + if (len <= 0) + break; + VFS_WRITE(f->dstfile, buffer, len); + } + VFS_CLOSE(f->dstfile); + f->dstfile = NULL; + } + } + + Cbuf_AddText(loadcommand, RESTRICT_LOCAL); + + f->flags |= HRF_ABORT; + Host_DoRunFile(f); + return; +} + +//only valid once the host has been initialised, as it needs a working filesystem. +//if file is specified, takes full ownership of said file, including destruction. +qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file) +{ + char *ext = COM_FileExtension(fname); + hrf_t *f = Z_Malloc(sizeof(*f) + nlen); + memcpy(f->fname, fname, nlen); + f->fname[nlen] = 0; + Q_strncpyz(f->ext, ext, sizeof(f->ext)); + Host_DoRunFile(f); + return true; +#if 0 + + char buffer[MAX_OSPATH]; + char *ext; + if (nlen >= MAX_OSPATH) + { + if (file) + VFS_CLOSE(file); + Con_Printf("Filename too long.\n"); + return true; + } + + memcpy(buffer, fname, nlen); + buffer[nlen] = 0; + fname = buffer; + ext = COM_FileExtension(fname); + if (!strncmp(fname, "qw:", 3)) + { + fname += 3; + if (!strncmp(fname, "//", 2)) + fname += 2; + ext = strchr(fname, '/'); //this also protects us against irc urls, etc. unsure if that's important right now. + if (ext) + *ext = 0; + Con_Printf("QW stream: \"%s\"\n", fname); + Cbuf_AddText(va("connect \"%s\"\n", fname), RESTRICT_LOCAL); + } + else if (!strcmp(ext, "qwd") || !strcmp(ext, "dem") || !strcmp(ext, "mvd")) + Cbuf_AddText(va("playdemo \"#%s\"\n", fname), RESTRICT_LOCAL); + else if (!strcmp(ext, "bsp") || !strcmp(ext, "pak") || !strcmp(ext, "pk3") || !strcmp(ext, "pk4") || !strcmp(ext, "fmf")) + { + extern qboolean waitingformanifest; + //these must all have a valid source file. just recurse if its not set. + if (!file) + { + if (!strncmp(fname, "http://", 7)) + { + if (HTTP_CL_Get(fname, NULL, Host_RunFileNotify)) + { + if (!strcmp(ext, "fmf")) + waitingformanifest = true; + return true; + } + return false; + } + if (!strncmp(fname, "file://", 7)) + fname += 7; + file = VFSOS_Open(fname, "rb"); //input file is a system path, or something. + if (file) + { + Host_RunFile(fname, strlen(fname), file); + return true; + } + return false; + } + + if (!strcmp(ext, "fmf")) + { + CL_Manifest_Parse(file, fname); + waitingformanifest = false; + } + else if (!strcmp(ext, "pak") || !strcmp(ext, "pk3") || !strcmp(ext, "pk4")) + { + //open the archive, look for a *readme.txt + //look for a -game inside that. + //if found, install the pak there. + //if a +map is found, load that map after install. + Con_Printf("Unable to install paks/pk3s at this time\n"); + } + else if (!strcmp(ext, "bsp")) + { + char qname[MAX_QPATH]; + vfsfile_t *qf; + qboolean overwrite = false; + COM_StripExtension(COM_SkipPath(fname), qname, sizeof(qname)); + qf = FS_OpenVFS(va("maps/%s.bsp", qname), "rb", FS_GAME); + if (qf) + { + if (VFS_GETLEN(file) != VFS_GETLEN(qf)) + overwrite = true; + VFS_SEEK(file, 0); + VFS_CLOSE(qf); + } + #ifdef _WIN32 + if (overwrite) + { + switch(MessageBox(mainwindow, va("Overwrite existing map: %s?", qname), "Install And Play", MB_YESNOCANCEL)) + { + case IDYES: + //overwrite it and load it up + overwrite = true; + break; + case IDNO: + //load up the old version + overwrite = false; + break; + default: + case IDCANCEL: + //quit or something + return false; + } + } + else if (!qf) + { + switch(MessageBox(mainwindow, va("Install new map: %s?", qname), "Install And Play", MB_OKCANCEL)) + { + case IDOK: + //overwrite it and load it up + overwrite = true; + break; + default: + case IDCANCEL: + //quit or something + return false; + } + } + + if (overwrite) + { + char buffer[8192]; + int len; + qf = FS_OpenVFS(va("maps/%s.bsp", qname), "wb", FS_GAMEONLY); + if (qf) + { + while(1) + { + len = VFS_READ(file, buffer, sizeof(buffer)); + if (len <= 0) + break; + VFS_WRITE(qf, buffer, len); + } + VFS_CLOSE(qf); + } + } + + Cbuf_AddText(va("map \"%s\"\n", qname), RESTRICT_LOCAL); + #endif + } + } + else + Cbuf_AddText(va("qtvplay \"#%s\"\n", fname), RESTRICT_LOCAL); + + if (file) + VFS_CLOSE(file); + return true; +#endif +} + /* ================== Host_Frame @@ -3550,6 +3953,7 @@ double Host_Frame (double time) float maxfps; qboolean maxfpsignoreserver; qboolean idle; + extern qboolean r_blockvidrestart; RSpeedLocals(); @@ -3575,6 +3979,15 @@ double Host_Frame (double time) HTTP_CL_Think(); #endif + if (r_blockvidrestart) + { + extern qboolean waitingformanifest; + if (waitingformanifest) + return 0.1; + Host_FinishLoading(); + return 0; + } + #ifdef PLUGINS Plug_Tick(); #endif @@ -3706,6 +4119,10 @@ double Host_Frame (double time) { IN_Move(NULL, 0); CL_CheckForResend (); + +#ifdef VOICECHAT + S_Voip_Transmit(0, NULL); +#endif } else { @@ -3855,7 +4272,7 @@ void CL_StartCinematicOrMenu(void) UI_Start(); #endif - Con_TPrintf (TLC_QUAKEWORLD_INITED, fs_gamename.string); + Con_TPrintf (TLC_QUAKEWORLD_INITED, *fs_gamename.string?fs_gamename.string:"Nothing"); //there might be some console command or somesuch waiting for the renderer to begin (demos or map command or whatever all need model support). realtime+=1; @@ -3905,13 +4322,48 @@ void CL_StartCinematicOrMenu(void) } } -//note that this does NOT include commandline. -void CL_ExecInitialConfigs(void) +void CL_ArgumentOverrides(void) { - int qrc, hrc, def, i; + int i; + if (COM_CheckParm ("-window") || COM_CheckParm ("-startwindowed")) + Cvar_Set(Cvar_FindVar("vid_fullscreen"), "0"); + if (COM_CheckParm ("-fullscreen")) + Cvar_Set(Cvar_FindVar("vid_fullscreen"), "1"); + + if ((i = COM_CheckParm ("-width"))) //width on it's own also sets height + { + Cvar_Set(Cvar_FindVar("vid_width"), com_argv[i+1]); + Cvar_SetValue(Cvar_FindVar("vid_height"), (atoi(com_argv[i+1])/4)*3); + } + if ((i = COM_CheckParm ("-height"))) + Cvar_Set(Cvar_FindVar("vid_height"), com_argv[i+1]); + + if ((i = COM_CheckParm ("-conwidth"))) //width on it's own also sets height + { + Cvar_Set(Cvar_FindVar("vid_conwidth"), com_argv[i+1]); + Cvar_SetValue(Cvar_FindVar("vid_conheight"), (atoi(com_argv[i+1])/4)*3); + } + if ((i = COM_CheckParm ("-conheight"))) + Cvar_Set(Cvar_FindVar("vid_conheight"), com_argv[i+1]); + + if ((i = COM_CheckParm ("-bpp"))) + Cvar_Set(Cvar_FindVar("vid_bpp"), com_argv[i+1]); + + if (COM_CheckParm ("-current")) + Cvar_Set(Cvar_FindVar("vid_desktopsettings"), "1"); +} + +//note that this does NOT include commandline. +void CL_ExecInitialConfigs(char *resetcommand) +{ + int qrc, hrc, def; Cbuf_AddText ("cl_warncmd 0\n", RESTRICT_LOCAL); + Cmd_ExecuteString("unbindall", RESTRICT_LOCAL); + Cmd_ExecuteString("cvarreset *", RESTRICT_LOCAL); + Cmd_ExecuteString(resetcommand, RESTRICT_LOCAL); + //who should we imitate? qrc = COM_FDepthFile("quake.rc", true); //q1 hrc = COM_FDepthFile("hexen.rc", true); //h2 @@ -3945,39 +4397,44 @@ void CL_ExecInitialConfigs(void) //assuming they didn't use any waits in their config (fools) //the configs should be fully loaded. //so convert the backwards compable commandline parameters in cvar sets. - - if (COM_CheckParm ("-window") || COM_CheckParm ("-startwindowed")) - Cvar_Set(Cvar_FindVar("vid_fullscreen"), "0"); - if (COM_CheckParm ("-fullscreen")) - Cvar_Set(Cvar_FindVar("vid_fullscreen"), "1"); - - if ((i = COM_CheckParm ("-width"))) //width on it's own also sets height - { - Cvar_Set(Cvar_FindVar("vid_width"), com_argv[i+1]); - Cvar_SetValue(Cvar_FindVar("vid_height"), (atoi(com_argv[i+1])/4)*3); - } - if ((i = COM_CheckParm ("-height"))) - Cvar_Set(Cvar_FindVar("vid_height"), com_argv[i+1]); - - if ((i = COM_CheckParm ("-conwidth"))) //width on it's own also sets height - { - Cvar_Set(Cvar_FindVar("vid_conwidth"), com_argv[i+1]); - Cvar_SetValue(Cvar_FindVar("vid_conheight"), (atoi(com_argv[i+1])/4)*3); - } - if ((i = COM_CheckParm ("-conheight"))) - Cvar_Set(Cvar_FindVar("vid_conheight"), com_argv[i+1]); - - if ((i = COM_CheckParm ("-bpp"))) - Cvar_Set(Cvar_FindVar("vid_bpp"), com_argv[i+1]); - - if (COM_CheckParm ("-current")) - Cvar_Set(Cvar_FindVar("vid_desktopsettings"), "1"); -// Cbuf_Execute (); //if the server initialisation causes a problem, give it a place to abort to + CL_ArgumentOverrides(); } +void Host_FinishLoading(void) +{ + //the filesystem has retrieved its manifest, but might still be waiting for paks to finish downloading. + //make sure the filesystem has some default if no manifest was loaded. + FS_ChangeGame(NULL, true); + + Con_History_Load(); + + Cmd_StuffCmds(); + Cbuf_Execute (); + + CL_ArgumentOverrides(); + +Con_TPrintf (TL_NL); + Con_Printf ("%s", version_string()); +Con_TPrintf (TL_NL); + + Con_DPrintf("This program is free software; you can redistribute it and/or " + "modify it under the terms of the GNU General Public License " + "as published by the Free Software Foundation; either version 2 " + "of the License, or (at your option) any later version." + "\n" + "This program is distributed in the hope that it will be useful, " + "but WITHOUT ANY WARRANTY; without even the implied warranty of " + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. " + "\n" + "See the GNU General Public License for more details.\n"); + + Renderer_Start(); + + CL_StartCinematicOrMenu(); +} /* ==================== @@ -3986,7 +4443,8 @@ Host_Init */ void Host_Init (quakeparms_t *parms) { - qboolean downloading = false; + int man; + com_parseutf8.ival = 1; //enable utf8 parsing even before cvars are registered. COM_InitArgv (parms->argc, parms->argv); @@ -4014,6 +4472,7 @@ void Host_Init (quakeparms_t *parms) Cbuf_Init (); Cmd_Init (); V_Init (); + NET_Init (); COM_Init (); #ifdef Q2BSPS CM_Init(); @@ -4023,7 +4482,6 @@ void Host_Init (quakeparms_t *parms) #endif Host_FixupModelNames(); - NET_Init (); NET_InitClient (); Netchan_Init (); Renderer_Init(); @@ -4077,42 +4535,11 @@ void Host_Init (quakeparms_t *parms) Sys_SendKeyEvents(); + //the engine is fully running, except the file system may be nulled out waiting for a manifest to download. - //the engine is technically initialised at this point, except for the renderer. now we exec configs and bring up the renderer - //anything that needs models cannot be run yet, but it should be safe to allow console commands etc. - //if we get a map command, we'll just stick it on the end of the console command buffer. - - Con_History_Load(); - - CL_ExecInitialConfigs(); - - if (CL_CheckBootDownloads()) - { - Cmd_StuffCmds(); - Cbuf_Execute (); - } - else - downloading = true; - -Con_TPrintf (TL_NL); - Con_Printf ("%s", version_string()); -Con_TPrintf (TL_NL); - - Con_DPrintf("This program is free software; you can redistribute it and/or " - "modify it under the terms of the GNU General Public License " - "as published by the Free Software Foundation; either version 2 " - "of the License, or (at your option) any later version." - "\n" - "This program is distributed in the hope that it will be useful, " - "but WITHOUT ANY WARRANTY; without even the implied warranty of " - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. " - "\n" - "See the GNU General Public License for more details.\n"); - - Renderer_Start(); - - if (!downloading) - CL_StartCinematicOrMenu(); + man = COM_CheckParm("-manifest"); + if (man && man < com_argc-1 && com_argv[man+1]) + Host_RunFile(com_argv[man+1], strlen(com_argv[man+1]), NULL); } /* @@ -4159,6 +4586,10 @@ void Host_Shutdown(void) NET_Shutdown (); #endif +#ifdef Q3CLIENT + VMQ3_FlushStringHandles(); +#endif + Cvar_Shutdown(); Validation_FlushFileList(); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 81ccc2d62..12f4951ba 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -603,17 +603,7 @@ void CL_DownloadFinished(void) Con_Printf("Couldn't rename %s to %s\n", nativetmp, nativefinal); } } - else if (strncmp(tempname,"skins/",6)) - { - if (!FS_Rename(tempname, filename, FS_GAME)) - { - char nativetmp[MAX_OSPATH], nativefinal[MAX_OSPATH];; - FS_NativePath(tempname, FS_GAME, nativetmp, sizeof(nativetmp)); - FS_NativePath(filename, FS_GAME, nativefinal, sizeof(nativefinal)); - Con_Printf("Couldn't rename %s to %s\n", nativetmp, nativefinal); - } - } - else + else if (!strncmp(tempname,"skins/",6)) { if (!FS_Rename(tempname+6, filename+6, FS_SKINS)) { @@ -623,6 +613,16 @@ void CL_DownloadFinished(void) Con_Printf("Couldn't rename %s to %s\n", nativetmp, nativefinal); } } + else + { + if (!FS_Rename(tempname, filename, FS_GAME)) + { + char nativetmp[MAX_OSPATH], nativefinal[MAX_OSPATH];; + FS_NativePath(tempname, FS_GAME, nativetmp, sizeof(nativetmp)); + FS_NativePath(filename, FS_GAME, nativefinal, sizeof(nativefinal)); + Con_Printf("Couldn't rename %s to %s\n", nativetmp, nativefinal); + } + } } } @@ -734,10 +734,11 @@ qboolean CL_CheckOrEnqueDownloadFile (char *filename, char *localname, unsigned if (flags & DLLF_NONGAME) { /*pak/pk3 downloads have an explicit leading package/ as an internal/network marker*/ - filename = va("package/%s", filename); + if (!strchr(filename, ':')) + filename = va("package/%s", filename); localname = va("package/%s", localname); } - /*files with a leading * should not be downloaded (inline models, sexed sounds, etc)*/ + /*files with a leading * should not be downloaded (inline models, sexed sounds, etc). also block anyone trying to explicitly download a package/ because our code (wrongly) uses that name internally*/ else if (*filename == '*' || !strncmp(filename, "package/", 8)) return true; @@ -1101,7 +1102,7 @@ int CL_LoadModels(int stage, qboolean dontactuallyload) endstage(); } - if (cl.playernum[0] == -1) + if (cl.playerview[0].playernum == -1) { //q2 cinematic - don't load the models. cl.worldmodel = cl.model_precache[1] = Mod_ForName ("", false); } @@ -2471,11 +2472,12 @@ void CLQW_ParseServerData (void) for (j = 0; j < MAX_SPLITS; j++) { - cl.playernum[j] = cl.allocated_client_slots + j; + cl.playerview[j].playernum = cl.allocated_client_slots + j; + cl.playerview[j].viewentity = 0; //free floating. for (i = 0; i < UPDATE_BACKUP; i++) { - cl.inframes[i].playerstate[cl.playernum[j]].pm_type = PM_SPECTATOR; - cl.inframes[i].playerstate[cl.playernum[j]].messagenum = 1; + cl.inframes[i].playerstate[cl.playerview[j].playernum].pm_type = PM_SPECTATOR; + cl.inframes[i].playerstate[cl.playerview[j].playernum].messagenum = 1; } } cl.spectator = true; @@ -2502,9 +2504,10 @@ void CLQW_ParseServerData (void) Host_EndGame("Server sent us too many alternate clients\n"); for (pnum = 0; pnum < cl.splitclients; pnum++) { - cl.playernum[pnum] = MSG_ReadByte(); - if (cl.playernum[pnum] >= cl.allocated_client_slots) + cl.playerview[pnum].playernum = MSG_ReadByte(); + if (cl.playerview[pnum].playernum >= cl.allocated_client_slots) Host_EndGame("unsupported local player slot\n"); + cl.playerview[pnum].viewentity = cl.playerview[pnum].playernum+1; } } else @@ -2515,16 +2518,17 @@ void CLQW_ParseServerData (void) { if (clnum == MAX_SPLITS) Host_EndGame("Server sent us too many alternate clients\n"); - cl.playernum[clnum] = pnum; - if (cl.playernum[clnum] & 128) + cl.playerview[clnum].playernum = pnum; + if (cl.playerview[clnum].playernum & 128) { cl.spectator = true; - cl.playernum[clnum] &= ~128; + cl.playerview[clnum].playernum &= ~128; } - if (cl.playernum[clnum] >= cl.allocated_client_slots) + if (cl.playerview[clnum].playernum >= cl.allocated_client_slots) Host_EndGame("unsupported local player slot\n"); + cl.playerview[clnum].viewentity = cl.playerview[clnum].playernum+1; if (!(cls.fteprotocolextensions & PEXT_SPLITSCREEN)) break; @@ -2553,8 +2557,8 @@ void CLQW_ParseServerData (void) for (clnum = 0; clnum < cl.splitclients; clnum++) { - cl.maxspeed[clnum] = maxspeed; - cl.entgravity[clnum] = entgrav; + cl.playerview[clnum].maxspeed = maxspeed; + cl.playerview[clnum].entgravity = entgrav; } // seperate the printfs so the server message can have a color @@ -2675,7 +2679,8 @@ void CLQ2_ParseServerData (void) // parse player entity number - cl.playernum[0] = MSG_ReadShort (); + cl.playerview[0].playernum = MSG_ReadShort (); + cl.playerview[0].viewentity = cl.playerview[0].playernum+1; cl.splitclients = 1; cl.spectator = false; @@ -2686,7 +2691,7 @@ void CLQ2_ParseServerData (void) str = MSG_ReadString (); Q_strncpyz (cl.levelname, str, sizeof(cl.levelname)); - if (cl.playernum[0] == -1) + if (cl.playerview[0].playernum == -1) { // playing a cinematic or showing a pic, not a level SCR_EndLoadingPlaque(); if (!Media_PlayFilm(str, false)) @@ -3765,7 +3770,7 @@ void CLQW_ParseStartSoundPacket(void) } - if (ent == cl.playernum[0]+1) + if (ent == cl.playerview[0].playernum+1) TP_CheckPickupSound(cl.sound_name[sound_num], pos); } @@ -3912,7 +3917,7 @@ void CLNQ_ParseStartSoundPacket(void) S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation, 0, pitchadj); } - if (ent == cl.playernum[0]+1) + if (ent == cl.playerview[0].playernum+1) TP_CheckPickupSound(cl.sound_name[sound_num], pos); } #endif @@ -4001,12 +4006,12 @@ void CL_NewTranslation (int slot) { if (cl.teamplay && cl.spectator) { - local = Cam_TrackNum(0); + local = Cam_TrackNum(&cl.playerview[0]); if (local < 0) - local = cl.playernum[0]; + local = cl.playerview[0].playernum; } else - local = cl.playernum[0]; + local = cl.playerview[0].playernum; if ((cl.teamplay || cls.protocol == CP_NETQUAKE) && !strcmp(player->team, cl.players[local].team)) { if (cl_teamtopcolor != ~0) @@ -4086,7 +4091,7 @@ void CL_ProcessUserInfo (int slot, player_info_t *player) player->colourised = TP_FindColours(player->name); // If it's us - if (slot == cl.playernum[0] && player->name[0]) + if (slot == cl.playerview[0].playernum && player->name[0]) { cl.spectator = player->spectator; @@ -4124,7 +4129,7 @@ void CL_UpdateUserinfo (void) - if (slot == cl.playernum[0] && player->name[0]) + if (slot == cl.playerview[0].playernum && player->name[0]) { char *qz; qz = Info_ValueForKey(player->userinfo, "Qizmo"); @@ -4239,7 +4244,7 @@ void CL_SetStatInt (int pnum, int stat, int value) cl.players[cls_lastto].stats[stat]=value; for (pnum = 0; pnum < cl.splitclients; pnum++) - if (spec_track[pnum] == cls_lastto) + if (cl.playerview[pnum].cam_spec_track == cls_lastto) CL_SetStat_Internal(pnum, stat, value); } else @@ -4257,14 +4262,14 @@ void CL_SetStatFloat (int pnum, int stat, float value) cl.players[cls_lastto].statsf[stat]=value; for (pnum = 0; pnum < cl.splitclients; pnum++) - if (spec_track[pnum] == cls_lastto) + if (cl.playerview[pnum].cam_spec_track == cls_lastto) cl.playerview[pnum].statsf[stat] = value; } else cl.playerview[pnum].statsf[stat] = value; if (stat == STAT_VIEWHEIGHT && cls.z_ext & Z_EXT_VIEWHEIGHT) - cl.viewheight[pnum] = value; + cl.playerview[pnum].viewheight = value; } void CL_SetStatString (int pnum, int stat, char *value) { @@ -4313,7 +4318,7 @@ void CL_MuzzleFlash (int destsplit) if (!cl_muzzleflash.ival) // remove all muzzleflashes return; - if (i-1 == cl.playernum[destsplit] && cl_muzzleflash.value == 2) + if (i-1 == cl.playerview[destsplit].playernum && cl_muzzleflash.value == 2) return; pack = &cl.inframes[cl.validsequence&UPDATE_MASK].packet_entities; @@ -5396,21 +5401,18 @@ void CLQW_ParseServerMessage (void) break; case svc_damage: - V_ParseDamage (destsplit); + V_ParseDamage (&cl.playerview[destsplit]); break; case svc_serverdata: Cbuf_Execute (); // make sure any stuffed commands are done CLQW_ParseServerData (); - vid.recalc_refdef = true; // leave full screen intermission break; #ifdef PEXT_SETVIEW case svc_setview: if (!(cls.fteprotocolextensions & PEXT_SETVIEW)) Con_Printf("^1PEXT_SETVIEW is meant to be disabled\n"); - cl.viewentity[destsplit]=MSGCL_ReadEntity(); - if (cl.viewentity[destsplit] == cl.playernum[destsplit]+1) - cl.viewentity[destsplit] = 0; + cl.playerview[destsplit].viewentity=MSGCL_ReadEntity(); break; #endif case svcfte_setangledelta: @@ -5427,12 +5429,13 @@ void CLQW_ParseServerMessage (void) ang[i] = MSG_ReadAngle(); for (j = 0; j < cl.splitclients; j++) { - if (Cam_TrackNum(j) == i) + playerview_t *pv = &cl.playerview[j]; + if (Cam_TrackNum(pv) == i) { - cl.playerview[j].fixangle=true; - VectorCopy(ang, cl.playerview[j].simangles); - VectorCopy(ang, cl.playerview[j].viewangles); - VectorCopy(ang, cl.playerview[j].fixangles); + pv->fixangle=true; + VectorCopy(ang, pv->simangles); + VectorCopy(ang, pv->viewangles); + VectorCopy(ang, pv->fixangles); } } break; @@ -5556,11 +5559,14 @@ void CLQW_ParseServerMessage (void) break; case svc_killedmonster: + //fixme: update all player stats cl.playerview[destsplit].stats[STAT_MONSTERS]++; + cl.playerview[destsplit].statsf[STAT_MONSTERS]++; break; - case svc_foundsecret: + //fixme: update all player stats cl.playerview[destsplit].stats[STAT_SECRETS]++; + cl.playerview[destsplit].statsf[STAT_SECRETS]++; break; case svcqw_updatestatbyte: @@ -5602,7 +5608,6 @@ void CLQW_ParseServerMessage (void) TP_ExecTrigger ("f_mapend"); cl.intermission = 1; cl.completed_time = cl.gametime; - vid.recalc_refdef = true; // go to full screen for (i=0 ; i<3 ; i++) cl.playerview[destsplit].simorg[i] = MSG_ReadCoord (); for (i=0 ; i<3 ; i++) @@ -5614,12 +5619,11 @@ void CLQW_ParseServerMessage (void) case svc_finale: if (!cl.intermission) for (i = 0; i < MAX_SPLITS; i++) - cl.playerview[i].simorg[2] += cl.viewheight[i]; + cl.playerview[i].simorg[2] += cl.playerview[i].viewheight; VectorCopy (cl.playerview[destsplit].fixangles, cl.playerview[destsplit].simangles); cl.intermission = 2; cl.completed_time = cl.gametime; - vid.recalc_refdef = true; // go to full screen SCR_CenterPrint (destsplit, MSG_ReadString (), false); break; @@ -5628,10 +5632,10 @@ void CLQW_ParseServerMessage (void) break; case svc_smallkick: - cl.punchangle[destsplit] = -2; + cl.playerview[destsplit].punchangle = -2; break; case svc_bigkick: - cl.punchangle[destsplit] = -4; + cl.playerview[destsplit].punchangle = -4; break; case svc_muzzleflash: @@ -5699,11 +5703,11 @@ void CLQW_ParseServerMessage (void) break; case svc_maxspeed: - cl.maxspeed[destsplit] = MSG_ReadFloat(); + cl.playerview[destsplit].maxspeed = MSG_ReadFloat(); break; case svc_entgravity: - cl.entgravity[destsplit] = MSG_ReadFloat(); + cl.playerview[destsplit].entgravity = MSG_ReadFloat(); break; case svc_setpause: @@ -6086,6 +6090,7 @@ qboolean CLNQ_ParseNQPrints(char *s) void CLNQ_ParseServerMessage (void) { + const int destsplit = 0; int cmd; char *s; int i, j; @@ -6227,7 +6232,6 @@ void CLNQ_ParseServerMessage (void) case svc_serverdata: Cbuf_Execute (); // make sure any stuffed commands are done CLNQ_ParseServerData (); - vid.recalc_refdef = true; // leave full screen intermission break; case svcdp_precache: @@ -6242,17 +6246,17 @@ void CLNQ_ParseServerMessage (void) break; case svc_setview: - if (!cl.viewentity[0]) + if (!cl.playerview[destsplit].viewentity) { - cl.playernum[0] = (cl.viewentity[0] = MSGCL_ReadEntity())-1; - if (cl.playernum[0] >= cl.allocated_client_slots) + cl.playerview[destsplit].playernum = (cl.playerview[destsplit].viewentity = MSGCL_ReadEntity())-1; + if (cl.playerview[destsplit].playernum >= cl.allocated_client_slots) { - Con_Printf(CON_WARNING "WARNING: Server put us in slot %i. We are not on the scoreboard.\n", cl.playernum[0]); - cl.playernum[0] = cl.allocated_client_slots; //pretend it's an mvd (we have that spare slot) + Con_Printf(CON_WARNING "WARNING: Server put us in slot %i. We are not on the scoreboard.\n", cl.playerview[destsplit].playernum); + cl.playerview[destsplit].playernum = cl.allocated_client_slots; //pretend it's an mvd (we have that spare slot) } } else - cl.viewentity[0]=MSGCL_ReadEntity(); + cl.playerview[destsplit].viewentity=MSGCL_ReadEntity(); break; case svc_signonnum: @@ -6303,9 +6307,10 @@ void CLNQ_ParseServerMessage (void) break; case svc_time: - cl.playerview[0].oldfixangle = cl.playerview[0].fixangle; - VectorCopy(cl.playerview[0].fixangles, cl.playerview[0].oldfixangles); - cl.playerview[0].fixangle = false; + //fixme: move this stuff to a common place + cl.playerview[destsplit].oldfixangle = cl.playerview[destsplit].fixangle; + VectorCopy(cl.playerview[destsplit].fixangles, cl.playerview[destsplit].oldfixangles); + cl.playerview[destsplit].fixangle = false; cls.netchan.outgoing_sequence++; cls.netchan.incoming_sequence = cls.netchan.outgoing_sequence-1; @@ -6351,6 +6356,7 @@ void CLNQ_ParseServerMessage (void) strcpy(cl.players[i].name, MSG_ReadString()); if (*cl.players[i].name) cl.players[i].userid = i+1; + Info_SetValueForKey(cl.players[i].userinfo, "name", cl.players[i].name, sizeof(cl.players[i].userinfo)); } break; @@ -6377,7 +6383,7 @@ void CLNQ_ParseServerMessage (void) if (cls.state == ca_active) Skin_Find (&cl.players[i]); - if (i == cl.playernum[0]) + if (i == cl.playerview[destsplit].playernum) Skin_FlushPlayers(); Sbar_Changed (); CL_NewTranslation (i); @@ -6410,9 +6416,9 @@ void CLNQ_ParseServerMessage (void) CL_SetStatFloat (0, i, j); break; case svc_setangle: - cl.playerview[0].fixangle=true; + cl.playerview[destsplit].fixangle=true; for (i=0 ; i<3 ; i++) - cl.playerview[0].viewangles[i] = cl.playerview[0].fixangles[i] = MSG_ReadAngle (); + cl.playerview[destsplit].viewangles[i] = cl.playerview[destsplit].fixangles[i] = MSG_ReadAngle (); // cl.viewangles[PITCH] = cl.viewangles[ROLL] = 0; break; @@ -6437,11 +6443,13 @@ void CLNQ_ParseServerMessage (void) break; case svc_killedmonster: - cl.playerview[0].stats[STAT_MONSTERS]++; + cl.playerview[destsplit].stats[STAT_MONSTERS]++; + cl.playerview[destsplit].statsf[STAT_MONSTERS]++; break; case svc_foundsecret: - cl.playerview[0].stats[STAT_SECRETS]++; + cl.playerview[destsplit].stats[STAT_SECRETS]++; + cl.playerview[destsplit].statsf[STAT_SECRETS]++; break; case svc_intermission: @@ -6449,20 +6457,17 @@ void CLNQ_ParseServerMessage (void) TP_ExecTrigger ("f_mapend"); cl.intermission = 1; cl.completed_time = cl.gametime; - vid.recalc_refdef = true; // go to full screen break; case svc_finale: cl.intermission = 2; cl.completed_time = cl.gametime; - vid.recalc_refdef = true; // go to full screen SCR_CenterPrint (0, MSG_ReadString (), false); break; case svc_cutscene: cl.intermission = 3; cl.completed_time = cl.gametime; - vid.recalc_refdef = true; // go to full screen SCR_CenterPrint (0, MSG_ReadString (), false); break; @@ -6471,7 +6476,7 @@ void CLNQ_ParseServerMessage (void) break; case svc_damage: - V_ParseDamage (0); + V_ParseDamage (&cl.playerview[destsplit]); break; case svcfitz_skybox: diff --git a/engine/client/cl_plugin.inc b/engine/client/cl_plugin.inc index 5adfb286a..1701458b3 100644 --- a/engine/client/cl_plugin.inc +++ b/engine/client/cl_plugin.inc @@ -321,9 +321,10 @@ qintptr_t VARGS Plug_GetPlayerInfo(void *offset, quintptr_t mask, const qintptr_ out = VM_POINTER(arg[1]); if (out) { - if (i == -1) + if (i < 0) { - i = cl.playernum[0]; + if (i >= -MAX_SPLITS) + i = cl.playerview[-i-1].playernum; if (i < 0) { memset(out, 0, sizeof(*out)); @@ -343,16 +344,16 @@ qintptr_t VARGS Plug_GetPlayerInfo(void *offset, quintptr_t mask, const qintptr_ Q_strncpyz(out->team, cl.players[i].team, sizeof(out->team)); } - pt = Cam_TrackNum(0); + pt = Cam_TrackNum(&cl.playerview[0]); if (pt < 0) - return (cl.playernum[0] == i); + return (cl.playerview[0].playernum == i); else return pt == i; } qintptr_t VARGS Plug_LocalPlayerNumber(void *offset, quintptr_t mask, const qintptr_t *arg) { - return cl.playernum[0]; + return cl.playerview[0].playernum; } qintptr_t VARGS Plug_GetServerInfo(void *offset, quintptr_t mask, const qintptr_t *arg) @@ -398,6 +399,8 @@ qintptr_t VARGS Plug_Con_SubPrint(void *offset, quintptr_t mask, const qintptr_t char *name = VM_POINTER(arg[0]); char *text = VM_POINTER(arg[1]); console_t *con; + if (!name) + name = ""; if (qrenderer == QR_NONE) return false; diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index f365a4a05..fab2c941f 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -116,7 +116,7 @@ void CLQ2_ClipMoveToEntities ( vec3_t start, vec3_t mins, vec3_t maxs, vec3_t en if (!ent->solid) continue; - if (ent->number == cl.playernum[0]+1) + if (ent->number == cl.playerview[0].playernum+1) continue; if (ent->solid == ES_SOLID_BSP) @@ -308,7 +308,7 @@ void CLQ2_PredictMovement (void) //q2 doesn't support split clients. cl.predicted_step_time = realtime - host_frametime * 0.5; } - cl.onground[0] = !!(pm.s.pm_flags & Q2PMF_ON_GROUND); + cl.playerview[0].onground = !!(pm.s.pm_flags & Q2PMF_ON_GROUND); // copy results out for rendering @@ -397,8 +397,8 @@ void CL_PredictUsercmd (int pnum, int entnum, player_state_t *from, player_state pmove.cmd = *u; pmove.skipent = entnum; - movevars.entgravity = cl.entgravity[pnum]; - movevars.maxspeed = cl.maxspeed[pnum]; + movevars.entgravity = cl.playerview[pnum].entgravity; + movevars.maxspeed = cl.playerview[pnum].maxspeed; movevars.bunnyspeedcap = cl.bunnyspeedcap; pmove.onladder = false; @@ -427,77 +427,76 @@ void CL_PredictUsercmd (int pnum, int entnum, player_state_t *from, player_state //Used when cl_nopred is 1 to determine whether we are on ground, otherwise stepup smoothing code produces ugly jump physics -void CL_CatagorizePosition (int pnum) +void CL_CatagorizePosition (playerview_t *pv) { if (cl.spectator) { - cl.onground[pnum] = false; // in air + pv->onground = false; // in air return; } VectorClear (pmove.velocity); - VectorCopy (cl.playerview[pnum].simorg, pmove.origin); + VectorCopy (pv->simorg, pmove.origin); pmove.numtouch = 0; PM_CategorizePosition (); - cl.onground[pnum] = pmove.onground; + pv->onground = pmove.onground; } //Smooth out stair step ups. //Called before CL_EmitEntities so that the player's lightning model origin is updated properly -void CL_CalcCrouch (int pnum, float stepchange) +void CL_CalcCrouch (playerview_t *pv, float stepchange) { qboolean teleported; - static vec3_t oldorigin[MAX_SPLITS]; - static float oldz[MAX_SPLITS] = {0}, extracrouch[MAX_SPLITS] = {0}, crouchspeed[MAX_SPLITS] = {100,100}; vec3_t delta; + float orgz = DotProduct(pv->simorg, pv->gravitydir); //compensate for running on walls. - VectorSubtract(cl.playerview[pnum].simorg, oldorigin[pnum], delta); + VectorSubtract(pv->simorg, pv->oldorigin, delta); teleported = Length(delta)>48; - VectorCopy (cl.playerview[pnum].simorg, oldorigin[pnum]); + VectorCopy (pv->simorg, pv->oldorigin); if (teleported) { // possibly teleported or respawned - oldz[pnum] = cl.playerview[pnum].simorg[2]; - extracrouch[pnum] = 0; - crouchspeed[pnum] = 100; - cl.crouch[pnum] = 0; - VectorCopy (cl.playerview[pnum].simorg, oldorigin[pnum]); + pv->oldz = orgz; + pv->extracrouch = 0; + pv->crouchspeed = 100; + pv->crouch = 0; + VectorCopy (pv->simorg, pv->oldorigin); return; } - if (cl.onground[pnum] && cl.playerview[pnum].simorg[2] - oldz[pnum] > 0) + if (pv->onground && orgz - pv->oldz > 0) { - if (cl.playerview[pnum].simorg[2] - oldz[pnum] > movevars.stepheight+2) + if (orgz - pv->oldz > movevars.stepheight+2) { // if on steep stairs, increase speed - if (crouchspeed[pnum] < 160) + if (pv->crouchspeed < 160) { - extracrouch[pnum] = cl.playerview[pnum].simorg[2] - oldz[pnum] - host_frametime * 200 - 15; - extracrouch[pnum] = min(extracrouch[pnum], 5); + pv->extracrouch = orgz - pv->oldz - host_frametime * 200 - 15; + pv->extracrouch = min(pv->extracrouch, 5); } - crouchspeed[pnum] = 160; + pv->crouchspeed = 160; } - oldz[pnum] += host_frametime * crouchspeed[pnum]; - if (oldz[pnum] > cl.playerview[pnum].simorg[2]) - oldz[pnum] = cl.playerview[pnum].simorg[2]; + pv->oldz += host_frametime * pv->crouchspeed; + if (pv->oldz > orgz) + pv->oldz = orgz; - if (cl.playerview[pnum].simorg[2] - oldz[pnum] > 15 + extracrouch[pnum]) - oldz[pnum] = cl.playerview[pnum].simorg[2] - 15 - extracrouch[pnum]; - extracrouch[pnum] -= host_frametime * 200; - extracrouch[pnum] = max(extracrouch[pnum], 0); + if (orgz - pv->oldz > 15 + pv->extracrouch) + pv->oldz = orgz - 15 - pv->extracrouch; + pv->extracrouch -= host_frametime * 200; + pv->extracrouch = max(pv->extracrouch, 0); - cl.crouch[pnum] = oldz[pnum] - cl.playerview[pnum].simorg[2]; + pv->crouch = pv->oldz - orgz; } else { // in air or moving down - oldz[pnum] = cl.playerview[pnum].simorg[2]; - cl.crouch[pnum] += host_frametime * 150; - if (cl.crouch[pnum] > 0) - cl.crouch[pnum] = 0; - crouchspeed[pnum] = 100; - extracrouch[pnum] = 0; + pv->oldz = orgz; + pv->crouch += host_frametime * 150; + if (pv->crouch > 0) + pv->crouch = 0; + pv->crouchspeed = 100; + pv->extracrouch = 0; } } @@ -665,6 +664,8 @@ void CL_CalcClientTime(void) max = cl.gametime; min = cl.oldgametime; + if (max < min) + max = min; if (max) cl.servertime += host_frametime; @@ -673,14 +674,14 @@ void CL_CalcClientTime(void) if (cl.servertime > max) { - if (cl.servertime > cl.gametime) + if (cl.servertime > max) { - cl.servertime = cl.gametime; + cl.servertime = max; // Con_Printf("clamped to new time\n"); } else { - cl.servertime -= 0.02*(cl.gametime - cl.servertime); + cl.servertime -= 0.02*(max - cl.servertime); } } if (cl.servertime < min) @@ -829,7 +830,7 @@ void CL_PlayerFrameUpdated(player_state_t *plstate, entity_state_t *state, int s cl.players[state->number-1].statsf[STAT_WEAPONFRAME] = state->u.q1.weaponframe; for (i = 0; i < cl.splitclients; i++) { - if (cl.playernum[i] == state->number-1) + if (cl.playerview[i].playernum == state->number-1) { cl.playerview[i].stats[STAT_WEAPONFRAME] = state->u.q1.weaponframe; cl.playerview[i].statsf[STAT_WEAPONFRAME] = state->u.q1.weaponframe; @@ -853,7 +854,7 @@ qboolean CL_PredictPlayer(lerpents_t *le, entity_state_t *state, int sequence) /*local players just interpolate for now. the prediction code will move it to the right place afterwards*/ for (pnum = 0; pnum < cl.splitclients; pnum++) { - if (state->number-1 == cl.playernum[pnum]) + if (state->number-1 == cl.playerview[pnum].playernum) return false; } @@ -886,8 +887,9 @@ qboolean CL_PredictPlayer(lerpents_t *le, entity_state_t *state, int sequence) CL_PredictMove ============== */ -void CL_PredictMovePNum (int vnum) +void CL_PredictMovePNum (int seat) { + playerview_t *pv = &cl.playerview[seat]; inframe_t indstate; outframe_t indcmd; int i; @@ -928,14 +930,14 @@ void CL_PredictMovePNum (int vnum) } } - cl.nolocalplayer[vnum] = !!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || (cls.protocol != CP_QUAKEWORLD); + pv->nolocalplayer = !!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || (cls.protocol != CP_QUAKEWORLD); #ifdef Q2CLIENT if (cls.protocol == CP_QUAKE2) { if (!cl.worldmodel || cl.worldmodel->needload) return; - cl.crouch[vnum] = 0; + pv->crouch = 0; CLQ2_PredictMovement(); return; } @@ -944,12 +946,12 @@ void CL_PredictMovePNum (int vnum) if (cl_pushlatency.value > 0) Cvar_Set (&cl_pushlatency, "0"); - if (cl.paused && !(cls.demoplayback!=DPB_MVD && cls.demoplayback!=DPB_EZTV) && (!cl.spectator || !autocam[vnum])) + if (cl.paused && !(cls.demoplayback!=DPB_MVD && cls.demoplayback!=DPB_EZTV) && (!cl.spectator || !pv->cam_auto)) return; if (cl.intermission==1 && cls.protocol == CP_QUAKEWORLD) { - cl.crouch[vnum] = 0; + pv->crouch = 0; return; } @@ -967,7 +969,7 @@ void CL_PredictMovePNum (int vnum) if (cl.movesequence - cl.ackedmovesequence >= UPDATE_BACKUP-1) { //lagging like poo. if (!cl.intermission) //keep the angles working though. - VectorCopy (cl.playerview[vnum].viewangles, cl.playerview[vnum].simangles); + VectorCopy (pv->viewangles, pv->simangles); return; } @@ -977,83 +979,87 @@ void CL_PredictMovePNum (int vnum) if (!cl.intermission) { - VectorCopy (cl.playerview[vnum].viewangles, cl.playerview[vnum].simangles); + VectorCopy (pv->viewangles, pv->simangles); } - vel = from->playerstate[cl.playernum[vnum]].velocity; - org = from->playerstate[cl.playernum[vnum]].origin; + vel = from->playerstate[pv->playernum].velocity; + org = from->playerstate[pv->playernum].origin; #ifdef PEXT_SETVIEW - if (cl.viewentity[vnum] && (cl.viewentity[vnum] != cl.playernum[vnum]+1 || cl.ackedmovesequence == cl.movesequence)) + //if the view is attached to an arbitary entity... + if (pv->viewentity && (pv->viewentity != pv->playernum+1 || cl.ackedmovesequence == cl.movesequence)) { - if (cl.viewentity[vnum] < cl.maxlerpents) + if (pv->viewentity >= 0 && pv->viewentity <= cl.allocated_client_slots && from->playerstate[pv->viewentity-1].messagenum == cl.validsequence) { - cl.nolocalplayer[vnum] = true; + } + else if (pv->viewentity < cl.maxlerpents) + { + pv->nolocalplayer = true; // Con_Printf("Using lerped pos\n"); - org = cl.lerpents[cl.viewentity[vnum]].origin; + org = cl.lerpents[pv->viewentity].origin; vel = vec3_origin; goto fixedorg; } } #endif - if (!from->playerstate[cl.playernum[vnum]].messagenum) + if (!from->playerstate[pv->playernum].messagenum) { //no player states?? put the view on an ent - if (cl.playernum[vnum] < cl.maxlerpents) + if (pv->playernum < cl.maxlerpents) { - cl.nolocalplayer[vnum] = true; + pv->nolocalplayer = true; // Con_Printf("Using lerped pos\n"); - org = cl.lerpents[cl.playernum[vnum]+1].origin; + org = cl.lerpents[pv->playernum+1].origin; vel = vec3_origin; goto fixedorg; } } - if (((cl_nopred.value && cls.demoplayback!=DPB_MVD && cls.demoplayback != DPB_EZTV)|| cl.playerview[vnum].fixangle || cl.paused)) + if (((cl_nopred.value && cls.demoplayback!=DPB_MVD && cls.demoplayback != DPB_EZTV)|| pv->fixangle || cl.paused)) { if (cl_lerp_players.ival && !cls.demoplayback) { lerpents_t *le; - if (cl.nolocalplayer[vnum]) - le = &cl.lerpents[spec_track[vnum]+1]; + if (pv->nolocalplayer) + le = &cl.lerpents[pv->cam_spec_track+1]; else - le = &cl.lerpplayers[spec_track[vnum]]; + le = &cl.lerpplayers[pv->cam_spec_track]; org = le->origin; vel = vec3_origin; } fixedorg: - VectorCopy (vel, cl.playerview[vnum].simvel); - VectorCopy (org, cl.playerview[vnum].simorg); + VectorCopy (vel, pv->simvel); + VectorCopy (org, pv->simorg); // to = &cl.inframes[cl.ackedinputsequence & UPDATE_MASK]; - CL_CatagorizePosition(vnum); + CL_CatagorizePosition(pv); goto out; } // predict forward until cl.time <= to->senttime oldphysent = pmove.numphysent; CL_SetSolidPlayers(); - pmove.skipent = cl.playernum[vnum]+1; + pmove.skipent = pv->playernum+1; // Con_Printf("%i<%i %i\n", cl.ackedmovesequence, cl.movesequence, cl.validsequence); to = &cl.inframes[cl.validsequence & UPDATE_MASK]; cmdto = &cl.outframes[cl.ackedmovesequence & UPDATE_MASK]; - if (Cam_TrackNum(vnum)>=0 && CL_MayLerp()) + if (pv->viewentity && pv->viewentity != pv->playernum+1 && CL_MayLerp()) { float f; if (cl_lerp_players.ival && (cls.demoplayback==DPB_MVD || cls.demoplayback == DPB_EZTV)) { - lerpents_t *le = &cl.lerpplayers[spec_track[vnum]]; + lerpents_t *le = &cl.lerpplayers[pv->cam_spec_track]; org = le->origin; vel = vec3_origin; - VectorCopy(le->angles, cl.playerview[vnum].simangles); + VectorCopy(le->angles, pv->simangles); goto fixedorg; } @@ -1078,19 +1084,19 @@ fixedorg: // calculate origin for (i=0 ; i<3 ; i++) { - lrp[i] = to->playerstate[spec_track[vnum]].origin[i] + - f * (from->playerstate[spec_track[vnum]].origin[i] - to->playerstate[spec_track[vnum]].origin[i]); + lrp[i] = to->playerstate[pv->cam_spec_track].origin[i] + + f * (from->playerstate[pv->cam_spec_track].origin[i] - to->playerstate[pv->cam_spec_track].origin[i]); - lrpv[i] = to->playerstate[spec_track[vnum]].velocity[i] + - f * (from->playerstate[spec_track[vnum]].velocity[i] - to->playerstate[spec_track[vnum]].velocity[i]); + lrpv[i] = to->playerstate[pv->cam_spec_track].velocity[i] + + f * (from->playerstate[pv->cam_spec_track].velocity[i] - to->playerstate[pv->cam_spec_track].velocity[i]); - cl.playerview[vnum].simangles[i] = LerpAngles16(to->playerstate[spec_track[vnum]].command.angles[i], from->playerstate[spec_track[vnum]].command.angles[i], f)*360.0f/65535; + pv->simangles[i] = LerpAngles16(to->playerstate[pv->cam_spec_track].command.angles[i], from->playerstate[pv->cam_spec_track].command.angles[i], f)*360.0f/65535; } org = lrp; vel = lrpv; - cl.pmovetype[vnum] = PM_NONE; + pv->pmovetype = PM_NONE; goto fixedorg; } else @@ -1103,10 +1109,10 @@ fixedorg: to->playerstate->pm_type = PM_SPECTATOR; simtime = cmdto->senttime; - VectorCopy (cl.playerview[vnum].simvel, from->playerstate[cl.playernum[vnum]].velocity); - VectorCopy (cl.playerview[vnum].simorg, from->playerstate[cl.playernum[vnum]].origin); + VectorCopy (pv->simvel, from->playerstate[pv->playernum].velocity); + VectorCopy (pv->simorg, from->playerstate[pv->playernum].origin); - CL_PredictUsercmd (vnum, 0, &from->playerstate[cl.playernum[vnum]], &to->playerstate[cl.playernum[vnum]], &cmdto->cmd[vnum]); + CL_PredictUsercmd (seat, 0, &from->playerstate[pv->playernum], &to->playerstate[pv->playernum], &cmdto->cmd[seat]); } else { @@ -1116,7 +1122,7 @@ fixedorg: to = &cl.inframes[(cl.validsequence+i) & UPDATE_MASK]; cmdto = &cl.outframes[(cl.ackedmovesequence+i) & UPDATE_MASK]; - CL_PredictUsercmd (vnum, cl.playernum[vnum]+1, &from->playerstate[cl.playernum[vnum]], &to->playerstate[cl.playernum[vnum]], &cmdto->cmd[vnum]); + CL_PredictUsercmd (seat, pv->playernum+1, &from->playerstate[pv->playernum], &to->playerstate[pv->playernum], &cmdto->cmd[seat]); if (cmdto->senttime >= simtime) break; @@ -1132,20 +1138,20 @@ fixedorg: from = to; to = &indstate; cmdto = &indcmd; - if (independantphysics[vnum].msec && !cls.demoplayback) - cmdto->cmd[vnum] = independantphysics[vnum]; + if (independantphysics[seat].msec && !cls.demoplayback) + cmdto->cmd[seat] = independantphysics[seat]; else - cmdto->cmd[vnum] = cmdfrom->cmd[vnum]; + cmdto->cmd[seat] = cmdfrom->cmd[seat]; cmdto->senttime = simtime; msec = ((cmdto->senttime - cmdfrom->senttime) * 1000) + 0.5; - cmdto->cmd[vnum].msec = bound(0, msec, 250); + cmdto->cmd[seat].msec = bound(0, msec, 250); - CL_PredictUsercmd (vnum, cl.playernum[vnum]+1, &from->playerstate[cl.playernum[vnum]] - , &to->playerstate[cl.playernum[vnum]], &cmdto->cmd[vnum]); + CL_PredictUsercmd (seat, pv->playernum+1, &from->playerstate[pv->playernum] + , &to->playerstate[pv->playernum], &cmdto->cmd[seat]); } - cl.onground[vnum] = pmove.onground; - cl.pmovetype[vnum] = to->playerstate[cl.playernum[vnum]].pm_type; - stepheight = to->playerstate[cl.playernum[vnum]].origin[2] - from->playerstate[cl.playernum[vnum]].origin[2]; + pv->onground = pmove.onground; + pv->pmovetype = to->playerstate[pv->playernum].pm_type; + stepheight = to->playerstate[pv->playernum].origin[2] - from->playerstate[pv->playernum].origin[2]; } //backdate it if our simulation time is in the past. this will happen on localhost, but not on 300-ping servers. @@ -1162,12 +1168,12 @@ fixedorg: // Con_Printf("%f %f %f\n", cmdfrom->senttime, simtime, cmdto->senttime); if (cmdto->senttime == cmdfrom->senttime) { - VectorCopy (to->playerstate[cl.playernum[vnum]].velocity, cl.playerview[vnum].simvel); - VectorCopy (to->playerstate[cl.playernum[vnum]].origin, cl.playerview[vnum].simorg); + VectorCopy (to->playerstate[pv->playernum].velocity, pv->simvel); + VectorCopy (to->playerstate[pv->playernum].origin, pv->simorg); } else { - int pnum = cl.playernum[vnum]; + int pnum = pv->playernum; // now interpolate some fraction of the final frame f = (simtime - cmdfrom->senttime) / (cmdto->senttime - cmdfrom->senttime); @@ -1179,40 +1185,40 @@ fixedorg: for (i=0 ; i<3 ; i++) if ( fabs(from->playerstate[pnum].origin[i] - to->playerstate[pnum].origin[i]) > 128) { // teleported, so don't lerp - VectorCopy (to->playerstate[pnum].velocity, cl.playerview[vnum].simvel); - VectorCopy (to->playerstate[pnum].origin, cl.playerview[vnum].simorg); + VectorCopy (to->playerstate[pnum].velocity, pv->simvel); + VectorCopy (to->playerstate[pnum].origin, pv->simorg); goto out; } for (i=0 ; i<3 ; i++) { - cl.playerview[vnum].simorg[i] = (1-f)*from->playerstate[pnum].origin[i] + f*to->playerstate[pnum].origin[i]; - cl.playerview[vnum].simvel[i] = (1-f)*from->playerstate[pnum].velocity[i] + f*to->playerstate[pnum].velocity[i]; + pv->simorg[i] = (1-f)*from->playerstate[pnum].origin[i] + f*to->playerstate[pnum].origin[i]; + pv->simvel[i] = (1-f)*from->playerstate[pnum].velocity[i] + f*to->playerstate[pnum].velocity[i]; /* if (cl.spectator && Cam_TrackNum(vnum) >= 0) - cl.playerview[vnum].simangles[i] = LerpAngles16(from->playerstate[pnum].command.angles[i], to->playerstate[pnum].command.angles[i], f) * (360.0/65535); + pv->simangles[i] = LerpAngles16(from->playerstate[pnum].command.angles[i], to->playerstate[pnum].command.angles[i], f) * (360.0/65535); else if (cls.demoplayback == DPB_QUAKEWORLD) - cl.playerview[vnum].simangles[i] = LerpAngles16(cmdfrom->cmd[vnum].angles[i], cmdto->cmd[vnum].angles[i], f) * (360.0/65535); + pv->simangles[i] = LerpAngles16(cmdfrom->cmd[vnum].angles[i], cmdto->cmd[vnum].angles[i], f) * (360.0/65535); */ } - CL_CatagorizePosition(vnum); + CL_CatagorizePosition(pv); } - if (cl.nolocalplayer[vnum] && cl.maxlerpents > cl.playernum[vnum]+1) + if (pv->nolocalplayer && cl.maxlerpents > pv->playernum+1) { //keep the entity tracking the prediction position, so mirrors don't go all weird - VectorCopy(to->playerstate[cl.playernum[vnum]].origin, cl.lerpents[cl.playernum[vnum]+1].origin); - VectorScale(cmdto->cmd[vnum].angles, 360.0f / 0xffff, cl.lerpents[cl.playernum[vnum]+1].angles); - cl.lerpents[cl.playernum[vnum]+1].angles[0] *= -0.333; + VectorCopy(to->playerstate[pv->playernum].origin, cl.lerpents[pv->playernum+1].origin); + VectorScale(pv->simangles, 1, cl.lerpents[pv->playernum+1].angles); + cl.lerpents[pv->playernum+1].angles[0] *= -0.333; } if (cls.demoplayback) - CL_LerpMove (vnum, cmdto->senttime); + CL_LerpMove (seat, cmdto->senttime); out: - CL_CalcCrouch (vnum, stepheight); - cl.waterlevel[vnum] = pmove.waterlevel; - VectorCopy(pmove.gravitydir, cl.playerview[vnum].gravitydir); + CL_CalcCrouch (pv, stepheight); + pv->waterlevel = pmove.waterlevel; + VectorCopy(pmove.gravitydir, pv->gravitydir); } void CL_PredictMove (void) diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index c68735350..88e0b74bf 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -918,8 +918,6 @@ void SCR_Fov_Callback (struct cvar_s *var, char *oldvalue) Cvar_ForceSet (var, "170"); return; } - - vid.recalc_refdef = true; } void SCR_Viewsize_Callback (struct cvar_s *var, char *oldvalue) @@ -934,130 +932,13 @@ void SCR_Viewsize_Callback (struct cvar_s *var, char *oldvalue) Cvar_ForceSet (var, "120"); return; } - - vid.recalc_refdef = true; } void CL_Sbar_Callback(struct cvar_s *var, char *oldvalue) { - vid.recalc_refdef = true; } -/* -================= -SCR_CalcRefdef - -Must be called whenever vid changes -Internal use only -================= -*/ -void SCR_CalcRefdef (void) -{ - float size; - int h; - qboolean full = false; - - vid.recalc_refdef = 0; - -// force the status bar to redraw - Sbar_Changed (); - -//======================================== - - r_refdef.flags = 0; - -// intermission is always full screen - if (cl.intermission) - size = 120; - else - size = scr_viewsize.value; - -#ifdef Q2CLIENT - if (cls.protocol == CP_QUAKE2) //q2 never has a hud. - sb_lines = 0; - else -#endif - - if (size >= 120) - sb_lines = 0; // no status bar at all - else if (size >= 110) - sb_lines = 24; // no inventory - else - sb_lines = 24+16+8; - - if (scr_viewsize.value >= 100.0 || scr_chatmode) - { - full = true; - size = 100.0; - } - else - size = scr_viewsize.value; - - if (cl.intermission) - { - full = true; - size = 100.0; - sb_lines = 0; - } - size /= 100.0; - - if (cl_sbar.value!=1 && full) - h = vid.height; - else - h = vid.height - sb_lines; - - r_refdef.vrect.width = vid.width * size; - if (r_refdef.vrect.width < 96) - { - size = 96.0 / r_refdef.vrect.width; - r_refdef.vrect.width = 96; // min for icons - } - - r_refdef.vrect.height = vid.height * size; - if (cl_sbar.value==1 || !full) - { - if (r_refdef.vrect.height > vid.height - sb_lines) - r_refdef.vrect.height = vid.height - sb_lines; - } - else if (r_refdef.vrect.height > vid.height) - r_refdef.vrect.height = vid.height; - - r_refdef.vrect.x = (vid.width - r_refdef.vrect.width)/2; - if (full) - r_refdef.vrect.y = 0; - else - r_refdef.vrect.y = (h - r_refdef.vrect.height)/2; - - if (scr_chatmode) - { - if (scr_chatmode != 2) - r_refdef.vrect.height= r_refdef.vrect.y=vid.height/2; - r_refdef.vrect.width = r_refdef.vrect.x=vid.width/2; - if (r_refdef.vrect.width<320 || r_refdef.vrect.height<200) //disable hud if too small - sb_lines=0; - } - - r_refdef.fov_x = scr_fov.value; - if (cl.playerview[r_refdef.currentplayernum].stats[STAT_VIEWZOOM]) - r_refdef.fov_x *= cl.playerview[r_refdef.currentplayernum].stats[STAT_VIEWZOOM]/255.0f; - - if (r_refdef.fov_x < 1) - r_refdef.fov_x = 1; - else if (r_refdef.fov_x > 170) - r_refdef.fov_x = 170; - - - r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height); - - - - -// r_refdef.vrect.height/=2; - - scr_vrect = r_refdef.vrect; -} - -void SCR_CrosshairPosition(int pnum, int *x, int *y) +void SCR_CrosshairPosition(playerview_t *pview, int *x, int *y) { extern cvar_t cl_crossx, cl_crossy, crosshaircorrect, v_viewheight; @@ -1072,9 +953,9 @@ void SCR_CrosshairPosition(int pnum, int *x, int *y) vec3_t start; vec3_t right, up, fwds; - AngleVectors(cl.playerview[pnum].simangles, fwds, right, up); + AngleVectors(pview->simangles, fwds, right, up); - VectorCopy(cl.playerview[pnum].simorg, start); + VectorCopy(pview->simorg, start); start[2]+=16; VectorMA(start, 100000, fwds, end); @@ -1090,7 +971,7 @@ void SCR_CrosshairPosition(int pnum, int *x, int *y) } else { - adj=cl.viewheight[pnum]; + adj=pview->viewheight; if (v_viewheight.value < -7) adj+=-7; else if (v_viewheight.value > 4) @@ -1099,7 +980,7 @@ void SCR_CrosshairPosition(int pnum, int *x, int *y) adj+=v_viewheight.value; start[2]+=adj; - Matrix4x4_CM_Project(tr.endpos, end, cl.playerview[pnum].simangles, start, r_refdef.fov_x, r_refdef.fov_y); + Matrix4x4_CM_Project(tr.endpos, end, pview->simangles, start, r_refdef.fov_x, r_refdef.fov_y); *x = rect.x+rect.width*end[0]; *y = rect.y+rect.height*(1-end[1]); return; @@ -1356,7 +1237,7 @@ void SCR_DrawUPS (void) if ((t - lastupstime) >= 1.0/20) { if (cl.spectator) - track = Cam_TrackNum(0); + track = Cam_TrackNum(&cl.playerview[0]); else track = -1; if (track != -1) @@ -1772,7 +1653,12 @@ void SCR_SetUpToDrawConsole (void) } else if ((key_dest == key_console || key_dest == key_game) && SCR_GetLoadingStage() == LS_NONE && cls.state < ca_active && !Media_PlayingFullScreen() && !CSQC_UnconnectedOkay(false)) { - if (cls.state < ca_demostart) +#ifdef VM_UI + if (key_dest == key_game && (UI_MenuState() || UI_OpenMenu())) + ; + else +#endif + if (cls.state < ca_demostart) key_dest = key_console; scr_con_current = scr_conlines = vid.height * fullscreenpercent; } @@ -2235,64 +2121,26 @@ void SCR_BringDownConsole (void) void SCR_TileClear (void) { -#ifdef PLUGINS -// extern cvar_t plug_sbar; -#endif - - if (cl.splitclients>1) - return; //splitclients always takes the entire screen. - -/*#ifdef PLUGINS - if (plug_sbar.ival) + if (r_refdef.vrect.width < r_refdef.grect.width) { - if (scr_vrect.x > 0) - { - // left - R2D_TileClear (0, 0, scr_vrect.x, vid.height); - // right - R2D_TileClear (scr_vrect.x + scr_vrect.width, 0, - vid.width - scr_vrect.x + scr_vrect.width, - vid.height); - } - if (scr_vrect.y > 0 || scr_vrect.height != vid.height) - { - // top - R2D_TileClear (scr_vrect.x, 0, - scr_vrect.width, - scr_vrect.y); - // bottom - R2D_TileClear (scr_vrect.x, - scr_vrect.y + scr_vrect.height, - scr_vrect.width, - vid.height); - } + int w; + // left + R2D_TileClear (r_refdef.grect.x, r_refdef.grect.y, r_refdef.vrect.x-r_refdef.grect.x, r_refdef.grect.height - sb_lines); + // right + w = (r_refdef.grect.x+r_refdef.grect.width) - (r_refdef.vrect.x+r_refdef.vrect.width); + R2D_TileClear ((r_refdef.grect.x+r_refdef.grect.width) - (w), r_refdef.grect.y, w, r_refdef.grect.height - sb_lines); } - else -#endif - */ + if (r_refdef.vrect.height < r_refdef.grect.height) { - if (scr_vrect.x > 0) - { - // left - R2D_TileClear (0, 0, scr_vrect.x, vid.height - sb_lines); - // right - R2D_TileClear (scr_vrect.x + scr_vrect.width, 0, - vid.width - scr_vrect.x + scr_vrect.width, - vid.height - sb_lines); - } - if (scr_vrect.y > 0) - { - // top - R2D_TileClear (scr_vrect.x, 0, - scr_vrect.width, - scr_vrect.y); - // bottom - R2D_TileClear (scr_vrect.x, - scr_vrect.y + scr_vrect.height, - scr_vrect.width, - vid.height - cl_sbar.value?sb_lines:0 - - (scr_vrect.height + scr_vrect.y)); - } + // top + R2D_TileClear (r_refdef.vrect.x, r_refdef.grect.y, + r_refdef.vrect.width, + r_refdef.vrect.y - r_refdef.grect.y); + // bottom + R2D_TileClear (r_refdef.vrect.x, + r_refdef.vrect.y + r_refdef.vrect.height, + r_refdef.vrect.width, + (r_refdef.grect.y+r_refdef.grect.height) - sb_lines - (r_refdef.vrect.y + r_refdef.vrect.height)); } } @@ -2303,6 +2151,8 @@ void SCR_DrawTwoDimensional(int uimenu, qboolean nohud) { RSpeedMark(); + R2D_ImageColours(1, 1, 1, 1); + // // draw any areas not covered by the refresh // @@ -2313,18 +2163,6 @@ void SCR_DrawTwoDimensional(int uimenu, qboolean nohud) { SCR_DrawLoading(); - if (!nohud) - { -#ifdef PLUGINS - Plug_SBar (); -#else - if (Sbar_ShouldDraw()) - { - Sbar_Draw (); - Sbar_DrawScoreboard (); - } -#endif - } SCR_ShowPics_Draw(); } else if (cl.intermission == 1) @@ -2352,15 +2190,6 @@ void SCR_DrawTwoDimensional(int uimenu, qboolean nohud) SCR_DrawGameClock(); SCR_DrawTurtle (); SCR_DrawPause (); -#ifdef PLUGINS - Plug_SBar (); -#else - if (Sbar_ShouldDraw()) - { - Sbar_Draw (); - Sbar_DrawScoreboard (); - } -#endif SCR_ShowPics_Draw(); CL_DrawPrydonCursor(); diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 164625f1e..d9c67531f 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -721,7 +721,8 @@ void CL_AddBeam (int tent, int ent, vec3_t start, vec3_t end) //fixme: use TE_ n { for (i = 0; i < cl.splitclients; i++) { - if (ent == (autocam[i]?(spec_track[i]+1):(cl.playernum[i]+1))) + playerview_t *pv = &cl.playerview[i]; + if (ent == (pv->cam_auto?(pv->cam_spec_track+1):(pv->playernum+1))) { VectorCopy(end, playerbeam_end[i]); break; @@ -3102,7 +3103,8 @@ void CL_UpdateBeams (void) { for (j = 0; j < cl.splitclients; j++) { - if (b->entity == ((cl.spectator&&autocam[j])?spec_track[j]+1:(cl.playernum[j]+1))) + playerview_t *pv = &cl.playerview[j]; + if (b->entity == ((cl.spectator&&pv->cam_auto)?pv->cam_spec_track+1:(pv->playernum+1))) { player_state_t *pl; // VectorSubtract(cl.simorg, b->start, org); @@ -3114,24 +3116,24 @@ void CL_UpdateBeams (void) vec3_t fwd, org, ang; float delta, f, len; - if (cl.spectator && autocam[j]) + if (cl.spectator && pv->cam_auto) { //if we're tracking someone, use their origin explicitly. vieworg = pl->origin; } else - vieworg = cl.playerview[j].simorg; - viewang = cl.playerview[j].simangles; + vieworg = pv->simorg; + viewang = pv->simangles; if (cl_truelightning.ival >= 2 && cls.netchan.outgoing_sequence > cl_truelightning.ival) { inframe_t *frame = &cl.inframes[(cls.netchan.outgoing_sequence-cl_truelightning.ival)&UPDATE_MASK]; - viewang = frame->playerstate[cl.playernum[j]].viewangles; - viewang[0] = (frame->playerstate[cl.playernum[j]].command.angles[0] * 360) / 65336.0; - viewang[1] = (frame->playerstate[cl.playernum[j]].command.angles[1] * 360) / 65336.0; + viewang = frame->playerstate[pv->playernum].viewangles; + viewang[0] = (frame->playerstate[pv->playernum].command.angles[0] * 360) / 65336.0; + viewang[1] = (frame->playerstate[pv->playernum].command.angles[1] * 360) / 65336.0; } VectorCopy (vieworg, b->start); - b->start[2] += cl.crouch[j] + bound(-7, v_viewheight.value, 4); + b->start[2] += pv->crouch + bound(-7, v_viewheight.value, 4); f = bound(0, cl_truelightning.value, 1); diff --git a/engine/client/cl_ui.c b/engine/client/cl_ui.c index 02d009b2c..19d47b9b0 100644 --- a/engine/client/cl_ui.c +++ b/engine/client/cl_ui.c @@ -341,6 +341,19 @@ struct q3polyvert_s #define MAX_VMQ3_CACHED_STRINGS 1024 char *stringcache[1024]; +void VMQ3_FlushStringHandles(void) +{ + int i; + for (i = 0; i < MAX_VMQ3_CACHED_STRINGS; i++) + { + if (stringcache[i]) + { + Z_Free(stringcache[i]); + stringcache[i] = NULL; + } + } +} + char *VMQ3_StringFromHandle(int handle) { if (!handle) @@ -406,7 +419,7 @@ void VQ3_AddEntity(const q3refEntity_t *q3) if (q3->renderfx & Q3RF_DEPTHHACK) ent.flags |= Q2RF_DEPTHHACK; if (q3->renderfx & Q3RF_THIRD_PERSON) - ent.externalmodelview = ~0; + ent.flags |= Q2RF_EXTERNALMODEL; if (q3->renderfx & Q3RF_NOSHADOW) ent.flags |= RF_NOSHADOW; @@ -491,7 +504,7 @@ int VM_LerpTag(void *out, model_t *model, int f1, int f2, float l2, char *tagnam tagnum = Mod_TagNumForName(model, tagname); found = Mod_GetTag(model, tagnum, &fstate, tr); - if (found) + if (found && tagnum) { ang[0] = tr[0]; ang[1] = tr[1]; @@ -575,7 +588,7 @@ void VQ3_RenderView(const q3refdef_t *ref) r_refdef.vrect.height = ref->height; r_refdef.time = ref->time/1000.0f; r_refdef.useperspective = true; - r_refdef.currentplayernum = -1; + r_refdef.playerview = &cl.playerview[0]; if (r_torch.ival) { @@ -590,6 +603,7 @@ void VQ3_RenderView(const q3refdef_t *ref) memcpy(cl.q2frame.areabits, ref->areamask, sizeof(cl.q2frame.areabits)); R_RenderView(); + r_refdef.playerview = NULL; #ifdef GLQUAKE if (qrenderer == QR_OPENGL) { @@ -597,7 +611,6 @@ void VQ3_RenderView(const q3refdef_t *ref) } #endif - vid.recalc_refdef = 1; r_refdef.time = 0; } diff --git a/engine/client/client.h b/engine/client/client.h index a5cc38319..19550e9e1 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -493,6 +493,93 @@ typedef struct { int sequence; /*so csqc code knows that the ent is still valid*/ entity_state_t *entstate; } lerpents_t; + +//state associated with each player 'seat' (one for each splitscreen client) +//note that this doesn't include networking inputlog info. +struct playerview_s +{ + int playernum; //cl.players index for this player. + qboolean nolocalplayer; //inhibit use of qw-style players, predict based on entities. +#ifdef PEXT_SETVIEW + int viewentity; //view is attached to this entity. +#endif + + // information for local display + int stats[MAX_CL_STATS]; // health, etc + float statsf[MAX_CL_STATS]; // health, etc + char *statsstr[MAX_CL_STATS]; // health, etc + float item_gettime[32]; // cl.time of aquiring item, for blinking + float faceanimtime; // use anim frame if cl.time < this + int sb_hexen2_cur_item;//hexen2 hud + float sb_hexen2_item_time; + qboolean sb_hexen2_extra_info;//show the extra stuff + qboolean sb_hexen2_infoplaque; + + +// the client maintains its own idea of view angles, which are +// sent to the server each frame. And only reset at level change +// and teleport times + vec3_t viewangles; + vec3_t viewanglechange; + vec3_t gravitydir; + + // pitch drifting vars + float pitchvel; + qboolean nodrift; + float driftmove; + double laststop; + + //prediction state + int pmovetype; + float entgravity; + float maxspeed; + vec3_t simorg; + vec3_t simvel; + vec3_t simangles; + float rollangle; + + float crouch; // local amount for smoothing stepups + vec3_t oldorigin; // to track step smoothing + float oldz, extracrouch, crouchspeed; // to track step smoothing + qboolean onground; + float viewheight; + int waterlevel; //for smartjump + + float punchangle; // temporary view kick from weapon firing + qboolean fixangle; //received a fixangle - so disable prediction till the next packet. + qboolean oldfixangle; //received a fixangle - so disable prediction till the next packet. + vec3_t fixangles; //received a fixangle - so disable prediction till the next packet. + vec3_t oldfixangles; //received a fixangle - so disable prediction till the next packet. + + float v_dmg_time; //various view knockbacks. + float v_dmg_roll; + float v_dmg_pitch; + + double bobtime; //sine wave + double bobcltime; //for tracking time increments + float bob; //bob height + + + vec3_t cam_desired_position; // where the camera wants to be + qboolean cam_locked; // + int cam_oldbuttons; // + vec3_t cam_viewangles; // + double cam_lastviewtime; // + int cam_spec_track; // player# of who we are tracking + enum + { + CAM_NONE = 0, + CAM_TRACK = 1 + } cam_auto; // + + entity_t viewent; // is this not utterly redundant yet? + struct model_s *oldmodel; + float lerptime; + float frameduration; + int prevframe; + int oldframe; +}; + // // the client_state_t structure is wiped completely at every // server signon @@ -546,60 +633,10 @@ typedef struct //when running splitscreen, we have multiple viewports all active at once int splitclients; //we are running this many clients split screen. - struct playerview_s - { - // information for local display - int stats[MAX_CL_STATS]; // health, etc - float statsf[MAX_CL_STATS]; // health, etc - char *statsstr[MAX_CL_STATS]; // health, etc - float item_gettime[32]; // cl.time of aquiring item, for blinking - float faceanimtime; // use anim frame if cl.time < this - - - // the client maintains its own idea of view angles, which are - // sent to the server each frame. And only reset at level change - // and teleport times - vec3_t viewangles; - vec3_t viewanglechange; - vec3_t gravitydir; - - // pitch drifting vars - float pitchvel; - qboolean nodrift; - float driftmove; - double laststop; - - vec3_t simorg; - vec3_t simvel; - vec3_t simangles; - float rollangle; - - qboolean fixangle; //received a fixangle - so disable prediction till the next packet. - qboolean oldfixangle; //received a fixangle - so disable prediction till the next packet. - vec3_t fixangles; //received a fixangle - so disable prediction till the next packet. - vec3_t oldfixangles; //received a fixangle - so disable prediction till the next packet. - } playerview[MAX_SPLITS]; - - float crouch[MAX_SPLITS]; // local amount for smoothing stepups - qboolean onground[MAX_SPLITS]; - float viewheight[MAX_SPLITS]; - - entity_t viewent[MAX_SPLITS]; // weapon model - float punchangle[MAX_SPLITS]; // temporary view kick from weapon firing - - int playernum[MAX_SPLITS]; - qboolean nolocalplayer[MAX_SPLITS]; - - #ifdef PEXT_SETVIEW - int viewentity[MAX_SPLITS]; - #endif - int waterlevel[MAX_SPLITS]; //for smartjump + playerview_t playerview[MAX_SPLITS]; // localized movement vars - float entgravity[MAX_SPLITS]; - float maxspeed[MAX_SPLITS]; float bunnyspeedcap; - int pmovetype[MAX_SPLITS]; // the client simulates or interpolates movement to get these values double time; // this is the time value that the client @@ -835,8 +872,8 @@ void CL_SetInfo (int pnum, char *key, char *value); void CL_BeginServerConnect(int port); char *CL_TryingToConnect(void); -void CL_ExecInitialConfigs(void); -qboolean CL_CheckBootDownloads(void); +void CL_ExecInitialConfigs(char *defaultexec); +ftemanifest_t *CL_Manifest_Parse(vfsfile_t *file, const char *defaultsourceurl); extern int cl_numvisedicts; extern int cl_maxvisedicts; @@ -1005,19 +1042,21 @@ qboolean CL_CheckBaselines (int size); // // view.c // -void V_StartPitchDrift (int pnum); -void V_StopPitchDrift (int pnum); +void V_StartPitchDrift (playerview_t *pv); +void V_StopPitchDrift (playerview_t *pv); void V_RenderView (void); void V_Register (void); -void V_ParseDamage (int pnum); +void V_ParseDamage (playerview_t *pv); void V_SetContentsColor (int contents); //used directly by csqc -void V_CalcRefdef (int pnum); -void V_CalcGunPositionAngle (int pnum, float bob); -float V_CalcBob (int pnum, qboolean queryold); -void DropPunchAngle (int pnum); +void V_CalcRefdef (playerview_t *pv); +void V_ClearRefdef(playerview_t *pv); +void V_ApplyRefdef(void); +void V_CalcGunPositionAngle (playerview_t *pv, float bob); +float V_CalcBob (playerview_t *pv, qboolean queryold); +void DropPunchAngle (playerview_t *pv); // @@ -1060,6 +1099,7 @@ void CL_ParsePlayerinfo (void); void CL_ParseClientPersist(void); //these last ones are needed for csqc handling of engine-bound ents. void CL_ClearEntityLists(void); +void CL_EditExternalModels(int newviewentity); void CL_FreeVisEdicts(void); void CL_LinkViewModel(void); void CL_LinkPlayers (void); @@ -1142,25 +1182,19 @@ void CL_CalcClientTime(void); // // cl_cam.c // -#define CAM_NONE 0 -#define CAM_TRACK 1 - -extern int autocam[MAX_SPLITS]; -extern int spec_track[MAX_SPLITS]; // player# of who we are tracking - -qboolean Cam_DrawViewModel(int pnum); -qboolean Cam_DrawPlayer(int pnum, int playernum); -int Cam_TrackNum(int pnum); -void Cam_Unlock(int pnum); -void Cam_Lock(int pnum, int playernum); -void Cam_SelfTrack(int pnum); -void Cam_Track(int pnum, usercmd_t *cmd); -void Cam_TrackCrosshairedPlayer(int pnum); +qboolean Cam_DrawViewModel(playerview_t *pv); +qboolean Cam_DrawEntity(playerview_t *pv, int entitykey); +int Cam_TrackNum(playerview_t *pv); +void Cam_Unlock(playerview_t *pv); +void Cam_Lock(playerview_t *pv, int playernum); +void Cam_SelfTrack(playerview_t *pv); +void Cam_Track(playerview_t *pv, usercmd_t *cmd); +void Cam_TrackCrosshairedPlayer(playerview_t *pv); void Cam_SetAutoTrack(int userid); -void Cam_FinishMove(int pnum, usercmd_t *cmd); +void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd); void Cam_Reset(void); -void Cam_TrackPlayer(int pnum, char *cmdname, char *plrarg); -void Cam_Lock(int pnum, int playernum); +void Cam_TrackPlayer(int seat, char *cmdname, char *plrarg); +void Cam_Lock(playerview_t *pv, int playernum); void CL_InitCam(void); void QDECL vectoangles(vec3_t fwd, vec3_t ang); diff --git a/engine/client/clq2_ents.c b/engine/client/clq2_ents.c index 36f64bb6a..9320cb2aa 100644 --- a/engine/client/clq2_ents.c +++ b/engine/client/clq2_ents.c @@ -1015,7 +1015,7 @@ void CLQ2_ParseFrame (void) i = MSG_ReadByte (); for (j=0 ; jnumber == cl.playernum[0]+1) //woo! this is us! + if (s1->number == cl.playerview[pnum].playernum+1) //woo! this is us! { // VectorCopy(cl.predicted_origin, ent.origin); // VectorCopy(cl.predicted_origin, ent.oldorigin); -// ent.flags |= Q2RF_EXTERNALMODEL; // only draw from mirrors + ent.flags |= Q2RF_EXTERNALMODEL; // only draw from mirrors if (effects & Q2EF_FLAG1) V_AddLight (ent.keynum, ent.origin, 225, 0.2, 0.05, 0.05); @@ -1508,7 +1505,7 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) // ent.skin = NULL; // never use a custom skin on others ent.skinnum = 0; - ent.flags = 0; + ent.flags &= Q2RF_EXTERNALMODEL; ent.shaderRGBAf[3] = 1; // duplicate for linked models @@ -1763,7 +1760,7 @@ void CLQ2_AddViewWeapon (q2player_state_t *ps, q2player_state_t *ops) return; //generate root matrix.. - view = &cl.viewent[0]; + view = &cl.playerview[0].viewent; VectorCopy(cl.playerview[0].simorg, view->origin); AngleVectors(cl.playerview[0].simangles, view->axis[0], view->axis[1], view->axis[2]); VectorInverse(view->axis[1]); @@ -1829,8 +1826,6 @@ void CLQ2_CalcViewValues (void) r_refdef.useperspective = true; - r_refdef.currentplayernum = 0; - // find the previous frame to interpolate from ps = &cl.q2frame.playerstate; i = (cl.q2frame.serverframe - 1) & Q2UPDATE_MASK; @@ -1919,10 +1914,6 @@ void CLQ2_AddEntities (void) if (cls.state != ca_active) return; - - r_refdef.currentplayernum = 0; - - if (cl.time*1000 > cl.q2frame.servertime) { // if (cl_showclamp.value) diff --git a/engine/client/clq3_parse.c b/engine/client/clq3_parse.c index 15d646c60..8cfae591a 100644 --- a/engine/client/clq3_parse.c +++ b/engine/client/clq3_parse.c @@ -492,7 +492,8 @@ qboolean CLQ3_SystemInfoChanged(char *str) VFS_CLOSE(f); continue; } - FS_GenCachedPakName(va("%s.pk3", com_token), crc, cls.downloadlocalname, sizeof(cls.downloadlocalname)); + if (!FS_GenCachedPakName(va("%s.pk3", com_token), crc, cls.downloadlocalname, sizeof(cls.downloadlocalname))) + continue; f = FS_OpenVFS(cls.downloadlocalname, "rb", FS_ROOT); if (f) { @@ -500,12 +501,14 @@ qboolean CLQ3_SystemInfoChanged(char *str) continue; } + if (!FS_GenCachedPakName(va("%s.tmp", com_token), crc, cls.downloadtempname, sizeof(cls.downloadtempname))) + continue; + //fixme: request to download it Con_Printf("Sending request to download %s\n", com_token); CLQ3_SendClientCommand("download %s.pk3", com_token); ccs.downloadchunknum = 0; //q3's downloads are relative to root, but they do at least force a pk3 extension. - FS_GenCachedPakName(va("%s.tmp", com_token), crc, cls.downloadtempname, sizeof(cls.downloadtempname)); snprintf(cls.downloadremotename, sizeof(cls.downloadremotename), "%s.pk3", com_token); cls.downloadmethod = DL_Q3; cls.downloadpercent = 0; @@ -514,11 +517,11 @@ qboolean CLQ3_SystemInfoChanged(char *str) pc = Info_ValueForKey(str, "sv_paks"); //the ones that we are allowed to use (in order!) pn = Info_ValueForKey(str, "sv_pakNames"); - FS_ForceToPure(pn, pc, ccs.fs_key); + FS_PureMode(2, pn, pc, ccs.fs_key); } else { - FS_ForceToPure(NULL, NULL, ccs.fs_key); + FS_PureMode(0, NULL, NULL, ccs.fs_key); } return true; //yay, we're in @@ -586,7 +589,7 @@ void CLQ3_ParseGameState(void) } } - cl.playernum[0] = MSG_ReadLong(); + cl.playerview[0].playernum = MSG_ReadLong(); ccs.fs_key = MSG_ReadLong(); if (!CLQ3_SystemInfoChanged(CG_GetConfigString(CFGSTR_SYSINFO))) @@ -916,13 +919,13 @@ void CLQ3_SendCmd(usercmd_t *cmd) if (key_dest != key_game || (keycatcher&3)) cmd->buttons |= 2; //add in the 'at console' button - cl.outframes[ccs.currentUserCmdNumber&CMD_MASK].cmd[0] = *cmd; - ccs.currentUserCmdNumber++; + cl.outframes[cl.movesequence&Q3UPDATE_MASK].cmd[0] = *cmd; + cl.movesequence++; frame = &cl.outframes[cls.netchan.outgoing_sequence & Q3UPDATE_MASK]; - frame->cmd_sequence = ccs.currentUserCmdNumber; + frame->cmd_sequence = cl.movesequence; frame->server_message_num = ccs.serverMessageNum; frame->server_time = cl.gametime; frame->client_time = Sys_DoubleTime()*1000; @@ -950,7 +953,7 @@ void CLQ3_SendCmd(usercmd_t *cmd) i = (cls.netchan.outgoing_sequence - 1); oldframe = &cl.outframes[i & Q3UPDATE_MASK]; - cmdcount = ccs.currentUserCmdNumber - oldframe->cmd_sequence; + cmdcount = cl.movesequence - oldframe->cmd_sequence; if (cmdcount > Q3UPDATE_MASK) cmdcount = Q3UPDATE_MASK; // begin a client move command, if any @@ -973,7 +976,7 @@ void CLQ3_SendCmd(usercmd_t *cmd) // send this and the previous cmds in the message, so // if the last packet was dropped, it can be recovered from = &nullcmd; - for (i = oldframe->cmd_sequence; i < ccs.currentUserCmdNumber; i++) + for (i = oldframe->cmd_sequence; i < cl.movesequence; i++) { to = &cl.outframes[i&CMD_MASK].cmd[0]; MSG_Q3_WriteDeltaUsercmd( &msg, key, from, to ); diff --git a/engine/client/clq3defs.h b/engine/client/clq3defs.h index 73cc0d0ce..14c719347 100644 --- a/engine/client/clq3defs.h +++ b/engine/client/clq3defs.h @@ -222,7 +222,6 @@ typedef struct { int lastServerCommandNum; int currentServerCommandNum; int numClientCommands; - int currentUserCmdNumber; int serverMessageNum; int serverTime; diff --git a/engine/client/fragstats.c b/engine/client/fragstats.c index 3f20371d0..85804ab28 100644 --- a/engine/client/fragstats.c +++ b/engine/client/fragstats.c @@ -123,8 +123,8 @@ void Stats_Evaluate(fragfilemsgtypes_t mt, int wid, int p1, int p2) p2 = tmp; } - u1 = (p1 == (cl.playernum[0]&127)); - u2 = (p2 == (cl.playernum[0]&127)); + u1 = (p1 == (cl.playerview[0].playernum)); + u2 = (p2 == (cl.playerview[0].playernum)); switch(mt) { diff --git a/engine/client/in_generic.c b/engine/client/in_generic.c index adc438c0f..e8e9f52de 100644 --- a/engine/client/in_generic.c +++ b/engine/client/in_generic.c @@ -376,11 +376,22 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum) } #ifdef PEXT_CSQC - if (mx || my) - if (CSQC_MouseMove(mx, my, mouse->qdeviceid)) + if (mouse->type == M_TOUCH) { - mx = 0; - my = 0; + if (CSQC_MousePosition(mouse->oldpos[0], mouse->oldpos[1], mouse->qdeviceid)) + { + mx = 0; + my = 0; + } + } + else + { + if (mx || my) + if (CSQC_MouseMove(mx, my, mouse->qdeviceid)) + { + mx = 0; + my = 0; + } } #endif @@ -433,7 +444,7 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum) } if (in_mlook.state[pnum] & 1) - V_StopPitchDrift (pnum); + V_StopPitchDrift (&cl.playerview[pnum]); if (!strafe_y) { diff --git a/engine/client/in_win.c b/engine/client/in_win.c index 6a72fb8bf..6fd3281ec 100644 --- a/engine/client/in_win.c +++ b/engine/client/in_win.c @@ -1887,7 +1887,7 @@ void INS_JoyMove (float *movements, int pnum) { cl.playerview[pnum].viewanglechange[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; } - V_StopPitchDrift(pnum); + V_StopPitchDrift(&cl.playerview[pnum]); } else { @@ -1896,7 +1896,7 @@ void INS_JoyMove (float *movements, int pnum) // *** this code can be removed when the lookspring bug is fixed // *** the bug always has the lookspring feature on if(lookspring.value == 0.0) - V_StopPitchDrift(pnum); + V_StopPitchDrift(&cl.playerview[pnum]); } } else @@ -1957,7 +1957,7 @@ void INS_JoyMove (float *movements, int pnum) { cl.playerview[pnum].viewanglechange[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * speed * 180.0; } - V_StopPitchDrift(pnum); + V_StopPitchDrift(&cl.playerview[pnum]); } else { @@ -1966,7 +1966,7 @@ void INS_JoyMove (float *movements, int pnum) // *** this code can be removed when the lookspring bug is fixed // *** the bug always has the lookspring feature on if(lookspring.value == 0.0) - V_StopPitchDrift(pnum); + V_StopPitchDrift(&cl.playerview[pnum]); } } break; diff --git a/engine/client/keys.c b/engine/client/keys.c index 212d31046..68dc8a799 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -580,7 +580,7 @@ void Key_DefaultLinkClicked(char *text, char *info) for (i = 0; i < cl.splitclients; i++) { - if (cl.playernum[i] == player) + if (cl.playerview[i].playernum == player) break; } if (i == cl.splitclients) @@ -594,7 +594,7 @@ void Key_DefaultLinkClicked(char *text, char *info) else { //we're playing. - if (cls.protocol == CP_QUAKEWORLD && strcmp(cl.players[cl.playernum[0]].team, cl.players[player].team)) + if (cls.protocol == CP_QUAKEWORLD && strcmp(cl.players[cl.playerview[0].playernum].team, cl.players[player].team)) Con_Footerf(true, " ^[[Join Team %s]\\cmd\\setinfo team %s^]", cl.players[player].team, cl.players[player].team); } Con_Footerf(true, " ^[%sgnore\\player\\%i\\action\\ignore^]", cl.players[player].ignored?"Uni":"I", player); @@ -655,6 +655,18 @@ void Key_DefaultLinkClicked(char *text, char *info) Cbuf_AddText(va("\nconnect %s\n", c), RESTRICT_LOCAL); return; } + c = Info_ValueForKey(info, "join"); + if (*c && !strchr(c, ';') && !strchr(c, '\n')) + { + Cbuf_AddText(va("\njoin %s\n", c), RESTRICT_LOCAL); + return; + } + c = Info_ValueForKey(info, "observe"); + if (*c && !strchr(c, ';') && !strchr(c, '\n')) + { + Cbuf_AddText(va("\nobserve %s\n", c), RESTRICT_LOCAL); + return; + } c = Info_ValueForKey(info, "qtv"); if (*c && !strchr(c, ';') && !strchr(c, '\n')) { @@ -765,6 +777,9 @@ void Key_ConsoleRelease(int key, int unicode) { //okay, its a valid link that they clicked *end = 0; +#ifdef PLUGINS + if (!Plug_ConsoleLink(buffer+2, info)) +#endif #ifdef CSQC_DAT if (!CSQC_ConsoleLink(buffer+2, info)) #endif diff --git a/engine/client/m_download.c b/engine/client/m_download.c index 25b4480a7..9b8269f76 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -796,445 +796,9 @@ void Menu_DownloadStuff_f (void) } } } -#elif defined(WEBCLIENT) +#else void Menu_DownloadStuff_f (void) { - Con_Printf("Not yet reimplemented\n"); -} -#endif - - -#if defined(WEBCLIENT) - -static int numbootdownloads; -#include "fs.h" -#ifdef AVAIL_ZLIB -extern searchpathfuncs_t zipfilefuncs; -static int QDECL CL_BootDownload_Extract(const char *fname, int fsize, void *ptr, void *spath) -{ - char buffer[512*1024]; - int read; - void *zip = ptr; - flocation_t loc; - int slashes; - const char *s; - vfsfile_t *compressedpak; - vfsfile_t *decompressedpak; - - if (zipfilefuncs.FindFile(zip, &loc, fname, NULL)) - { - compressedpak = zipfilefuncs.OpenVFS(zip, &loc, "rb"); - if (compressedpak) - { - //this extra logic is so we can handle things like nexuiz/data/blah.pk3 - //as well as just data/blah.pk3 - slashes = 0; - for (s = strchr(fname, '/'); s; s = strchr(s+1, '/')) - slashes++; - for (; slashes > 1; slashes--) - fname = strchr(fname, '/')+1; - - if (!slashes) - { - FS_CreatePath(fname, FS_GAMEONLY); - decompressedpak = FS_OpenVFS(fname, "wb", FS_GAMEONLY); - } - else - { - FS_CreatePath(fname, FS_ROOT); - decompressedpak = FS_OpenVFS(fname, "wb", FS_ROOT); - } - if (decompressedpak) - { - for(;;) - { - read = VFS_READ(compressedpak, buffer, sizeof(buffer)); - if (read <= 0) - break; - if (VFS_WRITE(decompressedpak, buffer, read) != read) - { - Con_Printf("write failed writing %s. disk full?\n", fname); - break; - } - } - VFS_CLOSE(decompressedpak); - } - VFS_CLOSE(compressedpak); - } - } - return true; -} -#endif - -qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, int *crc, qboolean copyprotect, qboolean istemporary, qboolean isexplicit); - -void FS_GenCachedPakName(char *pname, char *crc, char *local, int llen); -static void CL_BootDownload_Complete(struct dl_download *dl) -{ - char *q = strchr(dl->url, '?'); - char *ext; - if (dl->file && dl->status == DL_FINISHED) - { - if (q) *q = '0'; - ext = COM_FileExtension(dl->url); - if (q) *q = '?'; - if (!stricmp(ext, "zip")) - { -#ifdef AVAIL_ZLIB - void *zip; - if (dl->status == DL_FINISHED) - zip = zipfilefuncs.OpenNew(dl->file, dl->url); - else - zip = NULL; - if (zip) - { - dl->file = NULL; //file is now owned by the zip context. - if (dl->user_ctx) - { - vfsfile_t *in, *out; - flocation_t loc; - qboolean found = false; - int crc; - - found = zipfilefuncs.FindFile(zip, &loc, dl->user_ctx, NULL); - if (!found) - { - char *s = COM_SkipPath(dl->user_ctx); - if (s != dl->user_ctx) - found = zipfilefuncs.FindFile(zip, &loc, s, NULL); - } - - if (found) - { - in = zipfilefuncs.OpenVFS(zip, &loc, "rb"); - if (in) - { - char local[MAX_OSPATH]; - FS_GenCachedPakName(dl->user_ctx, va("%i", dl->user_num), local, sizeof(local)); - FS_CreatePath(local, FS_ROOT); - out = FS_OpenVFS(local, "wb", FS_ROOT); - if (out) - { - char buffer[8192]; - int read; - for(;;) - { - read = VFS_READ(in, buffer, sizeof(buffer)); - if (read <= 0) - break; - if (VFS_WRITE(out, buffer, read) != read) - { - Con_Printf("write failed writing %s. disk full?\n", local); - break; - } - } - VFS_CLOSE(out); - out = FS_OpenVFS(local, "rb", FS_ROOT); - crc = dl->user_num; - if (!FS_LoadPackageFromFile(out, dl->user_ctx, local, &crc, true, false, true)) - { - if (crc == dl->user_num) - Con_Printf(CON_WARNING "Manifest package \"%s\" is unusable.\n", (char*)dl->user_ctx); - else - Con_Printf(CON_WARNING "Manifest package \"%s\" is unusable or has invalid crc. Stated crc %#x is not calculated crc %#x\n", (char*)dl->user_ctx, dl->user_num, crc); - FS_Remove(local, FS_ROOT); - } - } - } - VFS_CLOSE(in); - } - - free(dl->user_ctx); - } - else - { - /*scan it to extract its contents*/ - zipfilefuncs.EnumerateFiles(zip, "*/*.pk3", CL_BootDownload_Extract, zip); - zipfilefuncs.EnumerateFiles(zip, "*/*.pak", CL_BootDownload_Extract, zip); - zipfilefuncs.EnumerateFiles(zip, "*/*/*.pk3", CL_BootDownload_Extract, zip); - zipfilefuncs.EnumerateFiles(zip, "*/*/*.pak", CL_BootDownload_Extract, zip); - } - - /*close it, delete the temp file from disk, etc*/ - zipfilefuncs.ClosePath(zip); - - /*restart the filesystem so those new files can be found*/ - Cmd_ExecuteString("fs_restart\n", RESTRICT_LOCAL); - } -#endif - } - else - { - //okay, its named directly, write it out as it is - vfsfile_t *out; - int crc, *crcptr = &crc; - char local[MAX_OSPATH]; - - qboolean ispackage = (!stricmp(ext, "pak") || !stricmp(ext, "pk3")); -#ifndef NACL - if (ispackage) - { - FS_GenCachedPakName(dl->user_ctx, va("%i", dl->user_num), local, sizeof(local)); -#else - //nacl permits any files to be listed in the manifest file, as there's no prior files to override/conflict, so don't mess about with crcs etc - crcptr = NULL; - Q_strncpyz(local, dl->user_ctx, sizeof(local)); - if (1) - { -#endif - FS_CreatePath(local, FS_ROOT); - - //write it out - out = FS_OpenVFS(local, "wb", FS_ROOT); - if (!out) - Con_Printf("Unable to open %s for writing\n", local); - else - { - char buffer[65535]; - int read; - for(;;) - { - read = VFS_READ(dl->file, buffer, sizeof(buffer)); - if (read <= 0) - break; - if (VFS_WRITE(out, buffer, read) != read) - { - Con_Printf("write failed writing %s. disk full?\n", local); - break; - } - } - VFS_CLOSE(out); - - if (ispackage) - { - if (crcptr) - { - //load it as a package - out = FS_OpenVFS(local, "rb", FS_ROOT); - if (out) - { - crc = dl->user_num; - if (!FS_LoadPackageFromFile(out, dl->user_ctx, local, crcptr, true, false, true)) - { - if (crc == dl->user_num) - Con_Printf(CON_WARNING "Manifest package \"%s\" is unusable.\n", (char*)dl->user_ctx); - else - Con_Printf(CON_WARNING "Manifest package \"%s\" is unusable or has invalid crc. Stated crc %#x is not calculated crc %#x\n", (char*)dl->user_ctx, dl->user_num, crc); - FS_Remove(local, FS_ROOT); - } - Cmd_ExecuteString("fs_restart\n", RESTRICT_LOCAL); - } - } - else - Cmd_ExecuteString("fs_restart\n", RESTRICT_LOCAL); - } - } - } - else - Con_Printf("File %s isn't a recognised package!\n", (char*)dl->user_ctx); - } - } - - if (!--numbootdownloads) - { - CL_ExecInitialConfigs(); - Cmd_StuffCmds(); - Cbuf_Execute (); - Cmd_ExecuteString("vid_restart\n", RESTRICT_LOCAL); - CL_StartCinematicOrMenu(); - } - - free(dl->user_ctx); -} - -static void CL_Manifest_Complete(struct dl_download *dl) -{ - if (!dl->file || dl->status == DL_FAILED) - Con_Printf("Unable to load manifest from %s\n", dl->url); - else - { - vfsfile_t *f; - char buffer[1024]; - char *fname; - int crc; - char local[MAX_OSPATH]; - while(VFS_GETS(dl->file, buffer, sizeof(buffer))) - { - Cmd_TokenizeString(buffer, false, false); - if (!Cmd_Argc()) - continue; - fname = Cmd_Argv(0); - - if (*fname == '*') - { - //for future expansion. - /*if (!stricmp(fname, "*game")) - { - //switch current gamedir to the new one, so we don't end up downloading if they already have it installed. - //this should be quake/quake2/etc. - }*/ - continue; - } - - crc = strtoul(Cmd_Argv(1), NULL, 0); - - f = FS_OpenVFS(fname, "rb", FS_ROOT); - if (f) - { - //should be loaded as needed - VFS_CLOSE(f); - } - else - { - FS_GenCachedPakName(fname, va("%i", crc), local, sizeof(local)); - f = FS_OpenVFS(local, "rb", FS_ROOT); - if (f) - { - int truecrc = crc; - if (!FS_LoadPackageFromFile(f, fname, local, &truecrc, true, false, true)) - { - if (crc == truecrc) - Con_Printf(CON_WARNING "Manifest package \"%s\" is unusable.\n", fname); - else - Con_Printf(CON_WARNING "Manifest package \"%s\" is unusable or has invalid crc. Stated crc %#x is not calculated crc %#x\n", fname, crc, truecrc); - FS_Remove(local, FS_ROOT); - } - } - else - { - int args = Cmd_Argc() - 2; - if (!args) - Con_Printf("No mirrors for \"%s\"\n", fname); - else - { - struct dl_download *ndl; -#ifdef NACL - args = 0; //nacl currently depends upon the browser's download cache for all file persistance, which means we want the same file every time. - //there's only one way to do that, sadly. -#else - args = rand() % args; -#endif - Con_Printf("Downloading \"%s\" from \"%s\"\n", fname, Cmd_Argv(2 + args)); - ndl = HTTP_CL_Get(Cmd_Argv(2), "", CL_BootDownload_Complete); - - if (ndl) - { - ndl->user_ctx = strdup(fname); - ndl->user_num = crc; - #ifdef MULTITHREAD - DL_CreateThread(ndl, FS_OpenTemp(), CL_BootDownload_Complete); - #endif - numbootdownloads++; - } - } - } - } - } - } - if (!--numbootdownloads) - { - CL_ExecInitialConfigs(); - Cmd_StuffCmds(); - Cbuf_Execute (); - Cmd_ExecuteString("vid_restart\n", RESTRICT_LOCAL); - CL_StartCinematicOrMenu(); - } -} - -qboolean CL_CheckBootDownloads(void) -{ - char *downloads = fs_gamedownload.string; - char token[2048]; - char *c, *s; - vfsfile_t *f; - struct dl_download *dl; - int mirrors; - - int man = COM_CheckParm("-manifest"); - if (man) - { - const char *fname = com_argv[man+1]; - if (*fname) - { - Con_Printf("Checking manifest from \"%s\"\n", fname); - - dl = HTTP_CL_Get(fname, "", CL_Manifest_Complete); - if (dl) - { - #ifdef MULTITHREAD - DL_CreateThread(dl, FS_OpenTemp(), CL_Manifest_Complete); - #endif - numbootdownloads++; - } - else - { - struct dl_download fake; - fake.file = VFSOS_Open(fname, "rb"); - fake.status = fake.file?DL_FINISHED:DL_FAILED; - numbootdownloads++; - CL_Manifest_Complete(&fake); - if (fake.file) - VFS_CLOSE(fake.file); - } - } - } - - while ((downloads = COM_ParseOut(downloads, token, sizeof(token)))) - { - //FIXME: do we want to add some sort of file size indicator? - c = token; - while(*c && *c != ':' && *c != '|') - c++; - if (!*c) //erp? - continue; - *c++ = 0; - f = FS_OpenVFS(token, "rb", FS_ROOT); - if (f) - { - Con_DPrintf("Already have %s\n", token); - VFS_CLOSE(f); - continue; - } - mirrors = 1; - for (s = c; *s; s++) - { - if (*s == '|') - mirrors++; - } - mirrors = rand() % mirrors; - - while(mirrors) - { - mirrors--; - while(*c != '|') - c++; - c++; - } - for (s = c; *s; s++) - { - if (*s == '|') - *s = 0; - } - - Con_Printf("Attempting to download %s\n", c); - - dl = HTTP_CL_Get(c, "", CL_BootDownload_Complete); - if (dl) - { -#ifdef MULTITHREAD - DL_CreateThread(dl, FS_OpenTemp(), CL_BootDownload_Complete); -#endif - numbootdownloads++; - } - } - - return !numbootdownloads; -} -#else -qboolean CL_CheckBootDownloads(void) -{ - if (COM_CheckParm("-manifest")) - Con_Printf("download manifests not supported in this build\n"); - return true; + Con_Printf("Download menu not implemented in this build\n"); } #endif diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index fd2b2a81e..761a6a25a 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -227,6 +227,23 @@ qboolean Media_FakeTrack(int i, qboolean loop) if (i > 0 && i <= 999) { found = false; + if (!found && i <= 99) + { + sprintf(trackname, "music/track%02i.ogg", i); + found = COM_FCheckExists(trackname); + } +#ifdef WINAVI + if (!found && i <= 99) + { + sprintf(trackname, "music/track%02i.mp3", i); + found = COM_FCheckExists(trackname); + } +#endif + if (!found && i <= 99) + { + sprintf(trackname, "music/track%02i.wav", i); + found = COM_FCheckExists(trackname); + } if (!found) { sprintf(trackname, "sound/cdtracks/track%03i.ogg", i); @@ -3145,6 +3162,9 @@ void Media_RecordDemo_f(void) { if (Cmd_Argc() < 2) return; + if (Cmd_FromGamecode()) + return; + CL_PlayDemo(Cmd_Argv(1)); if (Cmd_Argc() > 2) Cmd_ShiftArgs(1, false); diff --git a/engine/client/m_single.c b/engine/client/m_single.c index c1e53e9fe..236d1db6b 100644 --- a/engine/client/m_single.c +++ b/engine/client/m_single.c @@ -369,7 +369,10 @@ typedef struct { menucustom_t *list; demoitem_t *selected; demoitem_t *firstitem; + int pathlen; + char path[MAX_OSPATH]; + int fsroot; //FS_ROOT, FS_GAME, FS_GAMEONLY. if FS_ROOT, executed command will have a leading # char *command[64]; //these let the menu be used for nearly any sort of file browser. char *ext[64]; @@ -429,7 +432,7 @@ static void M_DemoDraw(int x, int y, menucustom_t *control, menu_t *menu) item = item->next; } } -static void ShowDemoMenu (menu_t *menu, char *path); +static void ShowDemoMenu (menu_t *menu, const char *path); static qboolean M_DemoKey(menucustom_t *control, menu_t *menu, int key) { demomenu_t *info = menu->data; @@ -474,7 +477,7 @@ static qboolean M_DemoKey(menucustom_t *control, menu_t *menu, int key) if (info->selected) { if (info->selected->isdir) - ShowDemoMenu(menu, va("%s", info->selected->name)); + ShowDemoMenu(menu, info->selected->name); else { int extnum; @@ -485,7 +488,7 @@ static qboolean M_DemoKey(menucustom_t *control, menu_t *menu, int key) if (extnum == info->numext) //wasn't on our list of extensions. extnum = 0; - Cbuf_AddText(va("%s \"%s\"\n", info->command[extnum], info->selected->name), RESTRICT_LOCAL); + Cbuf_AddText(va("%s \"%s%s\"\n", info->command[extnum], (info->fsroot==FS_ROOT)?"#":"", info->selected->name), RESTRICT_LOCAL); M_ToggleMenu_f(); } @@ -640,15 +643,31 @@ static void M_Demo_Remove (menu_t *menu) M_Demo_Flush(info); } -static void ShowDemoMenu (menu_t *menu, char *path) +static void ShowDemoMenu (menu_t *menu, const char *path) { + demomenu_t *info = menu->data; + int c; char *s; char match[256]; - while (!strcmp(path+strlen(path)-3, "../")) + + if (*path == '/') + path++; + Q_strncpyz(info->path, path, sizeof(info->path)); + if (info->fsroot == FS_GAME) + { + if (!strcmp(path, "../")) + { + info->fsroot = FS_ROOT; + FS_NativePath("", FS_ROOT, info->path, sizeof(info->path)); + while(s = strchr(info->path, '\\')) + *s = '/'; + } + } + while (!strcmp(info->path+strlen(info->path)-3, "../")) { c = 0; - for (s = path+strlen(path)-3; s >= path; s--) + for (s = info->path+strlen(info->path)-3; s >= info->path; s--) { if (*s == '/') { @@ -659,21 +678,42 @@ static void ShowDemoMenu (menu_t *menu, char *path) } } if (c<2) - *path = '\0'; + *info->path = '\0'; } - ((demomenu_t*)menu->data)->selected = NULL; - ((demomenu_t*)menu->data)->pathlen = strlen(path); + info->selected = NULL; + info->pathlen = strlen(info->path); M_Demo_Flush(menu->data); - if (*path) + if (info->fsroot == FS_ROOT) { - Q_snprintfz(match, sizeof(match), "%s../", path); - DemoAddItem(match, 0, menu->data, NULL); + s = strchr(info->path, '/'); + if (s && strchr(s+1, '/')) + { + Q_snprintfz(match, sizeof(match), "%s../", info->path); + DemoAddItem(match, 0, info, NULL); + } } - Q_snprintfz(match, sizeof(match), "%s*", path); - - COM_EnumerateFiles(match, DemoAddItem, menu->data); - M_Demo_Flatten(menu->data); + else if (*info->path) + { + Q_snprintfz(match, sizeof(match), "%s../", info->path); + DemoAddItem(match, 0, info, NULL); + } + else if (info->fsroot == FS_GAME) + { + Q_snprintfz(match, sizeof(match), "../", info->path); + DemoAddItem(match, 0, info, NULL); + } + if (info->fsroot == FS_ROOT) + { + Q_snprintfz(match, sizeof(match), *info->path?"%s*":"/*", info->path); + Sys_EnumerateFiles("", match, DemoAddItem, info, NULL); + } + else + { + Q_snprintfz(match, sizeof(match), "%s*", info->path); + COM_EnumerateFiles(match, DemoAddItem, info); + } + M_Demo_Flatten(info); } void M_Menu_Demos_f (void) @@ -688,6 +728,8 @@ void M_Menu_Demos_f (void) menu->remove = M_Demo_Remove; info = menu->data; + info->fsroot = FS_GAME; + info->command[0] = "playdemo"; info->ext[0] = ".qwd"; info->command[1] = "playdemo"; @@ -724,6 +766,8 @@ void M_Menu_MediaFiles_f (void) menu->remove = M_Demo_Remove; info = menu->data; + info->fsroot = FS_GAME; + info->ext[0] = ".m3u"; info->command[0] = "mediaplaylist"; info->ext[1] = ".mp3"; diff --git a/engine/client/merged.h b/engine/client/merged.h index 6feb9a7d5..ad5c191e5 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -217,6 +217,17 @@ typedef struct vboarray_s }; } vboarray_t; +//scissor rects +typedef struct +{ + float x; + float y; + float width; + float height; + double dmin; + double dmax; +} srect_t; + typedef struct texnums_s { texid_t base; texid_t bump; @@ -340,6 +351,7 @@ typedef struct rendererinfo_s { void (*BE_UploadAllLightmaps)(void); void (*BE_SelectEntity)(struct entity_s *ent); void (*BE_SelectDLight)(struct dlight_s *dl, vec3_t colour); + void (*BE_Scissor)(srect_t *rect); /*check to see if an ent should be drawn for the selected light*/ qboolean (*BE_LightCullModel)(vec3_t org, struct model_s *model); void (*BE_VBO_Begin)(vbobctx_t *ctx, unsigned int maxsize); @@ -378,3 +390,4 @@ typedef struct rendererinfo_s { #define BE_VBO_Data rf->BE_VBO_Data #define BE_VBO_Finish rf->BE_VBO_Finish #define BE_VBO_Destroy rf->BE_VBO_Destroy +#define BE_Scissor rf->BE_Scissor diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index fc980ff07..54f89a76b 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -63,12 +63,12 @@ static qboolean csprogs_promiscuous; static unsigned int csprogs_checksum; static csqctreadstate_t *csqcthreads; qboolean csqc_resortfrags; -qboolean csqc_drawsbar; qboolean csqc_addcrosshair; static int csqc_fakereadbyte; world_t csqc_world; -static int csqc_lplayernum; +int csqc_playerseat; //can be negative. +static playerview_t *csqc_playerview; static qboolean csqc_isdarkplaces; static qboolean csqc_singlecheats; /*single player or cheats active, allowing custom addons*/ @@ -130,6 +130,7 @@ extern sfx_t *cl_sfx_r_exp3; globalentity(other, "other"); /*entity Written before entering most qc functions*/ \ \ globalfloat(maxclients, "maxclients"); /*float max number of players allowed*/ \ + globalfloat(numclientseats, "numclientseats"); /*float number of seats/splitscreen clients running on this client*/ \ \ globalvector(forward, "v_forward"); /*vector written by anglevectors*/ \ globalvector(right, "v_right"); /*vector written by anglevectors*/ \ @@ -191,46 +192,59 @@ typedef struct { } csqcglobals_t; static csqcglobals_t csqcg; -static void CSQC_ChangeLocalPlayer(int lplayernum) +playerview_t csqc_nullview; + +//fixme: we should be using entity numbers, not view numbers. +static void CSQC_ChangeLocalPlayer(int seat) { - csqc_lplayernum = lplayernum; + if (seat < 0 || seat >= MAX_SPLITS) + { + csqc_playerseat = -1; + csqc_playerview = &csqc_nullview; + } + else + { + csqc_playerseat = seat; + csqc_playerview = &cl.playerview[seat]; + } if (csqcg.player_localentnum) { - if (cl.viewentity[lplayernum]) - *csqcg.player_localentnum = cl.viewentity[lplayernum]; - else if (cl.spectator && Cam_TrackNum(csqc_lplayernum) >= 0) - *csqcg.player_localentnum = Cam_TrackNum(csqc_lplayernum) + 1; + if (csqc_playerview->viewentity) + *csqcg.player_localentnum = csqc_playerview->viewentity; + else if (cl.spectator && Cam_TrackNum(csqc_playerview) >= 0) + *csqcg.player_localentnum = Cam_TrackNum(csqc_playerview) + 1; else - *csqcg.player_localentnum = cl.playernum[lplayernum]+1; + *csqcg.player_localentnum = csqc_playerview->playernum+1; } if (csqcg.player_localnum) - *csqcg.player_localnum = cl.playernum[lplayernum]; + *csqcg.player_localnum = csqc_playerview->playernum; if (csqcg.view_angles) { - csqcg.view_angles[0] = cl.playerview[csqc_lplayernum].viewangles[0]; - csqcg.view_angles[1] = cl.playerview[csqc_lplayernum].viewangles[1]; - csqcg.view_angles[2] = cl.playerview[csqc_lplayernum].viewangles[2]; + csqcg.view_angles[0] = csqc_playerview->viewangles[0]; + csqcg.view_angles[1] = csqc_playerview->viewangles[1]; + csqcg.view_angles[2] = csqc_playerview->viewangles[2]; } if (dpcompat_corruptglobals.ival || csqc_isdarkplaces) { if (csqcg.pmove_org) { - csqcg.pmove_org[0] = cl.playerview[csqc_lplayernum].simorg[0]; - csqcg.pmove_org[1] = cl.playerview[csqc_lplayernum].simorg[1]; - csqcg.pmove_org[2] = cl.playerview[csqc_lplayernum].simorg[2]; + csqcg.pmove_org[0] = csqc_playerview->simorg[0]; + csqcg.pmove_org[1] = csqc_playerview->simorg[1]; + csqcg.pmove_org[2] = csqc_playerview->simorg[2]; } if (csqcg.input_angles) { - csqcg.input_angles[0] = cl.playerview[csqc_lplayernum].viewangles[0]; - csqcg.input_angles[1] = cl.playerview[csqc_lplayernum].viewangles[1]; - csqcg.input_angles[2] = cl.playerview[csqc_lplayernum].viewangles[2]; + csqcg.input_angles[0] = csqc_playerview->viewangles[0]; + csqcg.input_angles[1] = csqc_playerview->viewangles[1]; + csqcg.input_angles[2] = csqc_playerview->viewangles[2]; } } } static void CSQC_FindGlobals(void) { + extern cvar_t cl_forcesplitclient; static float csphysicsmode = 0; #define globalfloat(name,qcname) csqcg.name = (float*)PR_FindGlobal(csqcprogs, qcname, 0, NULL); #define globalvector(name,qcname) csqcg.name = (float*)PR_FindGlobal(csqcprogs, qcname, 0, NULL); @@ -251,7 +265,7 @@ static void CSQC_FindGlobals(void) if (csqcg.cltime) *csqcg.cltime = cl.time; - CSQC_ChangeLocalPlayer(0); + CSQC_ChangeLocalPlayer(cl_forcesplitclient.ival?(cl_forcesplitclient.ival - 1) % cl.splitclients:0); csqc_world.g.self = csqcg.self; csqc_world.g.other = csqcg.other; @@ -548,7 +562,8 @@ static void QCBUILTIN PF_NoCSQC (pubprogfuncs_t *prinst, struct globalvars_s *pr static void QCBUILTIN PF_cl_cprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { char *str = PF_VarString(prinst, 0, pr_globals); - SCR_CenterPrint(csqc_lplayernum, str, true); + if (csqc_playerseat >= 0) + SCR_CenterPrint(csqc_playerseat, str, true); } static void QCBUILTIN PF_cs_makevectors (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -590,13 +605,15 @@ static qboolean CopyCSQCEdictToEntity(csqcedict_t *in, entity_t *out) out->model = model; rflags = in->xv->renderflags; + if (csqc_isdarkplaces) + rflags ^= CSQCRF_FRAMETIMESARESTARTTIMES; if (rflags) { rflags = in->xv->renderflags; if (rflags & CSQCRF_VIEWMODEL) out->flags |= Q2RF_DEPTHHACK|Q2RF_WEAPONMODEL; if (rflags & CSQCRF_EXTERNALMODEL) - out->externalmodelview = ~0; + out->flags |= Q2RF_EXTERNALMODEL; if (rflags & CSQCRF_DEPTHHACK) out->flags |= Q2RF_DEPTHHACK; if (rflags & CSQCRF_ADDITIVE) @@ -604,7 +621,10 @@ static qboolean CopyCSQCEdictToEntity(csqcedict_t *in, entity_t *out) //CSQCRF_USEAXIS is below if (rflags & CSQCRF_NOSHADOW) out->flags |= RF_NOSHADOW; - //CSQCRF_FRAMETIMESARESTARTTIMES is below + //CSQCRF_FRAMETIMESARESTARTTIMES is handled by cs_getframestate below + + if (rflags & CSQCRF_REMOVED) + Con_Printf("Warning: CSQCRF_NOAUTOADD is no longer supported\n"); } effects = in->v->effects; @@ -898,6 +918,10 @@ static void QCBUILTIN PF_R_DynamicLight_Add(pubprogfuncs_t *prinst, struct globa //if the org matches self, then attach it. dl = CL_NewDlight (VectorCompare(self->v->origin, org)?-self->entnum:0, org, radius, -0.1, rgb[0], rgb[1], rgb[2]); + VectorCopy(csqcg.forward, dl->axis[0]); + VectorCopy(csqcg.right, dl->axis[1]); + VectorCopy(csqcg.up, dl->axis[2]); + if (pflags & PFLAGS_NOSHADOW) dl->flags |= LFLAG_NOSHADOWS; if (pflags & PFLAGS_CORONA) @@ -933,28 +957,59 @@ static void QCBUILTIN PF_R_AddEntityMask(pubprogfuncs_t *prinst, struct globalva } } - maxe = *prinst->parms->sv_num_edicts; - for (e=1; e < maxe; e++) + if (csqc_isdarkplaces) { - ent = (void*)EDICT_NUM(prinst, e); - if (ent->isfree) - continue; - - if ((int)ent->xv->drawmask & mask) + //hopelessly inefficient version for compat with DP. + maxe = *prinst->parms->sv_num_edicts; + for (e=1; e < maxe; e++) { + ent = (void*)EDICT_NUM(prinst, e); + if (ent->isfree) + continue; + WPhys_RunThink (&csqc_world, (wedict_t*)ent); + if (ent->isfree) + continue; if (ent->xv->predraw) { *csqcg.self = EDICT_TO_PROG(prinst, (void*)ent); PR_ExecuteProgram(prinst, ent->xv->predraw); - - if (ent->isfree || (int)ent->xv->renderflags & CSQCRF_NOAUTOADD) + if (ent->isfree) continue; //bummer... } - - if (CopyCSQCEdictToEntity(ent, &rent)) + if ((int)ent->xv->drawmask & mask) { - CLQ1_AddShadow(&rent); - V_AddAxisEntity(&rent); + if (CopyCSQCEdictToEntity(ent, &rent)) + { + CLQ1_AddShadow(&rent); + V_AddAxisEntity(&rent); + } + } + } + } + else + { + maxe = *prinst->parms->sv_num_edicts; + for (e=1; e < maxe; e++) + { + ent = (void*)EDICT_NUM(prinst, e); + if (ent->isfree) + continue; + + if ((int)ent->xv->drawmask & mask) + { + if (ent->xv->predraw) + { + *csqcg.self = EDICT_TO_PROG(prinst, (void*)ent); + PR_ExecuteProgram(prinst, ent->xv->predraw); + + if (ent->isfree || G_FLOAT(OFS_RETURN)) + continue; //bummer... + } + if (CopyCSQCEdictToEntity(ent, &rent)) + { + CLQ1_AddShadow(&rent); + V_AddAxisEntity(&rent); + } } } } @@ -1173,29 +1228,24 @@ static void QCBUILTIN PF_cs_unproject (pubprogfuncs_t *prinst, struct globalvars //clear scene, and set up the default stuff. static void QCBUILTIN PF_R_ClearScene (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { + extern cvar_t cl_forcesplitclient; if (prinst->callargc > 0) CSQC_ChangeLocalPlayer(G_FLOAT(OFS_PARM0)); csqc_rebuildmatricies = true; CL_DecayLights (); - CL_TransitionEntities(); - - if (cl.worldmodel) - { - // do client side motion prediction - CL_PredictMove (); - } #if defined(SKELETALOBJECTS) || defined(RAGDOLLS) skel_dodelete(csqcprogs); #endif CL_ClearEntityLists(); - V_CalcRefdef(csqc_lplayernum); //set up the defaults (for player 0) - + V_ClearRefdef(csqc_playerview); + csqc_playerview->drawsbar = false; //csqc defaults to no sbar. csqc_addcrosshair = false; - csqc_drawsbar = false; + + V_CalcRefdef(csqc_playerview); //set up the defaults } static void QCBUILTIN PF_R_GetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -1209,6 +1259,9 @@ static void QCBUILTIN PF_R_GetViewFlag(pubprogfuncs_t *prinst, struct globalvars switch(parametertype) { + case VF_LPLAYER: + *r = csqc_playerseat; + break; case VF_FOV: r[0] = r_refdef.fov_x; r[1] = r_refdef.fov_y; @@ -1222,11 +1275,8 @@ static void QCBUILTIN PF_R_GetViewFlag(pubprogfuncs_t *prinst, struct globalvars *r = r_refdef.fov_y; break; -#ifdef warningmsg -#pragma warningmsg("fixme: AFOV not retrievable") -#endif case VF_AFOV: - *r = r_refdef.fov_x; + *r = r_refdef.afov; break; case VF_ORIGIN: @@ -1257,12 +1307,12 @@ static void QCBUILTIN PF_R_GetViewFlag(pubprogfuncs_t *prinst, struct globalvars break; case VF_CL_VIEWANGLES_V: - VectorCopy(cl.playerview[csqc_lplayernum].viewangles, r); + VectorCopy(csqc_playerview->viewangles, r); break; case VF_CL_VIEWANGLES_X: case VF_CL_VIEWANGLES_Y: case VF_CL_VIEWANGLES_Z: - *r = cl.playerview[csqc_lplayernum].viewangles[parametertype-VF_CL_VIEWANGLES_X]; + *r = csqc_playerview->viewangles[parametertype-VF_CL_VIEWANGLES_X]; break; case VF_CARTESIAN_ANGLES: @@ -1301,7 +1351,7 @@ static void QCBUILTIN PF_R_GetViewFlag(pubprogfuncs_t *prinst, struct globalvars *r = !(r_refdef.flags&Q2RDF_NOWORLDMODEL); break; case VF_ENGINESBAR: - *r = csqc_drawsbar; + *r = r_refdef.drawsbar; break; case VF_DRAWCROSSHAIR: *r = csqc_addcrosshair; @@ -1343,38 +1393,49 @@ static void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars G_FLOAT(OFS_RETURN) = 1; switch(parametertype) { + case VF_LPLAYER: + CSQC_ChangeLocalPlayer(*p); + V_CalcRefdef(csqc_playerview); //set up the default position+angles for the named player. + break; + case VF_VIEWENTITY: + //switches over EXTERNALMODEL flags and clears WEAPONMODEL flagged entities. + //FIXME: make affect addentities(MASK_ENGINE) calls too. + CL_EditExternalModels(*p); + break; case VF_FOV: + //explicit fov overrides aproximate fov + r_refdef.afov = 0; r_refdef.fov_x = p[0]; r_refdef.fov_y = p[1]; + r_refdef.dirty |= RDFD_FOV; break; case VF_FOVX: + r_refdef.afov = 0; r_refdef.fov_x = *p; + r_refdef.dirty |= RDFD_FOV; break; case VF_FOVY: + r_refdef.afov = 0; r_refdef.fov_y = *p; + r_refdef.dirty |= RDFD_FOV; break; case VF_AFOV: - { - float frustumx, frustumy; - frustumy = tan(p[0] * (M_PI/360)) * 0.75; - if (prinst->callargc > 2) - frustumy *= G_FLOAT(OFS_PARM2); - frustumx = (frustumy * r_refdef.vrect.width) / r_refdef.vrect.height /* / vid.pixelheight*/; - r_refdef.fov_x = atan2(frustumx, 1) * (360/M_PI); - r_refdef.fov_y = atan2(frustumy, 1) * (360/M_PI); - } + r_refdef.afov = *p; + r_refdef.fov_x = 0; + r_refdef.fov_y = 0; + r_refdef.dirty |= RDFD_FOV; break; case VF_ORIGIN: VectorCopy(p, r_refdef.vieworg); - cl.crouch[csqc_lplayernum] = 0; + csqc_playerview->crouch = 0; break; case VF_ORIGIN_Z: - cl.crouch[csqc_lplayernum] = 0; + csqc_playerview->crouch = 0; case VF_ORIGIN_X: case VF_ORIGIN_Y: r_refdef.vieworg[parametertype-VF_ORIGIN_X] = *p; @@ -1390,12 +1451,12 @@ static void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars break; case VF_CL_VIEWANGLES_V: - VectorCopy(p, cl.playerview[csqc_lplayernum].viewangles); + VectorCopy(p, csqc_playerview->viewangles); break; case VF_CL_VIEWANGLES_X: case VF_CL_VIEWANGLES_Y: case VF_CL_VIEWANGLES_Z: - cl.playerview[csqc_lplayernum].viewangles[parametertype-VF_CL_VIEWANGLES_X] = *p; + csqc_playerview->viewangles[parametertype-VF_CL_VIEWANGLES_X] = *p; break; case VF_CARTESIAN_ANGLES: @@ -1403,40 +1464,44 @@ static void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars break; case VF_VIEWPORT: - r_refdef.vrect.x = p[0]; - r_refdef.vrect.y = p[1]; + r_refdef.grect.x = p[0]; + r_refdef.grect.y = p[1]; p = G_VECTOR(OFS_PARM2); - r_refdef.vrect.width = p[0]; - r_refdef.vrect.height = p[1]; + r_refdef.grect.width = p[0]; + r_refdef.grect.height = p[1]; + r_refdef.dirty |= RDFD_FOV; break; case VF_SIZE_X: - r_refdef.vrect.width = *p; + r_refdef.grect.width = *p; + r_refdef.dirty |= RDFD_FOV; break; case VF_SIZE_Y: - r_refdef.vrect.height = *p; + r_refdef.grect.height = *p; + r_refdef.dirty |= RDFD_FOV; break; case VF_SIZE: - r_refdef.vrect.width = p[0]; - r_refdef.vrect.height = p[1]; + r_refdef.grect.width = p[0]; + r_refdef.grect.height = p[1]; + r_refdef.dirty |= RDFD_FOV; break; case VF_MIN_X: - r_refdef.vrect.x = *p; + r_refdef.grect.x = *p; break; case VF_MIN_Y: - r_refdef.vrect.y = *p; + r_refdef.grect.y = *p; break; case VF_MIN: - r_refdef.vrect.x = p[0]; - r_refdef.vrect.y = p[1]; + r_refdef.grect.x = p[0]; + r_refdef.grect.y = p[1]; break; case VF_DRAWWORLD: r_refdef.flags = (r_refdef.flags&~Q2RDF_NOWORLDMODEL) | (*p?0:Q2RDF_NOWORLDMODEL); break; case VF_ENGINESBAR: - csqc_drawsbar = *p; + r_refdef.drawsbar = !!*p; break; case VF_DRAWCROSSHAIR: csqc_addcrosshair = *p; @@ -1459,42 +1524,56 @@ static void QCBUILTIN PF_R_RenderScene(pubprogfuncs_t *prinst, struct globalvars if (cl.worldmodel) R_PushDlights (); - r_refdef.currentplayernum = csqc_lplayernum; + r_refdef.playerview = csqc_playerview; - V_CalcGunPositionAngle(csqc_lplayernum, V_CalcBob(csqc_lplayernum, true)); + V_CalcGunPositionAngle(csqc_playerview, V_CalcBob(csqc_playerview, true)); + V_ApplyRefdef(); R_RenderView(); R2D_PolyBlend (); - vid.recalc_refdef = 1; + { + srect_t srect; + srect.x = (float)r_refdef.grect.x / vid.width; + srect.y = (float)r_refdef.grect.y / vid.height; + srect.width = (float)r_refdef.grect.width / vid.width; + srect.height = (float)r_refdef.grect.height / vid.height; + srect.dmin = -99999; + srect.dmax = 99999; + srect.y = (1-srect.y) - srect.height; + BE_Scissor(&srect); + } - if (csqc_drawsbar) + if (r_refdef.drawsbar) { #ifdef PLUGINS - Plug_SBar(); + Plug_SBar (r_refdef.playerview); #else if (Sbar_ShouldDraw()) { - Sbar_Draw (); + Sbar_Draw (r_refdef.playerview); Sbar_DrawScoreboard (); } #endif + SCR_TileClear (); } if (csqc_addcrosshair) R2D_DrawCrosshair(); + + BE_Scissor(NULL); } static void QCBUILTIN PF_cs_getstati(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int stnum = G_FLOAT(OFS_PARM0); - G_INT(OFS_RETURN) = cl.playerview[csqc_lplayernum].stats[stnum]; + G_INT(OFS_RETURN) = csqc_playerview->stats[stnum]; } static void QCBUILTIN PF_cs_getstatbits(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { //convert an int stat into a qc float. int stnum = G_FLOAT(OFS_PARM0); - int val = cl.playerview[csqc_lplayernum].stats[stnum]; + int val = csqc_playerview->stats[stnum]; if (prinst->callargc > 1) { int first, count; @@ -1506,23 +1585,23 @@ static void QCBUILTIN PF_cs_getstatbits(pubprogfuncs_t *prinst, struct globalvar G_FLOAT(OFS_RETURN) = (((unsigned int)val)&(((1<>first; } else - G_FLOAT(OFS_RETURN) = cl.playerview[csqc_lplayernum].statsf[stnum]; + G_FLOAT(OFS_RETURN) = csqc_playerview->statsf[stnum]; } static void QCBUILTIN PF_cs_getstats(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int stnum = G_FLOAT(OFS_PARM0); - RETURN_TSTRING(cl.playerview[csqc_lplayernum].statsstr[stnum]); + RETURN_TSTRING(csqc_playerview->statsstr[stnum]); /* char out[17]; //the network protocol byteswaps - ((unsigned int*)out)[0] = LittleLong(cl.playerview[csqc_lplayernum].stats[stnum+0]); - ((unsigned int*)out)[1] = LittleLong(cl.playerview[csqc_lplayernum].stats[stnum+1]); - ((unsigned int*)out)[2] = LittleLong(cl.playerview[csqc_lplayernum].stats[stnum+2]); - ((unsigned int*)out)[3] = LittleLong(cl.playerview[csqc_lplayernum].stats[stnum+3]); + ((unsigned int*)out)[0] = LittleLong(csqc_playerview->stats[stnum+0]); + ((unsigned int*)out)[1] = LittleLong(csqc_playerview->stats[stnum+1]); + ((unsigned int*)out)[2] = LittleLong(csqc_playerview->stats[stnum+2]); + ((unsigned int*)out)[3] = LittleLong(csqc_playerview->stats[stnum+3]); ((unsigned int*)out)[4] = 0; //make sure it's null terminated RETURN_TSTRING(out);*/ @@ -2145,9 +2224,16 @@ static void cs_get_input_state (usercmd_t *cmd) //get the input commands, and stuff them into some globals. static void QCBUILTIN PF_cs_getinputstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - int f; usercmd_t *cmd; extern usercmd_t independantphysics[MAX_SPLITS]; + int f = G_FLOAT(OFS_PARM0); + int seat = ((prinst->callargc>1)?G_FLOAT(OFS_PARM1):csqc_playerseat); + + if (seat < 0 || seat >= MAX_SPLITS) + { + G_FLOAT(OFS_RETURN) = false; + return; + } f = G_FLOAT(OFS_PARM0); if (cl.paused && f >= cl.ackedmovesequence) @@ -2169,12 +2255,12 @@ static void QCBUILTIN PF_cs_getinputstate (pubprogfuncs_t *prinst, struct global /*outgoing_sequence says how many packets have actually been sent, but there's an extra pending packet which has not been sent yet - be warned though, its data will change in the coming frames*/ if (f == cl.movesequence) { - cmd = &independantphysics[csqc_lplayernum]; + cmd = &independantphysics[seat]; for (f=0 ; f<3 ; f++) - cmd->angles[f] = ((int)(cl.playerview[csqc_lplayernum].viewangles[f]*65536.0/360)&65535); + cmd->angles[f] = ((int)(csqc_playerview->viewangles[f]*65536.0/360)&65535); } else - cmd = &cl.outframes[f&UPDATE_MASK].cmd[csqc_lplayernum]; + cmd = &cl.outframes[f&UPDATE_MASK].cmd[seat]; cs_set_input_state(cmd); @@ -2331,7 +2417,7 @@ static void QCBUILTIN PF_cs_serverkey (pubprogfuncs_t *prinst, struct globalvars else ret = NET_AdrToString(adr, sizeof(adr), &cls.netchan.remote_address); } - else if (!strcmp(keyname, "state")) + else if (!strcmp(keyname, "constate")) { if (cls.state == ca_disconnected) ret = "disconnected"; @@ -2422,7 +2508,7 @@ static void QCBUILTIN PF_cs_getplayerkey (pubprogfuncs_t *prinst, struct globalv { if (csqc_resortfrags) { - Sbar_SortFrags(false, false); + Sbar_SortFrags(true, false); csqc_resortfrags = false; } if (pnum >= -scoreboardlines) @@ -2438,7 +2524,7 @@ static void QCBUILTIN PF_cs_getplayerkey (pubprogfuncs_t *prinst, struct globalv ret = buffer; sprintf(ret, "%i", pnum+1); } - else if (!*cl.players[pnum].userinfo) + else if (!*cl.players[pnum].name) ret = ""; //player isn't on the server. else if (!strcmp(keyname, "ping")) { @@ -2469,6 +2555,30 @@ static void QCBUILTIN PF_cs_getplayerkey (pubprogfuncs_t *prinst, struct globalv ret = buffer; sprintf(ret, "%i", (int)cl.players[pnum].entertime); } + else if (!strcmp(keyname, "topcolor_rgb")) //packet loss + { + unsigned int col = cl.players[pnum].ttopcolor; + ret = buffer; + if (col < 16) + { + col = Sbar_ColorForMap(col); + sprintf(ret, "'%g %g %g'", host_basepal[col*3+0]/255.0, host_basepal[col*3+1]/255.0, host_basepal[col*3+2]/255.0); + } + else + sprintf(ret, "'%g %g %g'", ((col&0xff0000)>>16)/255.0, ((col&0x00ff00)>>8)/255.0, ((col&0x0000ff)>>0)/255.0); + } + else if (!strcmp(keyname, "bottomcolor_rgb")) //packet loss + { + unsigned int col = cl.players[pnum].tbottomcolor; + ret = buffer; + if (col < 16) + { + col = Sbar_ColorForMap(col); + sprintf(ret, "'%g %g %g'", host_basepal[col*3+0]/255.0, host_basepal[col*3+1]/255.0, host_basepal[col*3+2]/255.0); + } + else + sprintf(ret, "'%g %g %g'", ((col&0xff0000)>>16)/255.0, ((col&0x00ff00)>>8)/255.0, ((col&0x0000ff)>>0)/255.0); + } else if (!strcmp(keyname, "ignored")) //checks to see if a player has locally been set to ignored (for text chat) { ret = buffer; @@ -2488,7 +2598,7 @@ static void QCBUILTIN PF_cs_getplayerkey (pubprogfuncs_t *prinst, struct globalv else if (!strcmp(keyname, "voiploudness")) { ret = buffer; - if (pnum == cl.playernum[0]) + if (pnum == csqc_playerview->playernum) sprintf(ret, "%i", S_Voip_Loudness(false)); else *ret = 0; @@ -3573,7 +3683,7 @@ void CSQC_PlayerStateToCSQC(int pnum, player_state_t *srcp, csqcedict_t *ent) { ent->xv->entnum = pnum+1; - if (cl.spectator && !Cam_DrawPlayer(0, pnum)) + if (cl.spectator && !Cam_DrawEntity(0, pnum+1)) { ent->v->modelindex = 0; } @@ -4243,8 +4353,9 @@ static struct { {"strpad", PF_strpad, 225}, // #225 string strpad(float pad, string str1, ...) strpad (FTE_STRINGS) {"infoadd", PF_infoadd, 226}, // #226 string(string old, string key, string value) infoadd {"infoget", PF_infoget, 227}, // #227 string(string info, string key) infoget + {"strcmp", PF_strncmp, 228}, // #228 float(string s1, string s2) strcmp (FTE_STRINGS) {"strncmp", PF_strncmp, 228}, // #228 float(string s1, string s2, float len) strncmp (FTE_STRINGS) - {"strcasecmp", PF_strcasecmp, 229}, // #229 float(string s1, string s2) strcasecmp (FTE_STRINGS) + {"strcasecmp", PF_strncasecmp, 229}, // #229 float(string s1, string s2) strcasecmp (FTE_STRINGS) //230 {"strncasecmp", PF_strncasecmp, 230}, // #230 float(string s1, string s2, float len) strncasecmp (FTE_STRINGS) @@ -4358,7 +4469,7 @@ static struct { //330 {"getstati", PF_cs_getstati, 330}, // #330 float(float stnum) getstati (EXT_CSQC) - {"getstatbits", PF_cs_getstatbits, 331}, // #331 float(float stnum) getstatbits (EXT_CSQC) + {"getstatf", PF_cs_getstatbits, 331}, // #331 float(float stnum) getstatf (EXT_CSQC) {"getstats", PF_cs_getstats, 332}, // #332 string(float firststnum) getstats (EXT_CSQC) {"setmodelindex", PF_cs_SetModelIndex, 333}, // #333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC) {"modelnameforindex", PF_cs_ModelnameForIndex, 334}, // #334 string(float mdlindex) modelnameforindex (EXT_CSQC) @@ -4562,20 +4673,20 @@ static struct { #ifndef NOMEDIA //DP_GECKO_SUPPORT - {"gecko_create", PF_cs_gecko_create, 487}, // #487 float(string name) gecko_create( string name ) - {"gecko_destroy", PF_cs_gecko_destroy, 488}, // #488 void(string name) gecko_destroy( string name ) - {"gecko_navigate", PF_cs_gecko_navigate, 489}, // #489 void(string name) gecko_navigate( string name, string URI ) - {"gecko_keyevent", PF_cs_gecko_keyevent, 490}, // #490 float(string name) gecko_keyevent( string name, float key, float eventtype ) - {"gecko_mousemove", PF_cs_gecko_mousemove, 491}, // #491 void gecko_mousemove( string name, float x, float y ) - {"gecko_resize", PF_cs_gecko_resize, 492}, // #492 void gecko_resize( string name, float w, float h ) + {"gecko_create", PF_cs_gecko_create, 487}, // #487 float(string name) gecko_create( string name ) + {"gecko_destroy", PF_cs_gecko_destroy, 488}, // #488 void(string name) gecko_destroy( string name ) + {"gecko_navigate", PF_cs_gecko_navigate, 489}, // #489 void(string name) gecko_navigate( string name, string URI ) + {"gecko_keyevent", PF_cs_gecko_keyevent, 490}, // #490 float(string name) gecko_keyevent( string name, float key, float eventtype ) + {"gecko_mousemove", PF_cs_gecko_mousemove, 491}, // #491 void gecko_mousemove( string name, float x, float y ) + {"gecko_resize", PF_cs_gecko_resize, 492}, // #492 void gecko_resize( string name, float w, float h ) {"gecko_get_texture_extent",PF_cs_gecko_get_texture_extent, 493}, // #493 vector gecko_get_texture_extent( string name ) #endif //DP_QC_CRC16 - {"crc16", PF_crc16, 494}, // #494 float(float caseinsensitive, string s, ...) crc16 + {"crc16", PF_crc16, 494}, // #494 float(float caseinsensitive, string s, ...) crc16 //DP_QC_CVAR_TYPE - {"cvar_type", PF_cvar_type, 495}, // #495 float(string name) cvar_type + {"cvar_type", PF_cvar_type, 495}, // #495 float(string name) cvar_type //DP_QC_ENTITYDATA {"numentityfields", PF_numentityfields, 496}, // #496 float() numentityfields @@ -4613,21 +4724,23 @@ static struct { {"gettime", PF_cs_gettime, 519}, - {"keynumtostring", PF_cl_keynumtostring, 520}, + {"keynumtostring_omgwtf", PF_cl_keynumtostring, 520}, {"findkeysforcommand", PF_cl_findkeysforcommand, 521}, {"loadfromdata", PF_loadfromdata, 529}, {"loadfromfile", PF_loadfromfile, 530}, {"soundlength", PF_soundlength, 534}, + {"buf_loadfile", PF_buf_loadfile, 535}, + {"buf_writefile", PF_buf_writefile, 536}, {"callfunction", PF_callfunction, 605}, {"writetofile", PF_writetofile, 606}, {"isfunction", PF_isfunction, 607}, {"parseentitydata", PF_parseentitydata, 608}, - {"keynumtostring", PF_cl_keynumtostring, 609}, + {"keynumtostring_menu", PF_cl_keynumtostring, 609}, //while present in dp's menuqc, dp doesn't actually support keynumtostring=609 in csqc. Which is probably a good thing because csqc would have 3 separate versions if it did. - {"findkeysforcommand", PF_cl_findkeysforcommand, 610}, + {"findkeysforcommand_dp", PF_cl_findkeysforcommand, 610}, {"gethostcachevalue", PF_cl_gethostcachevalue, 611}, {"gethostcachestring", PF_cl_gethostcachestring, 612}, {"parseentitydata", PF_parseentitydata, 613}, @@ -4987,24 +5100,26 @@ qboolean CSQC_Inited(void) qboolean CSQC_UnconnectedOkay(qboolean inprinciple) { +#ifndef _DEBUG return false; +#endif + if (!pr_csqc_formenus.ival) + return false; if (!inprinciple) { if (!csqcprogs) return false; - return true; - } - else - { - if (pr_csqc_formenus.ival) - return true; - return false; } + return true; } qboolean CSQC_UnconnectedInit(void) { - return false; + if (!CSQC_UnconnectedOkay(false)) + return false; + + + return CSQC_Init(true, true, 0); } double csqctime; @@ -5232,6 +5347,7 @@ void CSQC_RendererRestarted(void) void CSQC_WorldLoaded(void) { + char *map; csqcedict_t *worldent; if (!csqcprogs) @@ -5239,7 +5355,10 @@ void CSQC_WorldLoaded(void) if (csqcmapentitydataloaded) return; csqcmapentitydataloaded = true; - csqcmapentitydata = cl.worldmodel->entities; + map = Info_ValueForKey(cl.serverinfo, "map"); + csqcmapentitydata = map?COM_LoadFile(va("maps/%s.ent", map), 1):NULL; + if (!csqcmapentitydata) + csqcmapentitydata = cl.worldmodel->entities; csqc_world.worldmodel = cl.worldmodel; #ifdef USEODE @@ -5250,6 +5369,8 @@ void CSQC_WorldLoaded(void) worldent->v->solid = SOLID_BSP; csqc_setmodel(csqcprogs, worldent, 1); + worldent->readonly = false; //just in case + if (csqcg.worldloaded) PR_ExecuteProgram(csqcprogs, csqcg.worldloaded); csqcmapentitydata = NULL; @@ -5470,6 +5591,7 @@ qboolean CSQC_DrawView(void) int ticlimit = 10; float mintic = 0.01; double clframetime = host_frametime; + extern cvar_t cl_forcesplitclient; csqc_resortfrags = true; @@ -5484,33 +5606,42 @@ qboolean CSQC_DrawView(void) if (csqcg.frametime) *csqcg.frametime = host_frametime; - while(1) + if (!csqc_isdarkplaces) { - host_frametime = cl.servertime - csqc_world.physicstime; - if (host_frametime < mintic) - break; - if (!--ticlimit) + while(1) { - csqc_world.physicstime = cl.servertime; - break; + host_frametime = cl.servertime - csqc_world.physicstime; + if (host_frametime < mintic) + break; + if (!--ticlimit) + { + csqc_world.physicstime = cl.servertime; + break; + } + if (host_frametime > mintic) + host_frametime = mintic; + csqc_world.physicstime += host_frametime; + + #ifdef USEODE + World_ODE_Frame(&csqc_world, host_frametime, 800); + #endif + + World_Physics_Frame(&csqc_world); } - if (host_frametime > mintic) - host_frametime = mintic; - csqc_world.physicstime += host_frametime; - -#ifdef USEODE - World_ODE_Frame(&csqc_world, host_frametime, 800); -#endif - - World_Physics_Frame(&csqc_world); } host_frametime = clframetime; + //always revert to a usable default. + CSQC_ChangeLocalPlayer(cl_forcesplitclient.ival?(cl_forcesplitclient.ival - 1) % cl.splitclients:0); + if (csqcg.frametime) *csqcg.frametime = host_frametime; - DropPunchAngle (0); + if (csqcg.numclientseats) + *csqcg.numclientseats = cl.splitclients; + + DropPunchAngle (csqc_playerview); if (cl.worldmodel) R_LessenStains(); @@ -5535,7 +5666,9 @@ qboolean CSQC_DrawView(void) if (csqcg.intermission) *csqcg.intermission = cl.intermission; - CSQC_ChangeLocalPlayer(0); + CL_TransitionEntities(); + if (cl.worldmodel) + CL_PredictMove (); if (csqcg.cltime) *csqcg.cltime = cl.time; @@ -5588,8 +5721,8 @@ qboolean CSQC_MousePosition(float xabs, float yabs, int devid) pr_globals = PR_globals(csqcprogs, PR_CURRENT); G_FLOAT(OFS_PARM0) = CSIE_MOUSEABS; - G_FLOAT(OFS_PARM1) = xabs; - G_FLOAT(OFS_PARM2) = yabs; + G_FLOAT(OFS_PARM1) = (xabs * vid.width) / vid.pixelwidth; + G_FLOAT(OFS_PARM2) = (yabs * vid.height) / vid.pixelheight; G_FLOAT(OFS_PARM3) = devid; PR_ExecuteProgram (csqcprogs, csqcg.input_event); diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 379aedaf3..994e29d51 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -10,7 +10,7 @@ #if defined(MENU_DAT) || defined(CSQC_DAT) #include "cl_master.h" -extern int r2d_be_flags; +extern unsigned int r2d_be_flags; #define DRAWFLAG_NORMAL 0 #define DRAWFLAG_ADD 1 #define DRAWFLAG_MODULATE 2 @@ -36,54 +36,35 @@ void QCBUILTIN PF_CL_drawfill (pubprogfuncs_t *prinst, struct globalvars_s *pr_g float *size = G_VECTOR(OFS_PARM1); float *rgb = G_VECTOR(OFS_PARM2); float alpha = G_FLOAT(OFS_PARM3); + int flag = prinst->callargc >= 5?G_FLOAT(OFS_PARM4):0; + r2d_be_flags = PF_SelectDPDrawFlag(flag); R2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha); R2D_FillBlock(pos[0], pos[1], size[0], size[1]); + r2d_be_flags = 0; G_FLOAT(OFS_RETURN) = 1; } //void drawsetcliparea(float x, float y, float width, float height) = #458; void QCBUILTIN PF_CL_drawsetcliparea (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - float x = G_FLOAT(OFS_PARM0), y = G_FLOAT(OFS_PARM1), w = G_FLOAT(OFS_PARM2), h = G_FLOAT(OFS_PARM3); + srect_t srect; + srect.x = G_FLOAT(OFS_PARM0) / (float)vid.width; + srect.y = (G_FLOAT(OFS_PARM1) / (float)vid.height); + srect.width = G_FLOAT(OFS_PARM2) / (float)vid.width; + srect.height = G_FLOAT(OFS_PARM3) / (float)vid.height; + srect.dmin = -99999; + srect.dmax = 99999; + srect.y = (1-srect.y) - srect.height; + BE_Scissor(&srect); -#ifdef GLQUAKE - if (qrenderer == QR_OPENGL && qglScissor) - { - - x *= (float)vid.pixelwidth/vid.width; - y *= (float)vid.pixelheight/vid.height; - - w *= (float)vid.pixelwidth/vid.width; - h *= (float)vid.pixelheight/vid.height; - - //add a pixel because this makes DP's menus come out right. - x-=1; - y-=1; - w+=2; - h+=2; - - - qglScissor (x, vid.pixelheight-(y+h), w, h); - qglEnable(GL_SCISSOR_TEST); - G_FLOAT(OFS_RETURN) = 1; - return; - } -#endif - G_FLOAT(OFS_RETURN) = 0; + G_FLOAT(OFS_RETURN) = 1; } //void drawresetcliparea(void) = #459; void QCBUILTIN PF_CL_drawresetcliparea (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { -#ifdef GLQUAKE - if (qrenderer == QR_OPENGL) - { - qglDisable(GL_SCISSOR_TEST); - G_FLOAT(OFS_RETURN) = 1; - return; - } -#endif - G_FLOAT(OFS_RETURN) = 0; + BE_Scissor(NULL); + G_FLOAT(OFS_RETURN) = 1; } #define FONT_SLOTS 16 @@ -265,7 +246,7 @@ void QCBUILTIN PF_CL_drawcolouredstring (pubprogfuncs_t *prinst, struct globalva g = G_FLOAT(OFS_PARM3 + 1); b = G_FLOAT(OFS_PARM3 + 2); alpha = G_FLOAT(OFS_PARM4); - flag = G_FLOAT(OFS_PARM5); + flag = G_FLOAT(OFS_PARM5); //flag is mandatory to distinguish it. } else { @@ -273,7 +254,7 @@ void QCBUILTIN PF_CL_drawcolouredstring (pubprogfuncs_t *prinst, struct globalva g = 1; b = 1; alpha = G_FLOAT(OFS_PARM3); - flag = G_FLOAT(OFS_PARM4); + flag = prinst->callargc >= 5?G_FLOAT(OFS_PARM4):0; } if (!text) @@ -315,7 +296,7 @@ void QCBUILTIN PF_CL_stringwidth(pubprogfuncs_t *prinst, struct globalvars_s *pr end = COM_ParseFunString(CON_WHITEMASK, text, buffer, sizeof(buffer), !usecolours); PR_CL_BeginString(prinst, 0, 0, size?size[0]:8, size?size[1]:8, &px, &py); - px = Font_LineWidth(buffer, end); + px = Font_LineScaleWidth(buffer, end); Font_EndString(NULL); G_FLOAT(OFS_RETURN) = (px * vid.width) / vid.rotpixelwidth; @@ -329,7 +310,7 @@ void QCBUILTIN PF_CL_drawpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl float *size = G_VECTOR(OFS_PARM2); float *rgb = G_VECTOR(OFS_PARM3); float alpha = G_FLOAT(OFS_PARM4); - int flag = (int)G_FLOAT(OFS_PARM5); + int flag = prinst->callargc >= 6?(int)G_FLOAT(OFS_PARM5):0; mpic_t *p; @@ -354,7 +335,7 @@ void QCBUILTIN PF_CL_drawsubpic (pubprogfuncs_t *prinst, struct globalvars_s *pr float *srcSize = G_VECTOR(OFS_PARM4); float *rgb = G_VECTOR(OFS_PARM5); float alpha = G_FLOAT(OFS_PARM6); - int flag = (int) G_FLOAT(OFS_PARM7); + int flag = prinst->callargc >= 8?(int) G_FLOAT(OFS_PARM7):0; mpic_t *p; @@ -434,7 +415,7 @@ void QCBUILTIN PF_CL_drawcharacter (pubprogfuncs_t *prinst, struct globalvars_s float *size = G_VECTOR(OFS_PARM2); float *rgb = G_VECTOR(OFS_PARM3); float alpha = G_FLOAT(OFS_PARM4); - int flag = G_FLOAT(OFS_PARM5); + int flag = prinst->callargc >= 6?G_FLOAT(OFS_PARM5):0; float x, y; @@ -468,7 +449,7 @@ void QCBUILTIN PF_CL_drawrawstring (pubprogfuncs_t *prinst, struct globalvars_s float *size = G_VECTOR(OFS_PARM2); float *rgb = G_VECTOR(OFS_PARM3); float alpha = G_FLOAT(OFS_PARM4); -// float flag = G_FLOAT(OFS_PARM5); + int flag = prinst->callargc >= 6?G_FLOAT(OFS_PARM5):0; float x, y; unsigned int c; int error; @@ -479,6 +460,7 @@ void QCBUILTIN PF_CL_drawrawstring (pubprogfuncs_t *prinst, struct globalvars_s return; } + r2d_be_flags = PF_SelectDPDrawFlag(flag); PR_CL_BeginString(prinst, pos[0], pos[1], size[0], size[1], &x, &y); Font_ForceColour(rgb[0], rgb[1], rgb[2], alpha); @@ -500,31 +482,57 @@ void QCBUILTIN PF_CL_drawrawstring (pubprogfuncs_t *prinst, struct globalvars_s } Font_InvalidateColour(); Font_EndString(NULL); + r2d_be_flags = 0; } -//void (float width, vector rgb, float alpha, float flags, vector pos1, ...) drawline; +//void (float width, vector pos1, vector pos2, vector rgb, float alpha, optional float flags) drawline; void QCBUILTIN PF_CL_drawline (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - float *rgb = G_VECTOR(OFS_PARM1); - float alpha = G_FLOAT(OFS_PARM2); - float *pos = G_VECTOR(OFS_PARM4); - int numpoints = prinst->callargc-4; + //float width = G_FLOAT(OFS_PARM0); + float *point1 = G_VECTOR(OFS_PARM1); + float *point2 = G_VECTOR(OFS_PARM2); + float *rgb = G_VECTOR(OFS_PARM3); + float alpha = G_FLOAT(OFS_PARM4); + int flags = prinst->callargc >= 6?G_FLOAT(OFS_PARM5):0; + shader_t *shader_draw_line; -#ifdef GLQUAKE // :( + mesh_t mesh; + vecV_t vpos[2]; + vec2_t vst[2]; + vec4_t vcol[2]; + index_t idx[2]; - if (qrenderer == QR_OPENGL) - { - qglColor4f(rgb[0], rgb[1], rgb[2], alpha); - qglBegin(GL_LINES); - while (numpoints-->0) - { - qglVertex3fv(pos); - pos += 3; - } + memset(&mesh, 0, sizeof(mesh)); + mesh.indexes = idx; + mesh.colors4f_array = vpos; + mesh.st_array = vst; + mesh.colors4f_array = vcol; - qglEnd(); - } -#endif + VectorCopy(point1, vpos[0]); + Vector2Set(vst[0], 0, 0); + Vector4Set(vcol[0], rgb[0], rgb[1], rgb[2], alpha); + + VectorCopy(point2, vpos[1]); + Vector2Set(vst[1], 0, 0); + Vector4Set(vcol[1], rgb[0], rgb[1], rgb[2], alpha); + mesh.numvertexes = 2; + + mesh.indexes[0] = 0; + mesh.indexes[1] = 1; + mesh.numindexes = 2; + + //this shader lookup might get pricy. + shader_draw_line = R_RegisterShader("shader_draw_line", + "{\n" + "program defaultfill\n" + "{\n" + "map $whiteimage\n" + "rgbgen exactvertex\n" + "alphagen vertex\n" + "}\n" + "}\n"); + + BE_DrawMesh_Single(shader_draw_line, &mesh, NULL, &shader_draw_line->defaulttextures, flags|BEF_LINES); } //vector drawgetimagesize(string pic) = #460; @@ -1309,8 +1317,9 @@ static struct { {"strpad", PF_strpad, 225}, {"infoadd", PF_infoadd, 226}, {"infoget", PF_infoget, 227}, + {"strcmp", PF_strncmp, 228}, {"strncmp", PF_strncmp, 228}, - {"strcasecmp", PF_strcasecmp, 229}, + {"strcasecmp", PF_strncasecmp, 229}, {"strncasecmp", PF_strncasecmp, 230}, //gap {"shaderforname", PF_shaderforname, 238}, @@ -1323,7 +1332,7 @@ static struct { {"hash_getkey", PF_hash_getkey, 292}, //gap {"print", PF_print, 339}, - {"keynumtostring", PF_cl_keynumtostring, 340}, + {"keynumtostring_csqc", PF_cl_keynumtostring, 340}, {"stringtokeynum", PF_cl_stringtokeynum, 341}, {"getkeybind", PF_cl_getkeybind, 342}, //gap @@ -1415,6 +1424,8 @@ static struct { {"cvar_description", PF_cvar_description, 518}, //gap {"soundlength", PF_soundlength, 534}, + {"buf_loadfile", PF_buf_loadfile, 535}, + {"buf_writefile", PF_buf_writefile, 536}, //gap {"setkeydest", PF_cl_setkeydest, 601}, {"getkeydest", PF_cl_getkeydest, 602}, diff --git a/engine/client/quakedef.h b/engine/client/quakedef.h index e8c5c76b3..3fd0aa49f 100644 --- a/engine/client/quakedef.h +++ b/engine/client/quakedef.h @@ -248,7 +248,7 @@ extern qboolean noclip_anglehack; extern quakeparms_t host_parms; extern cvar_t fs_gamename; -extern cvar_t fs_gamedownload; +extern cvar_t fs_gamemanifest; extern cvar_t com_protocolname; extern cvar_t com_modname; extern cvar_t com_nogamedirnativecode; @@ -276,6 +276,7 @@ NORETURN void VARGS Host_Error (char *error, ...) LIKEPRINTF(1); NORETURN void VARGS Host_EndGame (char *message, ...) LIKEPRINTF(1); qboolean Host_SimulationTime(float time); double Host_Frame (double time); +qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file); void Host_Quit_f (void); void VARGS Host_ClientCommands (char *fmt, ...) LIKEPRINTF(1); void Host_ShutdownServer (qboolean crash); diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c index 14a0b0740..a33a7f694 100644 --- a/engine/client/r_2d.c +++ b/engine/client/r_2d.c @@ -174,9 +174,9 @@ void R2D_Init(void) "}\n" "}\n"); if (!TEXVALID(draw_backtile->defaulttextures.base)) - draw_backtile->defaulttextures.base = R_LoadHiResTexture("gfx/backtile", NULL, IF_2D|IF_NOPICMIP|IF_NOMIPMAP); + draw_backtile->defaulttextures.base = R_LoadHiResTexture("gfx/backtile", NULL, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP); if (!TEXVALID(draw_backtile->defaulttextures.base)) - draw_backtile->defaulttextures.base = R_LoadHiResTexture("gfx/menu/backtile", NULL, IF_2D|IF_NOPICMIP|IF_NOMIPMAP); + draw_backtile->defaulttextures.base = R_LoadHiResTexture("gfx/menu/backtile", NULL, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP); shader_draw_fill = R_RegisterShader("fill_opaque", "{\n" @@ -468,7 +468,7 @@ void R2D_TransPicTranslate (int x, int y, int width, int height, qbyte *pic, qby translate_shader->defaulttextures.base = translate_texture; } /* could avoid reuploading already translated textures but this func really isn't used enough anyway */ - R_Upload(translate_texture, NULL, TF_RGBA32, trans, NULL, 64, 64, IF_2D|IF_NOMIPMAP|IF_NOGAMMA); + R_Upload(translate_texture, NULL, TF_RGBA32, trans, NULL, 64, 64, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); R2D_ScalePic(x, y, width, height, translate_shader); } @@ -722,8 +722,6 @@ void R2D_Console_Resize(void) vid.width = cwidth; vid.height = cheight; - vid.recalc_refdef = true; - if (font_tiny) Font_Free(font_tiny); font_tiny = NULL; @@ -1055,7 +1053,7 @@ void R2D_Crosshair_Update(void) return; else if (crosshairimage.string[0] && c == 1) { - shader_crosshair->defaulttextures.base = R_LoadHiResTexture (crosshairimage.string, "crosshairs", IF_2D|IF_NOMIPMAP|IF_NOGAMMA); + shader_crosshair->defaulttextures.base = R_LoadHiResTexture (crosshairimage.string, "crosshairs", IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); if (TEXVALID(shader_crosshair->defaulttextures.base)) return; } @@ -1066,7 +1064,7 @@ void R2D_Crosshair_Update(void) c = c % (sizeof(crosshair_pixels) / (CS_HEIGHT*sizeof(*crosshair_pixels))); if (!TEXVALID(ch_int_texture)) - ch_int_texture = R_AllocNewTexture("***crosshair***", CS_WIDTH, CS_HEIGHT, IF_2D|IF_NOMIPMAP); + ch_int_texture = R_AllocNewTexture("***crosshair***", CS_WIDTH, CS_HEIGHT, IF_UIPIC|IF_NOMIPMAP); shader_crosshair->defaulttextures.base = ch_int_texture; Q_memset(crossdata, 0, sizeof(crossdata)); @@ -1083,7 +1081,7 @@ void R2D_Crosshair_Update(void) } } - R_Upload(ch_int_texture, NULL, TF_RGBA32, crossdata, NULL, CS_WIDTH, CS_HEIGHT, IF_2D|IF_NOMIPMAP|IF_NOGAMMA); + R_Upload(ch_int_texture, NULL, TF_RGBA32, crossdata, NULL, CS_WIDTH, CS_HEIGHT, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); } @@ -1128,7 +1126,7 @@ void R2D_DrawCrosshair(void) size = -size; for (sc = 0; sc < cl.splitclients; sc++) { - SCR_CrosshairPosition(sc, &x, &y); + SCR_CrosshairPosition(&cl.playerview[sc], &x, &y); Font_BeginScaledString(font_conchar, x, y, size, size, &sx, &sy); sx -= Font_CharScaleWidth('+' | 0xe000 | CON_WHITEMASK)/2; sy -= Font_CharScaleHeight()/2; @@ -1163,7 +1161,7 @@ void R2D_DrawCrosshair(void) R2D_ImageColours(ch_color[0], ch_color[1], ch_color[2], crosshairalpha.value); for (sc = 0; sc < cl.splitclients; sc++) { - SCR_CrosshairPosition(sc, &x, &y); + SCR_CrosshairPosition(&cl.playerview[sc], &x, &y); //translate to pixel coord, for rounding x = ((x-sizex+(sizex/CS_WIDTH))*vid.rotpixelwidth) / (float)vid.width; diff --git a/engine/client/r_partset.c b/engine/client/r_partset.c index 15eba20b7..c6dcf0354 100644 --- a/engine/client/r_partset.c +++ b/engine/client/r_partset.c @@ -1295,6 +1295,26 @@ char *particle_set_highfps = char *particle_set_high = +/////////////////////////////// +//rain +"r_part te_rain\n" +"{\n" +"texture ball; scalefactor 1; count 1; alpha 0.4; rgb 255 255 255; die 2; veladd 2; scale 2; type texturedspark\n" +"cliptype rainsplash\n" +"clipbounce 1\n" +"clipcount 5\n" +"}\n" + +"r_part rainsplash\n" +"{\n" +"randomvel 50 50\n" +"count 1;\n" +"texture ball; scalefactor 1; alpha 0.1; rgb 255 255 255; die 0.4; scale 50;\n" +"stretchfactor 4\n" +"veladd 50; scale 1; type texturedspark\n" +"gravity 400\n" +"}\n" + /////////////////////////////// //rocket trail diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index 4e6c983ca..9d4078443 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -2579,7 +2579,11 @@ void Surf_BuildModelLightmaps (model_t *m) { for (t = m->numtextures-1; t >= 0; t--) { - ptype = P_FindParticleType(va("tex_%s", m->textures[t]->name)); + char *pn = va("tex_%s", m->textures[t]->name); + char *h = strchr(pn, '#'); + if (h) + *h = 0; + ptype = P_FindParticleType(pn); if (ptype != P_INVALID) { diff --git a/engine/client/render.h b/engine/client/render.h index c497f579c..0fd4d06e7 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -119,7 +119,6 @@ typedef struct entity_s framestate_t framestate; - unsigned int externalmodelview; int flags; refEntityType_t rtype; @@ -139,21 +138,25 @@ typedef struct entity_s #endif } entity_t; +#define RDFD_FOV 1 typedef struct { - vrect_t vrect; // subwindow in video for refresh - // FIXME: not need vrect next field here? + vrect_t grect; // game rectangle. fullscreen except for csqc/splitscreen. + vrect_t vrect; // subwindow in grect for 3d view vec3_t pvsorigin; /*render the view using this point for pvs (useful for mirror views)*/ vec3_t vieworg; /*logical view center*/ vec3_t viewangles; vec3_t viewaxis[3]; /*forward, left, up (NOT RIGHT)*/ - float fov_x, fov_y; + float fov_x, fov_y, afov; - int flags; + qboolean drawsbar; + int flags; //(Q2)RDF_ flags + int dirty; - int currentplayernum; + playerview_t *playerview; +// int currentplayernum; float time; // float waterheight; //updated by the renderer. stuff sitting at this height generate ripple effects @@ -269,7 +272,7 @@ enum imageflags /*warning: many of these flags only apply the first time it is requested*/ IF_CLAMP = 1<<0, IF_NEAREST = 1<<1, - IF_2D = 1<<10, //subject to texturemode2d + IF_UIPIC = 1<<10, //subject to texturemode2d IF_LINEAR = 1<<11, IF_NOPICMIP = 1<<2, IF_NOMIPMAP = 1<<3, diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 8c9faa5e1..a061cbebd 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -646,7 +646,7 @@ void Renderer_Init(void) Cvar_Register(&scr_viewsize, SCREENOPTIONS); Cvar_Register(&scr_fov, SCREENOPTIONS); - Cvar_Register(&scr_chatmodecvar, SCREENOPTIONS); +// Cvar_Register(&scr_chatmodecvar, SCREENOPTIONS); Cvar_Register (&scr_sshot_type, SCREENOPTIONS); Cvar_Register (&scr_sshot_compression, SCREENOPTIONS); @@ -880,6 +880,7 @@ rendererinfo_t dedicatedrendererinfo = { NULL, NULL, NULL, + NULL, "" }; @@ -1135,8 +1136,6 @@ qboolean R_ApplyRenderer_Load (rendererstate_t *newr) BZ_Free(colormap); } - R_GenPaletteLookup(); - if (h2playertranslations) BZ_Free(h2playertranslations); h2playertranslations = FS_LoadMallocFile ("gfx/player.lmp"); @@ -1145,6 +1144,7 @@ qboolean R_ApplyRenderer_Load (rendererstate_t *newr) vid.fullbright = 0; //transparent colour doesn't count. q2colormap: + R_GenPaletteLookup(); TRACE(("dbg: R_ApplyRenderer: Palette loaded\n")); @@ -1499,7 +1499,7 @@ TRACE(("dbg: R_RestartRenderer_f\n")); { int i; if (*vid_renderer.string) - Con_Printf("vid_renderer unsupported. Using default.\n"); + Con_Printf("vid_renderer \"%s\" unsupported. Using default.\n", vid_renderer.string); else Con_DPrintf("vid_renderer unset. Using default.\n"); @@ -1660,7 +1660,9 @@ void R_SetRenderer_f (void) } Cvar_Set(&vid_renderer, param); - R_RestartRenderer_f(); + + if (!r_blockvidrestart) + R_RestartRenderer_f(); } diff --git a/engine/client/sbar.c b/engine/client/sbar.c index f192854e1..2e644fa03 100644 --- a/engine/client/sbar.c +++ b/engine/client/sbar.c @@ -74,10 +74,6 @@ cvar_t sbar_teamstatus = SCVAR("sbar_teamstatus", "1"); int sb_updates; // if >= vid.numpages, no update needed -int sb_hexen2_cur_item[MAX_SPLITS];//hexen2 hud -qboolean sb_hexen2_extra_info[MAX_SPLITS];//show the extra stuff -qboolean sb_hexen2_infoplaque[MAX_SPLITS]; -float sb_hexen2_item_time[MAX_SPLITS]; qboolean sbar_parsingteamstatuses; //so we don't eat it if its not displayed @@ -122,15 +118,15 @@ int sb_lines; // scan lines to draw void Sbar_DeathmatchOverlay (int start); void Sbar_TeamOverlay (void); -static void Sbar_MiniDeathmatchOverlay (int pnum); -void Sbar_ChatModeOverlay(void); +static void Sbar_MiniDeathmatchOverlay (playerview_t *pv); +void Sbar_ChatModeOverlay(playerview_t *pv); -int Sbar_PlayerNum(void) +int Sbar_PlayerNum(playerview_t *pv) { int num; - num = cl.spectator?Cam_TrackNum(0):-1; + num = cl.spectator?Cam_TrackNum(pv):-1; if (num < 0) - num = cl.playernum[0]; + num = pv->playernum; return num; } @@ -660,14 +656,15 @@ void Sbar_Hexen2InvLeft_f(void) { int tries = 15; int pnum = CL_TargettedSplit(false); - sb_hexen2_item_time[pnum] = realtime; + playerview_t *pv = &cl.playerview[pnum]; + pv->sb_hexen2_item_time = realtime; while (tries-- > 0) { - sb_hexen2_cur_item[pnum]--; - if (sb_hexen2_cur_item[pnum] < 0) - sb_hexen2_cur_item[pnum] = 14; + pv->sb_hexen2_cur_item--; + if (pv->sb_hexen2_cur_item < 0) + pv->sb_hexen2_cur_item = 14; - if (cl.playerview[pnum].stats[STAT_H2_CNT_TORCH+sb_hexen2_cur_item[pnum]] > 0) + if (pv->stats[STAT_H2_CNT_TORCH+pv->sb_hexen2_cur_item] > 0) break; } } @@ -682,14 +679,15 @@ void Sbar_Hexen2InvRight_f(void) { int tries = 15; int pnum = CL_TargettedSplit(false); - sb_hexen2_item_time[pnum] = realtime; + playerview_t *pv = &cl.playerview[pnum]; + pv->sb_hexen2_item_time = realtime; while (tries-- > 0) { - sb_hexen2_cur_item[pnum]++; - if (sb_hexen2_cur_item[pnum] > 14) - sb_hexen2_cur_item[pnum] = 0; + pv->sb_hexen2_cur_item++; + if (pv->sb_hexen2_cur_item > 14) + pv->sb_hexen2_cur_item = 0; - if (cl.playerview[pnum].stats[STAT_H2_CNT_TORCH+sb_hexen2_cur_item[pnum]] > 0) + if (pv->stats[STAT_H2_CNT_TORCH+pv->sb_hexen2_cur_item] > 0) break; } } @@ -703,28 +701,29 @@ void Sbar_Hexen2InvUse_f(void) else { int pnum = CL_TargettedSplit(false); - Cmd_ExecuteString(va("impulse %d\n", 100+sb_hexen2_cur_item[pnum]), Cmd_ExecLevel); + playerview_t *pv = &cl.playerview[pnum]; + Cmd_ExecuteString(va("impulse %d\n", 100+pv->sb_hexen2_cur_item), Cmd_ExecLevel); } } void Sbar_Hexen2ShowInfo_f(void) { - int pnum = CL_TargettedSplit(false); - sb_hexen2_extra_info[pnum] = true; + playerview_t *pv = &cl.playerview[CL_TargettedSplit(false)]; + pv->sb_hexen2_extra_info = true; } void Sbar_Hexen2DontShowInfo_f(void) { - int pnum = CL_TargettedSplit(false); - sb_hexen2_extra_info[pnum] = false; + playerview_t *pv = &cl.playerview[CL_TargettedSplit(false)]; + pv->sb_hexen2_extra_info = false; } void Sbar_Hexen2PInfoPlaque_f(void) { - int pnum = CL_TargettedSplit(false); - sb_hexen2_infoplaque[pnum] = true; + playerview_t *pv = &cl.playerview[CL_TargettedSplit(false)]; + pv->sb_hexen2_infoplaque = true; } void Sbar_Hexen2MInfoPlaque_f(void) { - int pnum = CL_TargettedSplit(false); - sb_hexen2_infoplaque[pnum] = false; + playerview_t *pv = &cl.playerview[CL_TargettedSplit(false)]; + pv->sb_hexen2_infoplaque = false; } /* @@ -1275,7 +1274,7 @@ void Sbar_SortFrags (qboolean includespec, qboolean teamsort) } } -void Sbar_SortTeams (void) +void Sbar_SortTeams (playerview_t *pv) { int i, j, k; player_info_t *s; @@ -1293,7 +1292,7 @@ void Sbar_SortTeams (void) for (i = 0; i < MAX_CLIENTS; i++) teams[i].plow = 999; - ownnum = Sbar_PlayerNum(); + ownnum = Sbar_PlayerNum(pv); for (i = 0; i < MAX_CLIENTS; i++) { @@ -1443,7 +1442,7 @@ void Sbar_CoopScoreboard (void) Sbar_DrawInventory =============== */ -void Sbar_DrawInventory (int pnum) +void Sbar_DrawInventory (playerview_t *pv) { int i; char num[6]; @@ -1460,7 +1459,7 @@ void Sbar_DrawInventory (int pnum) { if (sbar_rogue) { - if ( cl.playerview[pnum].stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN ) + if ( pv->stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN ) Sbar_DrawPic (0, -24, 320, 24, rsb_invbar[0]); else Sbar_DrawPic (0, -24, 320, 24, rsb_invbar[1]); @@ -1471,15 +1470,15 @@ void Sbar_DrawInventory (int pnum) // weapons for (i=0 ; i<7 ; i++) { - if (cl.playerview[pnum].stats[STAT_ITEMS] & (IT_SHOTGUN<stats[STAT_ITEMS] & (IT_SHOTGUN<item_gettime[i]; flashon = (int)((cl.time - time)*10); if (flashon < 0) flashon = 0; if (flashon >= 10) { - if ( cl.playerview[pnum].stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<= RIT_LAVA_NAILGUN ) + if ( pv->stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN ) { for (i=0;i<5;i++) { - if (cl.playerview[pnum].stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i)) + if (pv->stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i)) { if (headsup) { @@ -1534,7 +1533,7 @@ void Sbar_DrawInventory (int pnum) } for (i=0 ; i<4 ; i++) { - snprintf (num, sizeof(num), "%3i",cl.playerview[pnum].stats[STAT_SHELLS+i] ); + snprintf (num, sizeof(num), "%3i", pv->stats[STAT_SHELLS+i] ); numc[0] = CON_WHITEMASK|0xe000|((num[0]!=' ')?(num[0] + 18-'0'):' '); numc[1] = CON_WHITEMASK|0xe000|((num[1]!=' ')?(num[1] + 18-'0'):' '); numc[2] = CON_WHITEMASK|0xe000|((num[2]!=' ')?(num[2] + 18-'0'):' '); @@ -1553,9 +1552,9 @@ void Sbar_DrawInventory (int pnum) // items for (i=0 ; i<6 ; i++) { - if (cl.playerview[pnum].stats[STAT_ITEMS] & (1<<(17+i))) + if (pv->stats[STAT_ITEMS] & (1<<(17+i))) { - time = cl.playerview[pnum].item_gettime[17+i]; + time = pv->item_gettime[17+i]; if (time && time > cl.time - 2 && flashon ) { // flash frame sb_updates = 0; @@ -1572,9 +1571,9 @@ void Sbar_DrawInventory (int pnum) // new rogue items for (i=0 ; i<2 ; i++) { - if (cl.playerview[pnum].stats[STAT_ITEMS] & (1<<(29+i))) + if (pv->stats[STAT_ITEMS] & (1<<(29+i))) { - time = cl.playerview[pnum].item_gettime[29+i]; + time = pv->item_gettime[29+i]; if (time && time > cl.time - 2 && flashon ) { // flash frame @@ -1595,9 +1594,9 @@ void Sbar_DrawInventory (int pnum) // sigils for (i=0 ; i<4 ; i++) { - if (cl.playerview[pnum].stats[STAT_ITEMS] & (1<<(28+i))) + if (pv->stats[STAT_ITEMS] & (1<<(28+i))) { - time = cl.playerview[pnum].item_gettime[28+i]; + time = pv->item_gettime[28+i]; if (time && time > cl.time - 2 && flashon ) { // flash frame sb_updates = 0; @@ -1618,7 +1617,7 @@ void Sbar_DrawInventory (int pnum) Sbar_DrawFrags =============== */ -void Sbar_DrawFrags (void) +void Sbar_DrawFrags (playerview_t *pv) { int i, k, l; int top, bottom; @@ -1629,7 +1628,7 @@ void Sbar_DrawFrags (void) Sbar_SortFrags (false, false); - ownnum = Sbar_PlayerNum(); + ownnum = Sbar_PlayerNum(pv); // draw the text l = scoreboardlines <= 4 ? scoreboardlines : 4; @@ -1683,41 +1682,41 @@ void Sbar_DrawFrags (void) Sbar_DrawFace =============== */ -void Sbar_DrawFace (int pnum) +void Sbar_DrawFace (playerview_t *pv) { int f, anim; - if ( (cl.playerview[pnum].stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY) ) + if ( (pv->stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY) ) == (IT_INVISIBILITY | IT_INVULNERABILITY) ) { Sbar_DrawPic (112, 0, 24, 24, sb_face_invis_invuln); return; } - if (cl.playerview[pnum].stats[STAT_ITEMS] & IT_QUAD) + if (pv->stats[STAT_ITEMS] & IT_QUAD) { Sbar_DrawPic (112, 0, 24, 24, sb_face_quad ); return; } - if (cl.playerview[pnum].stats[STAT_ITEMS] & IT_INVISIBILITY) + if (pv->stats[STAT_ITEMS] & IT_INVISIBILITY) { Sbar_DrawPic (112, 0, 24, 24, sb_face_invis ); return; } - if (cl.playerview[pnum].stats[STAT_ITEMS] & IT_INVULNERABILITY) + if (pv->stats[STAT_ITEMS] & IT_INVULNERABILITY) { Sbar_DrawPic (112, 0, 24, 24, sb_face_invuln); return; } - if (cl.playerview[pnum].stats[STAT_HEALTH] >= 100) + if (pv->stats[STAT_HEALTH] >= 100) f = 4; else - f = cl.playerview[pnum].stats[STAT_HEALTH] / 20; + f = pv->stats[STAT_HEALTH] / 20; if (f < 0) f=0; - if (cl.time <= cl.playerview[pnum].faceanimtime) + if (cl.time <= pv->faceanimtime) { anim = 1; sb_updates = 0; // make sure the anim gets drawn over @@ -1732,13 +1731,13 @@ void Sbar_DrawFace (int pnum) Sbar_DrawNormal ============= */ -void Sbar_DrawNormal (int pnum) +void Sbar_DrawNormal (playerview_t *pv) { if (cl_sbar.value || (scr_viewsize.value<100&&cl.splitclients==1)) Sbar_DrawPic (0, 0, 320, 24, sb_sbar); // armor - if (cl.playerview[pnum].stats[STAT_ITEMS] & IT_INVULNERABILITY) + if (pv->stats[STAT_ITEMS] & IT_INVULNERABILITY) { Sbar_DrawNum (24, 0, 666, 3, 1); Sbar_DrawPic (0, 0, 24, 24, draw_disc); @@ -1747,67 +1746,67 @@ void Sbar_DrawNormal (int pnum) { if (sbar_rogue) { - Sbar_DrawNum (24, 0, cl.playerview[pnum].stats[STAT_ARMOR], 3, - cl.playerview[pnum].stats[STAT_ARMOR] <= 25); - if (cl.playerview[pnum].stats[STAT_ITEMS] & RIT_ARMOR3) + Sbar_DrawNum (24, 0, pv->stats[STAT_ARMOR], 3, + pv->stats[STAT_ARMOR] <= 25); + if (pv->stats[STAT_ITEMS] & RIT_ARMOR3) Sbar_DrawPic (0, 0, 24, 24, sb_armor[2]); - else if (cl.playerview[pnum].stats[STAT_ITEMS] & RIT_ARMOR2) + else if (pv->stats[STAT_ITEMS] & RIT_ARMOR2) Sbar_DrawPic (0, 0, 24, 24, sb_armor[1]); - else if (cl.playerview[pnum].stats[STAT_ITEMS] & RIT_ARMOR1) + else if (pv->stats[STAT_ITEMS] & RIT_ARMOR1) Sbar_DrawPic (0, 0, 24, 24, sb_armor[0]); } else { - Sbar_DrawNum (24, 0, cl.playerview[pnum].stats[STAT_ARMOR], 3, - cl.playerview[pnum].stats[STAT_ARMOR] <= 25); - if (cl.playerview[pnum].stats[STAT_ITEMS] & IT_ARMOR3) + Sbar_DrawNum (24, 0, pv->stats[STAT_ARMOR], 3, + pv->stats[STAT_ARMOR] <= 25); + if (pv->stats[STAT_ITEMS] & IT_ARMOR3) Sbar_DrawPic (0, 0, 24, 24, sb_armor[2]); - else if (cl.playerview[pnum].stats[STAT_ITEMS] & IT_ARMOR2) + else if (pv->stats[STAT_ITEMS] & IT_ARMOR2) Sbar_DrawPic (0, 0, 24, 24, sb_armor[1]); - else if (cl.playerview[pnum].stats[STAT_ITEMS] & IT_ARMOR1) + else if (pv->stats[STAT_ITEMS] & IT_ARMOR1) Sbar_DrawPic (0, 0, 24, 24, sb_armor[0]); } } // face - Sbar_DrawFace (pnum); + Sbar_DrawFace (pv); // health - Sbar_DrawNum (136, 0, cl.playerview[pnum].stats[STAT_HEALTH], 3 - , cl.playerview[pnum].stats[STAT_HEALTH] <= 25); + Sbar_DrawNum (136, 0, pv->stats[STAT_HEALTH], 3 + , pv->stats[STAT_HEALTH] <= 25); // ammo icon if (sbar_rogue) { - if (cl.playerview[pnum].stats[STAT_ITEMS] & RIT_SHELLS) + if (pv->stats[STAT_ITEMS] & RIT_SHELLS) Sbar_DrawPic (224, 0, 24, 24, sb_ammo[0]); - else if (cl.playerview[pnum].stats[STAT_ITEMS] & RIT_NAILS) + else if (pv->stats[STAT_ITEMS] & RIT_NAILS) Sbar_DrawPic (224, 0, 24, 24, sb_ammo[1]); - else if (cl.playerview[pnum].stats[STAT_ITEMS] & RIT_ROCKETS) + else if (pv->stats[STAT_ITEMS] & RIT_ROCKETS) Sbar_DrawPic (224, 0, 24, 24, sb_ammo[2]); - else if (cl.playerview[pnum].stats[STAT_ITEMS] & RIT_CELLS) + else if (pv->stats[STAT_ITEMS] & RIT_CELLS) Sbar_DrawPic (224, 0, 24, 24, sb_ammo[3]); - else if (cl.playerview[pnum].stats[STAT_ITEMS] & RIT_LAVA_NAILS) + else if (pv->stats[STAT_ITEMS] & RIT_LAVA_NAILS) Sbar_DrawPic (224, 0, 24, 24, rsb_ammo[0]); - else if (cl.playerview[pnum].stats[STAT_ITEMS] & RIT_PLASMA_AMMO) + else if (pv->stats[STAT_ITEMS] & RIT_PLASMA_AMMO) Sbar_DrawPic (224, 0, 24, 24, rsb_ammo[1]); - else if (cl.playerview[pnum].stats[STAT_ITEMS] & RIT_MULTI_ROCKETS) + else if (pv->stats[STAT_ITEMS] & RIT_MULTI_ROCKETS) Sbar_DrawPic (224, 0, 24, 24, rsb_ammo[2]); } else { - if (cl.playerview[pnum].stats[STAT_ITEMS] & IT_SHELLS) + if (pv->stats[STAT_ITEMS] & IT_SHELLS) Sbar_DrawPic (224, 0, 24, 24, sb_ammo[0]); - else if (cl.playerview[pnum].stats[STAT_ITEMS] & IT_NAILS) + else if (pv->stats[STAT_ITEMS] & IT_NAILS) Sbar_DrawPic (224, 0, 24, 24, sb_ammo[1]); - else if (cl.playerview[pnum].stats[STAT_ITEMS] & IT_ROCKETS) + else if (pv->stats[STAT_ITEMS] & IT_ROCKETS) Sbar_DrawPic (224, 0, 24, 24, sb_ammo[2]); - else if (cl.playerview[pnum].stats[STAT_ITEMS] & IT_CELLS) + else if (pv->stats[STAT_ITEMS] & IT_CELLS) Sbar_DrawPic (224, 0, 24, 24, sb_ammo[3]); } - Sbar_DrawNum (248, 0, cl.playerview[pnum].stats[STAT_AMMO], 3 - , cl.playerview[pnum].stats[STAT_AMMO] <= 10); + Sbar_DrawNum (248, 0, pv->stats[STAT_AMMO], 3 + , pv->stats[STAT_AMMO] <= 10); } qboolean Sbar_ShouldDraw (void) @@ -1879,12 +1878,12 @@ void Sbar_DrawScoreboard (void) } -void Sbar_Hexen2DrawItem(int pnum, int x, int y, int itemnum) +static void Sbar_Hexen2DrawItem(playerview_t *pv, int x, int y, int itemnum) { int num; Sbar_DrawPic(x, y, 29, 28, R2D_SafeCachePic(va("gfx/arti%02d.lmp", itemnum))); - num = cl.playerview[pnum].stats[STAT_H2_CNT_TORCH+itemnum]; + num = pv->stats[STAT_H2_CNT_TORCH+itemnum]; if(num > 0) { if (num >= 10) @@ -1893,7 +1892,7 @@ void Sbar_Hexen2DrawItem(int pnum, int x, int y, int itemnum) } } -void Sbar_Hexen2DrawInventory(int pnum) +static void Sbar_Hexen2DrawInventory(playerview_t *pv) { int i; int x, y=-37; @@ -1903,50 +1902,50 @@ void Sbar_Hexen2DrawInventory(int pnum) /*always select an artifact that we actually have whether we are drawing the full bar or not.*/ for (i = 0; i < 15; i++) { - if (cl.playerview[pnum].stats[STAT_H2_CNT_TORCH+(i+sb_hexen2_cur_item[pnum])%15]) + if (pv->stats[STAT_H2_CNT_TORCH+(i+pv->sb_hexen2_cur_item)%15]) { - sb_hexen2_cur_item[pnum] = (sb_hexen2_cur_item[pnum] + i)%15; + pv->sb_hexen2_cur_item = (pv->sb_hexen2_cur_item + i)%15; break; } } - if (sb_hexen2_item_time[pnum]+3 < realtime) + if (pv->sb_hexen2_item_time+3 < realtime) return; - for (i = sb_hexen2_cur_item[pnum]; i < 15; i++) - if (sb_hexen2_cur_item[pnum] == i || cl.playerview[pnum].stats[STAT_H2_CNT_TORCH+i] > 0) + for (i = pv->sb_hexen2_cur_item; i < 15; i++) + if (pv->sb_hexen2_cur_item == i || pv->stats[STAT_H2_CNT_TORCH+i] > 0) activeright++; - for (i = sb_hexen2_cur_item[pnum]-1; i >= 0; i--) - if (sb_hexen2_cur_item[pnum] == i || cl.playerview[pnum].stats[STAT_H2_CNT_TORCH+i] > 0) + for (i = pv->sb_hexen2_cur_item-1; i >= 0; i--) + if (pv->sb_hexen2_cur_item == i || pv->stats[STAT_H2_CNT_TORCH+i] > 0) activeleft++; if (activeleft > 3 + (activeright<=3?(4-activeright):0)) activeleft = 3 + (activeright<=3?(4-activeright):0); x=320/2-114 + (activeleft-1)*33; - for (i = sb_hexen2_cur_item[pnum]-1; x>=320/2-114; i--) + for (i = pv->sb_hexen2_cur_item-1; x>=320/2-114; i--) { - if (!cl.playerview[pnum].stats[STAT_H2_CNT_TORCH+i]) + if (!pv->stats[STAT_H2_CNT_TORCH+i]) continue; - if (i == sb_hexen2_cur_item[pnum]) + if (i == pv->sb_hexen2_cur_item) Sbar_DrawPic(x+9, y-12, 11, 11, R2D_SafeCachePic("gfx/artisel.lmp")); - Sbar_Hexen2DrawItem(pnum, x, y, i); + Sbar_Hexen2DrawItem(pv, x, y, i); x -= 33; } x=320/2-114 + activeleft*33; - for (i = sb_hexen2_cur_item[pnum]; i < 15 && x < 320/2-114+7*33; i++) + for (i = pv->sb_hexen2_cur_item; i < 15 && x < 320/2-114+7*33; i++) { - if (i != sb_hexen2_cur_item[pnum] && !cl.playerview[pnum].stats[STAT_H2_CNT_TORCH+i]) + if (i != pv->sb_hexen2_cur_item && !pv->stats[STAT_H2_CNT_TORCH+i]) continue; - if (i == sb_hexen2_cur_item[pnum]) + if (i == pv->sb_hexen2_cur_item) Sbar_DrawPic(x+9, y-12, 11, 11, R2D_SafeCachePic("gfx/artisel.lmp")); - Sbar_Hexen2DrawItem(pnum, x, y, i); + Sbar_Hexen2DrawItem(pv, x, y, i); x+=33; } } -void Sbar_Hexen2DrawExtra (int pnum) +static void Sbar_Hexen2DrawExtra (playerview_t *pv) { unsigned int i, slot; unsigned int pclass; @@ -1962,25 +1961,25 @@ void Sbar_Hexen2DrawExtra (int pnum) "Demoness" }; - if (sb_hexen2_infoplaque[pnum]) + if (pv->sb_hexen2_infoplaque) { int i; Con_Printf("Objectives:\n"); for (i = 0; i < 64; i++) { - if (cl.playerview[pnum].stats[STAT_H2_OBJECTIVE1 + i/32] & (1<<(i&31))) + if (pv->stats[STAT_H2_OBJECTIVE1 + i/32] & (1<<(i&31))) Con_Printf("%s\n", T_GetInfoString(i)); } - sb_hexen2_infoplaque[pnum] = false; + pv->sb_hexen2_infoplaque = false; } - if (!sb_hexen2_extra_info[pnum]) + if (!pv->sb_hexen2_extra_info) { sbar_rect.y -= 46-SBAR_HEIGHT; return; } - pclass = cl.players[cl.playernum[pnum]].h2playerclass; + pclass = cl.players[pv->playernum].h2playerclass; if (pclass >= sizeof(pclassname)/sizeof(pclassname[0])) pclass = sizeof(pclassname)/sizeof(pclassname[0]) - 1; @@ -1994,44 +1993,44 @@ void Sbar_Hexen2DrawExtra (int pnum) Sbar_DrawTinyString (11, 48, pclassname[pclass]); Sbar_DrawTinyString (11, 58, va("int")); - Sbar_DrawTinyString (33, 58, va("%02d", cl.playerview[pnum].stats[STAT_H2_INTELLIGENCE])); + Sbar_DrawTinyString (33, 58, va("%02d", pv->stats[STAT_H2_INTELLIGENCE])); Sbar_DrawTinyString (11, 64, va("wis")); - Sbar_DrawTinyString (33, 64, va("%02d", cl.playerview[pnum].stats[STAT_H2_WISDOM])); + Sbar_DrawTinyString (33, 64, va("%02d", pv->stats[STAT_H2_WISDOM])); Sbar_DrawTinyString (11, 70, va("dex")); - Sbar_DrawTinyString (33, 70, va("%02d", cl.playerview[pnum].stats[STAT_H2_DEXTERITY])); + Sbar_DrawTinyString (33, 70, va("%02d", pv->stats[STAT_H2_DEXTERITY])); Sbar_DrawTinyString (58, 58, va("str")); - Sbar_DrawTinyString (80, 58, va("%02d", cl.playerview[pnum].stats[STAT_H2_STRENGTH])); + Sbar_DrawTinyString (80, 58, va("%02d", pv->stats[STAT_H2_STRENGTH])); Sbar_DrawTinyString (58, 64, va("lvl")); - Sbar_DrawTinyString (80, 64, va("%02d", cl.playerview[pnum].stats[STAT_H2_LEVEL])); + Sbar_DrawTinyString (80, 64, va("%02d", pv->stats[STAT_H2_LEVEL])); Sbar_DrawTinyString (58, 70, va("exp")); - Sbar_DrawTinyString (80, 70, va("%06d", cl.playerview[pnum].stats[STAT_H2_EXPERIENCE])); + Sbar_DrawTinyString (80, 70, va("%06d", pv->stats[STAT_H2_EXPERIENCE])); Sbar_DrawTinyString (11, 79, va("abilities")); - if (cl.playerview[pnum].stats[STAT_H2_FLAGS] & (1<<22)) + if (pv->stats[STAT_H2_FLAGS] & (1<<22)) Sbar_DrawTinyString (8, 89, T_GetString(400 + 2*(pclass-1) + 0)); - if (cl.playerview[pnum].stats[STAT_H2_FLAGS] & (1<<23)) + if (pv->stats[STAT_H2_FLAGS] & (1<<23)) Sbar_DrawTinyString (8, 96, T_GetString(400 + 2*(pclass-1) + 1)); for (i = 0; i < 4; i++) { - if (cl.playerview[pnum].stats[STAT_H2_ARMOUR1+i] > 0) + if (pv->stats[STAT_H2_ARMOUR1+i] > 0) { Sbar_DrawPic (164+i*40, 115, 28, 19, R2D_SafeCachePic(va("gfx/armor%d.lmp", i+1))); - Sbar_DrawTinyString (168+i*40, 136, va("+%d", cl.playerview[pnum].stats[STAT_H2_ARMOUR1+i])); + Sbar_DrawTinyString (168+i*40, 136, va("+%d", pv->stats[STAT_H2_ARMOUR1+i])); } } for (i = 0; i < 4; i++) { - if (cl.playerview[pnum].stats[STAT_H2_FLIGHT_T+i] > 0) + if (pv->stats[STAT_H2_FLIGHT_T+i] > 0) { Sbar_DrawPic (ringpos[i], 119, 32, 22, R2D_SafeCachePic(va("gfx/ring_f.lmp"))); - val = cl.playerview[pnum].stats[STAT_H2_FLIGHT_T+i]; + val = pv->stats[STAT_H2_FLIGHT_T+i]; if (val > 100) val = 100; if (val < 0) @@ -2044,9 +2043,9 @@ void Sbar_Hexen2DrawExtra (int pnum) slot = 0; for (i = 0; i < 8; i++) { - if (cl.playerview[pnum].statsstr[STAT_H2_PUZZLE1+i]) + if (pv->statsstr[STAT_H2_PUZZLE1+i]) { - Sbar_DrawPic (194+(slot%4)*31, slot<4?51:82, 26, 26, R2D_SafeCachePic(va("gfx/puzzle/%s.lmp", cl.playerview[pnum].statsstr[STAT_H2_PUZZLE1+i]))); + Sbar_DrawPic (194+(slot%4)*31, slot<4?51:82, 26, 26, R2D_SafeCachePic(va("gfx/puzzle/%s.lmp", pv->statsstr[STAT_H2_PUZZLE1+i]))); slot++; } } @@ -2054,7 +2053,7 @@ void Sbar_Hexen2DrawExtra (int pnum) Sbar_DrawPic(134, 50, 49, 56, R2D_SafeCachePic(va("gfx/cport%d.lmp", pclass))); } -int Sbar_Hexen2ArmourValue(int pnum) +static int Sbar_Hexen2ArmourValue(playerview_t *pv) { int i; float ac = 0; @@ -2072,23 +2071,23 @@ int Sbar_Hexen2ArmourValue(int pnum) }; int classno; - classno = cl.players[cl.playernum[pnum]].h2playerclass; + classno = cl.players[pv->playernum].h2playerclass; if (classno >= 1 && classno <= 5) { classno--; for (i = 0; i < 4; i++) { - if (cl.playerview[pnum].stats[STAT_H2_ARMOUR1+i]) + if (pv->stats[STAT_H2_ARMOUR1+i]) { ac += acv[classno][i]; - ac += cl.playerview[pnum].stats[STAT_H2_ARMOUR1+i]/5.0; + ac += pv->stats[STAT_H2_ARMOUR1+i]/5.0; } } } return ac; } -void Sbar_Hexen2DrawBasic(int pnum) +static void Sbar_Hexen2DrawBasic(playerview_t *pv) { int chainpos; int val, maxval; @@ -2099,8 +2098,8 @@ void Sbar_Hexen2DrawBasic(int pnum) Sbar_DrawPic(269, -23, 51, 23, R2D_SafeCachePic("gfx/topbumpr.lmp")); //mana1 - maxval = cl.playerview[pnum].stats[STAT_H2_MAXMANA]; - val = cl.playerview[pnum].stats[STAT_H2_BLUEMANA]; + maxval = pv->stats[STAT_H2_MAXMANA]; + val = pv->stats[STAT_H2_BLUEMANA]; val = bound(0, val, maxval); Sbar_DrawTinyString(201, 22, va("%03d", val)); if(val) @@ -2110,8 +2109,8 @@ void Sbar_Hexen2DrawBasic(int pnum) } //mana2 - maxval = cl.playerview[pnum].stats[STAT_H2_MAXMANA]; - val = cl.playerview[pnum].stats[STAT_H2_GREENMANA]; + maxval = pv->stats[STAT_H2_MAXMANA]; + val = pv->stats[STAT_H2_GREENMANA]; val = bound(0, val, maxval); Sbar_DrawTinyString(243, 22, va("%03d", val)); if(val) @@ -2122,17 +2121,17 @@ void Sbar_Hexen2DrawBasic(int pnum) //health - val = cl.playerview[pnum].stats[STAT_HEALTH]; + val = pv->stats[STAT_HEALTH]; if (val < -99) val = -99; Sbar_Hexen2DrawNum(58, 14, val, 3); //armour - val = Sbar_Hexen2ArmourValue(pnum); + val = Sbar_Hexen2ArmourValue(pv); Sbar_Hexen2DrawNum(105, 14, val, 2); // SetChainPosition(cl.v.health, cl.v.max_health); - chainpos = (195.0f*cl.playerview[pnum].stats[STAT_HEALTH]) / cl.playerview[pnum].stats[STAT_H2_MAXHEALTH]; + chainpos = (195.0f*pv->stats[STAT_HEALTH]) / pv->stats[STAT_H2_MAXHEALTH]; if (chainpos < 0) chainpos = 0; Sbar_DrawPic(45+((int)chainpos&7), 38, 222, 5, R2D_SafeCachePic("gfx/hpchain.lmp")); @@ -2141,24 +2140,24 @@ void Sbar_Hexen2DrawBasic(int pnum) Sbar_DrawPic(267, 36, 10, 10, R2D_SafeCachePic("gfx/chnrcov.lmp")); - Sbar_Hexen2DrawItem(pnum, 144, 3, sb_hexen2_cur_item[pnum]); + Sbar_Hexen2DrawItem(pv, 144, 3, pv->sb_hexen2_cur_item); } -void Sbar_Hexen2DrawMinimal(int pnum) +static void Sbar_Hexen2DrawMinimal(playerview_t *pv) { int y; y = -16; Sbar_DrawPic(3, y, 31, 17, R2D_SafeCachePic("gfx/bmmana.lmp")); Sbar_DrawPic(3, y+18, 31, 17, R2D_SafeCachePic("gfx/gmmana.lmp")); - Sbar_DrawTinyString(10, y+6, va("%03d", cl.playerview[pnum].stats[STAT_H2_BLUEMANA])); - Sbar_DrawTinyString(10, y+18+6, va("%03d", cl.playerview[pnum].stats[STAT_H2_GREENMANA])); + Sbar_DrawTinyString(10, y+6, va("%03d", pv->stats[STAT_H2_BLUEMANA])); + Sbar_DrawTinyString(10, y+18+6, va("%03d", pv->stats[STAT_H2_GREENMANA])); - Sbar_Hexen2DrawNum(38, y+18, cl.playerview[pnum].stats[STAT_HEALTH], 3); + Sbar_Hexen2DrawNum(38, y+18, pv->stats[STAT_HEALTH], 3); } -static void Sbar_DrawTeamStatus(int pnum) +static void Sbar_DrawTeamStatus(playerview_t *pv) { int p; int y; @@ -2168,13 +2167,13 @@ static void Sbar_DrawTeamStatus(int pnum) return; y = -32; - track = Cam_TrackNum(pnum); + track = Cam_TrackNum(pv); if (track == -1 || !cl.spectator) - track = cl.playernum[pnum]; + track = pv->playernum; for (p = 0; p < MAX_CLIENTS; p++) { - if (cl.playernum[pnum] == p) //self is not shown + if (pv->playernum == p) //self is not shown continue; if (track == p) //nor is the person you are tracking continue; @@ -2323,17 +2322,14 @@ static void Sbar_Voice(int y) Sbar_Draw =============== */ -void Sbar_Draw (void) +void Sbar_Draw (playerview_t *pv) { qboolean headsup; char st[512]; - int pnum; int sbarwidth; qboolean minidmoverlay; extern cvar_t scr_centersbar; - - headsup = !(cl_sbar.value || (scr_viewsize.value<100&&cl.splitclients==1)); if ((sb_updates >= vid.numpages) && !headsup) return; @@ -2348,7 +2344,7 @@ void Sbar_Draw (void) #ifdef Q2CLIENT if (cls.protocol == CP_QUAKE2) { - SCR_VRectForPlayer(&sbar_rect, 0); + sbar_rect = r_refdef.grect; R2D_ImageColours(1, 1, 1, 1); if (*cl.q2statusbar) Sbar_ExecuteLayoutString(cl.q2statusbar); @@ -2365,154 +2361,139 @@ void Sbar_Draw (void) R2D_ImageColours(1, 1, 1, 1); - for (pnum = 0; pnum < cl.splitclients; pnum++) + minidmoverlay = cl.deathmatch; + sbar_rect = r_refdef.grect; + + sbarwidth = 320; + if (minidmoverlay && r_refdef.grect.width >= 640 && cl.teamplay) + sbarwidth += 320; + else if (minidmoverlay && r_refdef.grect.width >= 512) + sbarwidth += 192; + else + minidmoverlay = 0; + + if (scr_centersbar.ival) { - minidmoverlay = cl.deathmatch; - if (cl.splitclients>1 || scr_chatmode) + int ofs = (sbar_rect.width - sbarwidth)/2; + sbar_rect.x += ofs; + sbar_rect.width -= ofs; + } + + sb_updates++; + + + if (sbar_hexen2) + { + //hexen2 hud + if (sb_lines > 24 || pv->sb_hexen2_extra_info) { - SCR_VRectForPlayer(&sbar_rect, pnum); - } - else - { //single player sbar takes full screen - sbar_rect.width = vid.width; - sbar_rect.height = vid.height; - sbar_rect.x = 0; - sbar_rect.y = 0; + Sbar_Hexen2DrawExtra(pv); + Sbar_Hexen2DrawBasic(pv); } + else if (sb_lines > 0) + Sbar_Hexen2DrawMinimal(pv); + Sbar_Hexen2DrawInventory(pv); - sbarwidth = 320; - if (minidmoverlay && vid.width >= 640 && cl.teamplay) - sbarwidth += 320; - else if (minidmoverlay && vid.width >= 512) - sbarwidth += 192; - else - minidmoverlay = 0; - - if (scr_centersbar.ival) + if (cl.deathmatch) + Sbar_MiniDeathmatchOverlay (pv); + } + else if (sbarfailed) //files failed to load. + { + //fallback hud + if (pv->stats[STAT_HEALTH] > 0) //when dead, show nothing { - int ofs = (sbar_rect.width - sbarwidth)/2; - sbar_rect.x += ofs; - sbar_rect.width -= ofs; - } - - sb_updates++; - - - if (sbar_hexen2) - { - //hexen2 hud - if (sb_lines > 24 || sb_hexen2_extra_info[pnum]) - { - Sbar_Hexen2DrawExtra(pnum); - Sbar_Hexen2DrawBasic(pnum); - } - else if (sb_lines > 0) - Sbar_Hexen2DrawMinimal(pnum); - Sbar_Hexen2DrawInventory(pnum); - - if (cl.deathmatch) - Sbar_MiniDeathmatchOverlay (pnum); - continue; - } - else if (sbarfailed) //files failed to load. - { - //fallback hud - if (cl.playerview[pnum].stats[STAT_HEALTH] <= 0) //when dead, show nothing - continue; - // if (scr_viewsize.value != 120) // Cvar_Set(&scr_viewsize, "120"); - Sbar_DrawString (0, -8, va("Health: %i", cl.playerview[pnum].stats[STAT_HEALTH])); - Sbar_DrawString (0, -16, va(" Armor: %i", cl.playerview[pnum].stats[STAT_ARMOR])); + Sbar_DrawString (0, -8, va("Health: %i", pv->stats[STAT_HEALTH])); + Sbar_DrawString (0, -16, va(" Armor: %i", pv->stats[STAT_ARMOR])); Sbar_Voice(-24); - continue; } - else + } + else + { + //standard quake(world) hud. + // top line + if (sb_lines > 24) { - //standard quake(world) hud. - // top line - if (sb_lines > 24) - { - if (!cl.spectator || autocam[pnum] == CAM_TRACK) - Sbar_DrawInventory (pnum); - if ((!headsup || sbar_rect.width<512) && cl.deathmatch) - Sbar_DrawFrags (); - } + if (!cl.spectator || pv->cam_auto == CAM_TRACK) + Sbar_DrawInventory (pv); + if ((!headsup || sbar_rect.width<512) && cl.deathmatch) + Sbar_DrawFrags (pv); + } - // main area - if (sb_lines > 0) + // main area + if (sb_lines > 0) + { + if (cl.spectator) { - if (cl.spectator) + if (pv->cam_auto != CAM_TRACK) { - if (autocam[pnum] != CAM_TRACK) - { - Sbar_DrawPic (0, 0, 320, 24, sb_scorebar); - Sbar_DrawString (160-7*8,4, "SPECTATOR MODE"); - Sbar_DrawString(160-14*8+4, 12, "Press [ATTACK] for AutoCamera"); - } - else - { - if (sb_showscores || sb_showteamscores || cl.playerview[pnum].stats[STAT_HEALTH] <= 0) - Sbar_SoloScoreboard (); - // else if (cls.gamemode != GAME_DEATHMATCH) - // Sbar_CoopScoreboard (); - else - Sbar_DrawNormal (pnum); - - if (hud_tracking_show.ival) - { - Q_snprintfz(st, sizeof(st), "Tracking %-.64s", - cl.players[spec_track[pnum]].name); - Sbar_DrawString(0, -8, st); - } - } - } - else if (sb_showscores || sb_showteamscores || (cl.playerview[pnum].stats[STAT_HEALTH] <= 0 && cl.splitclients == 1)) - { - if (!pnum) - { - if (!cls.deathmatch) - Sbar_CoopScoreboard (); - else - Sbar_SoloScoreboard (); - } + Sbar_DrawPic (0, 0, 320, 24, sb_scorebar); + Sbar_DrawString (160-7*8,4, "SPECTATOR MODE"); + Sbar_DrawString(160-14*8+4, 12, "Press [ATTACK] for AutoCamera"); } else - Sbar_DrawNormal (pnum); + { + if (sb_showscores || sb_showteamscores || pv->stats[STAT_HEALTH] <= 0) + Sbar_SoloScoreboard (); +// else if (cls.gamemode != GAME_DEATHMATCH) +// Sbar_CoopScoreboard (); + else + Sbar_DrawNormal (pv); + + if (hud_tracking_show.ival) + { + Q_snprintfz(st, sizeof(st), "Tracking %-.64s", + cl.players[pv->cam_spec_track].name); + Sbar_DrawString(0, -8, st); + } + } + } + else if (sb_showscores || sb_showteamscores || (pv->stats[STAT_HEALTH] <= 0 && cl.splitclients == 1)) + { + if (pv == cl.playerview) + { + if (!cls.deathmatch) + Sbar_CoopScoreboard (); + else + Sbar_SoloScoreboard (); + } } - - if (sb_lines > 24) - Sbar_Voice(-32); - else if (sb_lines > 0) - Sbar_Voice(-8); else - Sbar_Voice(16); - - if (minidmoverlay) - Sbar_MiniDeathmatchOverlay (pnum); - - if (sb_lines > 0) - Sbar_DrawTeamStatus(pnum); - R2D_ImageColours (1, 1, 1, 1); + Sbar_DrawNormal (pv); } + + if (sb_lines > 24) + Sbar_Voice(-32); + else if (sb_lines > 0) + Sbar_Voice(-8); + else + Sbar_Voice(16); + + if (minidmoverlay) + Sbar_MiniDeathmatchOverlay (pv); + + if (sb_lines > 0) + Sbar_DrawTeamStatus(pv); + R2D_ImageColours (1, 1, 1, 1); } if (cl_sbar.value == 1 || scr_viewsize.value<100) { - if (cl.splitclients==1 && sbar_rect.x>0) + if (sbar_rect.x>r_refdef.grect.x) { // left - R2D_TileClear (0, sbar_rect.height - sb_lines, sbar_rect.x, sb_lines); + R2D_TileClear (r_refdef.grect.x, r_refdef.grect.y+sbar_rect.height - sb_lines, sbar_rect.x - r_refdef.grect.x, sb_lines); } - if (sbar_rect.x + 320 <= sbar_rect.width && !headsup) - R2D_TileClear (sbar_rect.x + 320, sbar_rect.height - sb_lines, sbar_rect.width - (320), sb_lines); + if (sbar_rect.x + 320 <= r_refdef.grect.x + sbar_rect.width && !headsup) + R2D_TileClear (sbar_rect.x + 320, r_refdef.grect.y+sbar_rect.height - sb_lines, sbar_rect.width - (320), sb_lines); } { extern int scr_chatmode; if (scr_chatmode) - Sbar_ChatModeOverlay(); + Sbar_ChatModeOverlay(pv); } } @@ -2553,7 +2534,7 @@ void Sbar_IntermissionNumber (int x, int y, int num, int digits, int color, qboo #define COL_TEAM_LOWAVGHIGH COLUMN("low/avg/high", 12*8, {sprintf (num, "%3i/%3i/%3i", plow, pavg, phigh); Draw_FunString ( x, y, num); }) #define COL_TEAM_TEAM COLUMN("team", 4*8, {Draw_FunStringWidth ( x, y, tm->team, 4*8); \ - if (!strncmp(cl.players[cl.playernum[0]].team, tm->team, 16))\ + if (!strncmp(cl.players[pv->playernum].team, tm->team, 16))\ {\ Draw_FunString ( x - 1*8, y, "^Ue010");\ Draw_FunString ( x + 4*8, y, "^Ue011");\ @@ -2580,6 +2561,7 @@ void Sbar_TeamOverlay (void) char num[12]; team_t *tm; int plow, phigh, pavg; + playerview_t *pv = r_refdef.playerview; // request new ping times every two second if (!cl.teamplay) @@ -2623,7 +2605,7 @@ void Sbar_TeamOverlay (void) #undef COLUMN // sort the teams - Sbar_SortTeams(); + Sbar_SortTeams(pv); // draw the text for (i=0 ; i < scoreboardteams && y <= vid.height-10 ; i++) @@ -2739,8 +2721,8 @@ ping time frags name Font_BeginString(font_conchar, x+24, y, &cx, &cy); \ Font_DrawChar(cx, cy, num[2] | 0xe000 | CON_WHITEMASK); \ \ - if ((cl.spectator && k == spec_track[0]) || \ - (!cl.spectator && k == cl.playernum[0])) \ + if ((cl.spectator && k == pv->cam_spec_track) ||\ + (!cl.spectator && k == pv->playernum)) \ { \ Font_BeginString(font_conchar, x, y, &cx, &cy); \ Font_DrawChar(cx, cy, 16 | 0xe000 | CON_WHITEMASK); \ @@ -2790,6 +2772,7 @@ void Sbar_DeathmatchOverlay (int start) int skip = 10; int showcolumns; int startx, rank_width; + playerview_t *pv = r_refdef.playerview; if (largegame) skip = 8; @@ -3007,7 +2990,7 @@ if (showcolumns & (1<=0) - Q_strncpyz (team, cl.players[Cam_TrackNum(0)].team, sizeof(team)); - else if (cl.playernum[0]>=0 && cl.playernum[0]=0) + Q_strncpyz (team, cl.players[Cam_TrackNum(pv)].team, sizeof(team)); + else if (pv->playernum>=0 && pv->playernumplayernum].team, sizeof(team)); else *team = '\0'; @@ -3069,12 +3052,12 @@ void Sbar_ChatModeOverlay(void) Sbar_FillPC ( x, y, 8*4, 4, top); Sbar_FillPC ( x, y+4, 8*4, 4, bottom); /* - if (cl.spectator && k == Cam_TrackNum(0)) + if (cl.spectator && k == Cam_TrackNum(pv)) { Draw_Character ( x, y, 16); Draw_Character ( x+8*3, y, 17); } - else if (!cl.spectator && k == cl.playernum[0]) + else if (!cl.spectator && k == pv->cl.playernum) { Draw_Character ( x, y, 16); Draw_Character ( x+8*3, y, 17); @@ -3110,7 +3093,7 @@ frags team name displayed to right of status bar if there's room ================== */ -static void Sbar_MiniDeathmatchOverlay (int pnum) +static void Sbar_MiniDeathmatchOverlay (playerview_t *pv) { int i, k; int top, bottom; @@ -3124,7 +3107,7 @@ static void Sbar_MiniDeathmatchOverlay (int pnum) // scores Sbar_SortFrags (false, false); if (sbar_rect.width >= 640) - Sbar_SortTeams(); + Sbar_SortTeams(pv); if (!scoreboardlines) return; // no one there? @@ -3137,7 +3120,7 @@ static void Sbar_MiniDeathmatchOverlay (int pnum) // find us for (i=0 ; i < scoreboardlines; i++) - if (fragsort[i] == cl.playernum[pnum]) + if (fragsort[i] == pv->playernum) break; if (i == scoreboardlines) // we're not there, we are probably a spectator, just display top @@ -3177,8 +3160,8 @@ static void Sbar_MiniDeathmatchOverlay (int pnum) Font_BeginString(font_conchar, x+24, y, &px, &py); Font_DrawChar ( px, py, num[2] | 0xe000 | CON_WHITEMASK); - if ((cl.spectator && k == spec_track[pnum]) || - (!cl.spectator && k == cl.playernum[pnum])) + if ((cl.spectator && k == pv->cam_spec_track) || + (!cl.spectator && k == pv->playernum)) { Font_BeginString(font_conchar, x, y, &px, &py); Font_DrawChar ( px, py, 16 | 0xe000 | CON_WHITEMASK); @@ -3222,7 +3205,7 @@ static void Sbar_MiniDeathmatchOverlay (int pnum) sprintf (num, "%5i", tm->frags); Draw_FunString(x + 40, y, num); - if (!strncmp(cl.players[cl.playernum[pnum]].team, tm->team, 16)) + if (!strncmp(cl.players[pv->playernum].team, tm->team, 16)) { Font_BeginString(font_conchar, x-8, y, &px, &py); Font_DrawChar(px, py, 16|0xe000|CON_WHITEMASK); diff --git a/engine/client/sbar.h b/engine/client/sbar.h index 8125a7791..bc0a08449 100644 --- a/engine/client/sbar.h +++ b/engine/client/sbar.h @@ -36,7 +36,7 @@ void Sbar_Changed (void); // call whenever any of the client stats represented on the sbar changes qboolean Sbar_ShouldDraw(void); -void Sbar_Draw (void); +void Sbar_Draw (playerview_t *pv); //uses the current r_refdef.grect void Sbar_DrawScoreboard (void); // called every frame by screen diff --git a/engine/client/screen.h b/engine/client/screen.h index ca40e458d..7e522aeb2 100644 --- a/engine/client/screen.h +++ b/engine/client/screen.h @@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // screen.h +typedef struct playerview_s playerview_t; extern float scr_con_current; extern float scr_conlines; // lines of console to display @@ -53,7 +54,7 @@ void SCR_ImageName (char *mapname); //this stuff is internal to the screen systems. void RSpeedShow(void); -void SCR_CrosshairPosition(int pnum, int *x, int *y); +void SCR_CrosshairPosition(playerview_t *pview, int *x, int *y); void SCR_DrawLoading (void); void SCR_CalcRefdef (void); void SCR_TileClear (void); @@ -113,6 +114,7 @@ void Font_InvalidateColour(void); /*these three functions deal with formatted blocks of text (including tabs and new lines)*/ int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int maxlines, conchar_t **starts, conchar_t **ends); int Font_LineWidth(conchar_t *start, conchar_t *end); +float Font_LineScaleWidth(conchar_t *start, conchar_t *end); void Font_LineDraw(int x, int y, conchar_t *start, conchar_t *end); extern struct font_s *font_conchar; extern struct font_s *font_tiny; diff --git a/engine/client/skin.c b/engine/client/skin.c index bff4faffb..0cb4f3528 100644 --- a/engine/client/skin.c +++ b/engine/client/skin.c @@ -56,12 +56,12 @@ char *Skin_FindName (player_info_t *sc) Q_strncpyz(name, baseskin.string, sizeof(name)); } - if (cl.spectator && (tracknum = Cam_TrackNum(0)) != -1) + if (cl.spectator && (tracknum = Cam_TrackNum(&cl.playerview[0])) != -1) skinforcing_team = cl.players[tracknum].team; else if (cl.spectator) skinforcing_team = "spec"; else - skinforcing_team = cl.players[cl.playernum[0]].team; + skinforcing_team = cl.players[cl.playerview[0].playernum].team; //Don't force skins in splitscreen (it's probable that the new skin would be wrong). //Don't force skins in TF (where skins are forced on a class basis by the mod). diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index 1408443bd..a27f5e35a 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -67,7 +67,7 @@ cvar_t volume = CVARFD( "volume", "0.7", CVAR_ARCHIVE, "Main volume level for all engine sound."); cvar_t nosound = CVARFD( "nosound", "0", CVAR_ARCHIVE, - "Disable all sound from the engine."); + "Disable all sound from the engine. Cannot be overriden by configs or anything if set via the -nosound commandline argument."); cvar_t precache = CVARAF( "s_precache", "1", "precache", 0); cvar_t loadas8bit = CVARAFD( "s_loadas8bit", "0", @@ -81,8 +81,8 @@ cvar_t snd_noextraupdate = CVARAF( "s_noextraupdate", "0", "snd_noextraupdate", 0); cvar_t snd_show = CVARAF( "s_show", "0", "snd_show", 0); -cvar_t snd_khz = CVARAFD( "s_khz", "44", - "snd_khz", CVAR_ARCHIVE, "Sound speed, in kilohertz. Common values are 11, 22, 44, 48."); +cvar_t snd_khz = CVARAFD( "s_khz", "48", + "snd_khz", CVAR_ARCHIVE, "Sound speed, in kilohertz. Common values are 11, 22, 44, 48. Values above 1000 are explicitly in hertz."); cvar_t snd_inactive = CVARAFD( "s_inactive", "0", "snd_inactive", 0, "Play sound while application is inactive (ex. tabbed out). Needs a snd_restart if changed." @@ -664,7 +664,7 @@ void S_Voip_Parse(void) //if testing, don't get confused if the server is echoing voice too! if (cl_voip_test.ival) - if (sender == cl.playernum[0]) + if (sender == cl.playerview[0].playernum) return; S_Voip_Decode(sender, codec, gen, seq, bytes, data); @@ -976,11 +976,11 @@ void S_Voip_Transmit(unsigned char clc, sizebuf_t *buf) } if (cl_voip_test.ival) - S_Voip_Decode(cl.playernum[0], s_voip.enccodec, s_voip.generation & 0x0f, initseq, outpos, outbuf); + S_Voip_Decode(cl.playerview[0].playernum, s_voip.enccodec, s_voip.generation & 0x0f, initseq, outpos, outbuf); //update our own lastspoke, so queries shows that we're speaking when we're speaking in a generic way, even if we can't hear ourselves. //but don't update general lastspoke, so ducking applies only when others speak. use capturingvol for yourself. they're more explicit that way. - s_voip.lastspoke[cl.playernum[0]] = realtime + 0.5; + s_voip.lastspoke[cl.playerview[0].playernum] = realtime + 0.5; } /*remove sent data*/ @@ -1794,7 +1794,7 @@ channel_t *SND_PickChannel(soundcardinfo_t *sc, int entnum, int entchannel) } // don't let monster sounds override player sounds - if (sc->channel[ch_idx].entnum == cl.playernum[0]+1 && entnum != cl.playernum[0]+1 && sc->channel[ch_idx].sfx) + if (sc->channel[ch_idx].entnum == cl.playerview[0].playernum+1 && entnum != cl.playerview[0].playernum+1 && sc->channel[ch_idx].sfx) continue; if (!sc->channel[ch_idx].sfx) @@ -1843,7 +1843,7 @@ void SND_Spatialize(soundcardinfo_t *sc, channel_t *ch) } return; } - if (ch->entnum == -1 || ch->entnum == cl.playernum[0]+1) + if (ch->entnum == -1 || ch->entnum == cl.playerview[0].playernum+1) { v = ch->master_vol * (ruleset_allow_localvolume.value ? snd_playersoundvolume.value : 1) * volume.value * voicevolumemod; v = bound(0, v, 255); @@ -2578,7 +2578,7 @@ void S_Play(void) else Q_strncpyz(name, Cmd_Argv(i), sizeof(name)); sfx = S_PrecacheSound(name); - S_StartSound(cl.playernum[0]+1, -1, sfx, vec3_origin, 1.0, 0.0, 0, 0); + S_StartSound(cl.playerview[0].playernum+1, -1, sfx, vec3_origin, 1.0, 0.0, 0, 0); i++; } } @@ -2602,7 +2602,7 @@ void S_PlayVol(void) Q_strncpy(name, Cmd_Argv(i), sizeof(name)); sfx = S_PrecacheSound(name); vol = Q_atof(Cmd_Argv(i+1)); - S_StartSound(cl.playernum[0]+1, -1, sfx, vec3_origin, vol, 0.0, 0, 0); + S_StartSound(cl.playerview[0].playernum+1, -1, sfx, vec3_origin, vol, 0.0, 0, 0); i+=2; } } diff --git a/engine/client/sys_axfte.cpp b/engine/client/sys_axfte.cpp index 6137d3355..00bc52fb9 100644 --- a/engine/client/sys_axfte.cpp +++ b/engine/client/sys_axfte.cpp @@ -799,6 +799,7 @@ struct {"Software\\MozillaPlugins\\@fteqw.com/FTE\\Vendor", DISTRIBUTIONLONG}, {"Software\\MozillaPlugins\\@fteqw.com/FTE\\Version", "***VERSION***"}, {"Software\\MozillaPlugins\\@fteqw.com/FTE\\MimeTypes\\application/x-fteplugin\\Description", "FTE Game Engine Plugin"}, + {"Software\\MozillaPlugins\\@fteqw.com/FTE\\MimeTypes\\application/x-ftemanifest\\Description", "FTE Game File Manifest Listing"}, {"Software\\MozillaPlugins\\@fteqw.com/FTE\\MimeTypes\\application/x-qtv\\Description", "QuakeTV Stream Information File"}, {"Software\\MozillaPlugins\\@fteqw.com/FTE\\MimeTypes\\application/x-qtv\\Suffixes", "qtv"}, {"Software\\MozillaPlugins\\@fteqw.com/FTE\\Suffixes\\qtv", ""}, diff --git a/engine/client/sys_droid.c b/engine/client/sys_droid.c index 50e7c1242..4d370da9d 100644 --- a/engine/client/sys_droid.c +++ b/engine/client/sys_droid.c @@ -158,9 +158,19 @@ JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_newglcontext(JNIEnv *env, j //fixme: wipe image handles } +//called when the user tries to use us to open one of our file types +JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_openfile(JNIEnv *env, jobject obj, + jstring openfile) +{ + const char *fname = (*env)->GetStringUTFChars(env, openfile, NULL); + Host_RunFile(fname); + Host_Frame + (*env)->ReleaseStringUTFChars(env, openfile, fname); +} + //called for init or resizes JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_init(JNIEnv *env, jobject obj, - jint width, jint height, jint glesversion, jstring japkpath, jstring jusrpath) + jint width, jint height, jint glesversion, jstring japkpath, jstring jusrpath) { const char *tmp; diff --git a/engine/client/sys_plugfte.c b/engine/client/sys_plugfte.c index 17289b0d2..ded56fff5 100644 --- a/engine/client/sys_plugfte.c +++ b/engine/client/sys_plugfte.c @@ -1075,6 +1075,8 @@ int Plug_GenCommandline(struct context *ctx, char **argv, int maxargs) ADDRARG("-addbasegame"); ADDCARG(tok); } + //make sure we always use the home directory. because its better than using some random browser-defined working directory. + ADDRARG("-usehome"); if (ctx->datadownload) { diff --git a/engine/client/sys_win.c b/engine/client/sys_win.c index 70869ef0b..da3919596 100644 --- a/engine/client/sys_win.c +++ b/engine/client/sys_win.c @@ -36,9 +36,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #endif -#include "fs.h" - -#ifdef GLQUAKE +#if defined(GLQUAKE) && defined(DEBUG) #define PRINTGLARRAYS #endif @@ -61,8 +59,6 @@ unsigned int sys_parenttop; unsigned int sys_parentwidth; //valid if sys_parentwindow is set unsigned int sys_parentheight; -extern int fs_switchgame; - //used to do special things with awkward windows versions. int qwinvermaj; int qwinvermin; @@ -265,188 +261,7 @@ typedef BOOL (WINAPI *MINIDUMPWRITEDUMP) ( PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, PMINIDUMP_CALLBACK_INFORMATION CallbackParam ); - -#ifdef PRINTGLARRAYS -#include "glquake.h" -#define GL_VERTEX_ARRAY_BINDING 0x85B5 -#define GL_ARRAY_BUFFER 0x8892 -#define GL_ELEMENT_ARRAY_BUFFER 0x8893 -#define GL_ARRAY_BUFFER_BINDING 0x8894 -#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 -#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 -#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 -#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 -#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A -#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 -#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 -#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 -#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 -#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A -#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 -#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F -#define GL_CURRENT_PROGRAM 0x8B8D - -char *DecodeGLEnum(GLenum num) -{ - switch(num) - { - case GL_CW: return "GL_CW"; - case GL_CCW: return "GL_CCW"; - case GL_NEVER: return "GL_NEVER"; - case GL_LESS: return "GL_LESS"; - case GL_EQUAL: return "GL_EQUAL"; - case GL_LEQUAL: return "GL_LEQUAL"; - case GL_GREATER: return "GL_GREATER"; - case GL_NOTEQUAL: return "GL_NOTEQUAL"; - case GL_GEQUAL: return "GL_GEQUAL"; - case GL_ALWAYS: return "GL_ALWAYS"; - case GL_FRONT: return "GL_FRONT"; - case GL_BACK: return "GL_BACK"; - case GL_FRONT_AND_BACK: return "GL_FRONT_AND_BACK"; - case GL_COMBINE_ARB: return "GL_COMBINE"; - case GL_MODULATE: return "GL_MODULATE"; - case GL_REPLACE: return "GL_REPLACE"; - case GL_ZERO: return "GL_ZERO"; - case GL_ONE: return "GL_ONE"; - case GL_SRC_COLOR: return "GL_SRC_COLOR"; - case GL_ONE_MINUS_SRC_COLOR: return "GL_ONE_MINUS_SRC_COLOR"; - case GL_SRC_ALPHA: return "GL_SRC_ALPHA"; - case GL_ONE_MINUS_SRC_ALPHA: return "GL_ONE_MINUS_SRC_ALPHA"; - case GL_DST_ALPHA: return "GL_DST_ALPHA"; - case GL_ONE_MINUS_DST_ALPHA: return "GL_ONE_MINUS_DST_ALPHA"; - case GL_DST_COLOR: return "GL_DST_COLOR"; - case GL_ONE_MINUS_DST_COLOR: return "GL_ONE_MINUS_DST_COLOR"; - case GL_SRC_ALPHA_SATURATE: return "GL_SRC_ALPHA_SATURATE"; - default: return va("0x%x", num); - } -} - -void DumpGLState(void) -{ - int rval; - void *ptr; - int i; - GLint glint; - GLint glint4[4]; - void (APIENTRY *qglGetVertexAttribiv) (GLuint index, GLenum pname, GLint* params); - void (APIENTRY *qglGetVertexAttribPointerv) (GLuint index, GLenum pname, GLvoid** pointer); - qglGetVertexAttribiv = (void*)wglGetProcAddress("glGetVertexAttribiv"); - qglGetVertexAttribPointerv = (void*)wglGetProcAddress("glGetVertexAttribPointerv"); -#pragma comment(lib,"opengl32.lib") - - if (qglGetVertexAttribiv) - { - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &rval); - Sys_Printf("VERTEX_ARRAY_BINDING: %i\n", rval); - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &rval); - Sys_Printf("GL_ARRAY_BUFFER_BINDING: %i\n", rval); - if (glIsEnabled(GL_COLOR_ARRAY)) - { - glGetIntegerv(GL_COLOR_ARRAY_BUFFER_BINDING, &rval); - glGetPointerv(GL_COLOR_ARRAY_POINTER, &ptr); - Sys_Printf("GL_COLOR_ARRAY: %s %i:%p\n", glIsEnabled(GL_COLOR_ARRAY)?"en":"dis", rval, ptr); - } -// if (glIsEnabled(GL_FOG_COORDINATE_ARRAY_EXT)) -// { -// glGetPointerv(GL_FOG_COORD_ARRAY_POINTER, &ptr); -// Sys_Printf("GL_FOG_COORDINATE_ARRAY_EXT: %i (%lx)\n", (int) glIsEnabled(GL_FOG_COORDINATE_ARRAY_EXT), (int) ptr); -// } -// if (glIsEnabled(GL_INDEX_ARRAY)) - { - glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &rval); - glGetPointerv(GL_INDEX_ARRAY_POINTER, &ptr); - Sys_Printf("GL_INDEX_ARRAY: %s %i:%p\n", glIsEnabled(GL_INDEX_ARRAY)?"en":"dis", rval, ptr); - } - if (glIsEnabled(GL_NORMAL_ARRAY)) - { - glGetIntegerv(GL_NORMAL_ARRAY_BUFFER_BINDING, &rval); - glGetPointerv(GL_NORMAL_ARRAY_POINTER, &ptr); - Sys_Printf("GL_NORMAL_ARRAY: %s %i:%p\n", glIsEnabled(GL_NORMAL_ARRAY)?"en":"dis", rval, ptr); - } - // glGetPointerv(GL_SECONDARY_COLOR_ARRAY_POINTER, &ptr); - // Sys_Printf("GL_SECONDARY_COLOR_ARRAY: %i (%lx)\n", (int) glIsEnabled(GL_SECONDARY_COLOR_ARRAY), (int) ptr); - for (i = 0; i < 4; i++) - { - qglClientActiveTextureARB(mtexid0 + i); - if (glIsEnabled(GL_TEXTURE_COORD_ARRAY)) - { - glGetIntegerv(GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, &rval); - glGetPointerv(GL_TEXTURE_COORD_ARRAY_POINTER, &ptr); - Sys_Printf("GL_TEXTURE_COORD_ARRAY %i: %s %i:%p\n", i, glIsEnabled(GL_TEXTURE_COORD_ARRAY)?"en":"dis", rval, ptr); - } - } - if (glIsEnabled(GL_VERTEX_ARRAY)) - { - glGetIntegerv(GL_VERTEX_ARRAY_BUFFER_BINDING, &rval); - glGetPointerv(GL_VERTEX_ARRAY_POINTER, &ptr); - Sys_Printf("GL_VERTEX_ARRAY: %s %i:%p\n", glIsEnabled(GL_VERTEX_ARRAY)?"en":"dis", rval, ptr); - } - - for (i = 0; i < 16; i++) - { - int en, bo, as, st, ty, no; - - qglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &en); - if (!en) - continue; - qglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &bo); - qglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_SIZE, &as); - qglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &st); - qglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_TYPE, &ty); - qglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &no); - qglGetVertexAttribPointerv(i, GL_VERTEX_ATTRIB_ARRAY_POINTER, &ptr); - - Sys_Printf("attrib%i: %s as:%i st:%i ty:%0x %s%i:%p\n", i, en?"en":"dis", as, st,ty,no?"norm ":"", bo, ptr); - } - - glGetIntegerv(GL_CURRENT_PROGRAM, &glint); - Sys_Printf("GL_CURRENT_PROGRAM: %i\n", glint); - - glGetIntegerv(GL_BLEND, &glint); - Sys_Printf("GL_BLEND: %i\n", glint); - glGetIntegerv(GL_BLEND_SRC, &glint); - Sys_Printf("GL_BLEND_SRC: %i\n", DecodeGLEnum(glint)); - glGetIntegerv(GL_BLEND_DST, &glint); - Sys_Printf("GL_BLEND_DST: %i\n", DecodeGLEnum(glint)); - - glGetIntegerv(GL_DEPTH_WRITEMASK, &glint); - Sys_Printf("GL_DEPTH_WRITEMASK: %i\n", glint); - glGetIntegerv(GL_DEPTH_TEST, &glint); - Sys_Printf("GL_DEPTH_TEST: %i\n", glint); - glGetIntegerv(GL_DEPTH_FUNC, &glint); - Sys_Printf("GL_DEPTH_FUNC: %s\n", DecodeGLEnum(glint)); - glGetIntegerv(GL_CULL_FACE, &glint); - Sys_Printf("GL_CULL_FACE: %i\n", glint); - glGetIntegerv(GL_CULL_FACE_MODE, &glint); - Sys_Printf("GL_CULL_FACE_MODE: %s\n", DecodeGLEnum(glint)); - glGetIntegerv(GL_FRONT_FACE, &glint); - Sys_Printf("GL_FRONT_FACE: %s\n", DecodeGLEnum(glint)); - glGetIntegerv(GL_SCISSOR_TEST, &glint); - Sys_Printf("GL_SCISSOR_TEST: %i\n", glint); - glGetIntegerv(GL_STENCIL_TEST, &glint); - Sys_Printf("GL_STENCIL_TEST: %i\n", glint); - glGetIntegerv(GL_COLOR_WRITEMASK, glint4); - Sys_Printf("GL_COLOR_WRITEMASK: %i %i %i %i\n", glint4[0], glint4[1], glint4[2], glint4[3]); - - GL_SelectTexture(0); - glGetIntegerv(GL_TEXTURE_2D, &glint); - Sys_Printf("GL_TEXTURE_2D: %i\n", glint); - glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &glint); - Sys_Printf("GL_TEXTURE_ENV_MODE: %s\n", DecodeGLEnum(glint)); - GL_SelectTexture(1); - glGetIntegerv(GL_TEXTURE_2D, &glint); - Sys_Printf("GL_TEXTURE_2D: %i\n", glint); - glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &glint); - Sys_Printf("GL_TEXTURE_ENV_MODE: %s\n", DecodeGLEnum(glint)); - GL_SelectTexture(2); - glGetIntegerv(GL_TEXTURE_2D, &glint); - Sys_Printf("GL_TEXTURE_2D: %i\n", glint); - glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &glint); - Sys_Printf("GL_TEXTURE_ENV_MODE: %s\n", DecodeGLEnum(glint)); - } -} -#endif - +void DumpGLState(void); DWORD CrashExceptionHandler (qboolean iswatchdog, DWORD exceptionCode, LPEXCEPTION_POINTERS exceptionInfo) { char dumpPath[1024]; @@ -1408,7 +1223,7 @@ char *Sys_ConsoleInput (void) { wchar_t wch = ch; WriteConsoleW(houtput, &wch, 1, &dummy, NULL); - len += 1; + len += utf8_encode(text+len, ch, sizeof(text)-1-len); } break; @@ -2455,109 +2270,6 @@ void VARGS Signal_Error_Handler(int i) #endif */ -qboolean Sys_RunFile(const char *fname, int nlen) -{ - char buffer[MAX_OSPATH]; - char *ext; - if (nlen >= MAX_OSPATH) - { - Con_Printf("Filename too long.\n"); - return true; - } - - memcpy(buffer, fname, nlen); - buffer[nlen] = 0; - fname = buffer; - ext = COM_FileExtension(fname); - if (!strncmp(fname, "qw:", 3)) - { - fname += 3; - if (!strncmp(fname, "//", 2)) - fname += 2; - ext = strchr(fname, '/'); //this also protects us against irc urls, etc. unsure if that's important right now. - if (ext) - *ext = 0; - Con_Printf("QW stream: \"%s\"\n", fname); - Cbuf_AddText(va("connect \"%s\"\n", fname), RESTRICT_LOCAL); - } - else if (!strcmp(ext, "qwd") || !strcmp(ext, "dem") || !strcmp(ext, "mvd")) - Cbuf_AddText(va("playdemo \"#%s\"\n", fname), RESTRICT_LOCAL); - else if (!strcmp(ext, "pak") || !strcmp(ext, "pk3") || !strcmp(ext, "pk4")) - Con_Printf("Unable to install paks/pk3s at this time\n"); - else if (!strcmp(ext, "bsp")) - { - char qname[MAX_QPATH]; - vfsfile_t *sf, *qf; - qboolean overwrite = false; - COM_StripExtension(COM_SkipPath(fname), qname, sizeof(qname)); - sf = VFSOS_Open(fname, "rb"); - qf = FS_OpenVFS(va("maps/%s.bsp", qname), "rb", FS_GAME); - if (qf) - { - if (VFS_GETLEN(sf) != VFS_GETLEN(qf)) - overwrite = true; - VFS_SEEK(sf, 0); - VFS_CLOSE(qf); - } - if (overwrite) - { - switch(MessageBox(mainwindow, va("Overwrite existing map: %s?", qname), "Install And Play", MB_YESNOCANCEL)) - { - case IDYES: - //overwrite it and load it up - overwrite = true; - break; - case IDNO: - //load up the old version - overwrite = false; - break; - default: - case IDCANCEL: - //quit or something - return false; - } - } - else if (!qf) - { - switch(MessageBox(mainwindow, va("Install new map: %s?", qname), "Install And Play", MB_OKCANCEL)) - { - case IDOK: - //overwrite it and load it up - overwrite = true; - break; - default: - case IDCANCEL: - //quit or something - return false; - } - } - - if (overwrite) - { - char buffer[8192]; - int len; - qf = FS_OpenVFS(va("maps/%s.bsp", qname), "wb", FS_GAMEONLY); - if (qf) - { - while(1) - { - len = VFS_READ(sf, buffer, sizeof(buffer)); - if (len <= 0) - break; - VFS_WRITE(qf, buffer, len); - } - VFS_CLOSE(qf); - } - } - VFS_CLOSE(sf); - - Cbuf_AddText(va("map \"%s\"\n", qname), RESTRICT_LOCAL); - } - else - Cbuf_AddText(va("qtvplay \"#%s\"\n", fname), RESTRICT_LOCAL); - return true; -} - int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // MSG msg; @@ -2830,7 +2542,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin if (qtvfile) { - if (!Sys_RunFile(qtvfile, strlen(qtvfile))) + if (!Host_RunFile(qtvfile, strlen(qtvfile), NULL)) { SetHookState(false); Host_Shutdown (); @@ -2892,20 +2604,6 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin Sys_Error("wut?"); #endif } - -#ifndef SERVERONLY - if (fs_switchgame != -1) - { - SetHookState(false); - - Host_Shutdown (); - - COM_InitArgv (parms.argc, parms.argv); - if (!Sys_Startup_CheckMem(&parms)) - return 0; - Host_Init (&parms); - } -#endif } } #ifdef CATCHCRASH diff --git a/engine/client/textedit.c b/engine/client/textedit.c index d73fa9857..5fab4abda 100644 --- a/engine/client/textedit.c +++ b/engine/client/textedit.c @@ -191,8 +191,11 @@ static void CloseEditor(void) if (key_dest == key_editor) key_dest = editor_oldkeydest; + if (key_dest == key_editor) + key_dest = key_game; editoractive = false; editprogfuncs = NULL; + cursorblock = NULL; if (!firstblock) return; @@ -686,6 +689,7 @@ void Editor_Key(int key, int unicode) if (editprogfuncs) editprogfuncs->AbortStack(editprogfuncs); CloseEditor(); + editormodal = false; break; case K_HOME: diff --git a/engine/client/valid.c b/engine/client/valid.c index 3334912c0..aae7e7b69 100644 --- a/engine/client/valid.c +++ b/engine/client/valid.c @@ -107,7 +107,7 @@ static void Validation_Version(void) { signed_buffer_t *resp; - resp = Security_Generate_Crc(cl.playernum[0], cl.players[cl.playernum[0]].userinfo, cl.serverinfo); + resp = Security_Generate_Crc(cl.playerview[0].playernum, cl.players[cl.playerview[0].playernum].userinfo, cl.serverinfo); if (!resp || !resp->buf) auth = ""; else @@ -273,7 +273,7 @@ void Validation_IncludeFile(char *filename, char *file, int filelen) static void Validation_FilesModified (void) { - Con_Printf ("Not implemented\n"); + Con_Printf ("f_modified not implemented\n"); } void Validation_FlushFileList(void) @@ -446,12 +446,6 @@ qboolean Validation_GetCurrentRulesetName(char *rsnames, int resultbuflen, qbool rs = rulesets; *rsnames = '\0'; -#ifdef warningmsg -#pragma warningmsg("here's a question... Should we latch the ruleset unconditionally, or only when someone actually cares?") -#pragma warningmsg("if we do it only when someone checks, we have a lot more checking, otherwise we have a freer tournament if the users choose to play that way") -#pragma warningmsg("I'm going to do it the old-fashioned way") -#pragma warningmsg("(yes, this is one for molgrum to resolve!)") -#endif for (rs = rulesets; rs->rulesetname; rs++) { rs->flagged = false; @@ -517,7 +511,7 @@ void Validation_AllChecks(void) char servername[22]; char playername[16]; char *enginebuild = version_string(); - char localpnamelen = strlen(cl.players[cl.playernum[0]].name); + char localpnamelen = strlen(cl.players[cl.playerview[0].playernum].name); char ruleset[1024]; //figure out the padding for the player's name. diff --git a/engine/client/vid.h b/engine/client/vid.h index 46ae3e093..93d03698e 100644 --- a/engine/client/vid.h +++ b/engine/client/vid.h @@ -46,7 +46,6 @@ extern rendererstate_t currentrendererstate; typedef struct vrect_s { int x,y,width,height; -// struct vrect_s *pnext; } vrect_t; typedef struct @@ -56,7 +55,6 @@ typedef struct unsigned width; /*virtual 2d width*/ unsigned height; /*virtual 2d height*/ int numpages; - int recalc_refdef; // if true, recalc vid-based stuff unsigned rotpixelwidth; /*width after rotation in pixels*/ unsigned rotpixelheight; /*pixel after rotation in pixels*/ diff --git a/engine/client/view.c b/engine/client/view.c index 3ab972aa7..93da315ec 100644 --- a/engine/client/view.c +++ b/engine/client/view.c @@ -97,8 +97,6 @@ cvar_t scr_autoid = SCVAR("scr_autoid", "1"); extern cvar_t cl_chasecam; -float v_dmg_time[MAX_SPLITS], v_dmg_roll[MAX_SPLITS], v_dmg_pitch[MAX_SPLITS]; - player_state_t *view_message; /* @@ -137,25 +135,25 @@ V_CalcBob =============== */ -float V_CalcBob (int pnum, qboolean queryold) +float V_CalcBob (playerview_t *pv, qboolean queryold) { - static double bobtime[MAX_SPLITS]; - static double cltime[MAX_SPLITS]; - static float bob[MAX_SPLITS]; float cycle; if (cl.spectator) return 0; - if (!cl.onground[pnum] || cl.paused) - return bob[pnum]; // just use old value + if (!pv->onground || cl.paused) + { + pv->bobcltime = cl.time; + return pv->bob; // just use old value + } if (cl_bobcycle.value <= 0) return 0; - bobtime[pnum] += cl.time - cltime[pnum]; - cltime[pnum] = cl.time; - cycle = bobtime[pnum] - (int)(bobtime[pnum]/cl_bobcycle.value)*cl_bobcycle.value; + pv->bobtime += cl.time - pv->bobcltime; + pv->bobcltime = cl.time; + cycle = pv->bobtime - (int)(pv->bobtime/cl_bobcycle.value)*cl_bobcycle.value; cycle /= cl_bobcycle.value; if (cycle < cl_bobup.value) cycle = M_PI * cycle / cl_bobup.value; @@ -164,14 +162,15 @@ float V_CalcBob (int pnum, qboolean queryold) // bob is proportional to simulated velocity in the xy plane // (don't count Z, or jumping messes it up) +//FIXME: gravitydir - bob[pnum] = sqrt(cl.playerview[pnum].simvel[0]*cl.playerview[pnum].simvel[0] + cl.playerview[pnum].simvel[1]*cl.playerview[pnum].simvel[1]) * cl_bob.value; - bob[pnum] = bob[pnum]*0.3 + bob[pnum]*0.7*sin(cycle); - if (bob[pnum] > 4) - bob[pnum] = 4; - else if (bob[pnum] < -7) - bob[pnum] = -7; - return bob[pnum]; + pv->bob = sqrt(pv->simvel[0]*pv->simvel[0] + pv->simvel[1]*pv->simvel[1]) * cl_bob.value; + pv->bob = pv->bob*0.3 + pv->bob*0.7*sin(cycle); + if (pv->bob > 4) + pv->bob = 4; + else if (pv->bob < -7) + pv->bob = -7; + return pv->bob; } @@ -183,27 +182,27 @@ cvar_t v_centermove = SCVAR("v_centermove", "0.15"); cvar_t v_centerspeed = SCVAR("v_centerspeed","500"); -void V_StartPitchDrift (int pnum) +void V_StartPitchDrift (playerview_t *pv) { #if 1 - if (cl.playerview[pnum].laststop == cl.time) + if (pv->laststop == cl.time) { return; // something else is keeping it from drifting } #endif - if (cl.playerview[pnum].nodrift || !cl.playerview[pnum].pitchvel) + if (pv->nodrift || !pv->pitchvel) { - cl.playerview[pnum].pitchvel = v_centerspeed.value; - cl.playerview[pnum].nodrift = false; - cl.playerview[pnum].driftmove = 0; + pv->pitchvel = v_centerspeed.value; + pv->nodrift = false; + pv->driftmove = 0; } } -void V_StopPitchDrift (int pnum) +void V_StopPitchDrift (playerview_t *pv) { - cl.playerview[pnum].laststop = cl.time; - cl.playerview[pnum].nodrift = true; - cl.playerview[pnum].pitchvel = 0; + pv->laststop = cl.time; + pv->nodrift = true; + pv->pitchvel = 0; } /* @@ -219,42 +218,42 @@ Drifting is enabled when the center view key is hit, mlook is released and lookspring is non 0, or when =============== */ -void V_DriftPitch (int pnum) +void V_DriftPitch (playerview_t *pv) { float delta, move; - if (!cl.onground || cls.demoplayback ) + if (!pv->onground || cls.demoplayback ) { - cl.playerview[pnum].driftmove = 0; - cl.playerview[pnum].pitchvel = 0; + pv->driftmove = 0; + pv->pitchvel = 0; return; } // don't count small mouse motion - if (cl.playerview[pnum].nodrift) + if (pv->nodrift) { - if ( fabs(cl.outframes[(cl.movesequence-1)&UPDATE_MASK].cmd[pnum].forwardmove) < 200) - cl.playerview[pnum].driftmove = 0; + if (Length(pv->simvel) < 200) + pv->driftmove = 0; else - cl.playerview[pnum].driftmove += host_frametime; + pv->driftmove += host_frametime; - if ( cl.playerview[pnum].driftmove > v_centermove.value) + if ( pv->driftmove > v_centermove.value) { - V_StartPitchDrift (pnum); + V_StartPitchDrift (pv); } return; } - delta = 0 - cl.playerview[pnum].viewangles[PITCH]; + delta = 0 - pv->viewangles[PITCH]; if (!delta) { - cl.playerview[pnum].pitchvel = 0; + pv->pitchvel = 0; return; } - move = host_frametime * cl.playerview[pnum].pitchvel; - cl.playerview[pnum].pitchvel += host_frametime * v_centerspeed.value; + move = host_frametime * pv->pitchvel; + pv->pitchvel += host_frametime * v_centerspeed.value; //Con_Printf ("move: %f (%f)\n", move, host_frametime); @@ -262,19 +261,19 @@ void V_DriftPitch (int pnum) { if (move > delta) { - cl.playerview[pnum].pitchvel = 0; + pv->pitchvel = 0; move = delta; } - cl.playerview[pnum].viewangles[PITCH] += move; + pv->viewangles[PITCH] += move; } else if (delta < 0) { if (move > -delta) { - cl.playerview[pnum].pitchvel = 0; + pv->pitchvel = 0; move = -delta; } - cl.playerview[pnum].viewangles[PITCH] -= move; + pv->viewangles[PITCH] -= move; } } @@ -366,7 +365,6 @@ V_CheckGamma void GLV_Gamma_Callback(struct cvar_s *var, char *oldvalue) { BuildGammaTable (v_gamma.value, v_contrast.value, v_brightness.value); - vid.recalc_refdef = 1; // force a surface cache flush V_UpdatePalette (true); } #endif @@ -376,7 +374,7 @@ void GLV_Gamma_Callback(struct cvar_s *var, char *oldvalue) V_ParseDamage =============== */ -void V_ParseDamage (int pnum) +void V_ParseDamage (playerview_t *pv) { int armor, blood; vec3_t from; @@ -401,7 +399,7 @@ void V_ParseDamage (int pnum) if (v_damagecshift.value >= 0) count *= v_damagecshift.value; - cl.playerview[pnum].faceanimtime = cl.time + 0.2; // but sbar face into pain frame + pv->faceanimtime = cl.time + 0.2; // but sbar face into pain frame cl.cshifts[CSHIFT_DAMAGE].percent += 3*count; if (cl.cshifts[CSHIFT_DAMAGE].percent < 0) @@ -431,18 +429,18 @@ void V_ParseDamage (int pnum) // // calculate view angle kicks // - VectorSubtract (from, cl.playerview[pnum].simorg, from); + VectorSubtract (from, pv->simorg, from); VectorNormalize (from); - AngleVectors (cl.playerview[pnum].simangles, forward, right, up); + AngleVectors (pv->simangles, forward, right, up); side = DotProduct (from, right); - v_dmg_roll[pnum] = count*side*v_kickroll.value; + pv->v_dmg_roll = count*side*v_kickroll.value; side = DotProduct (from, forward); - v_dmg_pitch[pnum] = count*side*v_kickpitch.value; + pv->v_dmg_pitch = count*side*v_kickpitch.value; - v_dmg_time[pnum] = v_kicktime.value; + pv->v_dmg_time = v_kicktime.value; } @@ -802,7 +800,7 @@ float angledelta (float a) CalcGunAngle ================== */ -void V_CalcGunPositionAngle (int pnum, float bob) +void V_CalcGunPositionAngle (playerview_t *pv, float bob) { float yaw, pitch, move; static float oldyaw = 0; @@ -848,37 +846,37 @@ void V_CalcGunPositionAngle (int pnum, float bob) oldyaw = yaw; oldpitch = pitch; - cl.viewent[pnum].angles[YAW] = r_refdef.viewangles[YAW] + yaw; - cl.viewent[pnum].angles[PITCH] = r_refdef.viewangles[PITCH] + pitch; + pv->viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw; + pv->viewent.angles[PITCH] = r_refdef.viewangles[PITCH] + pitch; - cl.viewent[pnum].angles[YAW] = r_refdef.viewangles[YAW]; - cl.viewent[pnum].angles[PITCH] = r_refdef.viewangles[PITCH]; - cl.viewent[pnum].angles[ROLL] = r_refdef.viewangles[ROLL]; + pv->viewent.angles[YAW] = r_refdef.viewangles[YAW]; + pv->viewent.angles[PITCH] = r_refdef.viewangles[PITCH]; + pv->viewent.angles[ROLL] = r_refdef.viewangles[ROLL]; - AngleVectors(cl.viewent[pnum].angles, cl.viewent[pnum].axis[0], cl.viewent[pnum].axis[1], cl.viewent[pnum].axis[2]); - VectorInverse(cl.viewent[pnum].axis[1]); - cl.viewent[pnum].angles[PITCH]*=-1; + AngleVectors(pv->viewent.angles, pv->viewent.axis[0], pv->viewent.axis[1], pv->viewent.axis[2]); + VectorInverse(pv->viewent.axis[1]); + pv->viewent.angles[PITCH]*=-1; - VectorCopy (r_refdef.vieworg, cl.viewent[pnum].origin); + VectorCopy (r_refdef.vieworg, pv->viewent.origin); for (i=0 ; i<3 ; i++) { - cl.viewent[pnum].origin[i] += cl.viewent[pnum].axis[0][i]*bob*0.4; -// cl.viewent[pnum].origin[i] += cl.viewent[pnum].axis[1][i]*sin(cl.time*5.5342452354235)*0.1; -// cl.viewent[pnum].origin[i] += cl.viewent[pnum].axis[2][i]*bob*0.8; + pv->viewent.origin[i] += pv->viewent.axis[0][i]*bob*0.4; +// pv->viewent.origin[i] += pv->viewent.axis[1][i]*sin(cl.time*5.5342452354235)*0.1; +// pv->viewent.origin[i] += pv->viewent.axis[2][i]*bob*0.8; } // fudge position around to keep amount of weapon visible // roughly equal with different FOV if (scr_viewsize.value == 110) - cl.viewent[pnum].origin[2] += 1; + pv->viewent.origin[2] += 1; else if (scr_viewsize.value == 100) - cl.viewent[pnum].origin[2] += 2; + pv->viewent.origin[2] += 2; else if (scr_viewsize.value == 90) - cl.viewent[pnum].origin[2] += 1; + pv->viewent.origin[2] += 1; else if (scr_viewsize.value == 80) - cl.viewent[pnum].origin[2] += 0.5; + pv->viewent.origin[2] += 0.5; } /* @@ -912,7 +910,7 @@ V_AddIdle Idle swaying ============== */ -void V_AddIdle (int pnum) +void V_AddIdle (playerview_t *pv) { //defaults: for use if idlescale is locked and the var isn't. float yaw_cycle = 2; @@ -940,9 +938,9 @@ void V_AddIdle (int pnum) r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*pitch_cycle) * pitch_level; r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*yaw_cycle) * yaw_level; - cl.viewent[pnum].angles[ROLL] -= v_idlescale.value * sin(cl.time*roll_cycle) * roll_level; - cl.viewent[pnum].angles[PITCH] -= v_idlescale.value * sin(cl.time*pitch_cycle) * pitch_level; - cl.viewent[pnum].angles[YAW] -= v_idlescale.value * sin(cl.time*yaw_cycle) * yaw_level; + pv->viewent.angles[ROLL] -= v_idlescale.value * sin(cl.time*roll_cycle) * roll_level; + pv->viewent.angles[PITCH] -= v_idlescale.value * sin(cl.time*pitch_cycle) * pitch_level; + pv->viewent.angles[YAW] -= v_idlescale.value * sin(cl.time*yaw_cycle) * yaw_level; } @@ -953,12 +951,12 @@ V_CalcViewRoll Roll is induced by movement and damage ============== */ -void V_CalcViewRoll (int pnum) +void V_CalcViewRoll (playerview_t *pv) { float side; float adjspeed; - side = V_CalcRoll (cl.playerview[pnum].simangles, cl.playerview[pnum].simvel); + side = V_CalcRoll (pv->simangles, pv->simvel); adjspeed = fabs(cl_rollangle.value); if (adjspeed<1) @@ -966,25 +964,25 @@ void V_CalcViewRoll (int pnum) if (adjspeed>45) adjspeed = 45; adjspeed*=20; - if (side > cl.playerview[pnum].rollangle) + if (side > pv->rollangle) { - cl.playerview[pnum].rollangle += host_frametime * adjspeed; - if (cl.playerview[pnum].rollangle > side) - cl.playerview[pnum].rollangle = side; + pv->rollangle += host_frametime * adjspeed; + if (pv->rollangle > side) + pv->rollangle = side; } - else if (side < cl.playerview[pnum].rollangle) + else if (side < pv->rollangle) { - cl.playerview[pnum].rollangle -= host_frametime * adjspeed; - if (cl.playerview[pnum].rollangle < side) - cl.playerview[pnum].rollangle = side; + pv->rollangle -= host_frametime * adjspeed; + if (pv->rollangle < side) + pv->rollangle = side; } - r_refdef.viewangles[ROLL] += cl.playerview[pnum].rollangle; + r_refdef.viewangles[ROLL] += pv->rollangle; - if (v_dmg_time[pnum] > 0) + if (pv->v_dmg_time > 0) { - r_refdef.viewangles[ROLL] += v_dmg_time[pnum]/v_kicktime.value*v_dmg_roll[pnum]; - r_refdef.viewangles[PITCH] += v_dmg_time[pnum]/v_kicktime.value*v_dmg_pitch[pnum]; - v_dmg_time[pnum] -= host_frametime; + r_refdef.viewangles[ROLL] += pv->v_dmg_time/v_kicktime.value*pv->v_dmg_roll; + r_refdef.viewangles[PITCH] += pv->v_dmg_time/v_kicktime.value*pv->v_dmg_pitch; + pv->v_dmg_time -= host_frametime; } } @@ -996,51 +994,220 @@ V_CalcIntermissionRefdef ================== */ -void V_CalcIntermissionRefdef (int pnum) +void V_CalcIntermissionRefdef (playerview_t *pv) { entity_t *view; float old; // view is the weapon model - view = &cl.viewent[pnum]; + view = &pv->viewent; - VectorCopy (cl.playerview[pnum].simorg, r_refdef.vieworg); - VectorCopy (cl.playerview[pnum].simangles, r_refdef.viewangles); + VectorCopy (pv->simorg, r_refdef.vieworg); + VectorCopy (pv->simangles, r_refdef.viewangles); view->model = NULL; // always idle in intermission old = v_idlescale.value; v_idlescale.value = 1; - V_AddIdle (pnum); + V_AddIdle (pv); v_idlescale.value = old; } +float CalcFov (float fov_x, float width, float height); +/* +================= +v_ApplyRefdef + +called to apply any dirty refdef bits and recalculates pending data. +================= +*/ +void V_ApplyRefdef (void) +{ + float size; + int h; + qboolean full = false; + +// force the status bar to redraw + Sbar_Changed (); + +//======================================== + + r_refdef.flags = 0; + +// intermission is always full screen + if (cl.intermission || !r_refdef.drawsbar) + size = 120; + else + size = scr_viewsize.value; + +#ifdef Q2CLIENT + if (cls.protocol == CP_QUAKE2) //q2 never has a hud. + sb_lines = 0; + else +#endif + if (size >= 120) + sb_lines = 0; // no status bar at all + else if (size >= 110) + sb_lines = 24; // no inventory + else + sb_lines = 24+16+8; + + if (scr_viewsize.value >= 100.0 || scr_chatmode) + { + full = true; + size = 100.0; + } + else + size = scr_viewsize.value; + + if (cl.intermission || !r_refdef.drawsbar) + { + full = true; + size = 100.0; + sb_lines = 0; + } + size /= 100.0; + + if (cl_sbar.value!=1 && full) + h = r_refdef.grect.height; + else + h = r_refdef.grect.height - sb_lines; + if (h < 0) + h = 0; + + r_refdef.vrect.width = r_refdef.grect.width * size; + if (r_refdef.vrect.width < 96) + r_refdef.vrect.width = 96; // min for icons + + r_refdef.vrect.height = r_refdef.grect.height * size; + if (cl_sbar.value==1 || !full) + { + if (r_refdef.vrect.height > r_refdef.grect.height - sb_lines) + r_refdef.vrect.height = r_refdef.grect.height - sb_lines; + } + else if (r_refdef.vrect.height > r_refdef.grect.height) + r_refdef.vrect.height = r_refdef.grect.height; + if (r_refdef.vrect.height < 0) + r_refdef.vrect.height = 0; + + r_refdef.vrect.x = (r_refdef.grect.width - r_refdef.vrect.width)/2; + if (full) + r_refdef.vrect.y = 0; + else + r_refdef.vrect.y = (h - r_refdef.vrect.height)/2; + + if (scr_chatmode) + { + if (scr_chatmode != 2) + r_refdef.vrect.height = r_refdef.vrect.y=r_refdef.grect.height/2; + r_refdef.vrect.width = r_refdef.vrect.x=r_refdef.grect.width/2; + if (r_refdef.vrect.width<320 || r_refdef.vrect.height<200) //disable hud if too small + sb_lines=0; + } + r_refdef.vrect.x += r_refdef.grect.x; + r_refdef.vrect.y += r_refdef.grect.y; + + if (r_refdef.dirty & RDFD_FOV) + { + //explicit fov overrides aproximate fov. + //aproximate fov is our regular fov value. explicit is settable by gamecode for weird aspect ratios + if (!r_refdef.fov_x || !r_refdef.fov_y) + { + extern cvar_t r_stereo_method, r_stereo_separation; + float ws; + + float afov = r_refdef.afov; + if (!afov) //make sure its sensible. + afov = scr_fov.value; + if (r_refdef.playerview->stats[STAT_VIEWZOOM]) + afov *= r_refdef.playerview->stats[STAT_VIEWZOOM]/255.0f; + + ws = 1; + if (r_stereo_method.ival == 5 && r_stereo_separation.value) + ws = 0.5; + + //attempt to retain a classic fov + if (ws*r_refdef.vrect.width < (r_refdef.vrect.height*640)/432) + { + r_refdef.fov_y = CalcFov(afov, (ws*r_refdef.vrect.width*vid.pixelwidth)/vid.width, (r_refdef.vrect.height*vid.pixelheight)/vid.height); + r_refdef.fov_x = afov;//CalcFov(r_refdef.fov_y, 432, 640); + } + else + { + r_refdef.fov_y = CalcFov(afov, 640, 432); + r_refdef.fov_x = CalcFov(r_refdef.fov_y, r_refdef.vrect.height, r_refdef.vrect.width*ws); + } + } + } + + r_refdef.dirty = 0; +} + +//if the view entities differ, removes all externalmodel flags except for adding it to the new entity, and removes weaponmodels. +void CL_EditExternalModels(int newviewentity) +{ + int i; + for (i = 0; i < cl_numvisedicts; ) + { + if (cl_visedicts[i].keynum == newviewentity && newviewentity) + cl_visedicts[i].flags |= Q2RF_EXTERNALMODEL; + else + cl_visedicts[i].flags &= ~Q2RF_EXTERNALMODEL; + + if (cl_visedicts[i].flags & Q2RF_WEAPONMODEL) + { + memmove(&cl_visedicts[i], &cl_visedicts[i+1], sizeof(*cl_visedicts) * (cl_numvisedicts-(i+1))); + cl_numvisedicts--; + } + else + i++; + } +} + +/* +clears the refdef to defaults. +*/ +void V_ClearRefdef(playerview_t *pv) +{ + r_refdef.playerview = pv; + r_refdef.dirty = ~0; + + r_refdef.grect.x = 0; + r_refdef.grect.y = 0; + r_refdef.grect.width = vid.width; + r_refdef.grect.height = vid.height; + + r_refdef.afov = scr_fov.value; //will have a better value applied if fov is bad. this allows setting. + r_refdef.fov_x = 0; + r_refdef.fov_y = 0; + + r_refdef.drawsbar = !cl.intermission; +} + /* ================== V_CalcRefdef ================== */ -void V_CalcRefdef (int pnum) +void V_CalcRefdef (playerview_t *pv) { entity_t *view; int i; float bob; float viewheight; - - r_refdef.currentplayernum = pnum; + r_refdef.playerview = pv; #ifdef Q2CLIENT if (cls.protocol == CP_QUAKE2) return; #endif - VectorCopy(cl.fog_colour, r_refdef.gfog_rgbd); r_refdef.gfog_rgbd[3] = cl.fog_density / 64; // view is the weapon model (only visible from inside body) - view = &cl.viewent[pnum]; + view = &pv->viewent; if (v_viewheight.value < -7) bob=-7; @@ -1049,10 +1216,10 @@ void V_CalcRefdef (int pnum) else if (v_viewheight.value) bob=v_viewheight.value; else - bob = V_CalcBob (pnum, false); + bob = V_CalcBob (pv, false); // refresh position from simulated origin - VectorCopy (cl.playerview[pnum].simorg, r_refdef.vieworg); + VectorCopy (pv->simorg, r_refdef.vieworg); r_refdef.useperspective = true; @@ -1063,9 +1230,9 @@ void V_CalcRefdef (int pnum) r_refdef.vieworg[1] += 1.0/16; r_refdef.vieworg[2] += 1.0/16; - if (cl.playerview[pnum].fixangle) + if (pv->fixangle) { - if (cl.playerview[pnum].oldfixangle) + if (pv->oldfixangle) { float frac, move; if (cl.gametime <= cl.oldgametime) @@ -1077,27 +1244,27 @@ void V_CalcRefdef (int pnum) } for (i = 0; i < 3; i++) { - move = cl.playerview[pnum].fixangles[i] - cl.playerview[pnum].oldfixangles[i]; + move = pv->fixangles[i] - pv->oldfixangles[i]; if (move >= 180) move -= 360; if (move <= -180) move += 360; - r_refdef.viewangles[i] = cl.playerview[pnum].oldfixangles[i] + frac * move; + r_refdef.viewangles[i] = pv->oldfixangles[i] + frac * move; } } else { - VectorCopy (cl.playerview[pnum].fixangles, r_refdef.viewangles); + VectorCopy (pv->fixangles, r_refdef.viewangles); } } else { - VectorCopy (cl.playerview[pnum].simangles, r_refdef.viewangles); + VectorCopy (pv->simangles, r_refdef.viewangles); } - V_CalcViewRoll (pnum); - V_AddIdle (pnum); + V_CalcViewRoll (pv); + V_AddIdle (pv); - viewheight = cl.viewheight[pnum]; + viewheight = pv->viewheight; if (viewheight == DEFAULT_VIEWHEIGHT) { if (view_message && view_message->flags & PF_GIB) @@ -1106,9 +1273,9 @@ void V_CalcRefdef (int pnum) viewheight = 16; // corpse view height } - viewheight += cl.crouch[pnum]; + viewheight += pv->crouch; - if (cl.playerview[pnum].stats[STAT_HEALTH] < 0 && spec_track[pnum] >= 0 && v_deathtilt.value) // PF_GIB will also set PF_DEAD + if (pv->stats[STAT_HEALTH] < 0 && pv->cam_spec_track >= 0 && v_deathtilt.value) // PF_GIB will also set PF_DEAD { if (!cl.spectator || !cl_chasecam.ival) r_refdef.viewangles[ROLL] = 80*v_deathtilt.value; // dead view angle @@ -1119,23 +1286,23 @@ void V_CalcRefdef (int pnum) viewheight += bob; } - VectorMA(r_refdef.vieworg, -viewheight, cl.playerview[pnum].gravitydir, r_refdef.vieworg); + VectorMA(r_refdef.vieworg, -viewheight, pv->gravitydir, r_refdef.vieworg); // set up gun position - V_CalcGunPositionAngle (pnum, bob); + V_CalcGunPositionAngle (pv, bob); - if (cl.playerview[pnum].stats[STAT_HEALTH] > 0 && (unsigned int)cl.playerview[pnum].stats[STAT_WEAPON] >= MAX_MODELS) + if (pv->statsf[STAT_HEALTH] <= 0 || (unsigned int)pv->stats[STAT_WEAPON] >= MAX_MODELS) view->model = NULL; else - view->model = cl.model_precache[cl.playerview[pnum].stats[STAT_WEAPON]]; + view->model = cl.model_precache[pv->stats[STAT_WEAPON]]; #ifdef HLCLIENT if (!CLHL_AnimateViewEntity(view)) #endif - view->framestate.g[FS_REG].frame[0] = cl.playerview[pnum].stats[STAT_WEAPONFRAME]; + view->framestate.g[FS_REG].frame[0] = pv->stats[STAT_WEAPONFRAME]; // set up the refresh position if (v_gunkick.value) - r_refdef.viewangles[PITCH] += cl.punchangle[pnum]*v_gunkick.value; + r_refdef.viewangles[PITCH] += pv->punchangle*v_gunkick.value; r_refdef.time = realtime; @@ -1153,19 +1320,19 @@ void V_CalcRefdef (int pnum) DropPunchAngle ============= */ -void DropPunchAngle (int pnum) +void DropPunchAngle (playerview_t *pv) { - if (cl.punchangle[pnum] < 0) + if (pv->punchangle < 0) { - cl.punchangle[pnum] += 10*host_frametime; - if (cl.punchangle[pnum] > 0) - cl.punchangle[pnum] = 0; + pv->punchangle += 10*host_frametime; + if (pv->punchangle > 0) + pv->punchangle = 0; } else { - cl.punchangle[pnum] -= 10*host_frametime; - if (cl.punchangle[pnum] < 0) - cl.punchangle[pnum] = 0; + pv->punchangle -= 10*host_frametime; + if (pv->punchangle < 0) + pv->punchangle = 0; } } @@ -1199,18 +1366,16 @@ entity_t *CL_EntityNum(int num) float CalcFov (float fov_x, float width, float height); void SCR_VRectForPlayer(vrect_t *vrect, int pnum) { - float ws; - extern cvar_t r_stereo_method, r_stereo_separation; #if MAX_SPLITS > 4 #pragma warning "Please change this function to cope with the new MAX_SPLITS value" #endif switch(cl.splitclients) { case 1: - vrect->width = scr_vrect.width; - vrect->height = scr_vrect.height; - vrect->x = scr_vrect.x; - vrect->y = scr_vrect.y; + vrect->width = vid.width; + vrect->height = vid.height; + vrect->x = 0; + vrect->y = 0; if (scr_chatmode == 2) { @@ -1253,25 +1418,6 @@ void SCR_VRectForPlayer(vrect_t *vrect, int pnum) default: Sys_Error("cl.splitclients is invalid."); } - - r_refdef.fov_x = scr_fov.value; - if (cl.playerview[pnum].stats[STAT_VIEWZOOM]) - r_refdef.fov_x *= cl.playerview[pnum].stats[STAT_VIEWZOOM]/255.0f; - - ws = 1; - if (r_stereo_method.ival == 5 && r_stereo_separation.value) - ws = 0.5; - - if (ws*vrect->width < (vrect->height*640)/432) - { - r_refdef.fov_y = CalcFov(r_refdef.fov_x, (ws*vrect->width*vid.pixelwidth)/vid.width, (vrect->height*vid.pixelheight)/vid.height); -// r_refdef.fov_x = CalcFov(r_refdef.fov_y, 432, 640); - } - else - { - r_refdef.fov_y = CalcFov(r_refdef.fov_x, 640, 432); - r_refdef.fov_x = CalcFov(r_refdef.fov_y, vrect->height, vrect->width*ws); - } } void Draw_ExpandedString(int x, int y, conchar_t *str); @@ -1304,11 +1450,11 @@ void R_DrawNameTags(void) { if (!nametagseen[i]) continue; - if (i == cl.playernum[r_refdef.currentplayernum]) + if (i == r_refdef.playerview->playernum) continue; // Don't draw tag for the local player if (cl.players[i].spectator) continue; - if (i == Cam_TrackNum(r_refdef.currentplayernum)) + if (i == Cam_TrackNum(r_refdef.playerview)) continue; if (TP_IsPlayerVisible(nametagorg[i])) @@ -1325,33 +1471,33 @@ void R_DrawNameTags(void) } void R2D_PolyBlend (void); -void V_RenderPlayerViews(int plnum) +void V_RenderPlayerViews(playerview_t *pv) { int oldnuments; int oldstris; #ifdef SIDEVIEWS int viewnum; #endif - SCR_VRectForPlayer(&r_refdef.vrect, plnum); // cl.simangles[plnum][ROLL] = 0; // FIXME @@@ + DropPunchAngle (pv); - DropPunchAngle (plnum); + Cam_SelfTrack(pv); if (cl.intermission) { // intermission / finale rendering - V_CalcIntermissionRefdef (plnum); + V_CalcIntermissionRefdef (pv); } else { - V_DriftPitch (plnum); - V_CalcRefdef (plnum); + V_DriftPitch (pv); + V_CalcRefdef (pv); } + V_ApplyRefdef(); oldnuments = cl_numvisedicts; oldstris = cl_numstris; CL_LinkViewModel (); - Cam_SelfTrack(plnum); R_RenderView (); R2D_PolyBlend (); R_DrawNameTags(); @@ -1361,21 +1507,18 @@ void V_RenderPlayerViews(int plnum) if (scr_chatmode == 2) { - extern vec3_t desired_position[MAX_SPLITS]; vec3_t dir; r_refdef.vrect.y -= r_refdef.vrect.height; - vid.recalc_refdef=true; r_secondaryview = 2; - VectorSubtract(r_refdef.vieworg, desired_position[plnum], dir); + VectorSubtract(r_refdef.vieworg, pv->cam_desired_position, dir); VectorAngles(dir, NULL, r_refdef.viewangles); r_refdef.viewangles[0] = -r_refdef.viewangles[0]; //flip the pitch. :( - VectorCopy(desired_position[plnum], r_refdef.vieworg); + VectorCopy(pv->cam_desired_position, r_refdef.vieworg); R_RenderView (); - vid.recalc_refdef=true; } @@ -1390,7 +1533,7 @@ void V_RenderPlayerViews(int plnum) for (viewnum = 0; viewnum < SIDEVIEWS; viewnum++) if (vsec_scalex[viewnum].value>0&&vsec_scaley[viewnum].value>0 && ((vsec_enabled[viewnum].value && vsec_enabled[viewnum].value != 2 && cls.allow_rearview) //rearview if v2_enabled = 1 and not 2 - || (vsec_enabled[viewnum].value && cl.playerview[plnum].stats[STAT_VIEW2]&&viewnum==0))) //v2 enabled if v2_enabled is non-zero + || (vsec_enabled[viewnum].value && pv->stats[STAT_VIEW2]&&viewnum==0))) //v2 enabled if v2_enabled is non-zero { vrect_t oldrect; vec3_t oldangles; @@ -1400,8 +1543,6 @@ void V_RenderPlayerViews(int plnum) float ofx; float ofy; - vid.recalc_refdef=true; - r_secondaryview = true; if (vsec_x[viewnum].value < 0) @@ -1427,9 +1568,9 @@ void V_RenderPlayerViews(int plnum) #ifdef PEXT_VIEW2 //secondary view entity. e=NULL; - if (viewnum==0&&cl.playerview[plnum].stats[STAT_VIEW2]) + if (viewnum==0&&pv->stats[STAT_VIEW2]) { - e = CL_EntityNum (cl.playerview[plnum].stats[STAT_VIEW2]); + e = CL_EntityNum (pv->stats[STAT_VIEW2]); } if (e) { @@ -1446,7 +1587,14 @@ void V_RenderPlayerViews(int plnum) r_refdef.viewangles[2]=e->angles[2];//*s+(1-s)*e->msg_angles[1][2]; r_refdef.viewangles[PITCH] *= -1; - r_refdef.externalview = true; //show the player + if (e->keynum >= 1 && e->keynum <= cl.allocated_client_slots) + { + r_refdef.viewangles[PITCH] *= 3; + r_refdef.vieworg[2] += pv->statsf[STAT_VIEWHEIGHT]; + } + + + CL_EditExternalModels(e->keynum); R_RenderView (); // r_framecount = old_framecount; @@ -1460,6 +1608,7 @@ void V_RenderPlayerViews(int plnum) r_refdef.viewangles[PITCH] *= -cos((vsec_yaw[viewnum].value / 180 * 3.14)+3.14); if (vsec_enabled[viewnum].value!=2) { + CL_EditExternalModels(0); R_RenderView (); } } @@ -1469,8 +1618,6 @@ void V_RenderPlayerViews(int plnum) memcpy(r_refdef.vieworg, oldposition, sizeof(vec3_t)); r_refdef.fov_x = ofx; r_refdef.fov_y = ofy; - - vid.recalc_refdef=true; } #endif r_refdef.externalview = false; @@ -1485,31 +1632,45 @@ void V_RenderView (void) if (cls.state != ca_active) return; - if (r_worldentity.model) - { - RSpeedMark(); - - CL_AllowIndependantSendCmd(false); - - CL_TransitionEntities(); - - CL_PredictMove (); - - // build a refresh entity list - CL_EmitEntities (); - - CL_AllowIndependantSendCmd(true); - - RSpeedEnd(RSPEED_LINKENTITIES); - } - R_PushDlights (); r_secondaryview = 0; for (viewnum = 0; viewnum < cl.splitclients; viewnum++) { - V_RenderPlayerViews(viewnum); + V_ClearRefdef(&cl.playerview[viewnum]); + if (viewnum) + { + //should be enough to just hack a few things. + CL_EditExternalModels(cl.playerview[viewnum].viewentity); + } + else + { + if (r_worldentity.model) + { + RSpeedMark(); + + CL_AllowIndependantSendCmd(false); + + CL_TransitionEntities(); + + CL_PredictMove (); + + // build a refresh entity list + CL_EmitEntities (); + + CL_AllowIndependantSendCmd(true); + + RSpeedEnd(RSPEED_LINKENTITIES); + } + } + SCR_VRectForPlayer(&r_refdef.grect, viewnum); + V_RenderPlayerViews(r_refdef.playerview); + + GL_Set2D (false); + Plug_SBar(r_refdef.playerview); + SCR_TileClear (); } + r_refdef.playerview = NULL; } //============================================================================ diff --git a/engine/client/zqtp.c b/engine/client/zqtp.c index 226f53f19..ad2e79552 100644 --- a/engine/client/zqtp.c +++ b/engine/client/zqtp.c @@ -523,7 +523,7 @@ static char *Macro_Powerups (void) if (cl.playerview[SP].stats[STAT_ITEMS] & IT_INVISIBILITY) MacroBuf_strcat_with_separator (tp_name_ring.string); - effects = cl.inframes[cl.parsecount&UPDATE_MASK].playerstate[cl.playernum[SP]].effects; + effects = cl.inframes[cl.parsecount&UPDATE_MASK].playerstate[cl.playerview[SP].playernum].effects; if ( (effects & (QWEF_FLAG1|QWEF_FLAG2)) || // CTF (cl.teamfortress && cl.playerview[SP].stats[STAT_ITEMS] & (IT_KEY1|IT_KEY2)) ) // TF MacroBuf_strcat_with_separator (tp_name_flag.string); @@ -716,7 +716,7 @@ static char *Skin_To_TFSkin (char *myskin) static char *Macro_TF_Skin (void) { - return Skin_To_TFSkin(Info_ValueForKey(cl.players[cl.playernum[0]].userinfo, "skin")); + return Skin_To_TFSkin(Info_ValueForKey(cl.players[cl.playerview[SP].playernum].userinfo, "skin")); } //Spike: added these: @@ -882,7 +882,7 @@ static void CountNearbyPlayers(qboolean dead) state = cl.inframes[cl.oldparsecount & UPDATE_MASK].playerstate; info = cl.players; for (i = 0; i < MAX_CLIENTS; i++, info++, state++) { - if (i != cl.playernum[0] && state->messagenum == cl.oldparsecount && !info->spectator && !ISDEAD(state->frame)) { + if (i != cl.playerview[SP].playernum && state->messagenum == cl.oldparsecount && !info->spectator && !ISDEAD(state->frame)) { if (cl.teamplay && !strcmp(info->team, TP_PlayerTeam())) vars.numfriendlies++; else @@ -1770,7 +1770,7 @@ int TP_CountPlayers (void) char *TP_PlayerTeam (void) { - return cl.players[cl.playernum[SP]].team; + return cl.players[cl.playerview[SP].playernum].team; } char *TP_EnemyTeam (void) @@ -1792,7 +1792,7 @@ char *TP_EnemyTeam (void) char *TP_PlayerName (void) { - return cl.players[cl.playernum[SP]].name; + return cl.players[cl.playerview[SP].playernum].name; } @@ -2123,7 +2123,7 @@ int TP_CategorizeMessage (char *s, int *offset, player_info_t **plr) // no team messages in teamplay 0, except for our own if (cl.spectator) { - unsigned int track = Cam_TrackNum(0); + unsigned int track = Cam_TrackNum(&cl.playerview[SP]); if (i == track || ( cl.teamplay && !strcmp(cl.players[track].team, player->team)) ) { @@ -2132,8 +2132,8 @@ int TP_CategorizeMessage (char *s, int *offset, player_info_t **plr) } else { - if (i == cl.playernum[SP] || ( cl.teamplay && - !strcmp(cl.players[cl.playernum[SP]].team, player->team)) ) + if (i == cl.playerview[SP].playernum || ( cl.teamplay && + !strcmp(cl.players[cl.playerview[SP].playernum].team, player->team)) ) { flags |= TPM_TEAM; } @@ -2511,7 +2511,7 @@ static int FindNearestItem (int flags, item_t **pitem) item_t *item; VectorCopy (cl.inframes[cl.validsequence&UPDATE_MASK] - .playerstate[cl.playernum[SP]].origin, org); + .playerstate[cl.playerview[SP].playernum].origin, org); // look in previous frame frame = &cl.inframes[cl.oldvalidsequence&UPDATE_MASK]; @@ -2561,9 +2561,9 @@ static int CountTeammates (void) return 0; count = 0; - myteam = cl.players[cl.playernum[SP]].team; + myteam = cl.players[cl.playerview[SP].playernum].team; for (i=0, player=cl.players; i < MAX_CLIENTS ; i++, player++) { - if (player->name[0] && !player->spectator && (i != cl.playernum[SP]) + if (player->name[0] && !player->spectator && (i != cl.playerview[SP].playernum) && !strcmp(player->team, myteam)) count++; } @@ -2587,9 +2587,9 @@ static qboolean CheckTrigger (void) return false; count = 0; - myteam = cl.players[cl.playernum[0]].team; + myteam = cl.players[cl.playerview[SP].playernum].team; for (i = 0, player= cl.players; i < MAX_CLIENTS; i++, player++) { - if (player->name[0] && !player->spectator && i != cl.playernum[0] && !strcmp(player->team, myteam)) + if (player->name[0] && !player->spectator && i != cl.playerview[SP].playernum && !strcmp(player->team, myteam)) count++; } @@ -2647,11 +2647,11 @@ void TP_ParsePlayerInfo(player_state_t *oldstate, player_state_t *state, player_ vars.enemy_powerups |= TP_RING; } } - if (!cl.spectator && !cl.teamfortress && info - cl.players == cl.playernum[SP]) + if (!cl.spectator && !cl.teamfortress && info - cl.players == cl.playerview[SP].playernum) { if ((state->effects & (QWEF_FLAG1|QWEF_FLAG2)) && !(oldstate->effects & (QWEF_FLAG1|QWEF_FLAG2))) { - ExecTookTrigger (tp_name_flag.string, it_flag, cl.inframes[cl.validsequence & UPDATE_MASK].playerstate[cl.playernum[SP]].origin); + ExecTookTrigger (tp_name_flag.string, it_flag, cl.inframes[cl.validsequence & UPDATE_MASK].playerstate[cl.playerview[SP].playernum].origin); } else if (!(state->effects & (QWEF_FLAG1|QWEF_FLAG2)) && (oldstate->effects & (QWEF_FLAG1|QWEF_FLAG2))) { @@ -2976,7 +2976,7 @@ static void TP_FindPoint (void) info = cl.players; for (j = 0; j < MAX_CLIENTS; j++, info++, state++) { - if (state->messagenum != cl.parsecount || j == cl.playernum[0] || info->spectator) + if (state->messagenum != cl.parsecount || j == cl.playerview[SP].playernum || info->spectator) continue; if ( @@ -3177,7 +3177,7 @@ void TP_StatChanged (int stat, int value) if (cl.teamfortress && !cl.spectator) { ExecTookTrigger (tp_name_flag.string, it_flag, - cl.inframes[cl.validsequence&UPDATE_MASK].playerstate[cl.playernum[SP]].origin); + cl.inframes[cl.validsequence&UPDATE_MASK].playerstate[cl.playerview[SP].playernum].origin); } } @@ -3420,7 +3420,7 @@ qboolean TP_SuppressMessage(char *buf) { *s++ = '\n'; *s++ = 0; - return (!cls.demoplayback && !cl.spectator && *s - 'A' == cl.playernum[0]); + return (!cls.demoplayback && !cl.spectator && *s - 'A' == cl.playerview[SP].playernum); } return false; } @@ -3505,7 +3505,7 @@ void CL_Say (qboolean team, char *extra) if (team) plrflags |= 2; - CL_PrintChat(&cl.players[cl.playernum[SP]], NULL, text, plrflags); + CL_PrintChat(&cl.players[cl.playerview[SP].playernum], NULL, text, plrflags); } //strip out the extra markup @@ -3533,7 +3533,7 @@ void CL_Say (qboolean team, char *extra) *d = '\0'; //mark the message so that we ignore it when we get the echo. - strlcat (sendtext, va("\x7f!%c", 'A'+cl.playernum[0]), sizeof(sendtext)); + strlcat (sendtext, va("\x7f!%c", 'A'+cl.playerview[SP].playernum), sizeof(sendtext)); } #ifdef Q3CLIENT diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index bdc2748cd..9d842c9eb 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // release version #define FTE_VER_MAJOR 1 -#define FTE_VER_MINOR 2 +#define FTE_VER_MINOR 3 //#define VERSION 2.56 #ifndef DISTRIBUTION @@ -240,10 +240,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define TCPCONNECT //a tcpconnect command, that allows the player to connect to tcp-encapsulated qw protocols. #define IRCCONNECT //an ircconnect command, that allows the player to connect to irc-encapsulated qw protocols... yeah, really. - #define PLUGINS + #define PLUGINS //qvm/dll plugins. + #define SUPPORT_ICE //Internet Connection Establishment protocol, for peer-to-peer connections #ifdef _DEBUG -// #define OFFSCREENGECKO +// #define OFFSCREENGECKO //FIXME: move to plugin and remove from engine #endif #define CSQC_DAT //support for csqc diff --git a/engine/common/bspfile.h b/engine/common/bspfile.h index 2c5d35cc5..8146706e0 100644 --- a/engine/common/bspfile.h +++ b/engine/common/bspfile.h @@ -25,24 +25,24 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define MAX_MAP_HULLSDH2 8 #define MAX_MAP_HULLSM 16 -#define MAX_MAP_MODELS 256 -#define MAX_MAP_BRUSHES 0x8000 -#define MAX_MAP_ENTITIES 1024 -#define MAX_MAP_ENTSTRING 65536 +//#define MAX_MAP_MODELS 256 +//#define MAX_MAP_BRUSHES 0x8000 +//#define MAX_MAP_ENTITIES 1024 +//#define MAX_MAP_ENTSTRING 65536 -#define MAX_MAP_PLANES 65636*2 -#define MAX_MAP_NODES 65535 // q2/q3 uses more than q1. :( -#define MAX_MAP_CLIPNODES 65535 // -#define MAX_MAP_LEAFS 65535 // -#define MAX_MAP_VERTS 65535 -#define MAX_MAP_FACES 65535 -#define MAX_MAP_MARKSURFACES 65535 -#define MAX_MAP_TEXINFO 4096 +#define MAX_MAP_PLANES 65636*2 //sanity (used by q2) +#define SANITY_MAX_MAP_NODES 65535 //sanity +#define SANITY_MAX_MAP_CLIPNODES 65535 //sanity +#define MAX_MAP_LEAFS 65535 //pvs buffer size. not sanity. +#define SANITY_MAX_MAP_VERTS 65535 //sanity +#define SANITY_MAX_MAP_FACES 65535 //sanity +//#define MAX_MAP_MARKSURFACES 65535 //sanity +//#define MAX_MAP_TEXINFO 4096 //sanity #define MAX_MAP_EDGES 256000 -#define MAX_MAP_SURFEDGES 512000 -#define MAX_MAP_MIPTEX 0x200000 -#define MAX_MAP_LIGHTING 0x100000 -#define MAX_MAP_VISIBILITY 0x200000 +//#define MAX_MAP_SURFEDGES 512000 +//#define MAX_MAP_MIPTEX 0x200000 +//#define MAX_MAP_LIGHTING 0x100000 +//#define MAX_MAP_VISIBILITY 0x200000 // key / value pair sizes @@ -406,8 +406,8 @@ typedef struct q2miptex_s // leaffaces, leafbrushes, planes, and verts are still bounded by // 16 bit short limits #define MAX_Q2MAP_MODELS 1024 -#define MAX_Q2MAP_BRUSHES MAX_MAP_BRUSHES #define MAX_Q2MAP_ENTITIES 2048 +#define SANITY_MAX_MAP_BRUSHES 0x8000 #define MAX_Q2MAP_AREAS 256 #define MAX_Q2MAP_AREAPORTALS 1024 @@ -415,12 +415,12 @@ typedef struct q2miptex_s #define MAX_Q2MAP_BRUSHSIDES 0x40000 #define MAX_Q2MAP_VERTS MAX_MAP_VERTS #define MAX_Q2MAP_FACES MAX_MAP_FACES -#define MAX_Q2MAP_LEAFFACES 262144 //sanity only +#define SANITY_MAX_MAP_LEAFFACES 262144 //sanity only #define MAX_Q2MAP_LEAFBRUSHES 65536 //used in an array -#define MAX_Q2MAP_PORTALS 65536 //unused -#define MAX_Q2MAP_EDGES 128000 //unused -#define MAX_Q2MAP_SURFEDGES 256000 //unused -#define MAX_Q2MAP_LIGHTING 0x200000 //unused +//#define MAX_Q2MAP_PORTALS 65536 //unused +//#define MAX_Q2MAP_EDGES 128000 //unused +//#define MAX_Q2MAP_SURFEDGES 256000 //unused +//#define MAX_Q2MAP_LIGHTING 0x200000 //unused //#define MAX_Q2MAP_VISIBILITY MAX_MAP_VISIBILITY // key / value pair sizes diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 0cf08c7a3..36972e9c1 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -45,7 +45,7 @@ typedef struct cmdalias_s cmdalias_t *cmd_alias; -cvar_t cl_warncmd = SCVAR("cl_warncmd", "0"); +cvar_t cl_warncmd = SCVAR("cl_warncmd", "1"); cvar_t cl_aliasoverlap = SCVARF("cl_aliasoverlap", "1", CVAR_NOTFROMSERVER); cvar_t tp_disputablemacros = SCVARF("tp_disputablemacros", "1", CVAR_SEMICHEAT); @@ -537,8 +537,8 @@ void Cmd_Exec_f (void) Con_DPrintf("Ignoring UTF-8 BOM\n"); s+=3; } - // don't execute anything as if it was from server - Cbuf_InsertText (s, Cmd_FromGamecode() ? RESTRICT_INSECURE : Cmd_ExecLevel, true); + // don't execute anything if it was from server (either the stuffcmd/localcmd, or the file) + Cbuf_InsertText (s, ((Cmd_FromGamecode() || com_file_untrusted) ? RESTRICT_INSECURE : Cmd_ExecLevel), true); FS_FreeFile(f); } @@ -1881,14 +1881,15 @@ void Cmd_ForwardToServer_f (void) } if (Q_strcasecmp(Cmd_Argv(1), "ptrack") == 0) { + playerview_t *pv = &cl.playerview[CL_TargettedSplit(false)]; if (!*Cmd_Argv(2)) { - Cam_Unlock(0); + Cam_Unlock(pv); } else { - Cam_Lock(0, atoi(Cmd_Argv(2))); - autocam[0] = CAM_TRACK; + Cam_Lock(pv, atoi(Cmd_Argv(2))); + pv->cam_auto = CAM_TRACK; } return; } @@ -3070,6 +3071,7 @@ void Cmd_Init (void) Cvar_Register(&tp_disputablemacros, "Teamplay"); Cvar_Register(&dpcompat_set, "Darkplaces compatibility"); + Cvar_Register (&cl_warncmd, "Warnings"); #ifndef SERVERONLY rcon_level.ival = atof(rcon_level.defaultstr); //client is restricted to not be allowed to change restrictions. diff --git a/engine/common/cmd.h b/engine/common/cmd.h index 102db4f16..a09eeb0bd 100644 --- a/engine/common/cmd.h +++ b/engine/common/cmd.h @@ -123,10 +123,13 @@ void Cmd_ExecuteString (char *text, int restrictionlevel); void Cmd_Args_Set(char *newargs); -#define RESTRICT_MAX 29 //1-64 it's all about bit size. This is max settable. servers are +1 or +2 +#define RESTRICT_MAX_TOTAL 31 +#define RESTRICT_MAX_USER 29 //1-64 it's all about bit size. This is max settable. servers are +1 or +2 #define RESTRICT_DEFAULT 20 //rcon get's 63, local always gets 64 #define RESTRICT_MIN 1 //rcon get's 63, local always gets 64 +#define RESTRICT_MAX RESTRICT_MAX_USER + #define RESTRICT_LOCAL RESTRICT_MAX #define RESTRICT_INSECURE RESTRICT_MAX+1 #define RESTRICT_SERVER RESTRICT_MAX+2 diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 245bf42a6..5fe845cb3 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -2023,6 +2023,10 @@ void Mod_BuildTextureVectors(galiasinfo_t *galias) int vbospace = 0; vbobctx_t vboctx; + //don't fail on dedicated servers + if (!BE_VBO_Begin) + return; + idx = (index_t*)((char*)galias + galias->ofs_indexes); tc = (vec2_t*)((char*)galias + galias->ofs_st_array); group = (galiasgroup_t*)((char*)galias + galias->groupofs); @@ -3600,7 +3604,7 @@ qboolean Mod_GetTag(model_t *model, int tagnum, framestate_t *fstate, float *res float f2ness; #ifdef warningmsg -#pragma warningmsg("fixme: no base info") +#pragma warningmsg("fixme: no baseframe info") #endif if (tagnum <= 0 || tagnum > inf->numbones) diff --git a/engine/common/com_mesh.h b/engine/common/com_mesh.h index 3382cb3f4..a2db7b5c4 100644 --- a/engine/common/com_mesh.h +++ b/engine/common/com_mesh.h @@ -9,61 +9,6 @@ int HLMod_BoneForName(model_t *mod, char *name); int HLMod_FrameForName(model_t *mod, char *name); -typedef struct { - int ofs_indexes; - int numindexes; - - int ofs_trineighbours; - - int numskins; -#ifndef SERVERONLY - int ofsskins; -#endif - - int shares_verts; //used with models with two shaders using the same vertex. set to the surface number to inherit from (or itself). - int shares_bones; //use last mesh's bones. set to the surface number to inherit from (or itself). - - int numverts; - -#ifndef SERVERONLY - int ofs_st_array; -#endif - - int groups; - int groupofs; - int baseframeofs; /*non-heirachical*/ - - int nextsurf; - -#ifdef SKELETALMODELS - int numbones; - int ofsbones; - int numswtransforms; - int ofsswtransforms; - - int ofs_skel_xyz; - int ofs_skel_norm; - int ofs_skel_svect; - int ofs_skel_tvect; - int ofs_skel_idx; - int ofs_skel_weight; - - vboarray_t vbo_skel_verts; - vboarray_t vbo_skel_normals; - vboarray_t vbo_skel_svector; - vboarray_t vbo_skel_tvector; - vboarray_t vbo_skel_bonenum; - vboarray_t vbo_skel_bweight; -#endif - vboarray_t vboindicies; - vboarray_t vbotexcoords; - -//these exist only in the root mesh. - int numtagframes; - int numtags; - int ofstags; -} galiasinfo_t; - //frame is an index into this typedef struct { @@ -142,6 +87,62 @@ typedef struct { } galiascolourmapped_t; #endif + +typedef struct { + int ofs_indexes; + int numindexes; + + int ofs_trineighbours; + + int numskins; +#ifndef SERVERONLY + int ofsskins; +#endif + + int shares_verts; //used with models with two shaders using the same vertex. set to the surface number to inherit from (or itself). + int shares_bones; //use last mesh's bones. set to the surface number to inherit from (or itself). + + int numverts; + +#ifndef SERVERONLY + int ofs_st_array; +#endif + + int groups; + int groupofs; + int baseframeofs; /*non-heirachical*/ + + int nextsurf; + +#ifdef SKELETALMODELS + int numbones; + int ofsbones; + int numswtransforms; + int ofsswtransforms; + + int ofs_skel_xyz; + int ofs_skel_norm; + int ofs_skel_svect; + int ofs_skel_tvect; + int ofs_skel_idx; + int ofs_skel_weight; + + vboarray_t vbo_skel_verts; + vboarray_t vbo_skel_normals; + vboarray_t vbo_skel_svector; + vboarray_t vbo_skel_tvector; + vboarray_t vbo_skel_bonenum; + vboarray_t vbo_skel_bweight; +#endif + vboarray_t vboindicies; + vboarray_t vbotexcoords; + +//these exist only in the root mesh. + int numtagframes; + int numtags; + int ofstags; +} galiasinfo_t; + float *Alias_GetBonePositions(galiasinfo_t *inf, framestate_t *fstate, float *buffer, int buffersize, qboolean renderable); #ifdef SKELETALMODELS void Alias_TransformVerticies(float *bonepose, galisskeletaltransforms_t *weights, int numweights, vecV_t *xyzout, vec3_t *normout); diff --git a/engine/common/common.c b/engine/common/common.c index 8fdd12276..822402c52 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -94,7 +94,7 @@ cvar_t gameversion = CVARFD("gameversion","", CVAR_SERVERINFO, "gamecode version cvar_t gameversion_min = CVARD("gameversion_min","", "gamecode version for server browsers"); cvar_t gameversion_max = CVARD("gameversion_max","", "gamecode version for server browsers"); cvar_t fs_gamename = CVARFD("fs_gamename", "", CVAR_NOSET, "The filesystem is trying to run this game"); -cvar_t fs_gamedownload = CVARFD("fs_gamedownload", "", CVAR_NOSET, "The place that the game can be downloaded from."); +cvar_t fs_gamemanifest = CVARFD("fs_gamemanifest", "", CVAR_NOSET, "A small updatable file containing a description of the game, including download mirrors."); cvar_t com_protocolname = CVARD("com_gamename", "", "The game name used for dpmaster queries"); cvar_t com_modname = CVARD("com_modname", "", "dpmaster information"); cvar_t com_parseutf8 = CVARD("com_parseutf8", "0", "Interpret console messages/playernames/etc as UTF-8. Requires special fonts. -1=iso 8859-1. 0=quakeascii(chat uses high chars). 1=utf8, revert to ascii on decode errors. 2=utf8 ignoring errors"); //1 parse. 2 parse, but stop parsing that string if a char was malformed. @@ -2306,7 +2306,7 @@ unsigned int unicode_byteofsfromcharofs(char *str, unsigned int charofs) { char *in = str; int error; - int chars = 0; + int chars; for(chars = 0; *in; chars+=1) { if (chars >= charofs) @@ -2314,7 +2314,7 @@ unsigned int unicode_byteofsfromcharofs(char *str, unsigned int charofs) unicode_decode(&error, in, &in); } - return chars; + return in - str; } //handy hacky function. unsigned int unicode_charofsfrombyteofs(char *str, unsigned int byteofs) @@ -2568,6 +2568,43 @@ char *COM_DeFunString(conchar_t *str, conchar_t *stop, char *out, int outsize, q return out; } +static unsigned int koi2wc (unsigned char uc) +{ + static const char koi2wc_table[64] = + { + 0x4e,0x30,0x31,0x46,0x34,0x35,0x44,0x33,0x45,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e, + 0x3f,0x4f,0x40,0x41,0x42,0x43,0x36,0x32,0x4c,0x4b,0x37,0x48,0x4d,0x49,0x47,0x4a, + 0x2e,0x10,0x11,0x26,0x14,0x15,0x24,0x13,0x25,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e, + 0x1f,0x2f,0x20,0x21,0x22,0x23,0x16,0x12,0x2c,0x2b,0x17,0x28,0x2d,0x29,0x27,0x2a + }; + if (uc >= 192 /* && (unsigned char)c <= 255 */) + return koi2wc_table[uc - 192] + 0x400; + else if (uc == '#' + 128) + return 0x0451; // russian small yo + else if (uc == '3' + 128) + return 0x0401; // russian capital yo + else if (uc == '4' + 128) + return 0x0404; // ukrainian capital round E + else if (uc == '$' + 128) + return 0x0454; // ukrainian small round E + else if (uc == '6' + 128) + return 0x0406; // ukrainian capital I + else if (uc == '&' + 128) + return 0x0456; // ukrainian small i + else if (uc == '7' + 128) + return 0x0407; // ukrainian capital I with two dots + else if (uc == '\'' + 128) + return 0x0457; // ukrainian small i with two dots + else if (uc == '>' + 128) + return 0x040e; // belarusian Y + else if (uc == '.' + 128) + return 0x045e; // belarusian y + else if (uc == '/' + 128) + return 0x042a; // russian capital hard sign + else + return uc; +} + //Takes a q3-style fun string, and returns an expanded string-with-flags (actual return value is the null terminator) //outsize parameter is in _BYTES_ (so sizeof is safe). conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t *out, int outsize, int flags) @@ -2837,6 +2874,7 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t } else if (*str == '&' && str[1] == 'r' && !(flags & PFS_NOMARKUP)) { + //ezquake revert ext = (COLOR_WHITE << CON_FGSHIFT) | (ext&~(CON_RICHFOREMASK|CON_RICHFORECOLOUR)); if (!keepmarkup) { @@ -2844,6 +2882,30 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t continue; } } + else if (str[0] == '=' && str[1] == '`' && str[2] == 'k' && str[3] == '8' && str[4] == ':' && !keepmarkup) + { + //this code can just recurse. saves affecting the rest of the code with weird encodings. + int l; + char temp[1024]; + str += 5; + while(*str) + { + l = 0; + while (*str && l < sizeof(temp)-32 && !(str[0] == '`' && str[1] == '=')) + l += utf8_encode(temp+l, koi2wc(*str++), sizeof(temp)-1); + //recurse + temp[l] = 0; + l = COM_ParseFunString(ext, temp, out, outsize, PFS_FORCEUTF8) - out; + outsize -= l; + out += l; + if (str[0] == '`' && str[1] == '=') + { + str+=2; + break; + } + } + continue; + } messedup: if (!--outsize) break; @@ -4598,7 +4660,7 @@ void Info_SetValueForKey (char *s, const char *key, const char *value, int maxsi Info_SetValueForStarKey (s, key, value, maxsize); } -void Info_Print (char *s) +void Info_Print (char *s, char *lineprefix) { char key[1024]; char value[1024]; @@ -4621,7 +4683,7 @@ void Info_Print (char *s) } else *o = 0; - Con_Printf ("%s", key); + Con_Printf ("%s%s", lineprefix, key); if (!*s) { diff --git a/engine/common/common.h b/engine/common/common.h index 22bfa87a8..556a3935b 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -326,6 +326,7 @@ char *VARGS va(char *format, ...) LIKEPRINTF(1); extern qboolean com_file_copyprotected; extern int com_filesize; +extern qboolean com_file_untrusted; struct cache_user_s; extern char com_quakedir[MAX_OSPATH]; @@ -355,7 +356,7 @@ char *FS_WhichPackForLocation(flocation_t *loc); qboolean FS_GetPackageDownloadable(const char *package); char *FS_GetPackHashes(char *buffer, int buffersize, qboolean referencedonly); char *FS_GetPackNames(char *buffer, int buffersize, int referencedonly, qboolean ext); -void FS_GenCachedPakName(char *pname, char *crc, char *local, int llen); +qboolean FS_GenCachedPakName(char *pname, char *crc, char *local, int llen); //returns false if the name is invalid. void FS_ReferenceControl(unsigned int refflag, unsigned int resetflags); FTE_DEPRECATED int COM_FOpenFile (const char *filename, FILE **file); @@ -386,6 +387,7 @@ typedef struct vfsfile_s char dbgname[MAX_QPATH]; #endif } vfsfile_t; +typedef struct searchpathfuncs_s searchpathfuncs_t; #define VFS_CLOSE(vf) (vf->Close(vf)) #define VFS_TELL(vf) (vf->Tell(vf)) @@ -421,9 +423,11 @@ qboolean FS_WriteFile (const char *filename, const void *data, int len, enum fs_ vfsfile_t *FS_OpenVFS(const char *filename, const char *mode, enum fs_relative relativeto); vfsfile_t *FS_OpenTemp(void); vfsfile_t *FS_OpenTCP(const char *name, int defaultport); + void FS_UnloadPackFiles(void); void FS_ReloadPackFiles(void); char *FSQ3_GenerateClientPacksList(char *buffer, int maxlen, int basechecksum); +void FS_PureMode(int mode, char *packagelist, char *crclist, int seed); //implies an fs_restart qbyte *QDECL COM_LoadStackFile (const char *path, void *buffer, int bufsize); @@ -432,15 +436,37 @@ qbyte *COM_LoadTempMoreFile (const char *path); //allocates a little bit more wi qbyte *COM_LoadHunkFile (const char *path); qbyte *COM_LoadMallocFile (const char *path); void COM_LoadCacheFile (const char *path, struct cache_user_s *cu); -void FS_ForceToPure(const char *str, const char *crcs, int seed); -void FS_ImpurePacks(const char *names, const char *crcs); -char *COM_GetPathInfo (int i, int *crc); -char *COM_NextPath (char *prevpath); + +searchpathfuncs_t *COM_IteratePaths (void **iterator, char *buffer, int buffersize); void COM_FlushFSCache(void); //a file was written using fopen void COM_RefreshFSCache_f(void); qboolean FS_Restarted(unsigned int *since); -void COM_InitFilesystem (void); +typedef struct +{ + char *updateurl; //url to download an updated manifest file from. + char *installation; //optional hardcoded commercial name, used for scanning the registry to find existing installs. + char *formalname; //the commercial name of the game. you'll get FULLENGINENAME otherwise. + char *protocolname; //the name used for purposes of dpmaster + char *defaultexec; //execed after cvars are reset, to give game-specific defaults. + struct + { + qboolean base; + char *path; + } gamepath[8]; + struct + { + char *path; //the 'pure' name + unsigned int crc; //the public crc + char *mirrors[8]; //a randomized (prioritized) list of http mirrors to use. + int mirrornum; //the index we last tried to download from, so we still work even if mirrors are down. + } package[64]; +} ftemanifest_t; +void FS_Manifest_Free(ftemanifest_t *man); +ftemanifest_t *FS_Manifest_Parse(const char *data); + +void COM_InitFilesystem (void); //does not set up any gamedirs. +qboolean FS_ChangeGame(ftemanifest_t *newgame, qboolean allowreloadconfigs); void FS_Shutdown(void); void COM_Gamedir (const char *dir); char *FS_GetGamedir(void); @@ -455,7 +481,7 @@ qbyte *COM_LoadFile (const char *path, int usehunk); qboolean COM_LoadMapPackFile(const char *name, int offset); void COM_FlushTempoaryPacks(void); -void COM_EnumerateFiles (const char *match, int (QDECL *func)(const char *fname, int fsize, void *parm, void *spath), void *parm); +void COM_EnumerateFiles (const char *match, int (QDECL *func)(const char *fname, int fsize, void *parm, searchpathfuncs_t *spath), void *parm); extern struct cvar_s registered; extern qboolean standard_quake; //fixme: remove @@ -474,7 +500,7 @@ void Info_RemovePrefixedKeys (char *start, char prefix); void Info_RemoveNonStarKeys (char *start); void Info_SetValueForKey (char *s, const char *key, const char *value, int maxsize); void Info_SetValueForStarKey (char *s, const char *key, const char *value, int maxsize); -void Info_Print (char *s); +void Info_Print (char *s, char *lineprefix); void Info_WriteToFile(vfsfile_t *f, char *info, char *commandname, int cvarflags); void Com_BlocksChecksum (int blocks, void **buffer, int *len, unsigned char *outbuf); diff --git a/engine/common/console.h b/engine/common/console.h index bdbdc1171..c19e4ca7b 100644 --- a/engine/common/console.h +++ b/engine/common/console.h @@ -88,6 +88,7 @@ extern conchar_t q3codemasks[MAXQ3COLOURS]; #define S_COLOR_MAGENTA "^6" #define S_COLOR_WHITE "^7" +#define CON_DEFAULT "^&--" #define CON_WARNING "^&E0" #define CON_ERROR "^&C0" #define CON_NOTICE "^&-1" diff --git a/engine/common/fs.c b/engine/common/fs.c index bf5e77b09..982caffee 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -8,6 +8,9 @@ #include "fs.h" #include "shader.h" +#ifdef _WIN32 +#include "winquake.h" +#endif #if defined(MINGW) && defined(_SDL) #include "./mingw-libs/SDL_syswm.h" // mingw sdl cross binary complains off sys_parentwindow @@ -21,24 +24,22 @@ int active_fs_cachetype; static int fs_referencetype; int fs_finds; -int fs_switchgame = -1; - struct { void *module; const char *extension; - searchpathfuncs_t *funcs; + searchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, const char *desc); qboolean loadscan; } searchpathformats[64]; -int FS_RegisterFileSystemType(void *module, const char *extension, searchpathfuncs_t *funcs, qboolean loadscan) +int FS_RegisterFileSystemType(void *module, const char *extension, searchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, const char *desc), qboolean loadscan) { unsigned int i; for (i = 0; i < sizeof(searchpathformats)/sizeof(searchpathformats[0]); i++) { if (searchpathformats[i].extension && !strcmp(searchpathformats[i].extension, extension)) break; //extension match always replaces - if (!searchpathformats[i].extension && !searchpathformats[i].funcs) + if (!searchpathformats[i].extension && !searchpathformats[i].OpenNew) break; } if (i == sizeof(searchpathformats)/sizeof(searchpathformats[0])) @@ -46,7 +47,7 @@ int FS_RegisterFileSystemType(void *module, const char *extension, searchpathfun searchpathformats[i].module = module; searchpathformats[i].extension = extension; - searchpathformats[i].funcs = funcs; + searchpathformats[i].OpenNew = OpenNew; searchpathformats[i].loadscan = loadscan; com_fschanged = true; @@ -58,7 +59,7 @@ void FS_UnRegisterFileSystemType(int idx) if ((unsigned int)(idx-1) >= sizeof(searchpathformats)/sizeof(searchpathformats[0])) return; - searchpathformats[idx-1].funcs = NULL; + searchpathformats[idx-1].OpenNew = NULL; searchpathformats[idx-1].module = NULL; com_fschanged = true; @@ -72,7 +73,7 @@ void FS_UnRegisterFileSystemModule(void *module) { if (searchpathformats[i].module == module) { - searchpathformats[i].funcs = NULL; + searchpathformats[i].OpenNew = NULL; searchpathformats[i].module = NULL; found = true; } @@ -113,6 +114,11 @@ char *VFS_GETS(vfsfile_t *vf, char *buffer, int buflen) } *out = '\0'; + //if there's a trailing \r, strip it. + if (out > buffer) + if (out[-1] == '\r') + out[-1] = 0; + return buffer; } @@ -140,7 +146,8 @@ char gamedirfile[MAX_OSPATH]; //the various COM_LoadFiles set these on return int com_filesize; -qboolean com_file_copyprotected; +qboolean com_file_copyprotected;//file should not be available for download. +qboolean com_file_untrusted; //file was downloaded inside a package //char *com_basedir; //obsolete @@ -170,22 +177,236 @@ static void COM_CreatePath (char *path); +//forget a manifest entirely. +void FS_Manifest_Free(ftemanifest_t *man) +{ + int i, j; + if (!man) + return; + Z_Free(man->updateurl); + Z_Free(man->installation); + Z_Free(man->formalname); + Z_Free(man->protocolname); + Z_Free(man->defaultexec); + for (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++) + { + Z_Free(man->gamepath[i].path); + } + for (i = 0; i < sizeof(man->package) / sizeof(man->package[0]); i++) + { + Z_Free(man->package[i].path); + for (j = 0; j < sizeof(man->package[i].mirrors) / sizeof(man->package[i].mirrors[0]); j++) + Z_Free(man->package[i].mirrors[j]); + } + Z_Free(man); +} + +//clone a manifest, so we can hack at it. +static ftemanifest_t *FS_Manifest_Clone(ftemanifest_t *oldm) +{ + ftemanifest_t *newm; + int i, j; + newm = Z_Malloc(sizeof(*newm)); + if (oldm->updateurl) + newm->updateurl = Z_StrDup(oldm->updateurl); + if (oldm->installation) + newm->installation = Z_StrDup(oldm->installation); + if (oldm->formalname) + newm->formalname = Z_StrDup(oldm->formalname); + if (oldm->protocolname) + newm->protocolname = Z_StrDup(oldm->protocolname); + + for (i = 0; i < sizeof(newm->gamepath) / sizeof(newm->gamepath[0]); i++) + { + if (oldm->gamepath[i].path) + newm->gamepath[i].path = Z_StrDup(oldm->gamepath[i].path); + newm->gamepath[i].base = oldm->gamepath[i].base; + } + for (i = 0; i < sizeof(newm->package) / sizeof(newm->package[0]); i++) + { + if (oldm->package[i].path) + newm->package[i].path = Z_StrDup(oldm->package[i].path); + for (j = 0; j < sizeof(newm->package[i].mirrors) / sizeof(newm->package[i].mirrors[0]); j++) + if (oldm->package[i].mirrors[j]) + newm->package[i].mirrors[j] = Z_StrDup(oldm->package[i].mirrors[j]); + } + + return newm; +} + +void FS_Manifest_Print(ftemanifest_t *man) +{ + int i, j; + if (man->updateurl) + Con_Printf("updateurl \"%s\"\n", man->updateurl); + if (man->installation) + Con_Printf("game \"%s\"\n", man->installation); + if (man->formalname) + Con_Printf("name \"%s\"\n", man->formalname); + if (man->protocolname) + Con_Printf("protocolname \"%s\"\n", man->protocolname); + + for (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++) + { + if (man->gamepath[i].path) + { + if (man->gamepath[i].base) + Con_Printf("basegame \"%s\"\n", man->gamepath[i].path); + else + Con_Printf("gamedir \"%s\"\n", man->gamepath[i].path); + } + } + + for (i = 0; i < sizeof(man->package) / sizeof(man->package[0]); i++) + { + if (man->package[i].path) + { + Con_Printf("package \"%s\" 0x%x", man->package[i].path, man->package[i].crc); + for (j = 0; j < sizeof(man->package[i].mirrors) / sizeof(man->package[i].mirrors[0]); j++) + if (man->package[i].mirrors[j]) + Con_Printf(" \"%s\"", man->package[i].mirrors[j]); + Con_Printf("\n", man->package[i].path, man->package[i].crc); + } + } +} + +//forget any mod dirs. +static void FS_Manifest_PurgeGamedirs(ftemanifest_t *man) +{ + int i; + for (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++) + { + if (man->gamepath[i].path && !man->gamepath[i].base) + { + Z_Free(man->gamepath[i].path); + man->gamepath[i].path = NULL; + } + } +} + +//create a new empty manifest with default values. +static ftemanifest_t *FS_Manifest_Create(void) +{ + ftemanifest_t *man = Z_Malloc(sizeof(*man)); + +// man->installation = Z_StrDup("quake"); + man->formalname = Z_StrDup(FULLENGINENAME); + return man; +} +//parse Cmd_Argv tokens into the manifest. +static void FS_Manifest_ParseTokens(ftemanifest_t *man) +{ + char *fname; + if (!Cmd_Argc()) + return; + fname = Cmd_Argv(0); + + if (*fname == '*') + fname++; + + if (!stricmp(fname, "game")) + { + Z_Free(man->installation); + man->installation = Z_StrDup(Cmd_Argv(1)); + } + else if (!stricmp(fname, "name")) + { + Z_Free(man->formalname); + man->formalname = Z_StrDup(Cmd_Argv(1)); + } + else if (!stricmp(fname, "protocolname")) + { + Z_Free(man->protocolname); + man->protocolname = Z_StrDup(Cmd_Argv(1)); + } + else if (!stricmp(fname, "basegame") || !stricmp(fname, "gamedir")) + { + int i; + char *newdir = Cmd_Argv(1); + + //reject various evil path arguments. + if (!*newdir || strchr(newdir, '\n') || strchr(newdir, '\r') || strchr(newdir, '.') || strchr(newdir, ':') || strchr(newdir, '?') || strchr(newdir, '*') || strchr(newdir, '/') || strchr(newdir, '\\') || strchr(newdir, '$')) + { + Con_Printf("Illegal path specified: %s\n", newdir); + } + else + { + for (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++) + { + if (!man->gamepath[i].path) + { + man->gamepath[i].base = !stricmp(fname, "basegame"); + man->gamepath[i].path = Z_StrDup(newdir); + break; + } + } + if (i == sizeof(man->gamepath) / sizeof(man->gamepath[0])) + { + Con_Printf("Too many game paths specified in manifest\n"); + } + } + } + else + { + int crc; + int i, j; + if (!stricmp(fname, "package")) + Cmd_ShiftArgs(1, false); + + crc = strtoul(Cmd_Argv(1), NULL, 0); + + for (i = 0; i < sizeof(man->package) / sizeof(man->package[0]); i++) + { + if (!man->package[i].path) + { + man->package[i].path = Z_StrDup(Cmd_Argv(0)); + man->package[i].crc = crc; + for (j = 0; j < Cmd_Argc()-2 && j < sizeof(man->package[i].mirrors) / sizeof(man->package[i].mirrors[0]); j++) + { + man->package[i].mirrors[j] = Z_StrDup(Cmd_Argv(2+j)); + } + break; + } + } + if (i == sizeof(man->package) / sizeof(man->package[0])) + { + Con_Printf("Too many packages specified in manifest\n"); + } + } +} +//read a manifest file +ftemanifest_t *FS_Manifest_Parse(const char *data) +{ + ftemanifest_t *man; + if (!data) + return NULL; + while (*data == ' ' || *data == '\t' || *data == '\r' || *data == '\n') + data++; + if (!*data) + return NULL; + + + man = FS_Manifest_Create(); + + while (data && *data) + { + data = Cmd_TokenizeString((char*)data, false, false); + FS_Manifest_ParseTokens(man); + } + return man; +} //====================================================================================================== - - typedef struct searchpath_s { - const searchpathfuncs_t *funcs; - qboolean copyprotected; //don't allow downloads from here. - qboolean istemporary; - qboolean isexplicit; //explicitly loaded (ie: id1|qw|$gamedir|fte) - qboolean referenced; - void *handle; + searchpathfuncs_t *handle; + unsigned int flags; + + char logicalpath[MAX_OSPATH]; //printable hunam-readable location of the package. generally includes a system path, including nested packages. char purepath[256]; //server tracks the path used to load them so it can tell the client int crc_check; //client sorts packs according to this checksum int crc_reply; //client sends a different crc back to the server, for the paks it's actually loaded. @@ -194,9 +415,15 @@ typedef struct searchpath_s struct searchpath_s *nextpure; } searchpath_t; -searchpath_t *com_searchpaths; -searchpath_t *com_purepaths; -searchpath_t *com_base_searchpaths; // without gamedirs +static ftemanifest_t *fs_manifest; //currently active manifest. +static searchpath_t *com_searchpaths; +static searchpath_t *com_purepaths; +static searchpath_t *com_base_searchpaths; // without gamedirs + +static int fs_puremode; //0=deprioritise pure, 1=prioritise pure, 2=pure only. +static char *fs_purenames; //list of allowed packages +static char *fs_purecrcs; //list of crcs for those packages. one token per package. +static unsigned int fs_pureseed; //used as a key so the server knows we're obeying. completely unreliable/redundant in an open source project, but needed for q3 network compat. int QDECL COM_FileSize(const char *path) { @@ -206,6 +433,22 @@ int QDECL COM_FileSize(const char *path) return len; } +//appends a / on the end of the directory if it does not already have one. +void FS_CleanDir(char *out, int outlen) +{ + int olen = strlen(out); + if (!olen || olen >= outlen-1) + return; + + if (out[olen-1] == '\\') + out[olen-1] = '/'; + else if (out[olen-1] != '/') + { + out[olen+1] = '\0'; + out[olen] = '/'; + } +} + /* ============ COM_Path_f @@ -214,21 +457,25 @@ COM_Path_f */ void COM_Path_f (void) { - char path[MAX_OSPATH]; searchpath_t *s; Con_TPrintf (TL_CURRENTSEARCHPATH); - if (com_purepaths) + if (com_purepaths || fs_puremode) { Con_Printf ("Pure paths:\n"); for (s=com_purepaths ; s ; s=s->nextpure) { - s->funcs->GetDisplayPath(s->handle, path, sizeof(path)); - Con_Printf("%s %s%s%s\n", path, s->referenced?"(ref)":"", s->istemporary?"(temp)":"", s->copyprotected?"(c)":""); + Con_Printf("%s %s%s%s\n", s->logicalpath, + (s->flags & SPF_REFERENCED)?"(ref)":"", + (s->flags & SPF_TEMPORARY)?"(temp)":"", + (s->flags & SPF_COPYPROTECTED)?"(c)":""); } Con_Printf ("----------\n"); - Con_Printf ("Impure paths:\n"); + if (fs_puremode == 2) + Con_Printf ("Inactive paths:\n"); + else + Con_Printf ("Impure paths:\n"); } @@ -237,8 +484,10 @@ void COM_Path_f (void) if (s == com_base_searchpaths) Con_Printf ("----------\n"); - s->funcs->GetDisplayPath(s->handle, path, sizeof(path)); - Con_Printf("%s %s%s%s\n", path, s->referenced?"(ref)":"", s->istemporary?"(temp)":"", s->copyprotected?"(c)":""); + Con_Printf("%s %s%s%s\n", s->logicalpath, + (s->flags & SPF_REFERENCED)?"(ref)":"", + (s->flags & SPF_TEMPORARY)?"(temp)":"", + (s->flags & SPF_COPYPROTECTED)?"(c)":""); } } @@ -256,12 +505,9 @@ static int QDECL COM_Dir_List(const char *name, int size, void *parm, void *spat for (s=com_searchpaths ; s ; s=s->next) { if (s->handle == spath) - { - s->funcs->GetDisplayPath(s->handle, pbuf, sizeof(pbuf)); break; - } } - Con_Printf("%s (%i) (%s)\n", name, size, pbuf); + Con_Printf("%s (%i) (%s)\n", name, size, s?s->logicalpath:"??"); return 1; } @@ -291,18 +537,16 @@ COM_Locate_f */ void COM_Locate_f (void) { - char path[MAX_OSPATH]; flocation_t loc; if (FS_FLocateFile(Cmd_Argv(1), FSLFRT_LENGTH, &loc)>=0) { - loc.search->funcs->GetDisplayPath(loc.search->handle, path, sizeof(path)); if (!*loc.rawname) { - Con_Printf("File is %i bytes compressed inside %s\n", loc.len, path); + Con_Printf("File is %i bytes compressed inside %s\n", loc.len, loc.search->logicalpath); } else { - Con_Printf("Inside %s (%i bytes)\n %s\n", loc.rawname, loc.len, path); + Con_Printf("Inside %s (%i bytes)\n %s\n", loc.rawname, loc.len, loc.search->logicalpath); } } else @@ -502,12 +746,15 @@ void FS_RebuildFSHash(void) { //go for the pure paths first. for (search = com_purepaths; search; search = search->nextpure) { - search->funcs->BuildHash(search->handle, depth++, FS_AddFileHash); + search->handle->BuildHash(search->handle, depth++, FS_AddFileHash); } } - for (search = com_searchpaths ; search ; search = search->next) + if (fs_puremode < 2) { - search->funcs->BuildHash(search->handle, depth++, FS_AddFileHash); + for (search = com_searchpaths ; search ; search = search->next) + { + search->handle->BuildHash(search->handle, depth++, FS_AddFileHash); + } } com_fschanged = false; @@ -556,43 +803,48 @@ int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation for (search = com_purepaths ; search ; search = search->nextpure) { fs_finds++; - if (search->funcs->FindFile(search->handle, loc, filename, pf)) + if (search->handle->FindFile(search->handle, loc, filename, pf)) { if (loc) { - search->referenced |= fs_referencetype; + search->flags |= fs_referencetype; loc->search = search; len = loc->len; } else len = 0; - com_file_copyprotected = search->copyprotected; + com_file_copyprotected = !!(search->flags & SPF_COPYPROTECTED); + com_file_untrusted = !!(search->flags & SPF_UNTRUSTED); goto out; } - depth += (search->isexplicit || returntype == FSLFRT_DEPTH_ANYPATH); + depth += ((search->flags & SPF_EXPLICIT) || returntype == FSLFRT_DEPTH_ANYPATH); } } + if (fs_puremode < 2) + { // // search through the path, one element at a time // - for (search = com_searchpaths ; search ; search = search->next) - { - fs_finds++; - if (search->funcs->FindFile(search->handle, loc, filename, pf)) + for (search = com_searchpaths ; search ; search = search->next) { - search->referenced |= fs_referencetype; - if (loc) + fs_finds++; + if (search->handle->FindFile(search->handle, loc, filename, pf)) { - loc->search = search; - len = loc->len; + search->flags |= fs_referencetype; + if (loc) + { + loc->search = search; + len = loc->len; + } + else + len = 1; + com_file_copyprotected = !!(search->flags & SPF_COPYPROTECTED); + com_file_untrusted = !!(search->flags & SPF_UNTRUSTED); + goto out; } - else - len = 1; - com_file_copyprotected = search->copyprotected; - goto out; + depth += ((search->flags & SPF_EXPLICIT) || returntype == FSLFRT_DEPTH_ANYPATH); } - depth += (search->isexplicit || returntype == FSLFRT_DEPTH_ANYPATH); } fail: if (loc) @@ -642,7 +894,7 @@ qboolean FS_GetPackageDownloadable(const char *package) for (search = com_searchpaths ; search ; search = search->next) { if (!strcmp(package, search->purepath)) - return !search->copyprotected; + return !(search->flags & SPF_COPYPROTECTED); } return false; } @@ -665,8 +917,8 @@ char *FS_GetPackHashes(char *buffer, int buffersize, qboolean referencedonly) { for (search = com_searchpaths ; search ; search = search->next) { - if (!search->crc_check && search->funcs->GeneratePureCRC) - search->crc_check = search->funcs->GeneratePureCRC(search->handle, 0, 0); + if (!search->crc_check && search->handle->GeneratePureCRC) + search->crc_check = search->handle->GeneratePureCRC(search->handle, 0, 0); if (search->crc_check) { Q_strncatz(buffer, va("%i ", search->crc_check), buffersize); @@ -693,9 +945,9 @@ char *FS_GetPackNames(char *buffer, int buffersize, int referencedonly, qboolean { for (search = com_purepaths ; search ; search = search->nextpure) { - if (referencedonly == 0 && !search->referenced) + if (referencedonly == 0 && !(search->flags & SPF_REFERENCED)) continue; - if (referencedonly == 2 && search->referenced) + if (referencedonly == 2 && (search->flags & SPF_REFERENCED)) Q_strncatz(buffer, "*", buffersize); if (!ext) @@ -714,18 +966,18 @@ char *FS_GetPackNames(char *buffer, int buffersize, int referencedonly, qboolean { for (search = com_searchpaths ; search ; search = search->next) { - if (!search->crc_check && search->funcs->GeneratePureCRC) - search->crc_check = search->funcs->GeneratePureCRC(search->handle, 0, 0); + if (!search->crc_check && search->handle->GeneratePureCRC) + search->crc_check = search->handle->GeneratePureCRC(search->handle, 0, 0); if (search->crc_check) { - if (referencedonly == 0 && !search->referenced) + if (referencedonly == 0 && !(search->flags & SPF_REFERENCED)) continue; - if (referencedonly == 2 && search->referenced) + if (referencedonly == 2 && (search->flags & SPF_REFERENCED)) { // '*' prefix is meant to mean 'referenced'. //really all that means to the client is that it definitely wants to download it. //if its copyrighted, the client shouldn't try to do so, as it won't be allowed. - if (!search->copyprotected) + if (!(search->flags & SPF_COPYPROTECTED)) Q_strncatz(buffer, "*", buffersize); } @@ -747,11 +999,15 @@ char *FS_GetPackNames(char *buffer, int buffersize, int referencedonly, qboolean void FS_ReferenceControl(unsigned int refflag, unsigned int resetflags) { searchpath_t *s; + + refflag &= SPF_REFERENCED; + resetflags &= SPF_REFERENCED; + if (resetflags) { for (s=com_searchpaths ; s ; s=s->next) { - s->referenced &= ~resetflags; + s->flags &= ~resetflags; } } @@ -805,6 +1061,7 @@ int COM_FOpenFile(char *filename, FILE **file) if (loc.search) { com_file_copyprotected = loc.search->copyprotected; + com_file_untrusted = !!(loc.search->flags & SPF_UNTRUSTED); com_filesize = COM_FOpenLocationFILE(&loc, file); } else @@ -890,7 +1147,7 @@ vfsfile_t *VFS_Filter(const char *filename, vfsfile_t *handle) #ifdef AVAIL_ZLIB // if (!stricmp(ext, ".gz")) { - return FS_DecompressGZip(handle); + return FS_DecompressGZip(handle, NULL); } #endif return handle; @@ -913,6 +1170,7 @@ qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out snprintf(out, outlen, "%s%s/%s", com_quakedir, gamedirfile, fname); break; case FS_SKINS: + //FIXME: validate that qw/ is actually loaded and valid if (*com_homedir) snprintf(out, outlen, "%sqw/skins/%s", com_homedir, fname); else @@ -931,6 +1189,7 @@ qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out snprintf(out, outlen, "%s%s", com_quakedir, fname); break; case FS_CONFIGONLY: + //FIXME: use the highest-precidence active system path instead if (*com_homedir) snprintf(out, outlen, "%sfte/%s", com_homedir, fname); else @@ -984,19 +1243,11 @@ vfsfile_t *FS_OpenVFS(const char *filename, const char *mode, enum fs_relative r if (*mode == 'w') COM_CreatePath(fullname); return VFSOS_Open(fullname, mode); - case FS_GAME: - if (*com_homedir) - snprintf(fullname, sizeof(fullname), "%s%s/%s", com_homedir, gamedirfile, filename); - else - snprintf(fullname, sizeof(fullname), "%s%s/%s", com_quakedir, gamedirfile, filename); + case FS_GAME: //load from paks in preference to system paths. overwriting be damned. + case FS_SKINS: //load from paks in preference to system paths. overwriting be damned. + FS_NativePath(filename, relativeto, fullname, sizeof(fullname)); break; - case FS_SKINS: - if (*com_homedir) - snprintf(fullname, sizeof(fullname), "%sqw/skins/%s", com_homedir, filename); - else - snprintf(fullname, sizeof(fullname), "%sqw/skins/%s", com_quakedir, filename); - break; - case FS_ROOT: + case FS_ROOT: //always bypass packs and gamedirs if (*com_homedir) { snprintf(fullname, sizeof(fullname), "%s%s", com_homedir, filename); @@ -1006,7 +1257,7 @@ vfsfile_t *FS_OpenVFS(const char *filename, const char *mode, enum fs_relative r } snprintf(fullname, sizeof(fullname), "%s%s", com_quakedir, filename); return VFSOS_Open(fullname, mode); - case FS_CONFIGONLY: + case FS_CONFIGONLY: //always bypass packs+pure. if (*com_homedir) { snprintf(fullname, sizeof(fullname), "%sfte/%s", com_homedir, filename); @@ -1025,8 +1276,9 @@ vfsfile_t *FS_OpenVFS(const char *filename, const char *mode, enum fs_relative r if (loc.search) { - com_file_copyprotected = loc.search->copyprotected; - return VFS_Filter(filename, loc.search->funcs->OpenVFS(loc.search->handle, &loc, mode)); + com_file_copyprotected = !!(loc.search->flags & SPF_COPYPROTECTED); + com_file_untrusted = !!(loc.search->flags & SPF_UNTRUSTED); + return VFS_Filter(filename, loc.search->handle->OpenVFS(loc.search->handle, &loc, mode)); } //if we're meant to be writing, best write to it. @@ -1043,8 +1295,9 @@ vfsfile_t *FS_OpenReadLocation(flocation_t *location) { if (location->search) { - com_file_copyprotected = location->search->copyprotected; - return VFS_Filter(NULL, location->search->funcs->OpenVFS(location->search->handle, location, "rb")); + com_file_copyprotected = !!(location->search->flags & SPF_COPYPROTECTED); + com_file_untrusted = !!(location->search->flags & SPF_UNTRUSTED); + return VFS_Filter(NULL, location->search->handle->OpenVFS(location->search->handle, location, "rb")); } return NULL; } @@ -1161,7 +1414,7 @@ qbyte *COM_LoadFile (const char *path, int usehunk) return NULL; //wasn't found - f = loc.search->funcs->OpenVFS(loc.search->handle, &loc, "rb"); + f = loc.search->handle->OpenVFS(loc.search->handle, &loc, "rb"); if (!f) return NULL; @@ -1258,13 +1511,13 @@ void FS_FreeFile(void *file) -void COM_EnumerateFiles (const char *match, int (QDECL *func)(const char *, int, void *, void *), void *parm) +void COM_EnumerateFiles (const char *match, int (QDECL *func)(const char *, int, void *, searchpathfuncs_t*), void *parm) { searchpath_t *search; for (search = com_searchpaths; search ; search = search->next) { // is the element a pak file? - if (!search->funcs->EnumerateFiles(search->handle, match, func, parm)) + if (!search->handle->EnumerateFiles(search->handle, match, func, parm)) break; } } @@ -1276,13 +1529,13 @@ void COM_FlushTempoaryPacks(void) while (*link) { sp = *link; - if (sp->istemporary) + if (sp->flags & SPF_TEMPORARY) { FS_FlushFSHashReally(); *link = sp->next; - sp->funcs->ClosePath(sp->handle); + sp->handle->ClosePath(sp->handle); Z_Free (sp); } else @@ -1294,101 +1547,43 @@ void COM_FlushTempoaryPacks(void) qboolean COM_LoadMapPackFile (const char *filename, int ofs) { return false; -/* - dpackheader_t header; - int i; - packfile_t *newfiles; - int numpackfiles; - pack_t *pack; - FILE *packhandle; - dpackfile_t info; - int fstart; - char *ballsup; - - flocation_t loc; - - FS_FLocateFile(filename, FSLFRT_LENGTH, &loc); - - if (!loc.search) - { - Con_Printf("Couldn't refind file\n"); - return false; - } - - if (!*loc.rawname) - { - Con_Printf("File %s is compressed\n"); - return false; - } - packhandle = fopen(loc.rawname, "rb"); - if (!packhandle) - { - Con_Printf("Couldn't reopen file\n"); - return false; - } - fseek(packhandle, loc.offset, SEEK_SET); - - fstart = loc.offset; - fseek(packhandle, ofs+fstart, SEEK_SET); - - fread (&header, 1, sizeof(header), packhandle); - if (header.id[0] != 'P' || header.id[1] != 'A' - || header.id[2] != 'C' || header.id[3] != 'K') - { - return false; - } - header.dirofs = LittleLong (header.dirofs); - header.dirlen = LittleLong (header.dirlen); - - numpackfiles = header.dirlen / sizeof(dpackfile_t); - - newfiles = (packfile_t*)Z_Malloc (numpackfiles * sizeof(packfile_t)); - - fseek (packhandle, header.dirofs+fstart, SEEK_SET); - - pack = (pack_t*)Z_Malloc (sizeof (pack_t)); - -// parse the directory - for (i=0 ; ifilename, loc.rawname); - pack->handle = packhandle; - pack->numfiles = numpackfiles; - pack->files = newfiles; - - Con_TPrintf (TL_ADDEDPACKFILE, filename, numpackfiles); - - COM_AddPathHandle(&packfilefuncs, pack, true, true); - return true; -*/ } -static searchpath_t *FS_AddPathHandle(const char *purepath, const char *probablepath, const searchpathfuncs_t *funcs, void *handle, qboolean copyprotect, qboolean istemporary, qboolean isexplicit, unsigned int loadstuff); +static searchpath_t *FS_AddPathHandle(searchpath_t **oldpaths, const char *purepath, const char *probablepath, searchpathfuncs_t *handle, unsigned int flags, unsigned int loadstuff); +searchpathfuncs_t *FS_GetOldPath(searchpath_t **oldpaths, const char *dir) +{ + searchpath_t *p; + searchpathfuncs_t *r = NULL; + while(*oldpaths) + { + p = *oldpaths; + + if (!stricmp(p->logicalpath, dir)) + { + *oldpaths = p->next; + r = p->handle; + Z_Free(p); + break; + } + + oldpaths = &(*oldpaths)->next; + } + return r; +} typedef struct { - const searchpathfuncs_t *funcs; - searchpath_t *parentpath; + searchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, const char *desc); + searchpath_t **oldpaths; const char *parentdesc; const char *puredesc; } wildpaks_t; -static int QDECL FS_AddWildDataFiles (const char *descriptor, int size, void *vparam, void *path) +static int QDECL FS_AddWildDataFiles (const char *descriptor, int size, void *vparam, searchpathfuncs_t *funcs) { wildpaks_t *param = vparam; vfsfile_t *vfs; - const searchpathfuncs_t *funcs = param->funcs; searchpath_t *search; - void *pak; + searchpathfuncs_t *newpak; char pakfile[MAX_OSPATH]; char purefile[MAX_OSPATH]; flocation_t loc; @@ -1397,115 +1592,183 @@ static int QDECL FS_AddWildDataFiles (const char *descriptor, int size, void *vp for (search = com_searchpaths; search; search = search->next) { - if (search->funcs != funcs) - continue; - if (!stricmp((char*)search->handle, pakfile)) //assumption: first member of structure is a char array + if (!stricmp(search->logicalpath, pakfile)) //assumption: first member of structure is a char array return true; //already loaded (base paths?) } - search = param->parentpath; - - fs_finds++; - if (!search->funcs->FindFile(search->handle, &loc, descriptor, NULL)) - return true; //not found.. - vfs = search->funcs->OpenVFS(search->handle, &loc, "rb"); - if (!vfs) - return true; - pak = funcs->OpenNew (vfs, pakfile); - if (!pak) + newpak = FS_GetOldPath(param->oldpaths, pakfile); + if (!newpak) { - VFS_CLOSE(vfs); - return true; + fs_finds++; + if (!funcs->FindFile(funcs, &loc, descriptor, NULL)) + return true; //not found.. + vfs = funcs->OpenVFS(funcs, &loc, "rb"); + if (!vfs) + return true; + newpak = param->OpenNew (vfs, pakfile); + if (!newpak) + { + VFS_CLOSE(vfs); + return true; + } } - Q_snprintfz (pakfile, sizeof(pakfile), "%s%s/", param->parentdesc, descriptor); + Q_snprintfz (pakfile, sizeof(pakfile), "%s%s", param->parentdesc, descriptor); if (*param->puredesc) snprintf (purefile, sizeof(purefile), "%s/%s", param->puredesc, descriptor); else Q_strncpyz(purefile, descriptor, sizeof(purefile)); - FS_AddPathHandle(purefile, pakfile, funcs, pak, !Q_strcasecmp(descriptor, "pak"), false, false, (unsigned int)-1); + FS_AddPathHandle(param->oldpaths, purefile, pakfile, newpak, ((!Q_strncasecmp(descriptor, "pak", 3))?SPF_COPYPROTECTED:0), (unsigned int)-1); return true; } -static void FS_AddDataFiles(const char *purepath, const char *pathto, searchpath_t *search, const char *extension, searchpathfuncs_t *funcs) +static void FS_AddDataFiles(searchpath_t **oldpaths, const char *purepath, const char *logicalpath, searchpath_t *search, const char *extension, searchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, const char *desc)) { //search is the parent int i; - void *handle; + searchpathfuncs_t *handle; char pakfile[MAX_OSPATH]; + char logicalpaths[MAX_OSPATH]; //with a slash char purefile[MAX_OSPATH]; vfsfile_t *vfs; flocation_t loc; wildpaks_t wp; + Q_strncpyz(logicalpaths, logicalpath, sizeof(logicalpaths)); + FS_CleanDir(logicalpaths, sizeof(logicalpaths)); + //first load all the numbered pak files for (i=0 ; ; i++) { snprintf (pakfile, sizeof(pakfile), "pak%i.%s", i, extension); fs_finds++; - if (!search->funcs->FindFile(search->handle, &loc, pakfile, NULL)) + if (!search->handle->FindFile(search->handle, &loc, pakfile, NULL)) break; //not found.. - snprintf (pakfile, sizeof(pakfile), "%spak%i.%s", pathto, i, extension); - vfs = search->funcs->OpenVFS(search->handle, &loc, "r"); - if (!vfs) - break; - handle = funcs->OpenNew (vfs, pakfile); + + snprintf (pakfile, sizeof(pakfile), "%spak%i.%s", logicalpaths, i, extension); + snprintf (purefile, sizeof(purefile), "%s/pak%i.%s", purepath, i, extension); + + handle = FS_GetOldPath(oldpaths, pakfile); if (!handle) - break; - snprintf (pakfile, sizeof(pakfile), "%spak%i.%s/", pathto, i, extension); - snprintf (purefile, sizeof(pakfile), "%s/pak%i.%s", purepath, i, extension); - FS_AddPathHandle(purefile, pakfile, funcs, handle, true, false, false, (unsigned int)-1); + { + vfs = search->handle->OpenVFS(search->handle, &loc, "r"); + if (!vfs) + break; + handle = OpenNew (vfs, pakfile); + if (!handle) + break; + } + FS_AddPathHandle(oldpaths, purefile, pakfile, handle, SPF_COPYPROTECTED, (unsigned int)-1); } //now load the random ones Q_snprintfz (pakfile, sizeof(pakfile), "*.%s", extension); - wp.funcs = funcs; - wp.parentdesc = pathto; - wp.parentpath = search; + wp.OpenNew = OpenNew; + wp.parentdesc = logicalpaths; wp.puredesc = purepath; - search->funcs->EnumerateFiles(search->handle, pakfile, FS_AddWildDataFiles, &wp); + wp.oldpaths = oldpaths; + search->handle->EnumerateFiles(search->handle, pakfile, FS_AddWildDataFiles, &wp); + + + //and load any named in the manifest (this happens when they're crced or whatever) + { + int ptlen, palen; + ptlen = strlen(purepath); + for (i = 0; i < sizeof(fs_manifest->package) / sizeof(fs_manifest->package[0]); i++) + { + if (fs_manifest->package[i].path && !strcmp(COM_FileExtension(fs_manifest->package[i].path), extension)) + { + palen = strlen(fs_manifest->package[i].path); + if (palen > ptlen && (fs_manifest->package[i].path[ptlen] == '/' || fs_manifest->package[i].path[ptlen] == '\\' )&& !strncmp(purepath, fs_manifest->package[i].path, ptlen)) + { + searchpath_t *oldp; + char pname[MAX_OSPATH]; + char lname[MAX_OSPATH]; + snprintf(lname, sizeof(lname), "%#x", fs_manifest->package[i].crc); + if (!FS_GenCachedPakName(fs_manifest->package[i].path, lname, pname, sizeof(pname))) + continue; + snprintf (lname, sizeof(lname), "%s%s", logicalpath, pname+ptlen+1); + + for (oldp = com_searchpaths; oldp; oldp = oldp->next) + { + if (!stricmp(oldp->purepath, fs_manifest->package[i].path)) + break; + if (!stricmp(oldp->logicalpath, lname)) + break; + } + if (!oldp) + { + handle = FS_GetOldPath(oldpaths, lname); + if (!handle) + { + if (search->handle->FindFile(search->handle, &loc, pname+ptlen+1, NULL)) + { + vfs = search->handle->OpenVFS(search->handle, &loc, "r"); + if (vfs) + handle = OpenNew (vfs, lname); + } + } + if (handle) + { + int truecrc = handle->GeneratePureCRC(handle, 0, false); + if (truecrc != fs_manifest->package[i].crc) + { + Con_Printf(CON_ERROR "File \"%s\" has hash %#x (required: %#x). Please delete it or move it away\n", lname, truecrc, fs_manifest->package[i].crc); + handle->ClosePath(handle); + handle = NULL; + } + } + if (handle) + FS_AddPathHandle(oldpaths, fs_manifest->package[i].path, lname, handle, SPF_COPYPROTECTED|SPF_UNTRUSTED, (unsigned int)-1); + } + } + } + } + } } -static searchpath_t *FS_AddPathHandle(const char *purepath, const char *probablepath, const searchpathfuncs_t *funcs, void *handle, qboolean copyprotect, qboolean istemporary, qboolean isexplicit, unsigned int loadstuff) +static searchpath_t *FS_AddPathHandle(searchpath_t **oldpaths, const char *purepath, const char *logicalpath, searchpathfuncs_t *handle, unsigned int flags, unsigned int loadstuff) { unsigned int i; searchpath_t *search, **link; - if (!funcs) + if (!handle) { - Con_Printf("COM_AddPathHandle: %s format not supported in this build\n", probablepath); + Con_Printf("COM_AddPathHandle: not a valid handle (%s)\n", logicalpath); + return NULL; + } + + if (handle->fsver != FSVER) + { + Con_Printf("%s: file system driver is outdated (%u should be %u)\n", logicalpath, handle->fsver, FSVER); + handle->ClosePath(handle); return NULL; } search = (searchpath_t*)Z_Malloc (sizeof(searchpath_t)); - search->copyprotected = copyprotect; - search->istemporary = istemporary; - search->isexplicit = isexplicit; + search->flags = flags; search->handle = handle; - search->funcs = funcs; - if (funcs == &osfilefuncs) - Q_strncpyz(search->purepath, probablepath, sizeof(search->purepath)); - else - Q_strncpyz(search->purepath, purepath, sizeof(search->purepath)); + Q_strncpyz(search->purepath, purepath, sizeof(search->purepath)); + Q_strncpyz(search->logicalpath, logicalpath, sizeof(search->logicalpath)); //temp packages also do not nest - if (!istemporary) + if (!(flags & SPF_TEMPORARY)) { for (i = 0; i < sizeof(searchpathformats)/sizeof(searchpathformats[0]); i++) { - if (!searchpathformats[i].extension || !searchpathformats[i].funcs || !searchpathformats[i].funcs->OpenNew || !searchpathformats[i].loadscan) + if (!searchpathformats[i].extension || !searchpathformats[i].OpenNew || !searchpathformats[i].loadscan) continue; if (loadstuff & (1<next) { - if (search->funcs->PollChanges) - com_fschanged |= search->funcs->PollChanges(search->handle); + if (search->handle->PollChanges) + com_fschanged |= search->handle->PollChanges(search->handle); } } } @@ -1562,7 +1825,7 @@ Sets com_gamedir, adds the directory to the head of the path, then loads and adds pak1.pak pak2.pak ... ================ */ -void FS_AddGameDirectory (const char *puredir, const char *dir, unsigned int loadstuff) +void FS_AddGameDirectory (searchpath_t **oldpaths, const char *puredir, const char *dir, unsigned int loadstuff) { searchpath_t *search; @@ -1578,77 +1841,46 @@ void FS_AddGameDirectory (const char *puredir, const char *dir, unsigned int loa for (search = com_searchpaths; search; search = search->next) { - if (search->funcs != &osfilefuncs) - continue; - if (!stricmp(search->handle, dir)) + if (!stricmp(search->logicalpath, dir)) return; //already loaded (base paths?) } // // add the directory to the search path // + handle = FS_GetOldPath(oldpaths, dir); + if (!handle) + handle = VFSOS_OpenPath(NULL, dir); - handle = osfilefuncs.OpenNew(NULL, dir); - FS_AddPathHandle((*dir?puredir:""), va("%s/", dir), &osfilefuncs, handle, false, false, true, loadstuff); + FS_AddPathHandle(oldpaths, puredir, dir, handle, SPF_EXPLICIT, loadstuff); } -char *COM_NextPath (char *prevpath) +searchpathfuncs_t *COM_IteratePaths (void **iterator, char *buffer, int buffersize) { searchpath_t *s; - char *prev; + void *prev; prev = NULL; for (s=com_searchpaths ; s ; s=s->next) { - if (s->funcs != &osfilefuncs) + if (!(s->flags & SPF_EXPLICIT)) continue; - if (prevpath == prev) - return s->purepath; - prev = s->purepath; + if (*iterator == prev) + { + *iterator = s->handle; + Q_strncpyz(buffer, s->logicalpath, buffersize-1); + FS_CleanDir(buffer, buffersize); + return s->handle; + } + prev = s->handle; } + *iterator = NULL; + *buffer = 0; return NULL; } -#if 0//ndef CLIENTONLY -char *COM_GetPathInfo (int i, int *crc) -{ -//#ifdef WEBSERVER -// extern cvar_t httpserver; -//#endif - - searchpath_t *s; - static char name[MAX_OSPATH]; -// char adr[MAX_ADR_SIZE]; - char *protocol; - - for (s=com_searchpaths ; s ; s=s->next) - { - i--; - if (!i) - break; - } - if (i) //too high. - return NULL; - -/* -#ifdef WEBSERVER - if (httpserver.value) - protocol = va("http://%s/", NET_AdrToString(adr, sizeof(adr), net_local_sv_ipadr)); - else -#endif - */ - protocol = "qw://"; - - *crc = 0;//s->crc; - strcpy(name, "FIXME"); -// Q_strncpyz(name, va("%s%s", protocol, COM_SkipPath(s->filename)), sizeof(name)); - return name; -} -#endif - - char *FS_GetGamedir(void) { return gamedirfile; @@ -1659,7 +1891,8 @@ char *FS_GetBasedir(void) return com_quakedir; } -void FS_CleanDir(char *in, char *out, int outlen) +//given a 'c:/foo/bar/' path, will extract 'bar'. +void FS_ExtractDir(char *in, char *out, int outlen) { char *end; if (!outlen) @@ -1692,6 +1925,7 @@ void FS_CleanDir(char *in, char *out, int outlen) } *out = 0; } + /* ================ COM_Gamedir @@ -1701,6 +1935,31 @@ Sets the gamedir and path to a different directory. */ void COM_Gamedir (const char *dir) { + ftemanifest_t *man; + if (!fs_manifest) + FS_ChangeGame(NULL, true); + + man = FS_Manifest_Clone(fs_manifest); + FS_Manifest_PurgeGamedirs(man); + if (*dir) + { + char *dup = Z_StrDup(dir); + dir = dup; + while ((dir = COM_ParseStringSet(dir))) + { + if (!strcmp(dir, ";")) + continue; + if (!*com_token) + continue; + + Cmd_TokenizeString(va("gamedir \"%s\"", com_token), false, false); + FS_Manifest_ParseTokens(man); + } + Z_Free(dup); + } + FS_ChangeGame(man, true); + +#if 0 char thispath[64]; searchpath_t *next; qboolean isbase; @@ -1719,6 +1978,7 @@ void COM_Gamedir (const char *dir) { if (next == com_base_searchpaths) isbase = true; + if (next->funcs == &osfilefuncs) { FS_CleanDir(next->purepath, thispath, sizeof(thispath)); @@ -1758,7 +2018,7 @@ void COM_Gamedir (const char *dir) // while (com_searchpaths != com_base_searchpaths) { - com_searchpaths->funcs->ClosePath(com_searchpaths->handle); + com_searchpaths->handle->ClosePath(com_searchpaths->handle); next = com_searchpaths->next; Z_Free (com_searchpaths); com_searchpaths = next; @@ -1818,11 +2078,13 @@ void COM_Gamedir (const char *dir) //FIXME: load new palette, if different cause a vid_restart. +#endif #endif } +#define QCFG "set allow_download_refpackages 0\n" /*stuff that makes dp-only mods work a bit better*/ -#define DPCOMPAT "set _cl_playermodel \"\"\n set dpcompat_set 1\n set dpcompat_trailparticles 1\nset dpcompat_corruptglobals 1\nset vid_pixelheight 1\n" +#define DPCOMPAT QCFG "set _cl_playermodel \"\"\n set dpcompat_set 1\n set dpcompat_trailparticles 1\nset dpcompat_corruptglobals 1\nset vid_pixelheight 1\n" /*nexuiz/xonotic has a few quirks/annoyances...*/ #define NEXCFG DPCOMPAT "set r_particlesdesc effectinfo\nset sv_maxairspeed \"400\"\nset sv_jumpvelocity 270\nset sv_mintic \"0.01\"\ncl_nolerp 0\npr_enable_uriget 0\n" /*some modern non-compat settings*/ @@ -1845,9 +2107,10 @@ typedef struct { const char *dir[4]; const char *poshname; //Full name for the game. - const char *downloadaddr; + const char *manifestfile; } gamemode_info_t; const gamemode_info_t gamemode_info[] = { +#define MASTER_PREFIX "FTE-" //note that there is no basic 'fte' gamemode, this is because we aim for network compatability. Darkplaces-Quake is the closest we get. //this is to avoid having too many gamemodes anyway. @@ -1856,12 +2119,12 @@ const gamemode_info_t gamemode_info[] = { //for quake, we also allow extracting all files from paks. some people think it loads faster that way or something. //cmdline switch exename protocol name(dpmaster) identifying file exec dir1 dir2 dir3 dir(fte) full name - {"-quake", "q1", "DarkPlaces-Quake", {"id1/pak0.pak", - "id1/quake.rc"}, NULL, {"id1", "qw", "fte"}, "Quake"/*, "id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/}, - {"-hipnotic", "hipnotic", "Darkplaces-Hipnotic", {"hipnotic/pak0.pak"}, NULL, {"id1", "qw", "hipnotic", "fte"}, "Quake: Scourge of Armagon"}, - {"-rogue", "rogue", "Darkplaces-Rogue", {"rogue/pak0.pak"}, NULL, {"id1", "qw", "rogue", "fte"}, "Quake: Dissolution of Eternity"}, + {"-quake", "q1", MASTER_PREFIX"Quake", {"id1/pak0.pak", + "id1/quake.rc"}, QCFG, {"id1", "qw", "fte"}, "Quake"/*, "id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/}, + {"-hipnotic", "hipnotic", MASTER_PREFIX"Hipnotic",{"hipnotic/pak0.pak"}, QCFG, {"id1", "qw", "hipnotic", "fte"}, "Quake: Scourge of Armagon"}, + {"-rogue", "rogue", MASTER_PREFIX"Rogue", {"rogue/pak0.pak"}, QCFG, {"id1", "qw", "rogue", "fte"}, "Quake: Dissolution of Eternity"}, {"-nexuiz", "nexuiz", "Nexuiz", {"nexuiz.exe"}, NEXCFG, {"data", "ftedata"}, "Nexuiz"}, - {"-xonotic", "xonotic", "Xonotic", {"xonotic.exe"}, NEXCFG, {"data", "ftedata"}, "Xonotic", "data/xonotic-20120308-data.pk3|http://localhost/xonotic-0.6.0.zip"}, + {"-xonotic", "xonotic", "Xonotic", {"xonotic.exe"}, NEXCFG, {"data", "ftedata"}, "Xonotic"}, {"-spark", "spark", "Spark", {"base/src/progs.src", "base/qwprogs.dat", "base/pak0.pak"}, DMFCFG, {"base", }, "Spark"}, @@ -1874,8 +2137,8 @@ const gamemode_info_t gamemode_info[] = { {"-portals", "h2mp", "FTE-H2MP", {"portals/hexen.rc", "portals/pak3.pak"}, HEX2CFG,{"data1", "portals", "fteh2"}, "Hexen II MP"}, {"-hexen2", "hexen2", "FTE-Hexen2", {"data1/pak0.pak"}, HEX2CFG,{"data1", "fteh2"}, "Hexen II"}, - {"-q2", "q2", "FTE-Quake2", {"baseq2/pak0.pak"}, Q2CFG, {"baseq2", "fteq2"}, "Quake II"}, - {"-q3", "q3", "FTE-Quake3", {"baseq3/pak0.pk3"}, Q3CFG, {"baseq3", "fteq3"}, "Quake III Arena"}, + {"-quake2", "q2", "FTE-Quake2", {"baseq2/pak0.pak"}, Q2CFG, {"baseq2", "fteq2"}, "Quake II"}, + {"-quake3", "q3", "FTE-Quake3", {"baseq3/pak0.pk3"}, Q3CFG, {"baseq3", "fteq3"}, "Quake III Arena"}, //can run in windows, needs {"-halflife", "hl", "FTE-HalfLife", {"valve/liblist.gam"}, NULL, {"valve", "ftehl"}, "Half-Life"}, @@ -1893,43 +2156,51 @@ const gamemode_info_t gamemode_info[] = { {NULL} }; -void FS_GenCachedPakName(char *pname, char *crc, char *local, int llen) +qboolean FS_GenCachedPakName(char *pname, char *crc, char *local, int llen) { char *fn; - unsigned int h; + char hex[16]; if (strstr(pname, "dlcache")) { - Q_strncpyz(local, pname, llen); - return; + *local = 0; + return false; } fn = COM_SkipPath(pname); + if (fn == pname) + { //only allow it if it has some game path first. + *local = 0; + return false; + } Q_strncpyz(local, pname, min((fn - pname) + 1, llen)); Q_strncatz(local, "dlcache/", llen); Q_strncatz(local, fn, llen); if (*crc) { Q_strncatz(local, ".", llen); - h = atoi(crc); - Q_strncatz(local, va("%x", h), llen); + snprintf(hex, sizeof(hex), "%x", strtoul(crc, NULL, 0)); + Q_strncatz(local, hex, llen); } + return true; } -qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, int *crc, qboolean copyprotect, qboolean istemporary, qboolean isexplicit) +#if 0 +qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, int *crc, unsigned int flags) { int i; char *ext = COM_FileExtension(pname); - void *handle; + searchpathfuncs_t *handle; + searchpath_t *oldlist = NULL; searchpath_t *sp; for (i = 0; i < sizeof(searchpathformats)/sizeof(searchpathformats[0]); i++) { - if (!searchpathformats[i].extension || !searchpathformats[i].funcs || !searchpathformats[i].funcs->OpenNew) + if (!searchpathformats[i].extension || !searchpathformats[i].OpenNew) continue; if (!strcmp(ext, searchpathformats[i].extension)) { - handle = searchpathformats[i].funcs->OpenNew (vfs, localname); + handle = searchpathformats[i].OpenNew (vfs, localname); if (!handle) { Con_Printf("file %s isn't a %s after all\n", pname, searchpathformats[i].extension); @@ -1937,7 +2208,7 @@ qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, in } if (crc) { - int truecrc = searchpathformats[i].funcs->GeneratePureCRC(handle, 0, false); + int truecrc = handle->GeneratePureCRC(handle, 0, false); if (truecrc != *crc) { *crc = truecrc; @@ -1945,7 +2216,7 @@ qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, in return false; } } - sp = FS_AddPathHandle(pname, localname, searchpathformats[i].funcs, handle, copyprotect, istemporary, isexplicit, (unsigned int)-1); + sp = FS_AddPathHandle(&oldlist, pname, localname, handle, flags, (unsigned int)-1); if (sp) { @@ -1958,7 +2229,34 @@ qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, in VFS_CLOSE(vfs); return false; } +#endif +void FS_PureMode(int puremode, char *packagenames, char *packagecrcs, int pureseed) +{ + qboolean pureflush; + + Z_Free(fs_purenames); + Z_Free(fs_purecrcs); + + pureflush = (fs_puremode != 2 && puremode == 2); + fs_puremode = puremode; + fs_purenames = packagenames?Z_StrDup(packagenames):NULL; + fs_purecrcs = packagecrcs?Z_StrDup(packagecrcs):NULL; + fs_pureseed = pureseed; + + FS_ChangeGame(fs_manifest, false); + + if (pureflush) + { +#ifndef SERVERONLY + Shader_NeedReload(true); +#endif + Mod_ClearAll(); + Cache_Flush(); + } +} + +#if 0 //if a server is using private pak files then load the same version of those, but deprioritise them //crcs are not used, but matched only if the server has a different version from a previous file void FS_ImpurePacks(const char *names, const char *crcs) @@ -1993,11 +2291,13 @@ void FS_ImpurePacks(const char *names, const char *crcs) char local[MAX_OSPATH]; vfsfile_t *vfs; - FS_GenCachedPakName(pname, va("%i", crc), local, sizeof(local)); - vfs = FS_OpenVFS(local, "rb", FS_ROOT); + if (FS_GenCachedPakName(pname, va("%i", crc), local, sizeof(local))) + vfs = FS_OpenVFS(local, "rb", FS_ROOT); + else + vfs = NULL; success = false; if (vfs) - success = FS_LoadPackageFromFile(vfs, pname, local, NULL, true, true, false); + success = FS_LoadPackageFromFile(vfs, pname, local, NULL, SPF_COPYPROTECTED|SPF_TEMPORARY); if (!success) Con_DPrintf("Unable to load matching package file %s\n", pname); @@ -2018,6 +2318,7 @@ void FS_ForceToPure(const char *names, const char *crcs, int seed) int crc; qboolean waspure = com_purepaths != NULL; char *pname; + searchpath_t *oldlist; if (!crcs || !*crcs) { //pure isn't in use. @@ -2033,11 +2334,11 @@ void FS_ForceToPure(const char *names, const char *crcs, int seed) com_purepaths = NULL; for (sp = com_searchpaths; sp; sp = sp->next) { - if (sp->funcs->GeneratePureCRC) + if (sp->handle->GeneratePureCRC) { sp->nextpure = (void*)0x1; - sp->crc_check = sp->funcs->GeneratePureCRC(sp->handle, seed, 0); - sp->crc_reply = sp->funcs->GeneratePureCRC(sp->handle, seed, 1); + sp->crc_check = sp->handle->GeneratePureCRC(sp->handle, seed, 0); + sp->crc_reply = sp->handle->GeneratePureCRC(sp->handle, seed, 1); } else { @@ -2082,23 +2383,25 @@ void FS_ForceToPure(const char *names, const char *crcs, int seed) void *handle; int i; - FS_GenCachedPakName(pname, va("%i", crc), local, sizeof(local)); - vfs = FS_OpenVFS(local, "rb", FS_ROOT); + if (FS_GenCachedPakName(pname, va("%i", crc), local, sizeof(local))) + vfs = FS_OpenVFS(local, "rb", FS_ROOT); + else + vfs = NULL; if (vfs) { for (i = 0; i < sizeof(searchpathformats)/sizeof(searchpathformats[0]); i++) { - if (!searchpathformats[i].extension || !searchpathformats[i].funcs || !searchpathformats[i].funcs->OpenNew) + if (!searchpathformats[i].extension || !searchpathformats[i].OpenNew) continue; if (!strcmp(ext, searchpathformats[i].extension)) { - handle = searchpathformats[i].funcs->OpenNew (vfs, local); + handle = searchpathformats[i].OpenNew (vfs, local); if (!handle) break; - sp = FS_AddPathHandle(pname, local, searchpathformats[i].funcs, handle, true, true, false, (unsigned int)-1); + sp = FS_AddPathHandle(&oldlist, pname, local, handle, SPF_COPYPROTECTED|SPF_TEMPORARY, (unsigned int)-1); - sp->crc_check = sp->funcs->GeneratePureCRC(sp->handle, seed, 0); - sp->crc_reply = sp->funcs->GeneratePureCRC(sp->handle, seed, 1); + sp->crc_check = sp->handle->GeneratePureCRC(sp->handle, seed, 0); + sp->crc_reply = sp->handle->GeneratePureCRC(sp->handle, seed, 1); if (sp->crc_check == crc) { @@ -2124,6 +2427,7 @@ void FS_ForceToPure(const char *names, const char *crcs, int seed) if (com_purepaths && !waspure) Con_Printf("Pure FS activated\n"); } +#endif char *FSQ3_GenerateClientPacksList(char *buffer, int maxlen, int basechecksum) { //this is for q3 compatibility. @@ -2168,67 +2472,162 @@ Called when the client has downloaded a new pak/pk3 file void FS_ReloadPackFilesFlags(unsigned int reloadflags) { searchpath_t *oldpaths; - searchpath_t *oldbase; searchpath_t *next; - - - //a lame way to fix pure paks (securitywise, the rest of the engine doesn't care if the filesystem changes too much) -#ifndef SERVERONLY - if (cls.state && com_purepaths) - { - CL_Disconnect_f(); - CL_Reconnect_f(); - } -#endif + int i; FS_FlushFSHashReally(); oldpaths = com_searchpaths; com_searchpaths = NULL; com_purepaths = NULL; - oldbase = com_base_searchpaths; com_base_searchpaths = NULL; - //invert the order - next = NULL; - while(oldpaths) + for (i = 0; i < sizeof(fs_manifest->gamepath) / sizeof(fs_manifest->gamepath[0]); i++) { - oldpaths->nextpure = next; - next = oldpaths; - oldpaths = oldpaths->next; - } - oldpaths = next; - - com_base_searchpaths = NULL; - - while(oldpaths) - { - next = oldpaths->nextpure; - - if (oldbase == oldpaths) - com_base_searchpaths = com_searchpaths; - - if (oldpaths->isexplicit) + if (fs_manifest->gamepath[i].path && fs_manifest->gamepath[i].base) { - if (oldpaths->funcs == &osfilefuncs) + FS_AddGameDirectory(&oldpaths, fs_manifest->gamepath[i].path, va("%s%s", com_quakedir, fs_manifest->gamepath[i].path), reloadflags); + if (*com_homedir) + FS_AddGameDirectory(&oldpaths, fs_manifest->gamepath[i].path, va("%s%s", com_homedir, fs_manifest->gamepath[i].path), reloadflags); + } + } + com_base_searchpaths = com_searchpaths; + for (i = 0; i < sizeof(fs_manifest->gamepath) / sizeof(fs_manifest->gamepath[0]); i++) + { + if (fs_manifest->gamepath[i].path && !fs_manifest->gamepath[i].base) + { + FS_AddGameDirectory(&oldpaths, fs_manifest->gamepath[i].path, va("%s%s", com_quakedir, fs_manifest->gamepath[i].path), reloadflags); + if (*com_homedir) + FS_AddGameDirectory(&oldpaths, fs_manifest->gamepath[i].path, va("%s%s", com_homedir, fs_manifest->gamepath[i].path), reloadflags); + } + } + + /*sv_pure: Reload pure paths*/ + if (fs_purenames && fs_purecrcs) + { + char crctok[64]; + char nametok[MAX_QPATH]; + searchpath_t *sp, *lastpure = NULL; + char *names = fs_purenames, *pname; + char *crcs = fs_purecrcs; + int crc; + + for (sp = com_searchpaths; sp; sp = sp->next) + { + if (sp->handle->GeneratePureCRC) { - char pure[64]; - FS_CleanDir(oldpaths->purepath, pure, sizeof(pure)); - FS_AddPathHandle(pure, oldpaths->purepath, oldpaths->funcs, oldpaths->handle, oldpaths->copyprotected, false, true, reloadflags); + sp->nextpure = (void*)0x1; + sp->crc_check = sp->handle->GeneratePureCRC(sp->handle, fs_pureseed, 0); + sp->crc_reply = sp->handle->GeneratePureCRC(sp->handle, fs_pureseed, 1); } else - FS_AddPathHandle(oldpaths->purepath, oldpaths->purepath, oldpaths->funcs, oldpaths->handle, oldpaths->copyprotected, false, true, reloadflags); + { + sp->nextpure = NULL; + sp->crc_check = 0; + sp->crc_reply = 0; + } } - else - oldpaths->funcs->ClosePath(oldpaths->handle); + + while(names && crcs) + { + crcs = COM_ParseOut(crcs, crctok, sizeof(crctok)); + names = COM_ParseOut(names, nametok, sizeof(nametok)); + + crc = strtoul(crctok, NULL, 0); + if (!crc) + continue; + + pname = nametok; + if (*pname == '*') // * means that its 'referenced' (read: actually useful) thus should be downloaded, which is not relevent here. + pname++; + + for (sp = com_searchpaths; sp; sp = sp->next) + { + if (sp->nextpure == (void*)0x1) //don't add twice. + if (sp->crc_check == crc) + { + if (fs_puremode) + { + if (lastpure) + lastpure->nextpure = sp; + else + com_purepaths = sp; + sp->nextpure = NULL; + lastpure = sp; + } + break; + } + } + if (!fs_puremode && !sp) + { //if we're not pure, we don't care if the version differs. don't load the server's version. + //this works around 1.01 vs 1.06 issues. + for (sp = com_searchpaths; sp; sp = sp->next) + { + if (!stricmp(pname, sp->purepath)) + break; + } + } + //if its not already loaded (via wildcards), load it from the download cache, if we can + if (!sp) + { + char local[MAX_OSPATH]; + vfsfile_t *vfs; + char *ext = COM_FileExtension(pname); + void *handle; + int i; + + if (FS_GenCachedPakName(pname, va("%i", crc), local, sizeof(local))) + vfs = FS_OpenVFS(local, "rb", FS_ROOT); + else + vfs = NULL; + if (vfs) + { + for (i = 0; i < sizeof(searchpathformats)/sizeof(searchpathformats[0]); i++) + { + if (!searchpathformats[i].extension || !searchpathformats[i].OpenNew) + continue; + if (!strcmp(ext, searchpathformats[i].extension)) + { + handle = searchpathformats[i].OpenNew (vfs, local); + if (!handle) + break; + sp = FS_AddPathHandle(&oldpaths, pname, local, handle, SPF_COPYPROTECTED|SPF_TEMPORARY, (unsigned int)-1); + + sp->crc_check = sp->handle->GeneratePureCRC(sp->handle, fs_pureseed, 0); + sp->crc_reply = sp->handle->GeneratePureCRC(sp->handle, fs_pureseed, 1); + + if (sp->crc_check == crc) + { + if (fs_puremode) + { + if (lastpure) + lastpure->nextpure = sp; + else + com_purepaths = sp; + sp->nextpure = NULL; + lastpure = sp; + } + } + break; + } + } + } + + if (!sp) + Con_DPrintf("Pure crc %i wasn't found\n", crc); + } + } + } + + while(oldpaths) + { + next = oldpaths->next; + + Con_Printf("%s is no longer needed\n", oldpaths->logicalpath); + oldpaths->handle->ClosePath(oldpaths->handle); Z_Free(oldpaths); oldpaths = next; } - - if (!com_base_searchpaths) - com_base_searchpaths = com_searchpaths; - - /*sv_pure: Reload pure paths*/ } void FS_UnloadPackFiles(void) @@ -2425,7 +2824,7 @@ qboolean Sys_FindGameData(const char *poshname, const char *gamename, char *base } */ - if (!strcmp(gamename, "hexen2")) + if (!strcmp(gamename, "hexen2") || !strcmp(gamename, "h2mp")) { //append SteamApps\common\hexen 2 if (Sys_SteamHasFile(basepath, basepathlen, "hexen 2", "glh2.exe")) @@ -2521,7 +2920,7 @@ void FS_Shutdown(void) // while (com_searchpaths) { - com_searchpaths->funcs->ClosePath(com_searchpaths->handle); + com_searchpaths->handle->ClosePath(com_searchpaths->handle); next = com_searchpaths->next; Z_Free (com_searchpaths); com_searchpaths = next; @@ -2536,28 +2935,33 @@ void FS_Shutdown(void) filesystemhash.bucket = NULL; filesystemhash.numbuckets = 0; } + + FS_Manifest_Free(fs_manifest); + fs_manifest = NULL; } -void FS_AddGamePack(const char *pakname) +#if 0 +static void FS_AddGamePack(const char *pakname) { int j; char *ext = COM_FileExtension(pakname); vfsfile_t *vfs = VFSOS_Open(pakname, "rb"); void *pak; + searchpath_t *oldlist = NULL; if (!vfs) Con_Printf("Unable to open %s - missing?\n", pakname); else { for (j = 0; j < sizeof(searchpathformats)/sizeof(searchpathformats[0]); j++) { - if (!searchpathformats[j].extension || !searchpathformats[j].funcs || !searchpathformats[j].funcs->OpenNew) + if (!searchpathformats[j].extension || !searchpathformats[j].OpenNew) continue; if (!strcmp(ext, searchpathformats[j].extension)) { - pak = searchpathformats[j].funcs->OpenNew(vfs, pakname); + pak = searchpathformats[j].OpenNew(vfs, pakname); if (pak) { - FS_AddPathHandle("", pakname, searchpathformats[j].funcs, pak, true, false, true, (unsigned int)-1); + FS_AddPathHandle(&oldlist, "", pakname, pak, SPF_COPYPROTECTED|SPF_EXPLICIT, (unsigned int)-1); } else { @@ -2576,9 +2980,10 @@ void FS_AddGamePack(const char *pakname) } } -void FS_StartupWithGame(int gamenum) +static void FS_StartupWithGame(int gamenum) { int i; + searchpath_t *oldlist = NULL; #ifdef AVAIL_ZLIB LibZ_Init(); @@ -2586,7 +2991,7 @@ void FS_StartupWithGame(int gamenum) Cvar_Set(&com_protocolname, gamemode_info[gamenum].protocolname); Cvar_ForceSet(&fs_gamename, gamemode_info[gamenum].poshname); - Cvar_ForceSet(&fs_gamedownload, gamemode_info[gamenum].downloadaddr?gamemode_info[gamenum].downloadaddr:""); +// Cvar_ForceSet(&fs_gamemanifest, gamemode_info[gamenum].manifestaddr?gamemode_info[gamenum].manifestaddr:""); i = COM_CheckParm ("-basepack"); while (i && i < com_argc-1) @@ -2604,9 +3009,9 @@ void FS_StartupWithGame(int gamenum) { do //use multiple -basegames { - FS_AddGameDirectory (com_argv[i+1], va("%s%s", com_quakedir, com_argv[i+1]), ~0); + FS_AddGameDirectory (&oldlist, com_argv[i+1], va("%s%s", com_quakedir, com_argv[i+1]), ~0); if (*com_homedir) - FS_AddGameDirectory (com_argv[i+1], va("%s%s", com_homedir, com_argv[i+1]), ~0); + FS_AddGameDirectory (&oldlist, com_argv[i+1], va("%s%s", com_homedir, com_argv[i+1]), ~0); i = COM_CheckNextParm ("-basegame", i); } @@ -2624,9 +3029,9 @@ void FS_StartupWithGame(int gamenum) } else if (gamemode_info[gamenum].dir[i]) { - FS_AddGameDirectory (gamemode_info[gamenum].dir[i], va("%s%s", com_quakedir, gamemode_info[gamenum].dir[i]), ~0); + FS_AddGameDirectory (&oldlist, gamemode_info[gamenum].dir[i], va("%s%s", com_quakedir, gamemode_info[gamenum].dir[i]), ~0); if (*com_homedir) - FS_AddGameDirectory (gamemode_info[gamenum].dir[i], va("%s%s", com_homedir, gamemode_info[gamenum].dir[i]), ~0); + FS_AddGameDirectory (&oldlist, gamemode_info[gamenum].dir[i], va("%s%s", com_homedir, gamemode_info[gamenum].dir[i]), ~0); } } } @@ -2637,9 +3042,9 @@ void FS_StartupWithGame(int gamenum) //reject various evil path arguments. if (*com_argv[i+1] && !(strchr(com_argv[i+1], '.') || strchr(com_argv[i+1], ':') || strchr(com_argv[i+1], '?') || strchr(com_argv[i+1], '*') || strchr(com_argv[i+1], '/') || strchr(com_argv[i+1], '\\') || strchr(com_argv[i+1], '$'))) { - FS_AddGameDirectory (com_argv[i+1], va("%s%s", com_quakedir, com_argv[i+1]), ~0); + FS_AddGameDirectory (&oldlist, com_argv[i+1], va("%s%s", com_quakedir, com_argv[i+1]), ~0); if (*com_homedir) - FS_AddGameDirectory (com_argv[i+1], va("%s%s", com_homedir, com_argv[i+1]), ~0); + FS_AddGameDirectory (&oldlist, com_argv[i+1], va("%s%s", com_homedir, com_argv[i+1]), ~0); } i = COM_CheckNextParm ("-addbasegame", i); @@ -2684,190 +3089,85 @@ void FS_StartupWithGame(int gamenum) if (gamemode_info[gamenum].customexec) Cbuf_AddText(gamemode_info[gamenum].customexec, RESTRICT_LOCAL); } - -void FS_ChangeGame_f(void) -{ - int i; - char *arg = Cmd_Argv(1); - - for (i = 0; gamemode_info[i].argname; i++) - { - if (!stricmp(gamemode_info[i].argname+1, arg)) - { - Con_Printf("Switching to %s\n", gamemode_info[i].argname+1); - fs_switchgame = i; - return; - } - } - Con_Printf("Game unknown\n"); -} - -/* -================ -COM_InitFilesystem -================ -*/ -void COM_InitFilesystem (void) -{ - vfsfile_t *f; - int i, j; - - char *ev; - qboolean usehome; - qboolean autobasedir = true; - - int gamenum=-1; - - FS_RegisterDefaultFileSystems(); - - Cmd_AddCommand("fs_restart", FS_ReloadPackFiles_f); -#ifdef _WIN32 - Cmd_AddCommand("fs_changegame", FS_ChangeGame_f); #endif -// -// -basedir -// Overrides the system supplied base directory (under id1) -// - i = COM_CheckParm ("-basedir"); - if (i && i < com_argc-1) +//just check each possible file, see if one is there. +static qboolean FS_DirHasGame(char *basedir, int gameidx) +{ + int j; + vfsfile_t *f; + for (j = 0; j < 4; j++) { - strcpy (com_quakedir, com_argv[i+1]); - autobasedir = false; - } - else - strcpy (com_quakedir, host_parms.basedir); - - if (*com_quakedir) - { - if (com_quakedir[strlen(com_quakedir)-1] == '\\') - com_quakedir[strlen(com_quakedir)-1] = '/'; - else if (com_quakedir[strlen(com_quakedir)-1] != '/') + if (!gamemode_info[gameidx].auniquefile[j]) + continue; //no more + f = VFSOS_Open(va("%s%s", basedir, gamemode_info[gameidx].auniquefile[j]), "rb"); + if (f) { - com_quakedir[strlen(com_quakedir)+1] = '\0'; - com_quakedir[strlen(com_quakedir)] = '/'; + VFS_CLOSE(f); + return true; } } + return false; +} - - - Cvar_Register(&fs_gamename, "FS"); - Cvar_Register(&fs_gamedownload, "FS"); - Cvar_Register(&com_protocolname, "Server Info"); - Cvar_Register(&com_modname, "Server Info"); - //identify the game from a telling file - for (i = 0; gamemode_info[i].argname && gamenum==-1; i++) +//check em all +static int FS_IdentifyDefaultGameFromDir(char *basedir) +{ + int i; + for (i = 0; gamemode_info[i].argname; i++) { - for (j = 0; j < 4; j++) + if (FS_DirHasGame(basedir, i)) + return i; + } + return -1; +} + +//attempt to work out which game we're meant to be trying to run based upon a few things +//1: fs_changegame console command override. fixme: needs to cope with manifests too. +//2: -quake3 argument implies that the user wants to run quake3. +//3: if we are ftequake3.exe then we always try to run quake3. +//4: identify characteristic files within the working directory (like id1/pak0.pak implies we're running quake) +//5: check where the exe actually is instead of simply where we're being run from. +//6: fallback to quake +//if autobasedir is not set, block gamedir changes/prompts. +static int FS_IdentifyDefaultGame(char *newbase, int sizeof_newbase, qboolean fixedbase) +{ + int i; + int gamenum = -1; + + if (gamenum == -1) + { + for (i = 0; gamemode_info[i].argname; i++) { - if (!gamemode_info[i].auniquefile[j]) - continue; //no more - f = VFSOS_Open(va("%s%s", com_quakedir, gamemode_info[i].auniquefile[j]), "rb"); - if (f) + if (COM_CheckParm(gamemode_info[i].argname)) { - VFS_CLOSE(f); gamenum = i; break; } } } - if (gamenum == -1 && host_parms.binarydir && *host_parms.binarydir && autobasedir) - { - for (i = 0; gamemode_info[i].argname && gamenum==-1; i++) - { - //look in the directory the exe exists in if we failed to find a valid installation in the working dir - for (j = 0; j < 4; j++) - { - if (gamemode_info[i].auniquefile[j]) - { - f = VFSOS_Open(va("%s%s", host_parms.binarydir, gamemode_info[i].auniquefile[j]), "rb"); - if (f) - { - //apply this as the new -basedir - Q_strncpyz(com_quakedir, host_parms.binarydir, sizeof(com_quakedir)); - gamenum = i; - //we found it, its all okay - VFS_CLOSE(f); - break; - } - } - } - } - } //use the game based on an exe name over the filesystem one (could easily have multiple fs path matches). - for (i = 0; gamemode_info[i].argname; i++) + if (gamenum == -1) { - ev = COM_SkipPath(com_argv[0]); - ev = strstr(ev, gamemode_info[i].exename); - if (ev && (!strchr(ev, '\\') && !strchr(ev, '/'))) - gamenum = i; - } - //use the game based on an parameter over all else. - for (i = 0; gamemode_info[i].argname; i++) - { - if ((fs_switchgame != -1 && i == fs_switchgame) || (fs_switchgame == -1 && COM_CheckParm(gamemode_info[i].argname))) + for (i = 0; gamemode_info[i].argname; i++) { - gamenum = i; - - if (autobasedir) - { - //try the working directory first - for (j = 0; j < 4; j++) - { - if (gamemode_info[gamenum].auniquefile[j]) - { - f = VFSOS_Open(va("%s%s", com_quakedir, gamemode_info[i].auniquefile[j]), "rb"); - if (f) - { - //we found it, its all okay - VFS_CLOSE(f); - break; - } - } - } - //try looking where the exe is - if (j == 4 && host_parms.binarydir && *host_parms.binarydir) - { - for (j = 0; j < 4; j++) - { - if (gamemode_info[gamenum].auniquefile[j]) - { - f = VFSOS_Open(va("%s%s", host_parms.binarydir, gamemode_info[i].auniquefile[j]), "rb"); - if (f) - { - Q_strncpyz(com_quakedir, host_parms.binarydir, sizeof(com_quakedir)); - //we found it, its all okay - VFS_CLOSE(f); - break; - } - } - } - } - //scan known locations/windows registry/etc to see if we can find an existing install - if (j == 4) - { - char realpath[MAX_OSPATH-1]; - if (Sys_FindGameData(gamemode_info[i].poshname, gamemode_info[i].exename, realpath, sizeof(realpath))) - { - Q_strncpyz(com_quakedir, realpath, sizeof(com_quakedir)); - if (com_quakedir[strlen(com_quakedir)-1] == '\\') - com_quakedir[strlen(com_quakedir)-1] = '/'; - else if (com_quakedir[strlen(com_quakedir)-1] != '/') - { - com_quakedir[strlen(com_quakedir)+1] = '\0'; - com_quakedir[strlen(com_quakedir)] = '/'; - } - } - else - { - Con_Printf("Couldn't find the gamedata for this game mode!\n"); - } - } - } - break; + char *ev = COM_SkipPath(com_argv[0]); + ev = strstr(ev, gamemode_info[i].exename); + if (ev && (!strchr(ev, '\\') && !strchr(ev, '/'))) + gamenum = i; } } - fs_switchgame = -1; + + //identify the game from a telling file in the working directory + if (gamenum == -1) + gamenum = FS_IdentifyDefaultGameFromDir(newbase); + //identify the game from a telling file relative to the exe's directory. for when shortcuts don't set the working dir sensibly. + if (gamenum == -1 && host_parms.binarydir && *host_parms.binarydir && !fixedbase) + { + gamenum = FS_IdentifyDefaultGameFromDir(host_parms.binarydir); + if (gamenum != -1) + Q_strncpyz(newbase, host_parms.binarydir, sizeof_newbase); + } //still failed? find quake and use that one by default if (gamenum<0) @@ -2877,26 +3177,421 @@ void COM_InitFilesystem (void) if (!strcmp(gamemode_info[i].argname, "-quake")) { gamenum = i; - - if (autobasedir) - { - char realpath[MAX_OSPATH-1]; - if (Sys_FindGameData(gamemode_info[i].poshname, gamemode_info[i].exename, realpath, sizeof(realpath))) - { - Q_strncpyz(com_quakedir, realpath, sizeof(com_quakedir)); - if (com_quakedir[strlen(com_quakedir)-1] == '\\') - com_quakedir[strlen(com_quakedir)-1] = '/'; - else if (com_quakedir[strlen(com_quakedir)-1] != '/') - { - com_quakedir[strlen(com_quakedir)+1] = '\0'; - com_quakedir[strlen(com_quakedir)] = '/'; - } - } - } break; } } } + return gamenum; +} + +//allowed to modify newbasedir if fixedbasedir isn't set +ftemanifest_t *FS_GenerateLegacyManifest(char *newbasedir, int sizeof_newbasedir, qboolean fixedbasedir, int game) +{ + int i; + ftemanifest_t *man; + + if (game == -1) + game = FS_IdentifyDefaultGame(newbasedir, sizeof_newbasedir, fixedbasedir); + if (gamemode_info[game].manifestfile) + man = FS_Manifest_Parse(gamemode_info[game].manifestfile); + else + { + man = FS_Manifest_Create(); + + Cmd_TokenizeString(va("game \"%s\"", gamemode_info[game].argname+1), false, false); + FS_Manifest_ParseTokens(man); + if (gamemode_info[game].poshname) + { + Cmd_TokenizeString(va("name \"%s\"", gamemode_info[game].poshname), false, false); + FS_Manifest_ParseTokens(man); + } + + i = COM_CheckParm ("-basegame"); + if (i) + { + do + { + Cmd_TokenizeString(va("basegame \"%s\"", com_argv[i+1]), false, false); + FS_Manifest_ParseTokens(man); + + i = COM_CheckNextParm ("-basegame", i); + } + while (i && i < com_argc-1); + } + + i = COM_CheckParm ("-game"); + if (i) + { + do + { + Cmd_TokenizeString(va("gamedir \"%s\"", com_argv[i+1]), false, false); + FS_Manifest_ParseTokens(man); + + i = COM_CheckNextParm ("-game", i); + } + while (i && i < com_argc-1); + } + + i = COM_CheckParm ("+gamedir"); + if (i) + { + do + { + Cmd_TokenizeString(va("gamedir \"%s\"", com_argv[i+1]), false, false); + FS_Manifest_ParseTokens(man); + + i = COM_CheckNextParm ("+gamedir", i); + } + while (i && i < com_argc-1); + } + } + return man; +} + +static char *FS_RelativeURL(char *base, char *file, char *buffer, int bufferlen) +{ + //fixme: cope with windows paths + qboolean baseisurl = !!strchr(base, ':'); + qboolean fileisurl = !!strchr(file, ':'); + qboolean baseisabsolute = (*base == '/' || *base == '\\'); + qboolean fileisabsolute = (*file == '/' || *file == '\\'); + char *ebase; + + if (fileisurl) + return file; + if (fileisabsolute) + { + if (baseisurl) + { + ebase = strchr(base, ':'); + ebase++; + while(*ebase == '/') + ebase++; + while(*ebase && *ebase != '/') + ebase++; + } + else + ebase = base; + } + else + ebase = COM_SkipPath(base); + memcpy(buffer, base, ebase-base); + strcpy(buffer+(ebase-base), file); + + return buffer; +} + +#ifdef WEBCLIENT +static struct dl_download *curpackagedownload; +static char fspdl_temppath[MAX_OSPATH]; +static char fspdl_finalpath[MAX_OSPATH]; +static void FS_BeginNextPackageDownload(void); +static void FS_PackageDownloaded(struct dl_download *dl) +{ + curpackagedownload = NULL; + + if (dl->file) + { + VFS_CLOSE(dl->file); + dl->file = NULL; + } + if (dl->status == DL_FINISHED) + { + //rename the file as needed. + COM_CreatePath(fspdl_finalpath); + if (!Sys_Rename(fspdl_temppath, fspdl_finalpath)) + { + Con_Printf("Unable to rename \"%s\" to \"%s\"\n", fspdl_temppath, fspdl_finalpath); + } + } + Sys_remove (fspdl_temppath); + + FS_ChangeGame(fs_manifest, true); + + FS_BeginNextPackageDownload(); +} +static void FS_BeginNextPackageDownload(void) +{ + int j; + ftemanifest_t *man = fs_manifest; + vfsfile_t *check; + if (curpackagedownload || !man) + return; + + for (j = 0; j < sizeof(fs_manifest->package) / sizeof(fs_manifest->package[0]); j++) + { + char buffer[MAX_OSPATH], *url; + if (!man->package[j].path) + continue; + + if (!FS_GenCachedPakName(man->package[j].path, va("%#x", man->package[j].crc), buffer, sizeof(buffer))) + continue; + + check = FS_OpenVFS(buffer, "rb", FS_ROOT); + if (check) + { + VFS_CLOSE(check); + continue; + } + FS_NativePath(buffer, FS_ROOT, fspdl_finalpath, sizeof(fspdl_finalpath)); + if (!FS_GenCachedPakName(va("%s.tmp", man->package[j].path), va("%#x", man->package[j].crc), buffer, sizeof(buffer))) + continue; + FS_NativePath(buffer, FS_ROOT, fspdl_temppath, sizeof(fspdl_temppath)); + + url = NULL; + while(!url) + { + //ran out of mirrors? + if (man->package[j].mirrornum == (sizeof(man->package[j].mirrors) / sizeof(man->package[j].mirrors[0]))) + break; + + if (man->package[j].mirrors[man->package[j].mirrornum]) + url = FS_RelativeURL(man->updateurl, man->package[j].mirrors[man->package[j].mirrornum], buffer, sizeof(buffer)); + man->package[j].mirrornum++; + } + //no valid mirrors + if (!url) + continue; + + curpackagedownload = HTTP_CL_Get(url, NULL, FS_PackageDownloaded); + if (curpackagedownload) + { + COM_CreatePath(fspdl_temppath); + curpackagedownload->file = VFSOS_Open(fspdl_temppath, "wb"); + return; + } + } +} +#else +void FS_BeginNextPackageDownload(void) +{ +} +#endif + +//this is potentially unsafe. needs lots of testing. +qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs) +{ + int i, j; + char realpath[MAX_OSPATH-1]; + char newbasedir[MAX_OSPATH]; + qboolean fixedbasedir; + qboolean reloadconfigs = false; + flocation_t loc; + + //if any of these files change location, the configs will be re-execed. + //note that we reuse path handles if they're still valid, so we can just check the pointer to see if it got unloaded/replaced. + char *conffile[] = {"quake.rc", "hexen.rc", "default.cfg", "server.cfg", NULL}; + searchpath_t *confpath[sizeof(conffile)/sizeof(conffile[0])]; + for (i = 0; conffile[i]; i++) + { + FS_FLocateFile(conffile[i], FSLFRT_IFFOUND, &loc); //q1 + confpath[i] = loc.search; + } + + i = COM_CheckParm ("-basedir"); + fixedbasedir = i && i < com_argc-1; + Q_strncpyz (newbasedir, fixedbasedir?com_argv[i+1]:host_parms.basedir, sizeof(newbasedir)); + + //make sure it has a trailing slash, or is empty. woo. + FS_CleanDir(newbasedir, sizeof(newbasedir)); + + if (!man) + { + //if we're already running a game, don't autodetect. + if (fs_manifest) + return false; + + man = FS_GenerateLegacyManifest(newbasedir, sizeof(newbasedir), fixedbasedir, -1); + } + + if (man == fs_manifest) + { + //don't close anything. theoretically nothing is changing, and we don't want to load new defaults either. + } + else if (!fs_manifest || !strcmp(fs_manifest->installation?fs_manifest->installation:"", man->installation?man->installation:"")) + { + if (!fs_manifest) + reloadconfigs = true; + FS_Manifest_Free(fs_manifest); + } + else + { + FS_Shutdown(); + + reloadconfigs = true; + } + fs_manifest = man; + + if (man->installation && *man->installation) + { + for (i = 0; gamemode_info[i].argname; i++) + { + if (!strcmp(man->installation, gamemode_info[i].argname+1)) + { + //if there's no base dirs, edit the manifest to give it its default ones. + for (j = 0; j < sizeof(man->gamepath) / sizeof(man->gamepath[0]); j++) + { + if (man->gamepath[j].path && man->gamepath[j].base) + break; + } + if (j == sizeof(man->gamepath) / sizeof(man->gamepath[0])) + { + for (j = 0; j < 4; j++) + if (gamemode_info[i].dir[j]) + { + Cmd_TokenizeString(va("basegame \"%s\"", gamemode_info[i].dir[j]), false, false); + FS_Manifest_ParseTokens(man); + } + } + + if (!man->protocolname && *gamemode_info[i].protocolname) + { + Cmd_TokenizeString(va("protocolname \"%s\"", gamemode_info[i].protocolname), false, false); + FS_Manifest_ParseTokens(man); + } + if (!man->defaultexec && gamemode_info[i].customexec) + { + man->defaultexec = Z_StrDup(gamemode_info[i].customexec); + } + + if (!fixedbasedir && !FS_DirHasGame(newbasedir, i)) + if (Sys_FindGameData(man->formalname, man->installation, realpath, sizeof(realpath))) + Q_strncpyz (newbasedir, realpath, sizeof(newbasedir)); + break; + } + } + } + Q_strncpyz (com_quakedir, newbasedir, sizeof(com_quakedir)); + //make sure it has a trailing slash, or is empty. woo. + FS_CleanDir(com_quakedir, sizeof(com_quakedir)); + +#ifdef ANDROID + { + vfsfile_t *f; + //write a .nomedia file to avoid people from getting random explosion sounds etc intersperced with their music + f = FS_OpenVFS(".nomedia", "rb", FS_ROOT); + if (f) + VFS_CLOSE(f); + else + FS_WriteFile(".nomedia", NULL, 0, FS_ROOT); + } +#endif + + FS_ReloadPackFilesFlags(~0); + + FS_BeginNextPackageDownload(); + + + if (allowreloadconfigs) + { + for (i = 0; conffile[i]; i++) + { + FS_FLocateFile(conffile[i], FSLFRT_IFFOUND, &loc); + if (confpath[i] != loc.search) + reloadconfigs = true; + } + + if (reloadconfigs) + { + //FIXME: flag this instead and do it after a delay + Cvar_ForceSet(&fs_gamename, man->formalname?man->formalname:"FTE"); + Cvar_ForceSet(&com_protocolname, man->protocolname?man->protocolname:"FTE"); + + if (isDedicated) + { +#ifndef CLIENTONLY + SV_ExecInitialConfigs(man->defaultexec?man->defaultexec:""); +#endif + } + else + { +#ifndef SERVERONLY + CL_ExecInitialConfigs(man->defaultexec?man->defaultexec:""); +#endif + } + } + } + + COM_Effectinfo_Clear(); +#ifndef SERVERONLY + Validation_FlushFileList(); //prevent previous hacks from making a difference. +#endif + + return true; +} + +void FS_ChangeGame_f(void) +{ + int i; + char *arg = Cmd_Argv(1); + + if (!*arg) + { + Con_Printf("Valid games are:\n"); + for (i = 0; gamemode_info[i].argname; i++) + { + Con_Printf(" %s\n", gamemode_info[i].argname+1); + } + } + else + { + for (i = 0; gamemode_info[i].argname; i++) + { + if (!stricmp(gamemode_info[i].argname+1, arg)) + { + Con_Printf("Switching to %s\n", gamemode_info[i].argname+1); + FS_ChangeGame(FS_GenerateLegacyManifest(NULL, 0, true, i), true); + return; + } + } + Con_Printf("Game unknown\n"); + } +} +void FS_ShowManifest_f(void) +{ + if (fs_manifest) + FS_Manifest_Print(fs_manifest); + else + Con_Printf("no manifest loaded...\n"); +} +/* +================ +COM_InitFilesystem + +note: does not actually load any packs, just makes sure the basedir+cvars+etc is set up. vfs_fopens will still fail. +================ +*/ +void COM_InitFilesystem (void) +{ + int i; + + char *ev; + qboolean usehome; + + FS_RegisterDefaultFileSystems(); + + Cmd_AddCommand("fs_restart", FS_ReloadPackFiles_f); + Cmd_AddCommand("fs_changegame", FS_ChangeGame_f); + Cmd_AddCommand("fs_showmanifest", FS_ShowManifest_f); + +// +// -basedir +// Overrides the system supplied base directory (under id1) +// + i = COM_CheckParm ("-basedir"); + if (i && i < com_argc-1) + strcpy (com_quakedir, com_argv[i+1]); + else + strcpy (com_quakedir, host_parms.basedir); + + FS_CleanDir(com_quakedir, sizeof(com_quakedir)); + + + + Cvar_Register(&fs_gamename, "FS"); + Cvar_Register(&fs_gamemanifest, "FS"); + Cvar_Register(&com_protocolname, "Server Info"); + Cvar_Register(&com_modname, "Server Info"); usehome = false; @@ -3010,8 +3705,6 @@ void COM_InitFilesystem (void) #ifdef PLUGINS Plug_Initialise(false); #endif - - FS_StartupWithGame(gamenum); } @@ -3019,23 +3712,28 @@ void COM_InitFilesystem (void) //this is at the bottom of the file to ensure these globals are not used elsewhere -extern searchpathfuncs_t packfilefuncs; -extern searchpathfuncs_t zipfilefuncs; -extern searchpathfuncs_t doomwadfilefuncs; +extern searchpathfuncs_t *(QDECL VFSOS_OpenPath) (vfsfile_t *file, const char *desc); +#ifdef AVAIL_ZLIB +extern searchpathfuncs_t *(QDECL FSZIP_LoadArchive) (vfsfile_t *packhandle, const char *desc); +#endif +extern searchpathfuncs_t *(QDECL FSPAK_LoadArchive) (vfsfile_t *packhandle, const char *desc); +#ifdef DOOMWADS +extern searchpathfuncs_t *(QDECL FSDWD_LoadArchive) (vfsfile_t *packhandle, const char *desc); +#endif void FS_RegisterDefaultFileSystems(void) { - FS_RegisterFileSystemType(NULL, "pak", &packfilefuncs, true); + FS_RegisterFileSystemType(NULL, "pak", FSPAK_LoadArchive, true); #if !defined(_WIN32) && !defined(ANDROID) /*for systems that have case sensitive paths, also include *.PAK */ - FS_RegisterFileSystemType(NULL, "PAK", &packfilefuncs, true); + FS_RegisterFileSystemType(NULL, "PAK", FSPAK_LoadArchive, true); #endif #ifdef AVAIL_ZLIB - FS_RegisterFileSystemType(NULL, "pk3", &zipfilefuncs, true); - FS_RegisterFileSystemType(NULL, "pk4", &zipfilefuncs, true); - FS_RegisterFileSystemType(NULL, "apk", &zipfilefuncs, false); - FS_RegisterFileSystemType(NULL, "zip", &zipfilefuncs, false); + FS_RegisterFileSystemType(NULL, "pk3", FSZIP_LoadArchive, true); + FS_RegisterFileSystemType(NULL, "pk4", FSZIP_LoadArchive, true); + FS_RegisterFileSystemType(NULL, "apk", FSZIP_LoadArchive, false); + FS_RegisterFileSystemType(NULL, "zip", FSZIP_LoadArchive, false); #endif #ifdef DOOMWADS - FS_RegisterFileSystemType(NULL, "wad", &doomwadfilefuncs, true); + FS_RegisterFileSystemType(NULL, "wad", FSDWD_LoadArchive, true); #endif } diff --git a/engine/common/fs.h b/engine/common/fs.h index fd51884e0..e6af9557a 100644 --- a/engine/common/fs.h +++ b/engine/common/fs.h @@ -1,4 +1,7 @@ #include "hash.h" + +#define FSVER 1 + typedef struct { bucket_t buck; @@ -9,31 +12,43 @@ extern int fs_hash_dups; //for tracking efficiency. no functional use. extern int fs_hash_files; //for tracking efficiency. no functional use. struct searchpath_s; -typedef struct { - void (QDECL *GetDisplayPath)(void *handle, char *outpath, unsigned int pathsize); - void (QDECL *ClosePath)(void *handle); - void (QDECL *BuildHash)(void *handle, int depth, void (QDECL *FS_AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle)); - qboolean (QDECL *FindFile)(void *handle, flocation_t *loc, const char *name, void *hashedresult); //true if found (hashedresult can be NULL) +struct searchpathfuncs_s +{ + int fsver; + void (QDECL *ClosePath)(searchpathfuncs_t *handle); + + void (QDECL *GetPathDetails)(searchpathfuncs_t *handle, char *outdetails, unsigned int sizeofdetails); + void (QDECL *BuildHash)(searchpathfuncs_t *handle, int depth, void (QDECL *FS_AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle)); + qboolean (QDECL *FindFile)(searchpathfuncs_t *handle, flocation_t *loc, const char *name, void *hashedresult); //true if found (hashedresult can be NULL) //note that if rawfile and offset are set, many Com_FileOpens will read the raw file //otherwise ReadFile will be called instead. - void (QDECL *ReadFile)(void *handle, flocation_t *loc, char *buffer); //reads the entire file in one go (size comes from loc, so make sure the loc is valid, this is for performance with compressed archives) - int (QDECL *EnumerateFiles)(void *handle, const char *match, int (QDECL *func)(const char *fname, int fsize, void *parm, void *spath), void *parm); + void (QDECL *ReadFile)(searchpathfuncs_t *handle, flocation_t *loc, char *buffer); //reads the entire file in one go (size comes from loc, so make sure the loc is valid, this is for performance with compressed archives) + int (QDECL *EnumerateFiles)(searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *fname, int fsize, void *parm, searchpathfuncs_t *spath), void *parm); - void *(QDECL *OpenNew)(vfsfile_t *file, const char *desc); //returns a handle to a new pak/path + int (QDECL *GeneratePureCRC) (searchpathfuncs_t *handle, int seed, int usepure); - int (QDECL *GeneratePureCRC) (void *handle, int seed, int usepure); + vfsfile_t *(QDECL *OpenVFS)(searchpathfuncs_t *handle, flocation_t *loc, const char *mode); - vfsfile_t *(QDECL *OpenVFS)(void *handle, flocation_t *loc, const char *mode); - - qboolean (QDECL *PollChanges)(void *handle); //returns true if there were changes -} searchpathfuncs_t; + qboolean (QDECL *PollChanges)(searchpathfuncs_t *handle); //returns true if there were changes +}; +//searchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, const char *desc); //returns a handle to a new pak/path //the stdio filesystem is special as that's the starting point of the entire filesystem //warning: the handle is known to be a string pointer to the dir name -extern searchpathfuncs_t osfilefuncs; +extern searchpathfuncs_t *(QDECL VFSOS_OpenPath) (vfsfile_t *file, const char *desc); +extern searchpathfuncs_t *(QDECL FSZIP_LoadArchive) (vfsfile_t *packhandle, const char *desc); +extern searchpathfuncs_t *(QDECL FSPAK_LoadArchive) (vfsfile_t *packhandle, const char *desc); +extern searchpathfuncs_t *(QDECL FSDWD_LoadArchive) (vfsfile_t *packhandle, const char *desc); vfsfile_t *QDECL VFSOS_Open(const char *osname, const char *mode); -vfsfile_t *FS_DecompressGZip(vfsfile_t *infile); +vfsfile_t *FS_DecompressGZip(vfsfile_t *infile, vfsfile_t *outfile); -int FS_RegisterFileSystemType(void *module, const char *extension, searchpathfuncs_t *funcs, qboolean loadscan); +int FS_RegisterFileSystemType(void *module, const char *extension, searchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, const char *desc), qboolean loadscan); void FS_UnRegisterFileSystemType(int idx); void FS_UnRegisterFileSystemModule(void *module); + +#define SPF_REFERENCED 1 //something has been loaded from this path. should filter out client references... +#define SPF_COPYPROTECTED 2 //downloads are not allowed fom here. +#define SPF_TEMPORARY 4 //a map-specific path, purged at map change. +#define SPF_EXPLICIT 8 //a root gamedir (bumps depth on gamedir depth checks). +#define SPF_UNTRUSTED 16 //has been downloaded from somewhere. configs inside it should never be execed with local access rights. +qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, int *crc, unsigned int flags); diff --git a/engine/common/fs_pak.c b/engine/common/fs_pak.c index 1fe8016f5..3dd9238e4 100644 --- a/engine/common/fs_pak.c +++ b/engine/common/fs_pak.c @@ -15,6 +15,7 @@ typedef struct typedef struct pack_s { + searchpathfuncs_t pub; char descname[MAX_OSPATH]; vfsfile_t *handle; unsigned int filepos; //the pos the subfiles left it at (to optimize calls to vfs_seek) @@ -54,16 +55,15 @@ typedef struct #define MAX_FILES_IN_PACK 2048 -void QDECL FSPAK_GetDisplayPath(void *handle, char *out, unsigned int outlen) +static void QDECL FSPAK_GetPathDetails(searchpathfuncs_t *handle, char *out, unsigned int outlen) { - pack_t *pak = handle; + pack_t *pak = (pack_t*)handle; + *out = 0; if (pak->references != 1) - Q_snprintfz(out, outlen, "%s (%i)", pak->descname, pak->references-1); - else - Q_snprintfz(out, outlen, "%s", pak->descname); + Q_snprintfz(out, outlen, "(%i)", pak->references-1); } -void QDECL FSPAK_ClosePath(void *handle) +static void QDECL FSPAK_ClosePath(void *handle) { pack_t *pak = handle; @@ -125,9 +125,9 @@ qboolean QDECL FSPAK_FLocate(void *handle, flocation_t *loc, const char *filenam } return false; } -int QDECL FSPAK_EnumerateFiles (void *handle, const char *match, int (QDECL *func)(const char *, int, void *, void *spath), void *parm) +static int QDECL FSPAK_EnumerateFiles (searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, int, void *, searchpathfuncs_t *spath), void *parm) { - pack_t *pak = handle; + pack_t *pak = (pack_t*)handle; int num; for (num = 0; num<(int)pak->numfiles; num++) @@ -171,102 +171,6 @@ int QDECL FSPAK_GeneratePureCRC(void *handle, int seed, int crctype) return result; } -/* -================= -COM_LoadPackFile - -Takes an explicit (not game tree related) path to a pak file. - -Loads the header and directory, adding the files at the beginning -of the list so they override previous pack files. -================= -*/ -void *QDECL FSPAK_LoadPackFile (vfsfile_t *file, const char *desc) -{ - dpackheader_t header; - int i; -// int j; - mpackfile_t *newfiles; - int numpackfiles; - pack_t *pack; - vfsfile_t *packhandle; - dpackfile_t info; - int read; -// unsigned short crc; - - packhandle = file; - if (packhandle == NULL) - return NULL; - - read = VFS_READ(packhandle, &header, sizeof(header)); - if (read < sizeof(header) || header.id[0] != 'P' || header.id[1] != 'A' - || header.id[2] != 'C' || header.id[3] != 'K') - { - Con_Printf("%s is not a pak - %c%c%c%c\n", desc, header.id[0], header.id[1], header.id[2], header.id[3]); - return NULL; - } - header.dirofs = LittleLong (header.dirofs); - header.dirlen = LittleLong (header.dirlen); - - numpackfiles = header.dirlen / sizeof(dpackfile_t); - -// if (numpackfiles > MAX_FILES_IN_PACK) -// Sys_Error ("%s has %i files", packfile, numpackfiles); - -// if (numpackfiles != PAK0_COUNT) -// com_modified = true; // not the original file - - newfiles = (mpackfile_t*)Z_Malloc (numpackfiles * sizeof(mpackfile_t)); - - VFS_SEEK(packhandle, header.dirofs); -// fread (&info, 1, header.dirlen, packhandle); - -// crc the directory to check for modifications -// crc = QCRC_Block((qbyte *)info, header.dirlen); - - -// QCRC_Init (&crc); - - pack = (pack_t*)Z_Malloc (sizeof (pack_t)); -// parse the directory - for (i=0 ; idescname, desc); - pack->handle = packhandle; - pack->numfiles = numpackfiles; - pack->files = newfiles; - pack->filepos = 0; - VFS_SEEK(packhandle, pack->filepos); - - pack->references++; - - Con_TPrintf (TL_ADDEDPACKFILE, desc, numpackfiles); - return pack; -} - - typedef struct { vfsfile_t funcs; pack_t *parentpak; @@ -376,22 +280,114 @@ void QDECL FSPAK_ReadFile(void *handle, flocation_t *loc, char *buffer) */ } -searchpathfuncs_t packfilefuncs = { - FSPAK_GetDisplayPath, - FSPAK_ClosePath, - FSPAK_BuildHash, - FSPAK_FLocate, - FSPAK_ReadFile, - FSPAK_EnumerateFiles, - FSPAK_LoadPackFile, - FSPAK_GeneratePureCRC, - FSPAK_OpenVFS -}; + +/* +================= +COM_LoadPackFile + +Takes an explicit (not game tree related) path to a pak file. + +Loads the header and directory, adding the files at the beginning +of the list so they override previous pack files. +================= +*/ +searchpathfuncs_t *QDECL FSPAK_LoadArchive (vfsfile_t *file, const char *desc) +{ + dpackheader_t header; + int i; +// int j; + mpackfile_t *newfiles; + int numpackfiles; + pack_t *pack; + vfsfile_t *packhandle; + dpackfile_t info; + int read; +// unsigned short crc; + + packhandle = file; + if (packhandle == NULL) + return NULL; + + read = VFS_READ(packhandle, &header, sizeof(header)); + if (read < sizeof(header) || header.id[0] != 'P' || header.id[1] != 'A' + || header.id[2] != 'C' || header.id[3] != 'K') + { + Con_Printf("%s is not a pak - %c%c%c%c\n", desc, header.id[0], header.id[1], header.id[2], header.id[3]); + return NULL; + } + header.dirofs = LittleLong (header.dirofs); + header.dirlen = LittleLong (header.dirlen); + + numpackfiles = header.dirlen / sizeof(dpackfile_t); + +// if (numpackfiles > MAX_FILES_IN_PACK) +// Sys_Error ("%s has %i files", packfile, numpackfiles); + +// if (numpackfiles != PAK0_COUNT) +// com_modified = true; // not the original file + + newfiles = (mpackfile_t*)Z_Malloc (numpackfiles * sizeof(mpackfile_t)); + + VFS_SEEK(packhandle, header.dirofs); +// fread (&info, 1, header.dirlen, packhandle); + +// crc the directory to check for modifications +// crc = QCRC_Block((qbyte *)info, header.dirlen); +// QCRC_Init (&crc); + + pack = (pack_t*)Z_Malloc (sizeof (pack_t)); +// parse the directory + for (i=0 ; idescname, desc); + pack->handle = packhandle; + pack->numfiles = numpackfiles; + pack->files = newfiles; + pack->filepos = 0; + VFS_SEEK(packhandle, pack->filepos); + + pack->references++; + + Con_TPrintf (TL_ADDEDPACKFILE, desc, numpackfiles); + + pack->pub.fsver = FSVER; + pack->pub.GetPathDetails = FSPAK_GetPathDetails; + pack->pub.ClosePath = FSPAK_ClosePath; + pack->pub.BuildHash = FSPAK_BuildHash; + pack->pub.FindFile = FSPAK_FLocate; + pack->pub.ReadFile = FSPAK_ReadFile; + pack->pub.EnumerateFiles = FSPAK_EnumerateFiles; + pack->pub.GeneratePureCRC = FSPAK_GeneratePureCRC; + pack->pub.OpenVFS = FSPAK_OpenVFS; + return &pack->pub; +} #ifdef DOOMWADS -void *QDECL FSPAK_LoadDoomWadFile (vfsfile_t *packhandle, const char *desc) +searchpathfuncs_t *QDECL FSDWD_LoadArchive (vfsfile_t *packhandle, const char *desc) { dwadheader_t header; int i; @@ -560,17 +556,16 @@ newsection: pack->references++; Con_TPrintf (TL_ADDEDPACKFILE, desc, numpackfiles); - return pack; + + pack->pub.fsver = FSVER; + pack->pub.GetPathDetails = FSPAK_GetPathDetails; + pack->pub.ClosePath = FSPAK_ClosePath; + pack->pub.BuildHash = FSPAK_BuildHash; + pack->pub.FindFile = FSPAK_FLocate; + pack->pub.ReadFile = FSPAK_ReadFile; + pack->pub.EnumerateFiles = FSPAK_EnumerateFiles; + pack->pub.GeneratePureCRC = FSPAK_GeneratePureCRC; + pack->pub.OpenVFS = FSPAK_OpenVFS; + return &pack->pub; } -searchpathfuncs_t doomwadfilefuncs = { - FSPAK_GetDisplayPath, - FSPAK_ClosePath, - FSPAK_BuildHash, - FSPAK_FLocate, - FSPAK_ReadFile, - FSPAK_EnumerateFiles, - FSPAK_LoadDoomWadFile, - NULL, - FSPAK_OpenVFS -}; #endif diff --git a/engine/common/fs_stdio.c b/engine/common/fs_stdio.c index e907d30e0..ca094dd1c 100644 --- a/engine/common/fs_stdio.c +++ b/engine/common/fs_stdio.c @@ -9,12 +9,13 @@ #define Z_Malloc malloc #else #if !defined(_WIN32) || defined(_SDL) -#define stdiofilefuncs osfilefuncs +#define FSSTDIO_OpenPath VFSOS_OpenPath #endif #define FSSTDIO_OpenTemp FS_OpenTemp #endif typedef struct { + searchpathfuncs_t pub; int depth; void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle); char rootpath[1]; @@ -209,11 +210,6 @@ static vfsfile_t *QDECL FSSTDIO_OpenVFS(void *handle, flocation_t *loc, const ch return f; } -static void QDECL FSSTDIO_GetDisplayPath(void *handle, char *out, unsigned int outlen) -{ - stdiopath_t *np = handle; - Q_strncpyz(out, np->rootpath, outlen); -} static void QDECL FSSTDIO_ClosePath(void *handle) { Z_Free(handle); @@ -223,20 +219,6 @@ static qboolean QDECL FSSTDIO_PollChanges(void *handle) // stdiopath_t *np = handle; return true; //can't verify that or not, so we have to assume the worst } -static void *QDECL FSSTDIO_OpenPath(vfsfile_t *mustbenull, const char *desc) -{ - stdiopath_t *np; - int dlen = strlen(desc); - if (mustbenull) - return NULL; - np = Z_Malloc(sizeof(*np) + dlen); - if (np) - { - np->depth = 0; - memcpy(np->rootpath, desc, dlen+1); - } - return np; -} static int QDECL FSSTDIO_RebuildFSHash(const char *filename, int filesize, void *data, void *spath) { stdiopath_t *sp = spath; @@ -325,24 +307,37 @@ static void QDECL FSSTDIO_ReadFile(void *handle, flocation_t *loc, char *buffer) fclose(f); } -static int QDECL FSSTDIO_EnumerateFiles (void *handle, const char *match, int (QDECL *func)(const char *, int, void *, void *spath), void *parm) +static int QDECL FSSTDIO_EnumerateFiles (searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, int, void *, searchpathfuncs_t *spath), void *parm) { - stdiopath_t *sp = handle; + stdiopath_t *sp = (stdiopath_t*)handle; return Sys_EnumerateFiles(sp->rootpath, match, func, parm, handle); } -searchpathfuncs_t stdiofilefuncs = { - FSSTDIO_GetDisplayPath, - FSSTDIO_ClosePath, - FSSTDIO_BuildHash, - FSSTDIO_FLocate, - FSSTDIO_ReadFile, - FSSTDIO_EnumerateFiles, - FSSTDIO_OpenPath, - NULL, - FSSTDIO_OpenVFS, - FSSTDIO_PollChanges -}; + +void *QDECL FSSTDIO_OpenPath(vfsfile_t *mustbenull, const char *desc) +{ + stdiopath_t *np; + int dlen = strlen(desc); + if (mustbenull) + return NULL; + np = Z_Malloc(sizeof(*np) + dlen); + if (np) + { + np->depth = 0; + memcpy(np->rootpath, desc, dlen+1); + } + + np->pub.fsver = FSVER; + np->pub.ClosePath = FSSTDIO_ClosePath; + np->pub.BuildHash = FSSTDIO_BuildHash; + np->pub.FindFile = FSSTDIO_FLocate; + np->pub.ReadFile = FSSTDIO_ReadFile; + np->pub.EnumerateFiles = FSSTDIO_EnumerateFiles; + np->pub.OpenVFS = FSSTDIO_OpenVFS; + np->pub.PollChanges = FSSTDIO_PollChanges; + return np; +} + #endif #endif diff --git a/engine/common/fs_win32.c b/engine/common/fs_win32.c index 9dea732a9..ac7c09868 100644 --- a/engine/common/fs_win32.c +++ b/engine/common/fs_win32.c @@ -10,10 +10,10 @@ //for write access, we use the stdio module as a fallback. #define VFSW32_Open VFSOS_Open -#define w32filefuncs osfilefuncs +#define VFSW32_OpenPath VFSOS_OpenPath typedef struct { - searchpathfuncs_t funcs; + searchpathfuncs_t pub; HANDLE changenotification; int hashdepth; char rootpath[1]; @@ -191,12 +191,6 @@ static vfsfile_t *QDECL VFSW32_OpenVFS(void *handle, flocation_t *loc, const cha return VFSW32_Open(loc->rawname, mode); } - -static void QDECL VFSW32_GetDisplayPath(void *handle, char *out, unsigned int outlen) -{ - vfsw32path_t *wp = handle; - Q_strncpyz(out, wp->rootpath, outlen); -} static void QDECL VFSW32_ClosePath(void *handle) { vfsw32path_t *wp = handle; @@ -229,21 +223,6 @@ static qboolean QDECL VFSW32_PollChanges(void *handle) } return result; } -static void *QDECL VFSW32_OpenPath(vfsfile_t *mustbenull, const char *desc) -{ - vfsw32path_t *np; - int dlen = strlen(desc); - if (mustbenull) - return NULL; - np = Z_Malloc(sizeof(*np) + dlen); - if (np) - { - memcpy(np->rootpath, desc, dlen+1); - - np->changenotification = FindFirstChangeNotification(np->rootpath, true, FILE_NOTIFY_CHANGE_FILE_NAME); - } - return np; -} static int QDECL VFSW32_RebuildFSHash(const char *filename, int filesize, void *handle, void *spath) { vfsw32path_t *wp = spath; @@ -317,22 +296,34 @@ static void QDECL VFSW32_ReadFile(void *handle, flocation_t *loc, char *buffer) fread(buffer, 1, loc->len, f); fclose(f); } -static int QDECL VFSW32_EnumerateFiles (void *handle, const char *match, int (QDECL *func)(const char *, int, void *, void *spath), void *parm) +static int QDECL VFSW32_EnumerateFiles (searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, int, void *, searchpathfuncs_t *spath), void *parm) { - vfsw32path_t *wp = handle; + vfsw32path_t *wp = (vfsw32path_t*)handle; return Sys_EnumerateFiles(wp->rootpath, match, func, parm, handle); } -searchpathfuncs_t w32filefuncs = { - VFSW32_GetDisplayPath, - VFSW32_ClosePath, - VFSW32_BuildHash, - VFSW32_FLocate, - VFSW32_ReadFile, - VFSW32_EnumerateFiles, - VFSW32_OpenPath, - NULL, - VFSW32_OpenVFS, - VFSW32_PollChanges -}; +searchpathfuncs_t *QDECL VFSW32_OpenPath(vfsfile_t *mustbenull, const char *desc) +{ + vfsw32path_t *np; + int dlen = strlen(desc); + if (mustbenull) + return NULL; + np = Z_Malloc(sizeof(*np) + dlen); + if (np) + { + memcpy(np->rootpath, desc, dlen+1); + + np->changenotification = FindFirstChangeNotification(np->rootpath, true, FILE_NOTIFY_CHANGE_FILE_NAME); + } + + np->pub.fsver = FSVER; + np->pub.ClosePath = VFSW32_ClosePath; + np->pub.BuildHash = VFSW32_BuildHash; + np->pub.FindFile = VFSW32_FLocate; + np->pub.ReadFile = VFSW32_ReadFile; + np->pub.EnumerateFiles = VFSW32_EnumerateFiles; + np->pub.OpenVFS = VFSW32_OpenVFS; + np->pub.PollChanges = VFSW32_PollChanges; + return &np->pub; +} \ No newline at end of file diff --git a/engine/common/fs_zip.c b/engine/common/fs_zip.c index b8ae992be..0a1f4a638 100644 --- a/engine/common/fs_zip.c +++ b/engine/common/fs_zip.c @@ -71,7 +71,8 @@ qboolean LibZ_Init(void) return ZLIB_LOADED(); } -vfsfile_t *FS_DecompressGZip(vfsfile_t *infile) +//outfile may be null +vfsfile_t *FS_DecompressGZip(vfsfile_t *infile, vfsfile_t *outfile) { char inchar; unsigned short inshort; @@ -127,11 +128,16 @@ vfsfile_t *FS_DecompressGZip(vfsfile_t *infile) - temp = FS_OpenTemp(); - if (!temp) + if (outfile) + temp = outfile; + else { - VFS_SEEK(infile, 0); //doh - return infile; + temp = FS_OpenTemp(); + if (!temp) + { + VFS_SEEK(infile, 0); //doh + return infile; + } } @@ -222,6 +228,8 @@ typedef struct typedef struct zipfile_s { + searchpathfuncs_t pub; + char filename[MAX_OSPATH]; unzFile handle; int numfiles; @@ -239,14 +247,14 @@ typedef struct zipfile_s } zipfile_t; -static void QDECL FSZIP_GetDisplayPath(void *handle, char *out, unsigned int outlen) +static void QDECL FSZIP_GetPathDetails(void *handle, char *out, unsigned int outlen) { zipfile_t *zip = handle; if (zip->references != 1) - Q_snprintfz(out, outlen, "%s (%i)\n", zip->filename, zip->references-1); + Q_snprintfz(out, outlen, "(%i)", zip->references-1); else - Q_strncpyz(out, zip->filename, outlen); + *out = '\0'; } static void QDECL FSZIP_ClosePath(void *handle) { @@ -336,7 +344,7 @@ static void QDECL FSZIP_ReadFile(void *handle, flocation_t *loc, char *buffer) return; } -static int QDECL FSZIP_EnumerateFiles (void *handle, const char *match, int (QDECL *func)(const char *, int, void *, void *spath), void *parm) +static int QDECL FSZIP_EnumerateFiles (void *handle, const char *match, int (QDECL *func)(const char *, int, void *, searchpathfuncs_t *spath), void *parm) { zipfile_t *zip = handle; int num; @@ -345,7 +353,7 @@ static int QDECL FSZIP_EnumerateFiles (void *handle, const char *match, int (QDE { if (wildcmp(match, zip->files[num].name)) { - if (!func(zip->files[num].name, zip->files[num].filelen, parm, handle)) + if (!func(zip->files[num].name, zip->files[num].filelen, parm, &zip->pub)) return false; } } @@ -353,68 +361,7 @@ static int QDECL FSZIP_EnumerateFiles (void *handle, const char *match, int (QDE return true; } -/* -================= -COM_LoadZipFile - -Takes an explicit (not game tree related) path to a pak file. - -Loads the header and directory, adding the files at the beginning -of the list so they override previous pack files. -================= -*/ -static void *QDECL FSZIP_LoadZipFile (vfsfile_t *packhandle, const char *desc) -{ - int i; - int nextfileziphandle; - - zipfile_t *zip; - zpackfile_t *newfiles; - - unz_global_info globalinf = {0}; - unz_file_info file_info; - - zip = Z_Malloc(sizeof(zipfile_t)); - Q_strncpyz(zip->filename, desc, sizeof(zip->filename)); - zip->handle = unzOpen ((zip->raw = packhandle)); - if (!zip->handle) - { - Z_Free(zip); - Con_TPrintf (TL_COULDNTOPENZIP, desc); - return NULL; - } - - unzGetGlobalInfo (zip->handle, &globalinf); - - zip->numfiles = globalinf.number_entry; - - zip->files = newfiles = Z_Malloc (zip->numfiles * sizeof(zpackfile_t)); - for (i = 0; i < zip->numfiles; i++) - { - if (unzGetCurrentFileInfo (zip->handle, &file_info, newfiles[i].name, sizeof(newfiles[i].name), NULL, 0, NULL, 0) != UNZ_OK) - Con_Printf("Zip Error\n"); - Q_strlwr(newfiles[i].name); - if (!*newfiles[i].name || newfiles[i].name[strlen(newfiles[i].name)-1] == '/') - newfiles[i].filelen = -1; - else - newfiles[i].filelen = file_info.uncompressed_size; - newfiles[i].filepos = file_info.c_offset; - - nextfileziphandle = unzGoToNextFile (zip->handle); - if (nextfileziphandle == UNZ_END_OF_LIST_OF_FILE) - break; - else if (nextfileziphandle != UNZ_OK) - Con_Printf("Zip Error\n"); - } - - zip->references = 1; - zip->currentfile = NULL; - - Con_TPrintf (TL_ADDEDZIPFILE, desc, zip->numfiles); - return zip; -} - -int QDECL FSZIP_GeneratePureCRC(void *handle, int seed, int crctype) +static int QDECL FSZIP_GeneratePureCRC(void *handle, int seed, int crctype) { zipfile_t *zip = handle; unz_file_info file_info; @@ -460,7 +407,7 @@ typedef struct { int index; int startpos; } vfszip_t; -qboolean VFSZIP_MakeActive(vfszip_t *vfsz) +static qboolean VFSZIP_MakeActive(vfszip_t *vfsz) { int i; char buffer[8192]; //must be power of two @@ -501,7 +448,7 @@ qboolean VFSZIP_MakeActive(vfszip_t *vfsz) return true; } -int QDECL VFSZIP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread) +static int QDECL VFSZIP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread) { int read; vfszip_t *vfsz = (vfszip_t*)file; @@ -531,12 +478,12 @@ int QDECL VFSZIP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestorea vfsz->pos += read; return read; } -int QDECL VFSZIP_WriteBytes (struct vfsfile_s *file, void *buffer, int bytestoread) -{ - Sys_Error("VFSZIP_WriteBytes: Not supported\n"); - return 0; -} -qboolean QDECL VFSZIP_Seek (struct vfsfile_s *file, unsigned long pos) +//static int QDECL VFSZIP_WriteBytes (struct vfsfile_s *file, void *buffer, int bytestoread) +//{ +// Sys_Error("VFSZIP_WriteBytes: Not supported\n"); +// return 0; +//} +static qboolean QDECL VFSZIP_Seek (struct vfsfile_s *file, unsigned long pos) { vfszip_t *vfsz = (vfszip_t*)file; @@ -607,7 +554,7 @@ qboolean QDECL VFSZIP_Seek (struct vfsfile_s *file, unsigned long pos) return true; } -unsigned long QDECL VFSZIP_Tell (struct vfsfile_s *file) +static unsigned long QDECL VFSZIP_Tell (struct vfsfile_s *file) { vfszip_t *vfsz = (vfszip_t*)file; @@ -616,12 +563,12 @@ unsigned long QDECL VFSZIP_Tell (struct vfsfile_s *file) return vfsz->pos; } -unsigned long QDECL VFSZIP_GetLen (struct vfsfile_s *file) +static unsigned long QDECL VFSZIP_GetLen (struct vfsfile_s *file) { vfszip_t *vfsz = (vfszip_t*)file; return vfsz->length; } -void QDECL VFSZIP_Close (struct vfsfile_s *file) +static void QDECL VFSZIP_Close (struct vfsfile_s *file) { vfszip_t *vfsz = (vfszip_t*)file; @@ -635,7 +582,7 @@ void QDECL VFSZIP_Close (struct vfsfile_s *file) Z_Free(vfsz); } -vfsfile_t *QDECL FSZIP_OpenVFS(void *handle, flocation_t *loc, const char *mode) +static vfsfile_t *QDECL FSZIP_OpenVFS(void *handle, flocation_t *loc, const char *mode) { int rawofs; zipfile_t *zip = handle; @@ -660,6 +607,7 @@ vfsfile_t *QDECL FSZIP_OpenVFS(void *handle, flocation_t *loc, const char *mode) vfsz->funcs.Close = VFSZIP_Close; vfsz->funcs.GetLen = VFSZIP_GetLen; vfsz->funcs.ReadBytes = VFSZIP_ReadBytes; + //vfsz->funcs.WriteBytes = VFSZIP_WriteBytes; vfsz->funcs.Seek = VFSZIP_Seek; vfsz->funcs.Tell = VFSZIP_Tell; vfsz->funcs.WriteBytes = NULL; @@ -694,17 +642,76 @@ vfsfile_t *QDECL FSZIP_OpenVFS(void *handle, flocation_t *loc, const char *mode) return (vfsfile_t*)vfsz; } -searchpathfuncs_t zipfilefuncs = { - FSZIP_GetDisplayPath, - FSZIP_ClosePath, - FSZIP_BuildHash, - FSZIP_FLocate, - FSZIP_ReadFile, - FSZIP_EnumerateFiles, - FSZIP_LoadZipFile, - FSZIP_GeneratePureCRC, - FSZIP_OpenVFS -}; +/* +================= +COM_LoadZipFile + +Takes an explicit (not game tree related) path to a pak file. + +Loads the header and directory, adding the files at the beginning +of the list so they override previous pack files. +================= +*/ +searchpathfuncs_t *QDECL FSZIP_LoadArchive (vfsfile_t *packhandle, const char *desc) +{ + int i; + int nextfileziphandle; + + zipfile_t *zip; + zpackfile_t *newfiles; + + unz_global_info globalinf = {0}; + unz_file_info file_info; + + zip = Z_Malloc(sizeof(zipfile_t)); + Q_strncpyz(zip->filename, desc, sizeof(zip->filename)); + zip->handle = unzOpen ((zip->raw = packhandle)); + if (!zip->handle) + { + Z_Free(zip); + Con_TPrintf (TL_COULDNTOPENZIP, desc); + return NULL; + } + + unzGetGlobalInfo (zip->handle, &globalinf); + + zip->numfiles = globalinf.number_entry; + + zip->files = newfiles = Z_Malloc (zip->numfiles * sizeof(zpackfile_t)); + for (i = 0; i < zip->numfiles; i++) + { + if (unzGetCurrentFileInfo (zip->handle, &file_info, newfiles[i].name, sizeof(newfiles[i].name), NULL, 0, NULL, 0) != UNZ_OK) + Con_Printf("Zip Error\n"); + Q_strlwr(newfiles[i].name); + if (!*newfiles[i].name || newfiles[i].name[strlen(newfiles[i].name)-1] == '/') + newfiles[i].filelen = -1; + else + newfiles[i].filelen = file_info.uncompressed_size; + newfiles[i].filepos = file_info.c_offset; + + nextfileziphandle = unzGoToNextFile (zip->handle); + if (nextfileziphandle == UNZ_END_OF_LIST_OF_FILE) + break; + else if (nextfileziphandle != UNZ_OK) + Con_Printf("Zip Error\n"); + } + + zip->references = 1; + zip->currentfile = NULL; + + Con_TPrintf (TL_ADDEDZIPFILE, desc, zip->numfiles); + + zip->pub.fsver = FSVER; + zip->pub.GetPathDetails = FSZIP_GetPathDetails; + zip->pub.ClosePath = FSZIP_ClosePath; + zip->pub.BuildHash = FSZIP_BuildHash; + zip->pub.FindFile = FSZIP_FLocate; + zip->pub.ReadFile = FSZIP_ReadFile; + zip->pub.EnumerateFiles = FSZIP_EnumerateFiles; + zip->pub.GeneratePureCRC = FSZIP_GeneratePureCRC; + zip->pub.OpenVFS = FSZIP_OpenVFS; + return &zip->pub; +} #endif diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index 276bf91c9..0304ad19a 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -4,11 +4,16 @@ #endif #include "com_mesh.h" +#ifdef _WIN32 +#include +#else +#include +#endif + #define MAX_Q3MAP_INDICES 0x800000 //just a sanity limit #define MAX_Q3MAP_VERTEXES 0x80000 //just a sanity limit #define MAX_Q3MAP_BRUSHSIDES 0x30000 #define MAX_CM_BRUSHSIDES (MAX_Q3MAP_BRUSHSIDES << 1) -#define MAX_CM_BRUSHES (MAX_Q2MAP_BRUSHES << 1) #define MAX_CM_PATCH_VERTS (4096) #define MAX_CM_FACES (MAX_Q2MAP_FACES) #define MAX_CM_PATCHES (0x10000) @@ -299,7 +304,7 @@ static int numcmodels; static cmodel_t map_cmodels[MAX_Q2MAP_MODELS]; static int numbrushes; -static q2cbrush_t map_brushes[MAX_Q2MAP_BRUSHES]; +static q2cbrush_t *map_brushes; static int numvisibility; static q2dvis_t *map_q2vis; @@ -319,8 +324,9 @@ static q2dareaportal_t map_areaportals[MAX_Q2MAP_AREAPORTALS]; static q3cpatch_t map_patches[MAX_CM_PATCHES]; static int numpatches; -static int map_leafpatches[MAX_CM_LEAFFACES]; +static int *map_leafpatches; static int numleafpatches; +static int maxleafpatches; static int numclusters = 1; @@ -915,12 +921,12 @@ qboolean CM_CreatePatchesForLeafs (void) q3cface_t *face; q2mapsurface_t *surf; q3cpatch_t *patch; - int checkout[MAX_CM_FACES]; + int *checkout = alloca(sizeof(int)*numfaces); if (map_noCurves.ival) return true; - memset (checkout, -1, sizeof(int)*MAX_CM_FACES); + memset (checkout, -1, sizeof(int)*numfaces); for (i = 0, leaf = map_leafs; i < numleafs; i++, leaf++) { @@ -933,11 +939,19 @@ qboolean CM_CreatePatchesForLeafs (void) for (j=0 ; jnumleaffaces ; j++) { k = leaf->firstleafface + j; - if (k >= numleaffaces) { + if (k >= numleaffaces) + { break; } k = map_leaffaces[k]; +#ifdef _DEBUG + if (k >= numfaces) + { + Con_Printf (CON_ERROR "CM_CreatePatchesForLeafs: corrupt map\n"); + break; + } +#endif face = &map_faces[k]; if (face->facetype != MST_PATCH || face->numverts <= 0) @@ -951,10 +965,16 @@ qboolean CM_CreatePatchesForLeafs (void) if ( !surf->c.value || (surf->c.flags & Q3SURF_NONSOLID) ) continue; - if ( numleafpatches >= MAX_CM_LEAFFACES ) + if (numleafpatches >= maxleafpatches) { - Con_Printf (CON_ERROR "CM_CreatePatchesForLeafs: map has too many faces\n"); - return false; + maxleafpatches *= 2; + maxleafpatches += 16; + if (numleafpatches > maxleafpatches) + { //detect overflow + Con_Printf (CON_ERROR "CM_CreatePatchesForLeafs: map is insanely huge!\n"); + return false; + } + map_leafpatches = realloc(map_leafpatches, sizeof(*map_leafpatches) * maxleafpatches); } // the patch was already built @@ -1436,7 +1456,7 @@ qboolean CMod_LoadNodes (lump_t *l) Con_Printf (CON_ERROR "Map has no nodes\n"); return false; } - if (count > MAX_MAP_NODES) + if (count > SANITY_MAX_MAP_NODES) { Con_Printf (CON_ERROR "Map has too many nodes\n"); return false; @@ -1499,12 +1519,14 @@ qboolean CMod_LoadBrushes (lump_t *l) } count = l->filelen / sizeof(*in); - if (count > MAX_Q2MAP_BRUSHES) + if (count > SANITY_MAX_MAP_BRUSHES) { Con_Printf (CON_ERROR "Map has too many brushes"); return false; } + map_brushes = Hunk_AllocName(sizeof(*out) * (count+1), "brushes"); + out = map_brushes; numbrushes = count; @@ -2210,7 +2232,7 @@ qboolean CModQ3_LoadFaces (lump_t *l) } count = l->filelen / sizeof(*in); - if (count > MAX_MAP_FACES) + if (count > SANITY_MAX_MAP_FACES) { Con_Printf (CON_ERROR "Map has too many faces\n"); return false; @@ -2251,7 +2273,7 @@ qboolean CModRBSP_LoadFaces (lump_t *l) } count = l->filelen / sizeof(*in); - if (count > MAX_MAP_FACES) + if (count > SANITY_MAX_MAP_FACES) { Con_Printf (CON_ERROR "Map has too many faces\n"); return false; @@ -2888,7 +2910,7 @@ qboolean CModQ3_LoadLeafFaces (lump_t *l) } count = l->filelen / sizeof(*in); - if (count > MAX_Q2MAP_LEAFFACES) + if (count > SANITY_MAX_MAP_LEAFFACES) { Con_Printf (CON_ERROR "Map has too many leaffaces\n"); return false; @@ -2930,7 +2952,7 @@ qboolean CModQ3_LoadNodes (lump_t *l) count = l->filelen / sizeof(*in); out = Hunk_AllocName ( count*sizeof(*out), loadname); - if (count > MAX_MAP_NODES) + if (count > SANITY_MAX_MAP_NODES) { Con_Printf (CON_ERROR "Too many nodes on map\n"); return false; @@ -2988,12 +3010,14 @@ qboolean CModQ3_LoadBrushes (lump_t *l) } count = l->filelen / sizeof(*in); - if (count > MAX_Q2MAP_BRUSHES) + if (count > SANITY_MAX_MAP_BRUSHES) { Con_Printf (CON_ERROR "Map has too many brushes"); return false; } + map_brushes = Hunk_AllocName(sizeof(*out) * (count+1), "brushes"); + out = map_brushes; numbrushes = count; @@ -4295,7 +4319,7 @@ void CM_InitBoxHull (void) box_model.nodes = Hunk_Alloc(sizeof(mnode_t)*6); box_planes = &map_planes[numplanes]; - if (numbrushes+1 > MAX_Q2MAP_BRUSHES + if (numbrushes+1 > SANITY_MAX_MAP_BRUSHES || numleafbrushes+1 > MAX_Q2MAP_LEAFBRUSHES || numbrushsides+6 > MAX_Q2MAP_BRUSHSIDES || numplanes+12 > MAX_Q2MAP_PLANES) diff --git a/engine/common/net.h b/engine/common/net.h index acb131eda..8ed8c5281 100644 --- a/engine/common/net.h +++ b/engine/common/net.h @@ -86,7 +86,7 @@ void NET_Init (void); void SVNET_RegisterCvars(void); void NET_InitClient (void); void NET_InitServer (void); -qboolean NET_WasSpecialPacket(void); +qboolean NET_WasSpecialPacket(netsrc_t netsrc); void NET_CloseServer (void); void UDP_CloseSocket (int socket); void NET_Shutdown (void); diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 19e5f65aa..8a4695dd6 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -86,7 +86,7 @@ cvar_t showdrop = SCVAR("showdrop", "0"); cvar_t qport = SCVAR("qport", "0"); cvar_t net_mtu = CVARD("net_mtu", "1450", "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 pext_replacementdeltas = CVAR("debug_pext_replacementdeltas", "1"); /*rename once the extension is finalized*/ +cvar_t pext_replacementdeltas = CVAR("pext_replacementdeltas", "1"); /*returns the entire bitmask of supported+enabled extensions*/ unsigned int Net_PextMask(int maskset, qboolean fornq) diff --git a/engine/common/net_ssl_winsspi.c b/engine/common/net_ssl_winsspi.c index 19d175cea..7cf2cc839 100644 --- a/engine/common/net_ssl_winsspi.c +++ b/engine/common/net_ssl_winsspi.c @@ -1,5 +1,7 @@ #include "quakedef.h" #if defined(_WIN32) && !defined(_SDL) && defined(HAVE_SSL) +cvar_t *tls_ignorecertificateerrors; + #include #define SECURITY_WIN32 #include @@ -48,6 +50,8 @@ static qboolean SSL_Init(void) {(void**)&crypt.pCertFreeCertificateChain, "CertFreeCertificateChain"}, {NULL, NULL} }; + + tls_ignorecertificateerrors = Cvar_Get("tls_ignorecertificateerrors", "0", CVAR_NOTFROMSERVER, "TLS"); if (!secur.lib) secur.lib = Sys_LoadLibrary("secur32.dll", secur_functable); @@ -174,6 +178,8 @@ static void SSPI_Decode(sslfile_t *f) if (ss < 0) { + if (ss == SEC_E_INCOMPLETE_MESSAGE) + return; //no error if its incomplete, we can just get more data later on. SSPI_Error(f, "DecryptMessage failed"); return; } @@ -351,7 +357,13 @@ static DWORD VerifyServerCertificate(PCCERT_CONTEXT pServerCert, PWSTR pwszServe case CERT_E_WRONG_USAGE: err = "CERT_E_WRONG_USAGE"; break; default: err = "(unknown)"; break; } - Sys_Printf("Error verifying certificate for %s: %s\n", pwszServerName, err); + Con_Printf("Error verifying certificate for '%S': %s\n", pwszServerName, err); + + if (tls_ignorecertificateerrors->ival) + { + Con_Printf("pretending it didn't happen... (tls_ignorecertificateerrors is set)\n"); + Status = SEC_E_OK; + } } else Status = SEC_E_OK; diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 5a48d3988..962d91ae4 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -516,8 +516,11 @@ char *NET_AdrToString (char *s, int len, netadr_t *a) *s = 0; doneblank = false; p = s; - snprintf (s, len-strlen(s), "["); - p += strlen(p); + if (a->port) + { + snprintf (s, len-strlen(s), "["); + p += strlen(p); + } for (i = 0; i < 16; i+=2) { @@ -554,8 +557,9 @@ char *NET_AdrToString (char *s, int len, netadr_t *a) } } - snprintf (p, len-strlen(s), "]:%i", - ntohs(a->port)); + if (a->port) + snprintf (p, len-strlen(s), "]:%i", + ntohs(a->port)); break; #endif #ifdef USEIPX @@ -2422,6 +2426,9 @@ ftenet_generic_connection_t *FTENET_Generic_EstablishConnection(int adrfamily, i setsockopt(newsocket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&_true, sizeof(_true)); #endif + + setsockopt(newsocket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&_true, sizeof(_true)); + bufsz = 1<<18; setsockopt(newsocket, SOL_SOCKET, SO_RCVBUF, (void*)&bufsz, sizeof(bufsz)); @@ -2573,7 +2580,7 @@ void tobase64(unsigned char *out, int outlen, unsigned char *in, int inlen) } #include "fs.h" -int SHA1(char *digest, int maxdigestsize, char *string); +int SHA1(char *digest, int maxdigestsize, char *string, int stringlen); qboolean FTENET_TCPConnect_GetPacket(ftenet_generic_connection_t *gcon) { ftenet_tcpconnect_connection_t *con = (ftenet_tcpconnect_connection_t*)gcon; @@ -2810,10 +2817,12 @@ closesvstream: { char acceptkey[20*2]; unsigned char sha1digest[20]; + char *blurgh; memmove(st->inbuffer, st->inbuffer+i, st->inlen - (i)); st->inlen -= i; - tobase64(acceptkey, sizeof(acceptkey), sha1digest, SHA1(sha1digest, sizeof(sha1digest), va("%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", arg[WCATTR_WSKEY]))); + blurgh = va("%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", arg[WCATTR_WSKEY]); + tobase64(acceptkey, sizeof(acceptkey), sha1digest, SHA1(sha1digest, sizeof(sha1digest), blurgh, strlen(blurgh))); Con_Printf("Websocket request for %s from %s\n", arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), &st->remoteaddr)); @@ -4451,6 +4460,9 @@ int TCP_OpenStream (netadr_t *remoteaddr) setsockopt(newsocket, SOL_SOCKET, SO_RCVBUF, (void*)&recvbufsize, sizeof(recvbufsize)); + if (ioctlsocket (newsocket, FIONBIO, &_true) == -1) + Sys_Error ("UDP_OpenSocket: ioctl FIONBIO: %s", strerror(qerrno)); + // memset(&loc, 0, sizeof(loc)); // ((struct sockaddr*)&loc)->sa_family = ((struct sockaddr*)&loc)->sa_family; // bind(newsocket, (struct sockaddr *)&loc, ((struct sockaddr_in*)&qs)->sin_family == AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)); @@ -4458,27 +4470,26 @@ int TCP_OpenStream (netadr_t *remoteaddr) if (connect(newsocket, (struct sockaddr *)&qs, temp) == INVALID_SOCKET) { int err = qerrno; - if (err == EADDRNOTAVAIL) + if (err != EWOULDBLOCK) { - char buf[128]; - NET_AdrToString(buf, sizeof(buf), remoteaddr); - if (remoteaddr->port == 0 && (remoteaddr->type == NA_IP || remoteaddr->type == NA_IPV6)) - Con_Printf ("TCP_OpenStream: no port specified\n"); + if (err == EADDRNOTAVAIL) + { + char buf[128]; + NET_AdrToString(buf, sizeof(buf), remoteaddr); + if (remoteaddr->port == 0 && (remoteaddr->type == NA_IP || remoteaddr->type == NA_IPV6)) + Con_Printf ("TCP_OpenStream: no port specified\n"); + else + Con_Printf ("TCP_OpenStream: invalid address trying to connect to %s\n", buf); + } + else if (err == EACCES) + Con_Printf ("TCP_OpenStream: access denied: check firewall\n"); else - Con_Printf ("TCP_OpenStream: invalid address trying to connect to %s\n", buf); + Con_Printf ("TCP_OpenStream: connect: error %i\n", err); + closesocket(newsocket); + return INVALID_SOCKET; } - else if (err == EACCES) - Con_Printf ("TCP_OpenStream: access denied: check firewall\n"); - else - Con_Printf ("TCP_OpenStream: connect: error %i\n", err); - closesocket(newsocket); - return INVALID_SOCKET; } - if (ioctlsocket (newsocket, FIONBIO, &_true) == -1) - Sys_Error ("UDP_OpenSocket: ioctl FIONBIO: %s", strerror(qerrno)); - - return newsocket; #endif } @@ -4856,6 +4867,224 @@ void NET_GetLocalAddress (int socket, netadr_t *out) #endif } +#ifdef SUPPORT_ICE +static struct icestate_s *icelist; +struct icestate_s *QDECL ICE_Find(void *module, char *conname) +{ + struct icestate_s *con; + + for (con = icelist; con; con = con->next) + { + if (con->module == module && !strcmp(con->conname, conname)) + return con; + } + return NULL; +} +struct icestate_s *QDECL ICE_Create(void *module, char *conname, char *peername, enum icemode_e mode) +{ + ftenet_connections_t *collection; + struct icestate_s *con; + int netsrc; + + if (conname) + if (ICE_Find(module, conname)) + return NULL; + + if (!conname) + { +#ifdef SERVERONLY + return NULL; +#else + int rnd[2]; + Sys_RandomBytes((void*)rnd, sizeof(rnd)); + conname = va("fte%08x%08x", rnd[0], rnd[1]); + collection = cls.sockets; //initiator is ALWAYS the game client. + netsrc = NS_CLIENT; +#endif + } + else + { +#ifdef CLIENTONLY + return NULL; +#else + collection = svs.sockets; //responder is ALWAYS the game server. + netsrc = NS_SERVER; +#endif + } + + con = Z_Malloc(sizeof(*con)); + con->conname = Z_StrDup(conname); + con->friendlyname = Z_StrDup(peername); + con->netsrc = netsrc; + + con->mode = mode; + con->mode = ICE_RAW; + + con->next = icelist; + icelist = con; + + { + int rnd[1]; //'must have at least 24 bits randomness' + Sys_RandomBytes((void*)rnd, sizeof(rnd)); + con->lfrag = Z_StrDup(va("%08x", rnd[0])); + } + { + int rnd[4]; //'must have at least 128 bits randomness' + Sys_RandomBytes((void*)rnd, sizeof(rnd)); + con->lpwd = Z_StrDup(va("%08x%08x%08x%08x", rnd[0], rnd[1], rnd[2], rnd[3])); + } + + if (collection) + { + int i; + int adrno, adrcount=1; + netadr_t adr; + char adrbuf[MAX_ADR_SIZE]; + int net = 0; + + for (i = 0; i < MAX_CONNECTIONS; i++) + { + if (!collection->conn[i]) + continue; + adrno = 0; + if (collection->conn[i]->GetLocalAddress) + { + for (adrcount=1; (adrcount = collection->conn[i]->GetLocalAddress(collection->conn[i], &adr, adrno)) && adrno < adrcount; adrno++) + { + struct icecandidate_s *cand; + int rnd[2]; + if (adr.type == NA_IP || adr.type == NA_IPV6) + { + cand = Z_Malloc(sizeof(*cand)); + cand->network = net; + cand->port = ntohs(adr.port); + adr.port = 0; //to make sure its not part of the string... + cand->addr = Z_StrDup(NET_AdrToString(adrbuf, sizeof(adrbuf), &adr)); + cand->generation = 0; + cand->component = 1; + cand->foundation = 1; + cand->priority = + (1<<24)*(126) + + (1<<8)*((adr.type == NA_IP?32768:0)+net*256+(255-adrcount)) + + (1<<0)*(256 - cand->component); + + Sys_RandomBytes((void*)rnd, sizeof(rnd)); + cand->candidateid = Z_StrDup(va("x%08x%08x", rnd[0], rnd[1])); + cand->dirty = true; + + cand->next = con->lc; + con->lc = cand; + } + } + } + net++; + } + } + + return con; +} +void QDECL ICE_Begin(struct icestate_s *con, char *stunip, int stunport) +{ + switch(con->mode) + { + case ICE_RAW: + //info is already as complete as it'll ever be... yeah, it sucks. sue me. + if (con->netsrc == NS_CLIENT) + { + struct icecandidate_s *rc; + rc = con->rc;//for (rc = con->rc; rc; + if (rc && (!strchr(rc->addr, ';') && !strchr(rc->addr, '\n'))) + Cbuf_AddText(va("connect [%s]:%i\n", rc->addr, rc->port), RESTRICT_LOCAL); + else + Con_Printf("Remote candidate is not valid\n"); + } + break; + case ICE_ICE: + //FIXME: schedule some stun requests to some public stun server + break; + } +} +struct icecandidate_s *QDECL ICE_GetLCandidateInfo(struct icestate_s *con) +{ + struct icecandidate_s *can; + for (can = con->lc; can; can = can->next) + { + if (can->dirty) + { + can->dirty = false; + return can; + } + } + return NULL; +} +void QDECL ICE_AddRCandidateInfo(struct icestate_s *con, struct icecandidate_s *n) +{ + struct icecandidate_s *o; + for (o = con->rc; o; o = o->next) + { + if (!strcmp(o->candidateid, n->candidateid)) + break; + } + if (!o) + { + o = Z_Malloc(sizeof(*o)); + o->next = con->rc; + con->rc = o; + o->candidateid = Z_StrDup(n->candidateid); + } + else + { + Z_Free(o->addr); + } + o->addr = Z_StrDup(n->addr); + o->port = n->port; + o->type = n->type; + o->priority = n->priority; + o->network = n->network; + o->generation = n->generation; + o->foundation = n->foundation; + o->component = n->component; + o->transport = n->transport; +} +static void ICE_Destroy(struct icestate_s *con) +{ + //has already been unlinked + Z_Free(con); +} +void QDECL ICE_Close(struct icestate_s *con) +{ + struct icestate_s **link; + + for (link = &icelist; *link; ) + { + if (con == *link) + { + *link = con->next; + ICE_Destroy(con); + return; + } + else + link = &(*link)->next; + } +} +void QDECL ICE_CloseModule(void *module) +{ + struct icestate_s **link, *con; + + for (link = &icelist; *link; ) + { + con = *link; + if (con->module == module) + { + *link = con->next; + ICE_Destroy(con); + } + else + link = &(*link)->next; + } +} +#endif + #ifndef CLIENTONLY void SVNET_AddPort_f(void) { @@ -4896,7 +5125,7 @@ typedef struct unsigned short attrtype; unsigned short attrlen; } stunattr_t; -static qboolean NET_WasStun(void) +static qboolean NET_WasStun(netsrc_t netsrc) { if ((net_from.type == NA_IP || net_from.type == NA_IPV6) && net_message.cursize >= 20) { @@ -4904,6 +5133,7 @@ static qboolean NET_WasStun(void) int stunlen = BigShort(stun->msglen); if (stun->msgtype == BigShort(0x0101) && net_message.cursize == stunlen + sizeof(*stun)) { + //binding reply stunattr_t *attr = (stunattr_t*)(stun+1); int alen; while(stunlen) @@ -4912,30 +5142,100 @@ static qboolean NET_WasStun(void) alen = BigShort(attr->attrlen); if (alen > stunlen) return false; - if (attr->attrtype == BigShort(1) && alen == 8 && ((qbyte*)attr)[5] == 1) //ipv4 MAPPED-ADDRESS + stunlen -= alen; + switch(BigShort(attr->attrtype)) { - netadr_t adr; - char str[256]; - adr.type = NA_IP; - adr.port = (((short*)attr)[3]); - memcpy(adr.address.ip, &((qbyte*)attr)[8], 4); - NET_AdrToString(str, sizeof(str), &adr); - Con_Printf("Public address %s\n", str); - } - else if (attr->attrtype == BigShort(1) && alen == 20 && ((qbyte*)attr)[5] == 2) //ipv6 MAPPED-ADDRESS - { - netadr_t adr; - char str[256]; - adr.type = NA_IPV6; - adr.port = (((short*)attr)[3]); - memcpy(adr.address.ip6, &((qbyte*)attr)[8], 16); - NET_AdrToString(str, sizeof(str), &adr); - Con_Printf("Public address %s\n", str); + case 1: + if (alen == 8 && ((qbyte*)attr)[5] == 1) //ipv4 MAPPED-ADDRESS + { + netadr_t adr; + char str[256]; + adr.type = NA_IP; + adr.port = (((short*)attr)[3]); + memcpy(adr.address.ip, &((qbyte*)attr)[8], 4); + NET_AdrToString(str, sizeof(str), &adr); + Con_Printf("Public address %s\n", str); + } + else if (alen == 20 && ((qbyte*)attr)[5] == 2) //ipv6 MAPPED-ADDRESS + { + netadr_t adr; + char str[256]; + adr.type = NA_IPV6; + adr.port = (((short*)attr)[3]); + memcpy(adr.address.ip6, &((qbyte*)attr)[8], 16); + NET_AdrToString(str, sizeof(str), &adr); + Con_Printf("Public address %s\n", str); + } + break; } attr = (stunattr_t*)((char*)(attr+1) + alen); } return true; } + else if (stun->msgtype == BigShort(0x0001) && net_message.cursize == stunlen + sizeof(*stun)) + { + char username[256]; + //binding request + stunattr_t *attr = (stunattr_t*)(stun+1); + int alen; + *username = 0; + while(stunlen) + { + alen = (unsigned short)BigShort(attr->attrlen); + if (alen+sizeof(*attr) > stunlen) + return false; + switch((unsigned short)BigShort(attr->attrtype)) + { + case 0x6: + //username + if (alen < sizeof(username)) + { + memcpy(username, attr+1, alen); + username[alen] = 0; + Con_Printf("Stun username = \"%s\"\n", username); + } + break; + case 0x8: + //message integrity + Con_Printf("integrity = \"%08x%08x%08x%08x%08x\"\n", BigLong(*(int*)(attr+1)), BigLong(*(int*)(attr+2)), BigLong(*(int*)(attr+3)), BigLong(*(int*)(attr+4)), BigLong(*(int*)(attr+5))); + break; + case 0x24: + //priority + Con_Printf("priority = \"%i\"\n", BigLong(*(int*)(attr+1))); + break; + case 0x8028: + //fingerprint + Con_Printf("fingerprint = \"%08x\"\n", BigLong(*(int*)(attr+1))); + break; + case 0x8029: + //ice controlled + Con_Printf("tie breaker = \"%08x%08x\"\n", BigLong(*(int*)(attr+1)), BigLong(*(int*)(attr+2))); + break; + } + alen = (alen+3)&~3; + attr = (stunattr_t*)((char*)(attr+1) + alen); + stunlen -= alen+sizeof(*attr); + } + + { + struct { + stunhdr_t hdr; + + stunattr_t unattr; + char uname[256]; + } stunmsg; + stunmsg.hdr.magiccookie = BigLong(0x2112a442); + Sys_RandomBytes((qbyte*)&stunmsg.hdr.transactid[0], sizeof(stunmsg.hdr.transactid)); // 'and SHOULD be cryptographically random' + stunmsg.hdr.msgtype = BigShort(0x0101); //binding result + strcpy(stunmsg.uname, username); + stunmsg.unattr.attrtype = BigShort(0x6); + stunmsg.unattr.attrlen = BigShort(strlen(stunmsg.uname)); + stunmsg.hdr.msglen = BigShort(sizeof(stunmsg.unattr)+(strlen(stunmsg.uname)+3)&~3); + NET_SendPacket(netsrc, sizeof(stunmsg.hdr) + BigShort(stunmsg.hdr.msglen), &stunmsg, &net_from); + } + + return true; + } } return false; } @@ -4972,14 +5272,29 @@ void NET_ClientPort_f(void) } #endif -qboolean NET_WasSpecialPacket(void) +qboolean NET_WasSpecialPacket(netsrc_t netsrc) { + ftenet_connections_t *collection = NULL; + if (netsrc == NS_SERVER) + { #ifndef CLIENTONLY - if (NET_WasStun()) + collection = svs.sockets; +#endif + } + else + { +#ifndef SERVERONLY + collection = cls.sockets; +#endif + } + + +#ifndef CLIENTONLY + if (NET_WasStun(netsrc)) return true; #endif #ifdef HAVE_NATPMP - if (NET_Was_NATPMP(svs.sockets)) + if (NET_Was_NATPMP(collection)) return true; #endif return false; @@ -5259,7 +5574,8 @@ void NET_Shutdown (void) typedef struct { vfsfile_t funcs; - int sock; + SOCKET sock; + qboolean conpending; char readbuffer[65536]; int readbuffered; @@ -5278,6 +5594,19 @@ int QDECL VFSTCP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestorea int len; int trying; + if (tf->conpending) + { + fd_set fd; + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + FD_ZERO(&fd); + FD_SET(tf->sock, &fd); + if (!select((int)tf->sock, NULL, &fd, NULL, &timeout)) + return 0; + tf->conpending = false; + } + if (tf->sock != INVALID_SOCKET) { trying = sizeof(tf->readbuffer) - tf->readbuffered; @@ -5294,6 +5623,12 @@ int QDECL VFSTCP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestorea case ECONNABORTED: Sys_Printf("connection aborted\n", e); break; + case ECONNREFUSED: + Sys_Printf("connection refused\n", e); + break; + case ECONNRESET: + Sys_Printf("connection reset\n", e); + break; default: Sys_Printf("socket error %i\n", e); } @@ -5340,9 +5675,29 @@ int QDECL VFSTCP_WriteBytes (struct vfsfile_s *file, const void *buffer, int byt if (tf->sock == INVALID_SOCKET) return 0; + if (tf->conpending) + { + fd_set fd; + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + FD_ZERO(&fd); + FD_SET(tf->sock, &fd); + if (!select((int)tf->sock, NULL, &fd, NULL, &timeout)) + return 0; + tf->conpending = false; + } + len = send(tf->sock, buffer, bytestoread, 0); if (len == -1 || len == 0) { + int e = qerrno; + switch(e) + { + default: + Sys_Printf("socket error %i\n", e); + break; + } // don't destroy it on write errors, because that prevents us from reading anything that was sent to us afterwards. // instead let the read handling kill it if there's nothing new to be read VFSTCP_ReadBytes(file, NULL, 0); @@ -5382,6 +5737,7 @@ vfsfile_t *FS_OpenTCP(const char *name, int defaultport) return NULL; newf = Z_Malloc(sizeof(*newf)); + newf->conpending = true; newf->sock = sock; newf->funcs.Close = VFSTCP_Close; newf->funcs.Flush = NULL; diff --git a/engine/common/netinc.h b/engine/common/netinc.h index 67c2f30e5..d00feb5ec 100644 --- a/engine/common/netinc.h +++ b/engine/common/netinc.h @@ -38,7 +38,11 @@ #ifdef _MSC_VER #define USEIPX #endif - #include "winquake.h" + #define WIN32_LEAN_AND_MEAN + #define byte winbyte + #include + #include +// #include "winquake.h" #ifdef USEIPX #include "wsipx.h" #endif @@ -191,3 +195,60 @@ #undef IPPROTO_IPV6 #endif +#if 1//def SUPPORT_ICE +struct icecandidate_s +{ + struct icecandidate_s *next; + char *candidateid; + char *addr; //v4/v6/fqdn. fqdn should prefer ipv6 + int port; + int transport; //0=udp. other values not supported + int foundation; //to figure out... + int component; //1-based. allows rtp+rtcp in a single ICE... we only support one. + int priority; //some random value... + enum + { + ICE_HOST=0, + ICE_SRFLX=1, + ICE_PRFLX=2, + ICE_RELAY=3, + } type; //says what sort of proxy is used. + char *reladdr; //when proxied, this is our local info + int relport; + int generation; //for ice restarts. starts at 0. + int network; //which network device this comes from. + + qboolean dirty; +}; +struct icestate_s +{ + struct icestate_s *next; + void *module; + + int netsrc; + enum icemode_e + { + ICE_RAW, //not actually interactive beyond a simple handshake. + ICE_ICE //rfc5245. meant to be able to holepunch, but not implemented properly yet. + } mode; + char *conname; //internal id. + char *friendlyname; //who you're talking to. + char *stunserver;//where to get our public ip from. + int stunport; + + struct icecandidate_s *lc; + char *lpwd; + char *lfrag; + + struct icecandidate_s *rc; + char *rpwd; + char *rfrag; +}; +struct icestate_s *QDECL ICE_Create(void *module, char *conname, char *peername, enum icemode_e mode); //doesn't start pinging anything. +struct icestate_s *QDECL ICE_Find(void *module, char *conname); +void QDECL ICE_Begin(struct icestate_s *con, char *stunip, int stunport); //begins sending stun packets and stuff as required. data flows automagically. caller should poll ICE_GetLCandidateInfo periodically to pick up new candidates that need to be reported to the peer. +struct icecandidate_s *QDECL ICE_GetLCandidateInfo(struct icestate_s *con); //retrieves candidates that need reporting to the peer. +void QDECL ICE_AddRCandidateInfo(struct icestate_s *con, struct icecandidate_s *cand); //stuff that came from the peer. +void QDECL ICE_Close(struct icestate_s *con); //bye then. +void QDECL ICE_CloseModule(void *module); //closes all unclosed connections, with warning. +#endif \ No newline at end of file diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 91f2856b0..0f935fa2a 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -128,6 +128,7 @@ typedef struct plugin_s { int tick; int executestring; #ifndef SERVERONLY + int consolelink; int conexecutecommand; int menufunction; int sbarlevel[3]; //0 - main sbar, 1 - supplementry sbar sections (make sure these can be switched off), 2 - overlays (scoreboard). menus kill all. @@ -203,6 +204,27 @@ void Plug_RegisterBuiltin(char *name, Plug_Builtin_t bi, int flags) plugbuiltins[newnum].flags = flags; } +static qintptr_t VARGS Plug_GetNativePointer(void *offset, quintptr_t mask, const qintptr_t *args) +{ + char *p = (char *)VM_POINTER(args[0]); +#ifdef SUPPORT_ICE + if (!strcmp(p, "ICE_Create")) + return (qintptr_t)ICE_Create; + if (!strcmp(p, "ICE_Find")) + return (qintptr_t)ICE_Find; + if (!strcmp(p, "ICE_Begin")) + return (qintptr_t)ICE_Begin; + if (!strcmp(p, "ICE_GetLCandidateInfo")) + return (qintptr_t)ICE_GetLCandidateInfo; + if (!strcmp(p, "ICE_AddRCandidateInfo")) + return (qintptr_t)ICE_AddRCandidateInfo; + if (!strcmp(p, "ICE_Close")) + return (qintptr_t)ICE_Close; +#endif + + return (qintptr_t)NULL; +} + /* static void Plug_RegisterBuiltinIndex(char *name, Plug_Builtin_t bi, int flags, int index) //I d { @@ -408,13 +430,15 @@ static qintptr_t VARGS Plug_ExportToEngine(void *offset, quintptr_t mask, const char *name = (char*)VM_POINTER(arg[0]); unsigned int functionid = VM_LONG(arg[1]); - if (!strcmp(name, "Tick")) + if (!strcmp(name, "Tick")) //void(int realtime) currentplug->tick = functionid; - else if (!strcmp(name, "ExecuteCommand")) + else if (!strcmp(name, "ExecuteCommand")) //bool(isinsecure) currentplug->executestring = functionid; - else if (!strcmp(name, "Shutdown")) + else if (!strcmp(name, "Shutdown")) //void() currentplug->shutdown = functionid; #ifndef SERVERONLY + else if (!strcmp(name, "ConsoleLink")) + currentplug->consolelink = functionid; else if (!strcmp(name, "ConExecuteCommand")) currentplug->conexecutecommand = functionid; else if (!strcmp(name, "MenuEvent")) @@ -657,13 +681,21 @@ static qintptr_t VARGS Plug_Cvar_GetFloat(void *offset, quintptr_t mask, const q { char *name = VM_POINTER(arg[0]); int ret; - cvar_t *var = Cvar_Get(name, "", 0, "Plugin vars"); - if (var) - { - VM_FLOAT(ret) = var->value; - } + cvar_t *var; +#ifndef CLIENTONLY + if (!strcmp(name, "sv.state")) + VM_FLOAT(ret) = sv.state; else - VM_FLOAT(ret) = 0; +#endif + { + var = Cvar_Get(name, "", 0, "Plugin vars"); + if (var) + { + VM_FLOAT(ret) = var->value; + } + else + VM_FLOAT(ret) = 0; + } return ret; } @@ -682,12 +714,22 @@ static qintptr_t VARGS Plug_Cvar_GetString(void *offset, quintptr_t mask, const ret = VM_POINTER(arg[1]); retsize = VM_LONG(arg[2]); + if (!strcmp(name, "sv.mapname")) + { +#ifdef CLIENTONLY + Q_strncpyz(ret, "", retsize); +#else + Q_strncpyz(ret, sv.name, retsize); +#endif + } + else + { + var = Cvar_Get(name, "", 0, "Plugin vars"); + if (strlen(var->name)+1 > retsize) + return false; - var = Cvar_Get(name, "", 0, "Plugin vars"); - if (strlen(var->name)+1 > retsize) - return false; - - strcpy(ret, var->string); + strcpy(ret, var->string); + } return true; } @@ -725,7 +767,7 @@ void Plug_Command_f(void) currentplug = plugincommandarray[i].plugin; if (currentplug->executestring) - VM_Call(currentplug->vm, currentplug->executestring, 0); + VM_Call(currentplug->vm, currentplug->executestring, Cmd_IsInsecure(), 0, 0, 0); break; } @@ -1090,6 +1132,7 @@ qintptr_t VARGS Plug_FS_Open(void *offset, quintptr_t mask, const qintptr_t *arg //char *data; char *mode; vfsfile_t *f; + char *fname = VM_POINTER(arg[0]); if (VM_OOB(arg[1], sizeof(int))) return -2; @@ -1106,7 +1149,10 @@ qintptr_t VARGS Plug_FS_Open(void *offset, quintptr_t mask, const qintptr_t *arg default: return -2; } - f = FS_OpenVFS(VM_POINTER(arg[0]), mode, FS_GAME); + if (!strcmp(fname, "**plugconfig")) + f = FS_OpenVFS(va("%s.cfg", currentplug->name), mode, FS_ROOT); + else + f = FS_OpenVFS(fname, mode, FS_GAME); if (!f) return -1; handle = Plug_NewStreamHandle(STREAM_VFS); @@ -1458,6 +1504,7 @@ void Plug_Initialise(qboolean fromgamedir) Cmd_AddCommand("plug_load", Plug_Load_f); Cmd_AddCommand("plug_list", Plug_List_f); + Plug_RegisterBuiltin("Plug_GetNativePointer", Plug_GetNativePointer, 0);//plugin wishes to find a builtin number. Plug_RegisterBuiltin("Plug_GetEngineFunction", Plug_GetBuiltin, 0);//plugin wishes to find a builtin number. Plug_RegisterBuiltin("Plug_ExportToEngine", Plug_ExportToEngine, 0); //plugin has a call back that we might be interested in. Plug_RegisterBuiltin("Plug_ExportNative", Plug_ExportNative, PLUG_BIF_DLLONLY); @@ -1580,13 +1627,30 @@ qboolean Plugin_ExecuteString(void) } #ifndef SERVERONLY +qboolean Plug_ConsoleLink(char *text, char *info) +{ + plugin_t *oldplug = currentplug; + for (currentplug = plugs; currentplug; currentplug = currentplug->next) + { + if (currentplug->consolelink) + { + char buffer[2048]; + Q_strncpyz(buffer, va("\"%s\" \"%s\"", text, info), sizeof(buffer)); + Cmd_TokenizeString(buffer, false, false); + VM_Call(currentplug->vm, currentplug->consolelink); + } + } + currentplug = oldplug; + return false; +} + void Plug_SubConsoleCommand(console_t *con, char *line) { char buffer[2048]; plugin_t *oldplug = currentplug; //shouldn't really be needed, but oh well currentplug = con->userdata; - Q_strncpyz(buffer, va("%s %s", con->name, line), sizeof(buffer)); + Q_strncpyz(buffer, va("\"%s\" %s", con->name, line), sizeof(buffer)); Cmd_TokenizeString(buffer, false, false); VM_Call(currentplug->vm, currentplug->conexecutecommand, 0); currentplug = oldplug; @@ -1653,13 +1717,12 @@ int Plug_ConnectionlessClientPacket(char *buffer, int size) } #endif #ifndef SERVERONLY -void Plug_SBar(void) +void Plug_SBar(playerview_t *pv) { extern qboolean sb_showscores, sb_showteamscores; plugin_t *oc=currentplug; - int cp, ret; - vrect_t rect; + int ret; if (!Sbar_ShouldDraw()) return; @@ -1673,31 +1736,24 @@ void Plug_SBar(void) { if (currentplug->sbarlevel[0]) { - for (cp = 0; cp < cl.splitclients; cp++) - { //if you don't use splitscreen, use a full videosize rect. - SCR_VRectForPlayer(&rect, cp); - R2D_ImageColours(1, 1, 1, 1); // ensure menu colors are reset - ret |= VM_Call(currentplug->vm, currentplug->sbarlevel[0], cp, rect.x, rect.y, rect.width, rect.height, sb_showscores+sb_showteamscores*2); - } + //if you don't use splitscreen, use a full videosize rect. + R2D_ImageColours(1, 1, 1, 1); // ensure menu colors are reset + ret |= VM_Call(currentplug->vm, currentplug->sbarlevel[0], pv-cl.playerview, r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, sb_showscores+sb_showteamscores*2); break; } } } if (!(ret & 1)) { - Sbar_Draw(); + Sbar_Draw(pv); } for (currentplug = plugs; currentplug; currentplug = currentplug->next) { if (currentplug->sbarlevel[1]) { - for (cp = 0; cp < cl.splitclients; cp++) - { //if you don't use splitscreen, use a full videosize rect. - SCR_VRectForPlayer(&rect, cp); - R2D_ImageColours(1, 1, 1, 1); // ensure menu colors are reset - ret |= VM_Call(currentplug->vm, currentplug->sbarlevel[1], cp, rect.x, rect.y, rect.width, rect.height, sb_showscores+sb_showteamscores*2); - } + R2D_ImageColours(1, 1, 1, 1); // ensure menu colors are reset + ret |= VM_Call(currentplug->vm, currentplug->sbarlevel[1], pv-cl.playerview, r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, sb_showscores+sb_showteamscores*2); } } @@ -1705,16 +1761,12 @@ void Plug_SBar(void) { if (currentplug->sbarlevel[2]) { - for (cp = 0; cp < cl.splitclients; cp++) - { //if you don't use splitscreen, use a full videosize rect. - SCR_VRectForPlayer(&rect, cp); - R2D_ImageColours(1, 1, 1, 1); // ensure menu colors are reset - ret |= VM_Call(currentplug->vm, currentplug->sbarlevel[2], cp, rect.x, rect.y, rect.width, rect.height, sb_showscores+sb_showteamscores*2); - } + R2D_ImageColours(1, 1, 1, 1); // ensure menu colors are reset + ret |= VM_Call(currentplug->vm, currentplug->sbarlevel[2], pv-cl.playerview, r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, sb_showscores+sb_showteamscores*2); } } - if (!(ret & 2)) + if (!(ret & 2) && pv == cl.playerview) { Sbar_DrawScoreboard(); } diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 8c2448e19..c7445153d 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -1023,11 +1023,10 @@ void QCBUILTIN PF_memptradd (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo //hash table stuff #define MAX_QC_HASHTABLES 256 -#define FIRST_QC_HASHTABLE_INDEX 1 - typedef struct { pubprogfuncs_t *prinst; + qboolean dupestrings; hashtable_t tab; void *bucketmem; } pf_hashtab_t; @@ -1035,73 +1034,127 @@ typedef struct { bucket_t buck; char *name; - vec3_t data; + union + { + vec3_t data; + char *stringdata; + }; } pf_hashentry_t; pf_hashtab_t pf_hashtab[MAX_QC_HASHTABLES]; +pf_hashtab_t pf_peristanthashtab; //persists over map changes. +pf_hashtab_t pf_reverthashtab; //pf_peristanthashtab as it was at map start, for map restarts. +static pf_hashtab_t *PF_hash_findtab(pubprogfuncs_t *prinst, int idx) +{ + if (!idx) + return &pf_peristanthashtab; + idx -= 1; + if (idx >= 0 && idx < MAX_QC_HASHTABLES && pf_hashtab[idx].prinst) + return &pf_hashtab[idx]; + else + PR_BIError(prinst, "PF_hash_findtab: invalid hash table\n"); + return NULL; +}; void QCBUILTIN PF_hash_getkey (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - int tab = G_FLOAT(OFS_PARM0) - FIRST_QC_HASHTABLE_INDEX; + pf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0)); int idx = G_FLOAT(OFS_PARM1); pf_hashentry_t *ent = NULL; G_INT(OFS_RETURN) = 0; - if (tab >= 0 && tab < MAX_QC_HASHTABLES && pf_hashtab[tab].prinst) + if (tab) { - ent = Hash_GetIdx(&pf_hashtab[tab].tab, idx); + ent = Hash_GetIdx(&tab->tab, idx); if (ent) RETURN_TSTRING(ent->name); } } void QCBUILTIN PF_hash_delete (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - int tab = G_FLOAT(OFS_PARM0) - FIRST_QC_HASHTABLE_INDEX; + pf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0)); char *name = PR_GetStringOfs(prinst, OFS_PARM1); pf_hashentry_t *ent = NULL; memset(G_VECTOR(OFS_RETURN), 0, sizeof(vec3_t)); - if (tab >= 0 && tab < MAX_QC_HASHTABLES && pf_hashtab[tab].prinst) + if (tab) { - ent = Hash_Get(&pf_hashtab[tab].tab, name); + ent = Hash_Get(&tab->tab, name); if (ent) { memcpy(G_VECTOR(OFS_RETURN), ent->data, sizeof(vec3_t)); - Hash_RemoveData(&pf_hashtab[tab].tab, name, ent); + Hash_RemoveData(&tab->tab, name, ent); BZ_Free(ent); } } - else - PR_BIError(prinst, "PF_hash_get: invalid hash table\n"); } void QCBUILTIN PF_hash_get (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - int tab = G_FLOAT(OFS_PARM0) - FIRST_QC_HASHTABLE_INDEX; + pf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0)); char *name = PR_GetStringOfs(prinst, OFS_PARM1); pf_hashentry_t *ent = NULL; - if (tab >= 0 && tab < MAX_QC_HASHTABLES && pf_hashtab[tab].prinst) + if (tab) + { + ent = Hash_Get(&tab->tab, name); + if (ent) + { + if (tab->dupestrings) + { + G_INT(OFS_RETURN+2) = 0; + G_INT(OFS_RETURN+1) = 0; + RETURN_TSTRING(ent->stringdata); + } + else + memcpy(G_VECTOR(OFS_RETURN), ent->data, sizeof(vec3_t)); + } + else + memcpy(G_VECTOR(OFS_RETURN), G_VECTOR(OFS_PARM2), sizeof(vec3_t)); + } +} +void QCBUILTIN PF_hash_getcb (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ +/* + pf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0)); + func_t callback = G_FUNCTION(OFS_PARM1); + char *name = PR_GetStringOfs(prinst, OFS_PARM2); + pf_hashentry_t *ent = NULL; + if (tab >= 0 && tab < MAX_QC_HASHTABLES && pf_hashtab[tab].valid) ent = Hash_Get(&pf_hashtab[tab].tab, name); else - PR_BIError(prinst, "PF_hash_get: invalid hash table\n"); + PR_BIError(prinst, "PF_hash_getcb: invalid hash table\n"); if (ent) memcpy(G_VECTOR(OFS_RETURN), ent->data, sizeof(vec3_t)); else memcpy(G_VECTOR(OFS_RETURN), G_VECTOR(OFS_PARM2), sizeof(vec3_t)); +*/ } void QCBUILTIN PF_hash_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - int tab = G_FLOAT(OFS_PARM0) - FIRST_QC_HASHTABLE_INDEX; + pf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0)); char *name = PR_GetStringOfs(prinst, OFS_PARM1); void *data = G_VECTOR(OFS_PARM2); pf_hashentry_t *ent = NULL; - if (tab >= 0 && tab < MAX_QC_HASHTABLES && pf_hashtab[tab].prinst) + if (tab) { - int nlen = strlen(name); - ent = BZ_Malloc(sizeof(*ent) + nlen + 1); - ent->name = (char*)(ent+1); - memcpy(ent->name, name, nlen+1); - memcpy(ent->data, data, sizeof(vec3_t)); - Hash_Add(&pf_hashtab[tab].tab, ent->name, ent, &ent->buck); + if (tab->dupestrings) + { + char *value = PR_GetStringOfs(prinst, OFS_PARM2); + int nlen = strlen(name); + int vlen = strlen(data); + ent = BZ_Malloc(sizeof(*ent) + nlen+1 + vlen+1); + ent->name = (char*)(ent+1); + ent->stringdata = ent->name+(nlen+1); + memcpy(ent->name, name, nlen+1); + memcpy(ent->stringdata, value, vlen+1); + Hash_Add(&tab->tab, ent->name, ent, &ent->buck); + } + else + { + int nlen = strlen(name); + ent = BZ_Malloc(sizeof(*ent) + nlen + 1); + ent->name = (char*)(ent+1); + memcpy(ent->name, name, nlen+1); + memcpy(ent->data, data, sizeof(vec3_t)); + Hash_Add(&tab->tab, ent->name, ent, &ent->buck); + } } - else - PR_BIError(prinst, "PF_hash_add: invalid hash table\n"); } static void PF_hash_destroytab_enum(void *ctx, void *ent) { @@ -1109,28 +1162,30 @@ static void PF_hash_destroytab_enum(void *ctx, void *ent) } void QCBUILTIN PF_hash_destroytab (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - int tab = G_FLOAT(OFS_PARM0) - FIRST_QC_HASHTABLE_INDEX; - if (tab >= 0 && tab < MAX_QC_HASHTABLES && pf_hashtab[tab].prinst) + pf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0)); + if (tab && tab->prinst == prinst) { - pf_hashtab[tab].prinst = NULL; - Hash_Enumerate(&pf_hashtab[tab].tab, PF_hash_destroytab_enum, NULL); - Z_Free(pf_hashtab[tab].bucketmem); + tab->prinst = NULL; + Hash_Enumerate(&tab->tab, PF_hash_destroytab_enum, NULL); + Z_Free(tab->bucketmem); } } void QCBUILTIN PF_hash_createtab (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int i; int numbuckets = G_FLOAT(OFS_PARM0); + qboolean dupestrings = (prinst->callargc>1)?G_FLOAT(OFS_PARM1):false; if (numbuckets < 4) numbuckets = 64; for (i = 0; i < MAX_QC_HASHTABLES; i++) { if (!pf_hashtab[i].prinst) { + pf_hashtab[i].dupestrings = dupestrings; pf_hashtab[i].prinst = prinst; pf_hashtab[i].bucketmem = Z_Malloc(Hash_BytesForBuckets(numbuckets)); Hash_InitTable(&pf_hashtab[i].tab, numbuckets, pf_hashtab[i].bucketmem); - G_FLOAT(OFS_RETURN) = i + FIRST_QC_HASHTABLE_INDEX; + G_FLOAT(OFS_RETURN) = i + 1; return; } } @@ -1157,11 +1212,32 @@ typedef struct { } pf_fopen_files_t; pf_fopen_files_t pf_fopen_files[MAX_QC_FILES]; +//returns false if the file is denied. +//fallbackread can be NULL, if the qc is not allowed to read that (original) file at all. +qboolean QC_FixFileName(char *name, char **result, char **fallbackread) +{ + if (strchr(name, ':') || //dos/win absolute path, ntfs ADS, amiga drives. reject them all. + strchr(name, '\\') || //windows-only paths. + *name == '/' || //absolute path was given - reject + strstr(name, "..")) //someone tried to be clever. + { + return false; + } + + *fallbackread = name; + //if its a user config, ban any fallback locations so that csqc can't read passwords or whatever. + if ((!strchr(name, '/') || strnicmp(name, "configs/", 8)) && !stricmp(COM_FileExtension(name), "cfg")) + *fallbackread = NULL; + *result = va("data/%s", name); + return true; +} + void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { char *name = PR_GetStringOfs(prinst, OFS_PARM0); int fmode = G_FLOAT(OFS_PARM1); int fsize = G_FLOAT(OFS_PARM2); + char *fallbackread; int i; for (i = 0; i < MAX_QC_FILES; i++) @@ -1175,15 +1251,14 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals return; } - if (name[1] == ':' || //dos filename absolute path specified - reject. - strchr(name, '\\') || *name == '/' || //absolute path was given - reject - strstr(name, "..")) //someone tried to be cleaver. + if (!QC_FixFileName(name, &name, &fallbackread)) { + Con_Printf("qcfopen: Access denied: %s\n", name); G_FLOAT(OFS_RETURN) = -1; return; } - Q_strncpyz(pf_fopen_files[i].name, va("data/%s", name), sizeof(pf_fopen_files[i].name)); + Q_strncpyz(pf_fopen_files[i].name, name, sizeof(pf_fopen_files[i].name)); pf_fopen_files[i].accessmode = fmode; switch (fmode) @@ -1222,9 +1297,9 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals case FRIK_FILE_READ: //read case FRIK_FILE_READNL: //read whole file pf_fopen_files[i].data = FS_LoadMallocFile(pf_fopen_files[i].name); - if (!pf_fopen_files[i].data) + if (!pf_fopen_files[i].data && fallbackread) { - Q_strncpyz(pf_fopen_files[i].name, name, sizeof(pf_fopen_files[i].name)); + Q_strncpyz(pf_fopen_files[i].name, fallbackread, sizeof(pf_fopen_files[i].name)); pf_fopen_files[i].data = FS_LoadMallocFile(pf_fopen_files[i].name); } @@ -1694,7 +1769,7 @@ void PR_fclose_progs (pubprogfuncs_t *prinst) void QCBUILTIN PF_isfunction (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { char *name = PR_GetStringOfs(prinst, OFS_PARM0); - G_FLOAT(OFS_RETURN) = !!PR_FindFunction(prinst, name, PR_CURRENT); + G_FLOAT(OFS_RETURN) = !!PR_FindFunction(prinst, name, PR_ANY); } //void callfunction(...) @@ -1706,7 +1781,7 @@ void QCBUILTIN PF_callfunction (pubprogfuncs_t *prinst, struct globalvars_s *pr_ PR_BIError(prinst, "callfunction needs at least one argument\n"); name = PR_GetStringOfs(prinst, OFS_PARM0+(prinst->callargc-1)*3); prinst->callargc -= 1; - f = PR_FindFunction(prinst, name, PR_CURRENT); + f = PR_FindFunction(prinst, name, PR_ANY); if (f) PR_ExecuteProgram(prinst, f); } @@ -1873,32 +1948,36 @@ void QCBUILTIN PF_print (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals //FTE_STRINGS //C style strncasecmp (compare first n characters - case insensative) +//C style strcasecmp (case insensative string compare) void QCBUILTIN PF_strncasecmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { char *a = PR_GetStringOfs(prinst, OFS_PARM0); char *b = PR_GetStringOfs(prinst, OFS_PARM1); - int len = G_FLOAT(OFS_PARM2); - int aofs = prinst->callargc>3?G_FLOAT(OFS_PARM3):0; - if (VMUTF8) + if (prinst->callargc > 2) { - aofs = unicode_byteofsfromcharofs(a, aofs); - len = unicode_byteofsfromcharofs(b, len); + int len = G_FLOAT(OFS_PARM2); + int aofs = prinst->callargc>3?G_FLOAT(OFS_PARM3):0; + int bofs = prinst->callargc>4?G_FLOAT(OFS_PARM4):0; + + if (VMUTF8) + { + aofs = aofs?unicode_byteofsfromcharofs(a, aofs):0; + bofs = bofs?unicode_byteofsfromcharofs(b, bofs):0; + len = max(unicode_byteofsfromcharofs(a+aofs, len), unicode_byteofsfromcharofs(b+bofs, len)); + } + else + { + if (aofs < 0 || (aofs && aofs > strlen(a))) + aofs = strlen(a); + if (bofs < 0 || (bofs && bofs > strlen(b))) + bofs = strlen(b); + } + + G_FLOAT(OFS_RETURN) = Q_strncasecmp(a+aofs, b+bofs, len); } - else if (aofs < 0 || (aofs && aofs > strlen(a))) - aofs = strlen(a); - - G_FLOAT(OFS_RETURN) = strnicmp(a+aofs, b, len); -} - -//FTE_STRINGS -//C style strcasecmp (case insensative string compare) -void QCBUILTIN PF_strcasecmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) -{ - char *a = PR_GetStringOfs(prinst, OFS_PARM0); - char *b = PR_GetStringOfs(prinst, OFS_PARM1); - - G_FLOAT(OFS_RETURN) = stricmp(a, b); + else + G_FLOAT(OFS_RETURN) = Q_strcasecmp(a, b); } //FTE_STRINGS @@ -1907,18 +1986,31 @@ void QCBUILTIN PF_strncmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globa { char *a = PR_GetStringOfs(prinst, OFS_PARM0); char *b = PR_GetStringOfs(prinst, OFS_PARM1); - int len = G_FLOAT(OFS_PARM2); - int aofs = prinst->callargc>3?G_FLOAT(OFS_PARM3):0; - if (VMUTF8) + if (prinst->callargc > 2) { - aofs = unicode_byteofsfromcharofs(a, aofs); - len = unicode_byteofsfromcharofs(b, len); - } - else if (aofs < 0 || (aofs && aofs > strlen(a))) - aofs = strlen(a); + int len = G_FLOAT(OFS_PARM2); + int aofs = prinst->callargc>3?G_FLOAT(OFS_PARM3):0; + int bofs = prinst->callargc>4?G_FLOAT(OFS_PARM4):0; - G_FLOAT(OFS_RETURN) = strncmp(a + aofs, b, len); + if (VMUTF8) + { + aofs = aofs?unicode_byteofsfromcharofs(a, aofs):0; + bofs = bofs?unicode_byteofsfromcharofs(b, bofs):0; + len = max(unicode_byteofsfromcharofs(a+aofs, len), unicode_byteofsfromcharofs(b+bofs, len)); + } + else + { + if (aofs < 0 || (aofs && aofs > strlen(a))) + aofs = strlen(a); + if (bofs < 0 || (bofs && bofs > strlen(b))) + bofs = strlen(b); + } + + G_FLOAT(OFS_RETURN) = Q_strncmp(a + aofs, b, len); + } + else + G_FLOAT(OFS_RETURN) = Q_strcmp(a, b); } //uses qw style \key\value strings @@ -2593,6 +2685,7 @@ struct strbuf { int allocated; }; +#define BUFSTRBASE 1 #define NUMSTRINGBUFS 64 struct strbuf strbuflist[NUMSTRINGBUFS]; @@ -2630,17 +2723,17 @@ void QCBUILTIN PF_buf_create (pubprogfuncs_t *prinst, struct globalvars_s *pr_g strbuflist[i].used = 0; strbuflist[i].allocated = 0; strbuflist[i].strings = NULL; - G_FLOAT(OFS_RETURN) = i+1; + G_FLOAT(OFS_RETURN) = i+BUFSTRBASE; return; } } - G_FLOAT(OFS_RETURN) = 0; + G_FLOAT(OFS_RETURN) = -1; } // #441 void(float bufhandle) buf_del (DP_QC_STRINGBUFFERS) void QCBUILTIN PF_buf_del (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int i; - int bufno = G_FLOAT(OFS_PARM0)-1; + int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE; if ((unsigned int)bufno >= NUMSTRINGBUFS) return; @@ -2660,7 +2753,7 @@ void QCBUILTIN PF_buf_del (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob // #442 float(float bufhandle) buf_getsize (DP_QC_STRINGBUFFERS) void QCBUILTIN PF_buf_getsize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - int bufno = G_FLOAT(OFS_PARM0)-1; + int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE; if ((unsigned int)bufno >= NUMSTRINGBUFS) return; @@ -2672,54 +2765,130 @@ void QCBUILTIN PF_buf_getsize (pubprogfuncs_t *prinst, struct globalvars_s *pr_ // #443 void(float bufhandle_from, float bufhandle_to) buf_copy (DP_QC_STRINGBUFFERS) void QCBUILTIN PF_buf_copy (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - int buffrom = G_FLOAT(OFS_PARM0)-1; - int bufto = G_FLOAT(OFS_PARM1)-1; + int buffrom = G_FLOAT(OFS_PARM0)-BUFSTRBASE; + int bufto = G_FLOAT(OFS_PARM1)-BUFSTRBASE; + int i; + if (bufto == buffrom) //err... + return; if ((unsigned int)buffrom >= NUMSTRINGBUFS) return; if (strbuflist[buffrom].prinst != prinst) return; - if ((unsigned int)bufto >= NUMSTRINGBUFS) return; if (strbuflist[bufto].prinst != prinst) return; - Con_Printf("PF_buf_copy: stub\n"); + //obliterate any and all existing data. + for (i = 0; i < strbuflist[bufto].used; i++) + Z_Free(strbuflist[bufto].strings[i]); + Z_Free(strbuflist[bufto].strings); + + //copy new data over. + strbuflist[bufto].used = strbuflist[bufto].allocated = strbuflist[buffrom].used; + strbuflist[bufto].strings = BZ_Malloc(strbuflist[buffrom].used * sizeof(char*)); + for (i = 0; i < strbuflist[buffrom].used; i++) + strbuflist[bufto].strings[i] = strbuflist[buffrom].strings[i]?Z_StrDup(strbuflist[buffrom].strings[i]):NULL; } -// #444 void(float bufhandle, float sortpower, float backward) buf_sort (DP_QC_STRINGBUFFERS) +static int PF_buf_sort_sortprefixlen; +static int QDECL PF_buf_sort_ascending(const void *a, const void *b) +{ + return strncmp(*(char**)a, *(char**)b, PF_buf_sort_sortprefixlen); +} +static int QDECL PF_buf_sort_descending(const void *b, const void *a) +{ + return strncmp(*(char**)a, *(char**)b, PF_buf_sort_sortprefixlen); +} +// #444 void(float bufhandle, float sortprefixlen, float backward) buf_sort (DP_QC_STRINGBUFFERS) void QCBUILTIN PF_buf_sort (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - int bufno = G_FLOAT(OFS_PARM0)-1; - //int sortpower = G_FLOAT(OFS_PARM1); - //int backwards = G_FLOAT(OFS_PARM2); + int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE; + int sortprefixlen = G_FLOAT(OFS_PARM1); + int backwards = G_FLOAT(OFS_PARM2); + int s,d; + char **strings; if ((unsigned int)bufno >= NUMSTRINGBUFS) return; if (strbuflist[bufno].prinst != prinst) return; - Con_Printf("PF_buf_sort: stub\n"); + if (sortprefixlen <= 0) + sortprefixlen = INT_MAX; + + //take out the nulls first, to avoid weird/crashy sorting + for (s = 0, d = 0, strings = strbuflist[bufno].strings; s < strbuflist[bufno].used; ) + { + if (!strings[s]) + { + s++; + continue; + } + strings[d++] = strings[s++]; + } + strbuflist[bufno].used = d; + + //no nulls now, sort it. + PF_buf_sort_sortprefixlen = sortprefixlen; //eww, a global. burn in hell. + if (backwards) //z first + qsort(strings, strbuflist[bufno].used, sizeof(char*), PF_buf_sort_descending); + else //a first + qsort(strings, strbuflist[bufno].used, sizeof(char*), PF_buf_sort_ascending); } // #445 string(float bufhandle, string glue) buf_implode (DP_QC_STRINGBUFFERS) void QCBUILTIN PF_buf_implode (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - int bufno = G_FLOAT(OFS_PARM0)-1; - //char *glue = PR_GetStringOfs(prinst, OFS_PARM1); + int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE; + char *glue = PR_GetStringOfs(prinst, OFS_PARM1); + unsigned int gluelen = strlen(glue); + unsigned int retlen, l, i; + char **strings; + char *ret; if ((unsigned int)bufno >= NUMSTRINGBUFS) return; if (strbuflist[bufno].prinst != prinst) return; - Con_Printf("PF_buf_implode: stub\n"); + //count neededlength + strings = strbuflist[bufno].strings; + for (i = 0, retlen = 0; i < strbuflist[bufno].used; i++) + { + if (strings[i]) + { + if (retlen) + retlen += gluelen; + retlen += strlen(strings[i]); + } + } - RETURN_TSTRING(""); + //generate the output + ret = malloc(retlen+1); + for (i = 0, retlen = 0; i < strbuflist[bufno].used; i++) + { + if (strings[i]) + { + if (retlen) + { + memcpy(ret+retlen, glue, gluelen); + retlen += gluelen; + } + l = strlen(strings[i]); + memcpy(ret+retlen, strings[i], l); + retlen += l; + } + } + + //add the null and return + ret[retlen] = 0; + RETURN_TSTRING(ret); + free(ret); } // #446 string(float bufhandle, float string_index) bufstr_get (DP_QC_STRINGBUFFERS) void QCBUILTIN PF_bufstr_get (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - int bufno = G_FLOAT(OFS_PARM0)-1; + int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE; int index = G_FLOAT(OFS_PARM1); if ((unsigned int)bufno >= NUMSTRINGBUFS) @@ -2744,7 +2913,7 @@ void QCBUILTIN PF_bufstr_get (pubprogfuncs_t *prinst, struct globalvars_s *pr_g // #447 void(float bufhandle, float string_index, string str) bufstr_set (DP_QC_STRINGBUFFERS) void QCBUILTIN PF_bufstr_set (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - int bufno = G_FLOAT(OFS_PARM0)-1; + int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE; int index = G_FLOAT(OFS_PARM1); char *string = PR_GetStringOfs(prinst, OFS_PARM2); int oldcount; @@ -2769,21 +2938,11 @@ void QCBUILTIN PF_bufstr_set (pubprogfuncs_t *prinst, struct globalvars_s *pr_g if (index >= strbuflist[bufno].used) strbuflist[bufno].used = index+1; } -// #448 float(float bufhandle, string str, float order) bufstr_add (DP_QC_STRINGBUFFERS) -void QCBUILTIN PF_bufstr_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) + +int PF_bufstr_add_internal(int bufno, char *string, int appendonend) { - int bufno = G_FLOAT(OFS_PARM0)-1; - char *string = PR_GetStringOfs(prinst, OFS_PARM1); - int order = G_FLOAT(OFS_PARM2); - int index; - - if ((unsigned int)bufno >= NUMSTRINGBUFS) - return; - if (strbuflist[bufno].prinst != prinst) - return; - - if (order) + if (appendonend) { //add on end index = strbuflist[bufno].used; @@ -2815,12 +2974,27 @@ void QCBUILTIN PF_bufstr_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_g if (index >= strbuflist[bufno].used) strbuflist[bufno].used = index+1; - G_FLOAT(OFS_RETURN) = index; + return index; +} + +// #448 float(float bufhandle, string str, float order) bufstr_add (DP_QC_STRINGBUFFERS) +void QCBUILTIN PF_bufstr_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE; + char *string = PR_GetStringOfs(prinst, OFS_PARM1); + int order = G_FLOAT(OFS_PARM2); + + if ((unsigned int)bufno >= NUMSTRINGBUFS) + return; + if (strbuflist[bufno].prinst != prinst) + return; + + G_FLOAT(OFS_RETURN) = PF_bufstr_add_internal(bufno, string, order); } // #449 void(float bufhandle, float string_index) bufstr_free (DP_QC_STRINGBUFFERS) void QCBUILTIN PF_bufstr_free (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - int bufno = G_FLOAT(OFS_PARM0)-1; + int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE; int index = G_FLOAT(OFS_PARM1); if ((unsigned int)bufno >= NUMSTRINGBUFS) @@ -2838,16 +3012,109 @@ void QCBUILTIN PF_bufstr_free (pubprogfuncs_t *prinst, struct globalvars_s *pr_ void QCBUILTIN PF_buf_cvarlist (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - int bufno = G_FLOAT(OFS_PARM0)-1; - //char *pattern = PR_GetStringOfs(prinst, OFS_PARM1); - //char *antipattern = PR_GetStringOfs(prinst, OFS_PARM2); + int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE; + char *pattern = PR_GetStringOfs(prinst, OFS_PARM1); + char *antipattern = PR_GetStringOfs(prinst, OFS_PARM2); + int i; + cvar_group_t *grp; + cvar_t *var; + extern cvar_group_t *cvar_groups; if ((unsigned int)bufno >= NUMSTRINGBUFS) return; if (strbuflist[bufno].prinst != prinst) return; - Con_Printf("PF_buf_cvarlist: stub\n"); + //obliterate any and all existing data. + for (i = 0; i < strbuflist[bufno].used; i++) + Z_Free(strbuflist[bufno].strings[i]); + Z_Free(strbuflist[bufno].strings); + strbuflist[bufno].used = strbuflist[bufno].allocated = 0; + + //ignore name2, no point listing it twice. + for (grp=cvar_groups ; grp ; grp=grp->next) + for (var=grp->cvars ; var ; var=var->next) + { + if (pattern && wildcmp(pattern, var->name)) + continue; + if (antipattern && !wildcmp(antipattern, var->name)) + continue; + + PF_bufstr_add_internal(bufno, var->name, true); + } +} + +//directly reads a file into a stringbuffer +void QCBUILTIN PF_buf_loadfile (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + char *fname = PR_GetStringOfs(prinst, OFS_PARM0); + int bufno = G_FLOAT(OFS_PARM1)-BUFSTRBASE; + vfsfile_t *file; + char line[8192]; + char *fallback; + + G_FLOAT(OFS_RETURN) = 0; + + if ((unsigned int)bufno >= NUMSTRINGBUFS) + return; + if (strbuflist[bufno].prinst != prinst) + return; + + if (!QC_FixFileName(fname, &fname, &fallback)) + { + Con_Printf("qcfopen: Access denied: %s\n", fname); + return; + } + + file = FS_OpenVFS(fname, "rb", FS_GAME); + if (!file && fallback) + file = FS_OpenVFS(fallback, "rb", FS_GAME); + if (!file) + return; + + while(VFS_GETS(file, line, sizeof(line))) + { + PF_bufstr_add_internal(bufno, line, true); + } + VFS_CLOSE(file); + + G_FLOAT(OFS_RETURN) = 1; +} + +void QCBUILTIN PF_buf_writefile (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + unsigned int fnum = G_FLOAT(OFS_PARM0) - FIRST_QC_FILE_INDEX; + int bufno = G_FLOAT(OFS_PARM1)-BUFSTRBASE; + char **strings; + int idx, midx; + + G_FLOAT(OFS_RETURN) = 0; + + if ((unsigned int)bufno >= NUMSTRINGBUFS) + return; + if (strbuflist[bufno].prinst != prinst) + return; + + if (fnum < 0 || fnum >= MAX_QC_FILES) + return; + if (pf_fopen_files[fnum].prinst != prinst) + return; + + if (prinst->callargc >= 3) + idx = G_FLOAT(OFS_PARM2); + else + idx = 0; + if (prinst->callargc >= 4) + midx = idx + G_FLOAT(OFS_PARM3); + else + midx = strbuflist[bufno].used - idx; + idx = bound(0, idx, strbuflist[bufno].used); + midx = min(midx, strbuflist[bufno].used); + for(strings = strbuflist[bufno].strings; idx < midx; idx++) + { + PF_fwrite (prinst, fnum, strings[idx], strlen(strings[idx])); + } + G_FLOAT(OFS_RETURN) = 1; } //515's String functions @@ -2866,7 +3133,7 @@ void QCBUILTIN PF_crc16 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals G_FLOAT(OFS_RETURN) = QCRC_Block(str, len); } -int SHA1(char *digest, int maxdigestsize, char *string); +int SHA1(char *digest, int maxdigestsize, char *string, int stringlen); void QCBUILTIN PF_digest_hex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { char *hashtype = PR_GetStringOfs(prinst, OFS_PARM0); @@ -2882,7 +3149,7 @@ void QCBUILTIN PF_digest_hex (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl } else if (!strcmp(hashtype, "SHA1")) { - digestsize = SHA1(digest, sizeof(digest), str); + digestsize = SHA1(digest, sizeof(digest), str, strlen(str)); } else if (!strcmp(hashtype, "CRC16")) { @@ -4300,7 +4567,7 @@ lh_extension_t QSG_Extensions[] = { {"FTE_PEXT_MODELDBL"}, //max of 512 models {"FTE_PEXT_ENTITYDBL"}, //max of 1024 ents {"FTE_PEXT_ENTITYDBL2"}, //max of 2048 ents - {"FTE_PEXT_ORIGINDBL"}, //-8k to +8k map size. + {"FTE_PEXT_FLOATCOORDS"}, {"FTE_PEXT_VWEAP"}, {"FTE_PEXT_Q2BSP"}, //supports q2 maps. No bugs are apparent. {"FTE_PEXT_Q3BSP"}, //quake3 bsp support. dp probably has an equivelent, but this is queryable per client. diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index e010faaf6..5ae3d5fa3 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -286,7 +286,6 @@ void QCBUILTIN PF_strconv (pubprogfuncs_t *prinst, struct globalvars_s *pr_globa void QCBUILTIN PF_infoadd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_infoget (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_strncmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); -void QCBUILTIN PF_strcasecmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_strncasecmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_strpad (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); @@ -378,11 +377,14 @@ void QCBUILTIN PF_bufstr_set (pubprogfuncs_t *prinst, struct globalvars_s *pr_g void QCBUILTIN PF_bufstr_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_bufstr_free (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_buf_cvarlist (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_buf_loadfile (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_buf_writefile (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_hash_createtab (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_hash_destroytab (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_hash_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_hash_get (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_hash_getcb (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_hash_delete (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_hash_getkey (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); @@ -528,6 +530,7 @@ typedef enum VF_AFOV = 203, //aproximate fov (match what the engine would normally use for the fov cvar). p0=fov, p1=zoom VF_SCREENVSIZE = 204, VF_SCREENPSIZE = 205, + VF_VIEWENTITY = 206, } viewflags; /*FIXME: this should be changed*/ @@ -540,7 +543,7 @@ typedef enum #define CSQCRF_USEAXIS 16 //use v_forward/v_right/v_up as an axis/matrix - predraw is needed to use this properly #define CSQCRF_NOSHADOW 32 //don't cast shadows upon other entities (can still be self shadowing, if the engine wishes, and not additive) #define CSQCRF_FRAMETIMESARESTARTTIMES 64 //EXT_CSQC_1: frame times should be read as (time-frametime). -#define CSQCRF_NOAUTOADD 128 //EXT_CSQC_1: don't automatically add after predraw was called +#define CSQCRF_REMOVED 128 //was stupid /*only read+append+write are standard frik_file*/ #define FRIK_FILE_READ 0 /*read-only*/ diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 131671473..b37226439 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -852,7 +852,7 @@ enum { ========================================================== */ -#define MAX_CLIENTS 32//255 /*max 255, min 32*/ +#define MAX_CLIENTS 255 /*max 255, min 32*/ #define QWMAX_CLIENTS 32 /*QW's standard max. clients might have issues above this value*/ #define NQMAX_CLIENTS 16 /*NQ's standard max. clients might have issues above this value*/ diff --git a/engine/common/qvm.c b/engine/common/qvm.c index da12174d2..9236edc61 100644 --- a/engine/common/qvm.c +++ b/engine/common/qvm.c @@ -89,7 +89,8 @@ dllhandle_t *QVM_LoadDLL(const char *name, qboolean binroot, void **vmMain, sys_ hVM=NULL; { char fname[MAX_OSPATH]; - char *gpath; + char gpath[MAX_OSPATH]; + void *iterator; if (binroot) { @@ -101,13 +102,9 @@ dllhandle_t *QVM_LoadDLL(const char *name, qboolean binroot, void **vmMain, sys_ else { // run through the search paths - gpath = NULL; - while (!hVM) + iterator = NULL; + while (!hVM && COM_IteratePaths(&iterator, gpath, sizeof(gpath))) { - gpath = COM_NextPath (gpath); - if (!gpath) - break; // couldn't find one anywhere - if (!hVM) { snprintf (fname, sizeof(fname), "%s/%s", gpath, dllname_arch); diff --git a/engine/common/sha1.c b/engine/common/sha1.c index 92f439dee..def7307e3 100644 --- a/engine/common/sha1.c +++ b/engine/common/sha1.c @@ -179,14 +179,14 @@ memset(&finalcount, 0, 8); } -int SHA1(char *digest, int maxdigestsize, char *string) +int SHA1(char *digest, int maxdigestsize, char *string, int stringlen) { SHA1_CTX context; if (maxdigestsize < DIGEST_SIZE) return 0; SHA1Init(&context); - SHA1Update(&context, (unsigned char*) string, strlen(string)); + SHA1Update(&context, (unsigned char*) string, stringlen); SHA1Final(digest, &context); return DIGEST_SIZE; diff --git a/engine/common/sys.h b/engine/common/sys.h index c797c7ab7..7bd4dd0b1 100644 --- a/engine/common/sys.h +++ b/engine/common/sys.h @@ -44,6 +44,8 @@ NORETURN void VARGS Sys_Error (const char *error, ...) LIKEPRINTF(1); void VARGS Sys_Printf (char *fmt, ...) LIKEPRINTF(1); // send text to the console +void Sys_Warn (char *fmt, ...) LIKEPRINTF(1); +//like Sys_Printf. dunno why there needs to be two of em. void Sys_Quit (void); void Sys_RecentServer(char *command, char *target, char *title, char *desc); diff --git a/engine/common/vm.h b/engine/common/vm.h index ce3c24420..b0e6d7939 100644 --- a/engine/common/vm.h +++ b/engine/common/vm.h @@ -69,19 +69,24 @@ qboolean Plug_CenterPrintMessage(char *buffer, int clientnum); qboolean Plug_ChatMessage(char *buffer, int talkernum, int tpflags); void Plug_Command_f(void); int Plug_ConnectionlessClientPacket(char *buffer, int size); +qboolean Plug_ConsoleLink(char *text, char *info); void Plug_DrawReloadImages(void); void Plug_Initialise(qboolean fromgamedir); void Plug_Shutdown(qboolean preliminary); qboolean Plug_Menu_Event(int eventtype, int param); void Plug_ResChanged(void); -void Plug_SBar(void); +void Plug_SBar(playerview_t *pv); qboolean Plug_ServerMessage(char *buffer, int messagelevel); void Plug_Tick(void); qboolean Plugin_ExecuteString(void); #endif - +#define VM_TOSTRCACHE(a) VMQ3_StringToHandle(VM_POINTER(a)) +#define VM_FROMSTRCACHE(a) VMQ3_StringFromHandle(a) +char *VMQ3_StringFromHandle(int handle); +int VMQ3_StringToHandle(char *str); +void VMQ3_FlushStringHandles(void); #ifdef VM_UI qboolean UI_Command(void); diff --git a/engine/common/zone.h b/engine/common/zone.h index 7a0d12734..bf0bd6450 100644 --- a/engine/common/zone.h +++ b/engine/common/zone.h @@ -118,6 +118,8 @@ void BZ_Free(void *ptr); #define BZF_Realloc(ptr, size) BZF_ReallocNamed(ptr, size, __FILE__, __LINE__) #endif +#define Z_StrDup(s) strcpy(Z_Malloc(strlen(s)+1), s) + void *Hunk_Alloc (int size); // returns 0 filled memory void *Hunk_AllocName (int size, char *name); diff --git a/engine/d3d/d3d11_backend.c b/engine/d3d/d3d11_backend.c index a4744f7dc..d23f53697 100644 --- a/engine/d3d/d3d11_backend.c +++ b/engine/d3d/d3d11_backend.c @@ -345,7 +345,7 @@ static void D3D11BE_ApplyShaderBits(unsigned int bits) if (delta & (SBITS_BLEND_BITS|SBITS_MASK_BITS)) { - D3D11_BLEND_DESC blend; + D3D11_BLEND_DESC blend = {0}; ID3D11BlendState *newblendstate; blend.IndependentBlendEnable = FALSE; blend.AlphaToCoverageEnable = FALSE; //FIXME @@ -354,27 +354,27 @@ static void D3D11BE_ApplyShaderBits(unsigned int bits) { switch(bits & SBITS_SRCBLEND_BITS) { - case SBITS_SRCBLEND_ZERO: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_ZERO; break; - case SBITS_SRCBLEND_ONE: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; break; - case SBITS_SRCBLEND_DST_COLOR: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_DEST_COLOR; break; - case SBITS_SRCBLEND_ONE_MINUS_DST_COLOR: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_INV_DEST_COLOR; break; - case SBITS_SRCBLEND_SRC_ALPHA: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; break; - case SBITS_SRCBLEND_ONE_MINUS_SRC_ALPHA: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_INV_SRC_ALPHA; break; - case SBITS_SRCBLEND_DST_ALPHA: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_DEST_ALPHA; break; - case SBITS_SRCBLEND_ONE_MINUS_DST_ALPHA: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_INV_DEST_ALPHA; break; - case SBITS_SRCBLEND_ALPHA_SATURATE: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA_SAT; break; + case SBITS_SRCBLEND_ZERO: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_ZERO; break; + case SBITS_SRCBLEND_ONE: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; break; + case SBITS_SRCBLEND_DST_COLOR: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_DEST_COLOR; break; + case SBITS_SRCBLEND_ONE_MINUS_DST_COLOR: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_INV_DEST_COLOR; break; + case SBITS_SRCBLEND_SRC_ALPHA: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; break; + case SBITS_SRCBLEND_ONE_MINUS_SRC_ALPHA: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_INV_SRC_ALPHA; break; + case SBITS_SRCBLEND_DST_ALPHA: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_DEST_ALPHA; break; + case SBITS_SRCBLEND_ONE_MINUS_DST_ALPHA: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_INV_DEST_ALPHA; break; + case SBITS_SRCBLEND_ALPHA_SATURATE: blend.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA_SAT; break; default: Sys_Error("Bad shader blend src\n"); return; } switch(bits & SBITS_DSTBLEND_BITS) { - case SBITS_DSTBLEND_ZERO: blend.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO; break; - case SBITS_DSTBLEND_ONE: blend.RenderTarget[0].DestBlend = D3D11_BLEND_ONE; break; - case SBITS_DSTBLEND_SRC_ALPHA: blend.RenderTarget[0].DestBlend = D3D11_BLEND_SRC_ALPHA; break; - case SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA: blend.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; break; - case SBITS_DSTBLEND_DST_ALPHA: blend.RenderTarget[0].DestBlend = D3D11_BLEND_DEST_ALPHA; break; - case SBITS_DSTBLEND_ONE_MINUS_DST_ALPHA: blend.RenderTarget[0].DestBlend = D3D11_BLEND_INV_DEST_ALPHA; break; - case SBITS_DSTBLEND_SRC_COLOR: blend.RenderTarget[0].DestBlend = D3D11_BLEND_SRC_COLOR; break; - case SBITS_DSTBLEND_ONE_MINUS_SRC_COLOR: blend.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_COLOR; break; + case SBITS_DSTBLEND_ZERO: blend.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO; break; + case SBITS_DSTBLEND_ONE: blend.RenderTarget[0].DestBlend = D3D11_BLEND_ONE; break; + case SBITS_DSTBLEND_SRC_ALPHA: blend.RenderTarget[0].DestBlend = D3D11_BLEND_SRC_ALPHA; break; + case SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA: blend.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; break; + case SBITS_DSTBLEND_DST_ALPHA: blend.RenderTarget[0].DestBlend = D3D11_BLEND_DEST_ALPHA; break; + case SBITS_DSTBLEND_ONE_MINUS_DST_ALPHA: blend.RenderTarget[0].DestBlend = D3D11_BLEND_INV_DEST_ALPHA; break; + case SBITS_DSTBLEND_SRC_COLOR: blend.RenderTarget[0].DestBlend = D3D11_BLEND_SRC_COLOR; break; + case SBITS_DSTBLEND_ONE_MINUS_SRC_COLOR: blend.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_COLOR; break; default: Sys_Error("Bad shader blend dst\n"); return; } blend.RenderTarget[0].BlendEnable = TRUE; @@ -386,8 +386,8 @@ static void D3D11BE_ApplyShaderBits(unsigned int bits) blend.RenderTarget[0].BlendEnable = FALSE; } blend.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; - blend.RenderTarget[0].SrcBlendAlpha = blend.RenderTarget[0].SrcBlend; - blend.RenderTarget[0].DestBlendAlpha = blend.RenderTarget[0].DestBlend; + blend.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;//blend.RenderTarget[0].SrcBlend; + blend.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;//blend.RenderTarget[0].DestBlend; blend.RenderTarget[0].BlendOpAlpha = blend.RenderTarget[0].BlendOp; if (bits&SBITS_MASK_BITS) @@ -2929,4 +2929,9 @@ void D3D11BE_VBO_Destroy(vboarray_t *vearray) { } +void D3D11BE_Scissor(srect_t *rect) +{ + //stub +} + #endif diff --git a/engine/d3d/d3d_backend.c b/engine/d3d/d3d_backend.c index e8f1c9a58..498a4a8e1 100644 --- a/engine/d3d/d3d_backend.c +++ b/engine/d3d/d3d_backend.c @@ -3175,4 +3175,24 @@ void D3D9BE_VBO_Destroy(vboarray_t *vearray) { } +void D3D9BE_Scissor(srect_t *srect) +{ + RECT rect; + if (srect) + { + rect.left = srect->x; + rect.right = srect->x + srect->width; + rect.top = srect->y; + rect.bottom = srect->y + srect->height; + } + else + { + rect.left = 0; + rect.right = vid.pixelwidth; + rect.top = 0; + rect.bottom = vid.pixelheight; + } + IDirect3DDevice9_SetScissorRect(pD3DDev9, &rect); +} + #endif diff --git a/engine/d3d/vid_d3d.c b/engine/d3d/vid_d3d.c index 7263b8e0b..7748d65b4 100644 --- a/engine/d3d/vid_d3d.c +++ b/engine/d3d/vid_d3d.c @@ -1299,6 +1299,7 @@ rendererinfo_t d3d9rendererinfo = D3D9BE_UploadAllLightmaps, D3D9BE_SelectEntity, D3D9BE_SelectDLight, + D3D9BE_Scissor, D3D9BE_LightCullModel, D3D9BE_VBO_Begin, diff --git a/engine/d3d/vid_d3d11.c b/engine/d3d/vid_d3d11.c index 2249daf0e..86accd11b 100644 --- a/engine/d3d/vid_d3d11.c +++ b/engine/d3d/vid_d3d11.c @@ -443,6 +443,7 @@ static LRESULT WINAPI D3D11_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR if (!D3D11AppActivate(!(fActive == WA_INACTIVE), fMinimized)) break;//so, urm, tell me microsoft, what changed? + if (modestate == MS_FULLDIB) ShowWindow(mainwindow, SW_SHOWNORMAL); if (ActiveApp && modestate == MS_FULLSCREEN) @@ -788,14 +789,7 @@ static qboolean D3D11_VID_Init(rendererstate_t *info, unsigned char *palette) mouseactive = false; } - { - void GLV_Gamma_Callback(struct cvar_s *var, char *oldvalue); - Cvar_Hook(&v_gamma, GLV_Gamma_Callback); - Cvar_Hook(&v_contrast, GLV_Gamma_Callback); - Cvar_Hook(&v_brightness, GLV_Gamma_Callback); - - Cvar_ForceCallback(&v_gamma); - } + Cvar_ForceCallback(&v_gamma); return true; } @@ -1375,6 +1369,7 @@ rendererinfo_t d3d11rendererinfo = D3D11BE_UploadAllLightmaps, D3D11BE_SelectEntity, D3D11BE_SelectDLight, + D3D11BE_Scissor, D3D11BE_LightCullModel, D3D11BE_VBO_Begin, diff --git a/engine/dotnet2005/droid.vcproj b/engine/dotnet2005/droid.vcproj index 4ff978b72..82135b684 100644 --- a/engine/dotnet2005/droid.vcproj +++ b/engine/dotnet2005/droid.vcproj @@ -23,7 +23,7 @@ > + + diff --git a/engine/dotnet2005/ftequake.sln b/engine/dotnet2005/ftequake.sln index 34a7bb2f7..5d3434265 100644 --- a/engine/dotnet2005/ftequake.sln +++ b/engine/dotnet2005/ftequake.sln @@ -37,6 +37,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jabbercl", "..\..\plugins\j EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fs_mpq", "..\..\plugins\mpq\fs_mpq.vcproj", "{72269FEE-293D-40BC-A7AE-E429F4496869}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "httpserver", "..\http\httpserver.vcproj", "{E6BAD203-4704-4860-9C38-D4702E9CAD7D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution D3DDebug|Win32 = D3DDebug|Win32 @@ -85,7 +87,6 @@ Global {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364}.GLDebug|x64.ActiveCfg = GLDebug|x64 {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364}.GLDebug|x64.Build.0 = GLDebug|x64 {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364}.GLRelease|Win32.ActiveCfg = GLRelease|Win32 - {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364}.GLRelease|Win32.Build.0 = GLRelease|Win32 {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364}.GLRelease|x64.ActiveCfg = GLRelease|x64 {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364}.GLRelease|x64.Build.0 = GLRelease|x64 {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364}.MDebug|Win32.ActiveCfg = MDebug|Win32 @@ -244,9 +245,9 @@ Global {2866F783-6B44-4655-A38D-D53874037454}.Debug|Win32.Build.0 = Debug|Win32 {2866F783-6B44-4655-A38D-D53874037454}.Debug|x64.ActiveCfg = Release|Win32 {2866F783-6B44-4655-A38D-D53874037454}.GLDebug|Win32.ActiveCfg = Debug|Win32 - {2866F783-6B44-4655-A38D-D53874037454}.GLDebug|Win32.Build.0 = Debug|Win32 {2866F783-6B44-4655-A38D-D53874037454}.GLDebug|x64.ActiveCfg = Debug|Win32 {2866F783-6B44-4655-A38D-D53874037454}.GLRelease|Win32.ActiveCfg = Release|Win32 + {2866F783-6B44-4655-A38D-D53874037454}.GLRelease|Win32.Build.0 = Release|Win32 {2866F783-6B44-4655-A38D-D53874037454}.GLRelease|x64.ActiveCfg = Release|Win32 {2866F783-6B44-4655-A38D-D53874037454}.MDebug|Win32.ActiveCfg = Debug|Win32 {2866F783-6B44-4655-A38D-D53874037454}.MDebug|Win32.Build.0 = Debug|Win32 @@ -513,6 +514,42 @@ Global {72269FEE-293D-40BC-A7AE-E429F4496869}.Release|Win32.ActiveCfg = Release|Win32 {72269FEE-293D-40BC-A7AE-E429F4496869}.Release|Win32.Build.0 = Release|Win32 {72269FEE-293D-40BC-A7AE-E429F4496869}.Release|x64.ActiveCfg = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.D3DDebug|Win32.ActiveCfg = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.D3DDebug|Win32.Build.0 = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.D3DDebug|x64.ActiveCfg = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.D3DRelease|Win32.ActiveCfg = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.D3DRelease|Win32.Build.0 = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.D3DRelease|x64.ActiveCfg = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.Debug Dedicated Server|Win32.ActiveCfg = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.Debug Dedicated Server|Win32.Build.0 = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.Debug Dedicated Server|x64.ActiveCfg = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.Debug|Win32.ActiveCfg = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.Debug|Win32.Build.0 = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.Debug|x64.ActiveCfg = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.GLDebug|Win32.ActiveCfg = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.GLDebug|Win32.Build.0 = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.GLDebug|x64.ActiveCfg = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.GLRelease|Win32.ActiveCfg = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.GLRelease|Win32.Build.0 = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.GLRelease|x64.ActiveCfg = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.MDebug|Win32.ActiveCfg = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.MDebug|Win32.Build.0 = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.MDebug|x64.ActiveCfg = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.MinGLDebug|Win32.ActiveCfg = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.MinGLDebug|Win32.Build.0 = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.MinGLDebug|x64.ActiveCfg = Debug|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.MinGLRelease|Win32.ActiveCfg = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.MinGLRelease|Win32.Build.0 = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.MinGLRelease|x64.ActiveCfg = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.MRelease|Win32.ActiveCfg = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.MRelease|Win32.Build.0 = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.MRelease|x64.ActiveCfg = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.Release Dedicated Server|Win32.ActiveCfg = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.Release Dedicated Server|Win32.Build.0 = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.Release Dedicated Server|x64.ActiveCfg = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.Release|Win32.ActiveCfg = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.Release|Win32.Build.0 = Release|Win32 + {E6BAD203-4704-4860-9C38-D4702E9CAD7D}.Release|x64.ActiveCfg = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/engine/droid/AndroidManifest.xml b/engine/droid/AndroidManifest.xml index 8cf63283c..4eeb6ee33 100644 --- a/engine/droid/AndroidManifest.xml +++ b/engine/droid/AndroidManifest.xml @@ -17,6 +17,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engine/droid/src/com/fteqw/FTEDroidActivity.java b/engine/droid/src/com/fteqw/FTEDroidActivity.java index 8eb2be756..577c91567 100644 --- a/engine/droid/src/com/fteqw/FTEDroidActivity.java +++ b/engine/droid/src/com/fteqw/FTEDroidActivity.java @@ -342,8 +342,11 @@ public class FTEDroidActivity extends Activity public void onSurfaceChanged(GL10 gl, int width, int height) { android.util.Log.i("FTEDroid", "Surface changed, now " + width + " by " + height + "."); - FTEDroidEngine.init(width, height, glesversion, basedir, userdir); - inited = true; + if (glesversion != 0) + { + FTEDroidEngine.init(width, height, glesversion, basedir, userdir); + inited = true; + } } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) @@ -706,6 +709,31 @@ public class FTEDroidActivity extends Activity view.audioResume(); } + + @Override + protected void onStart() + { + super.onStart(); + final android.content.Intent intent = getIntent(); + if (intent != null) + { + final android.net.Uri data = intent.getData(); + if (data != null) + { + String myloc; + if (data.getScheme().equals("content")) + { //wtf. + Cursor cursor = this.getContentResolver().query(data, null, null, null, null); + cursor.moveToFirst(); + myloc = cursor.getString(0); + cursor.close(); + } + else + myloc = data.toString(); + FTEDroidEngine.openfile(myloc); + } + } + } @Override protected void onStop() diff --git a/engine/droid/src/com/fteqw/FTEDroidEngine.java b/engine/droid/src/com/fteqw/FTEDroidEngine.java index 1dce57fa9..0d2c39e12 100644 --- a/engine/droid/src/com/fteqw/FTEDroidEngine.java +++ b/engine/droid/src/com/fteqw/FTEDroidEngine.java @@ -4,6 +4,7 @@ public class FTEDroidEngine { public static native void init(int w, int h, int gles2, String apkpath, String usrpath); /* init/reinit */ public static native int frame(float ax, float ay, float az); + public static native int openfile(String filename); public static native int getvibrateduration(); //in ms public static native void keypress(int down, int qkey, int unicode); public static native void motion(int act, int pointerid, float x, float y, float size); diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c index df0507599..c56849588 100644 --- a/engine/gl/gl_alias.c +++ b/engine/gl/gl_alias.c @@ -1536,9 +1536,7 @@ void GL_GenerateNormals(float *orgs, float *normals, int *indicies, int numtris, qboolean BE_ShouldDraw(entity_t *e) { - if (!r_refdef.externalview && (e->externalmodelview & (1<keynum-1)) + if (!r_refdef.externalview && (e->flags & Q2RF_EXTERNALMODEL)) return false; return true; } @@ -1710,14 +1708,14 @@ static void R_DB_Sprite(batch_t *batch) return; } - if (e->flags & Q2RF_WEAPONMODEL && r_refdef.currentplayernum >= 0) + if (e->flags & Q2RF_WEAPONMODEL && r_refdef.playerview->viewentity > 0) { - sprorigin[0] = cl.viewent[r_refdef.currentplayernum].origin[0]; - sprorigin[1] = cl.viewent[r_refdef.currentplayernum].origin[1]; - sprorigin[2] = cl.viewent[r_refdef.currentplayernum].origin[2]; - VectorMA(sprorigin, e->origin[0], cl.viewent[r_refdef.currentplayernum].axis[0], sprorigin); - VectorMA(sprorigin, e->origin[1], cl.viewent[r_refdef.currentplayernum].axis[1], sprorigin); - VectorMA(sprorigin, e->origin[2], cl.viewent[r_refdef.currentplayernum].axis[2], sprorigin); + sprorigin[0] = r_refdef.playerview->viewent.origin[0]; + sprorigin[1] = r_refdef.playerview->viewent.origin[1]; + sprorigin[2] = r_refdef.playerview->viewent.origin[2]; + VectorMA(sprorigin, e->origin[0], r_refdef.playerview->viewent.axis[0], sprorigin); + VectorMA(sprorigin, e->origin[1], r_refdef.playerview->viewent.axis[1], sprorigin); + VectorMA(sprorigin, e->origin[2], r_refdef.playerview->viewent.axis[2], sprorigin); VectorMA(sprorigin, 12, vpn, sprorigin); batch->flags |= BEF_FORCENODEPTH; @@ -1748,11 +1746,11 @@ static void R_DB_Sprite(batch_t *batch) { case SPR_ORIENTED: // bullet marks on walls - if (e->flags & Q2RF_WEAPONMODEL && r_refdef.currentplayernum >= 0) + if ((e->flags & Q2RF_WEAPONMODEL) && r_refdef.playerview->viewentity > 0) { vec3_t ea[3]; AngleVectors (e->angles, ea[0], ea[1], ea[2]); - Matrix3_Multiply(ea, cl.viewent[r_refdef.currentplayernum].axis, spraxis); + Matrix3_Multiply(ea, r_refdef.playerview->viewent.axis, spraxis); } else AngleVectors (e->angles, spraxis[0], spraxis[1], spraxis[2]); diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 2473b195d..af5c33a1d 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -416,7 +416,7 @@ void GL_LazyBind(int tmu, int target, texid_t texnum) #endif { if (shaderstate.curtexturetype[tmu]) - qglBindTexture (shaderstate.curtexturetype[tmu], texnum.num); + qglBindTexture (shaderstate.curtexturetype[tmu], 0); if (gl_config.nofixedfunc) { shaderstate.curtexturetype[tmu] = target; @@ -945,7 +945,7 @@ void R_IBrokeTheArrays(void) void GLBE_SetupForShadowMap(texid_t shadowmaptex, int texwidth, int texheight, float shadowscale) { shaderstate.lightshadowmapinfo[0] = 1.0/texwidth; - shaderstate.lightshadowmapinfo[1] = 1.0/texwidth; + shaderstate.lightshadowmapinfo[1] = 1.0/texheight; shaderstate.lightshadowmapinfo[2] = 1.0; shaderstate.lightshadowmapinfo[3] = shadowscale; @@ -3270,7 +3270,7 @@ static qboolean GLBE_RegisterLightShader(int mode) void GLBE_SelectDLight(dlight_t *dl, vec3_t colour) { - float view[16], proj[16]; + float view[16]; int lmode; extern cvar_t gl_specular; extern cvar_t r_shadow_shadowmapping; @@ -3279,13 +3279,14 @@ void GLBE_SelectDLight(dlight_t *dl, vec3_t colour) float nearplane = 4; if (dl->fov) { + float proj[16]; Matrix4x4_CM_Projection_Far(proj, dl->fov, dl->fov, nearplane, dl->radius); Matrix4x4_CM_ModelViewMatrixFromAxis(view, dl->axis[0], dl->axis[1], dl->axis[2], dl->origin); Matrix4_Multiply(proj, view, shaderstate.lightprojmatrix); } else { - Matrix4x4_CM_Projection_Far(proj, 90, 90, nearplane, dl->radius); +// Matrix4x4_CM_Projection_Far(proj, 90, 90, nearplane, dl->radius); Matrix4x4_CM_ModelViewMatrixFromAxis(shaderstate.lightprojmatrix, dl->axis[0], dl->axis[1], dl->axis[2], dl->origin); } @@ -3315,6 +3316,31 @@ void GLBE_SelectDLight(dlight_t *dl, vec3_t colour) shaderstate.lightmode = lmode; } +void GLBE_Scissor(srect_t *rect) +{ + if (rect) + { + qglScissor( + floor(r_refdef.pxrect.x + rect->x*r_refdef.pxrect.width), + floor((r_refdef.pxrect.y + rect->y*r_refdef.pxrect.height) - r_refdef.pxrect.height), + ceil(rect->width * r_refdef.pxrect.width), + ceil(rect->height * r_refdef.pxrect.height)); + qglEnable(GL_SCISSOR_TEST); + + if (qglDepthBoundsEXT) + { + qglDepthBoundsEXT(rect->dmin, rect->dmax); + qglEnable(GL_DEPTH_BOUNDS_TEST_EXT); + } + } + else + { + qglDisable(GL_SCISSOR_TEST); + if (qglDepthBoundsEXT) + qglDisable(GL_DEPTH_BOUNDS_TEST_EXT); + } +} + void GLBE_PushOffsetShadow(qboolean pushdepth) { extern cvar_t r_polygonoffset_stencil_offset, r_polygonoffset_stencil_factor; diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index 34d8470e7..c1b62592f 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -293,7 +293,7 @@ void GL_Mipcap_Callback (struct cvar_s *var, char *oldvalue) void GL_Texturemode_Apply(GLenum targ, unsigned int flags) { int mag; - if (flags & IF_2D) + if (flags & IF_UIPIC) { qglTexParameteri(targ, GL_TEXTURE_MIN_FILTER, gl_filter_max_2d); if (flags & IF_NEAREST) @@ -353,7 +353,7 @@ void GL_Texturemode_Callback (struct cvar_s *var, char *oldvalue) // change all the existing mipmap texture objects for (glt=gltextures ; glt ; glt=glt->next) { - if (!(glt->flags & IF_2D)) + if (!(glt->flags & IF_UIPIC)) { if (glt->flags & IF_CUBEMAP) targ = GL_TEXTURE_CUBE_MAP_ARB; @@ -392,7 +392,7 @@ void GL_Texturemode2d_Callback (struct cvar_s *var, char *oldvalue) // change all the existing mipmap texture objects for (glt=gltextures ; glt ; glt=glt->next) { - if (glt->flags & IF_2D) + if (glt->flags & IF_UIPIC) { GL_MTBind(0, GL_TEXTURE_2D, glt->texnum); GL_Texturemode_Apply(GL_TEXTURE_2D, glt->flags); diff --git a/engine/gl/gl_font.c b/engine/gl/gl_font.c index 7e4504ec2..08c2a0c55 100644 --- a/engine/gl/gl_font.c +++ b/engine/gl/gl_font.c @@ -26,7 +26,7 @@ void Font_EndString(struct font_s *font); int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int maxlines, conchar_t **starts, conchar_t **ends); struct font_s *font_conchar; struct font_s *font_tiny; -extern int r2d_be_flags; +extern unsigned int r2d_be_flags; #ifdef AVAIL_FREETYPE #include @@ -262,7 +262,7 @@ void Font_Init(void) for (i = 0; i < FONTPLANES; i++) { - TEXASSIGN(fontplanes.texnum[i], R_AllocNewTexture("***fontplane***", PLANEWIDTH, PLANEHEIGHT, IF_2D|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA)); + TEXASSIGN(fontplanes.texnum[i], R_AllocNewTexture("***fontplane***", PLANEWIDTH, PLANEHEIGHT, IF_UIPIC|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA)); } fontplanes.shader = R_RegisterShader("ftefont", @@ -302,7 +302,7 @@ static void Font_Flush(void) return; if (fontplanes.planechanged) { - R_Upload(fontplanes.texnum[fontplanes.activeplane], NULL, TF_RGBA32, (void*)fontplanes.plane, NULL, PLANEWIDTH, PLANEHEIGHT, IF_2D|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA); + R_Upload(fontplanes.texnum[fontplanes.activeplane], NULL, TF_RGBA32, (void*)fontplanes.plane, NULL, PLANEWIDTH, PLANEHEIGHT, IF_UIPIC|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA); fontplanes.planechanged = false; } @@ -368,7 +368,7 @@ void Font_FlushPlane(void) if (fontplanes.planechanged) { - R_Upload(fontplanes.texnum[fontplanes.activeplane], NULL, TF_RGBA32, (void*)fontplanes.plane, NULL, PLANEWIDTH, PLANEHEIGHT, IF_2D|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA); + R_Upload(fontplanes.texnum[fontplanes.activeplane], NULL, TF_RGBA32, (void*)fontplanes.plane, NULL, PLANEWIDTH, PLANEHEIGHT, IF_UIPIC|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA); fontplanes.planechanged = false; } @@ -759,15 +759,15 @@ static texid_t Font_LoadReplacementConchars(void) { texid_t tex; //q1 replacement - tex = R_LoadReplacementTexture("gfx/conchars.lmp", NULL, IF_2D|IF_NOMIPMAP|IF_NOGAMMA); + tex = R_LoadReplacementTexture("gfx/conchars.lmp", NULL, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); if (TEXVALID(tex)) return tex; //q2 - tex = R_LoadHiResTexture("pics/conchars.pcx", NULL, IF_2D|IF_NOMIPMAP|IF_NOGAMMA); + tex = R_LoadHiResTexture("pics/conchars.pcx", NULL, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); if (TEXVALID(tex)) return tex; //q3 - tex = R_LoadHiResTexture("gfx/2d/bigchars.tga", NULL, IF_2D|IF_NOMIPMAP|IF_NOGAMMA); + tex = R_LoadHiResTexture("gfx/2d/bigchars.tga", NULL, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); if (TEXVALID(tex)) return tex; return r_nulltex; @@ -790,7 +790,7 @@ static texid_t Font_LoadQuakeConchars(void) if (lump[i] == 0) lump[i] = 255; // proper transparent color - return R_LoadTexture8("charset", 128, 128, (void*)lump, IF_2D|IF_NOMIPMAP|IF_NOGAMMA, 1); + return R_LoadTexture8("charset", 128, 128, (void*)lump, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA, 1); } return r_nulltex; } @@ -876,7 +876,7 @@ static texid_t Font_LoadHexen2Conchars(qboolean iso88591) for (i=0 ; i<128*128 ; i++) if (outbuf[i] == 0) outbuf[i] = 255; // proper transparent color - tex = R_LoadTexture8 (iso88591?"gfx/menu/8859-1.lmp":"charset", 128, 128, outbuf, IF_2D|IF_NOMIPMAP|IF_NOGAMMA, 1); + tex = R_LoadTexture8 (iso88591?"gfx/menu/8859-1.lmp":"charset", 128, 128, outbuf, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA, 1); Z_Free(outbuf); return tex; } @@ -902,7 +902,7 @@ static texid_t Font_LoadFallbackConchars(void) lump[i*4+1] = 255; lump[i*4+2] = 255; } - tex = R_LoadTexture32("charset", width, height, (void*)lump, IF_2D|IF_NOMIPMAP|IF_NOGAMMA); + tex = R_LoadTexture32("charset", width, height, (void*)lump, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); BZ_Free(lump); return tex; } @@ -1061,7 +1061,7 @@ struct font_s *Font_LoadFont(int vheight, char *fontfilename) for (x = 0; x < 128; x++) img[x + y*PLANEWIDTH] = w[x + y*128]?d_8to24rgbtable[w[x + y*128]]:0; - f->singletexture = R_LoadTexture(fontfilename,PLANEWIDTH,PLANEWIDTH,TF_RGBA32,img,IF_2D|IF_NOPICMIP|IF_NOMIPMAP); + f->singletexture = R_LoadTexture(fontfilename,PLANEWIDTH,PLANEWIDTH,TF_RGBA32,img,IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP); Z_Free(img); for (i = 0x00; i <= 0xff; i++) @@ -1123,7 +1123,7 @@ struct font_s *Font_LoadFont(int vheight, char *fontfilename) { //default to only map the ascii-compatible chars from the quake font. if (*fontfilename) - f->singletexture = R_LoadHiResTexture(fontfilename, "fonts", IF_2D|IF_NOMIPMAP); + f->singletexture = R_LoadHiResTexture(fontfilename, "fonts", IF_UIPIC|IF_NOMIPMAP); for ( ; i < 32; i++) { @@ -1396,6 +1396,7 @@ int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int max int Font_LineWidth(conchar_t *start, conchar_t *end) { + //fixme: does this do the right thing with tabs? int x = 0; struct font_s *font = curfont; for (; start < end; start++) @@ -1404,6 +1405,16 @@ int Font_LineWidth(conchar_t *start, conchar_t *end) } return x; } +float Font_LineScaleWidth(conchar_t *start, conchar_t *end) +{ + int x = 0; + struct font_s *font = curfont; + for (; start < end; start++) + { + x = Font_CharEndCoord(font, x, *start); + } + return x * curfont_scale[0]; +} void Font_LineDraw(int x, int y, conchar_t *start, conchar_t *end) { int lx = 0; diff --git a/engine/gl/gl_ngraph.c b/engine/gl/gl_ngraph.c index fa84006f9..2dd36c37f 100644 --- a/engine/gl/gl_ngraph.c +++ b/engine/gl/gl_ngraph.c @@ -122,7 +122,7 @@ void R_NetGraph (void) Draw_FunString(8, y, st); y += 8; - R_Upload(netgraphtexture, "***netgraph***", TF_RGBA32, ngraph_pixels, NULL, NET_TIMINGS, NET_GRAPHHEIGHT, IF_2D|IF_NOMIPMAP|IF_NOPICMIP); + R_Upload(netgraphtexture, "***netgraph***", TF_RGBA32, ngraph_pixels, NULL, NET_TIMINGS, NET_GRAPHHEIGHT, IF_UIPIC|IF_NOMIPMAP|IF_NOPICMIP); x=8; R2D_Image(x, y, NET_TIMINGS, NET_GRAPHHEIGHT, 0, 0, 1, 1, netgraphshader); } @@ -163,14 +163,14 @@ void R_FrameTimeGraph (int frametime) Draw_FunString(8, y, st); y += 8; - R_Upload(netgraphtexture, "***netgraph***", TF_RGBA32, ngraph_pixels, NULL, NET_TIMINGS, NET_GRAPHHEIGHT, IF_2D|IF_NOMIPMAP|IF_NOPICMIP); + R_Upload(netgraphtexture, "***netgraph***", TF_RGBA32, ngraph_pixels, NULL, NET_TIMINGS, NET_GRAPHHEIGHT, IF_UIPIC|IF_NOMIPMAP|IF_NOPICMIP); x=8; R2D_Image(x, y, NET_TIMINGS, NET_GRAPHHEIGHT, 0, 0, 1, 1, netgraphshader); } void R_NetgraphInit(void) { - TEXASSIGN(netgraphtexture, R_AllocNewTexture("***netgraph***", NET_TIMINGS, NET_GRAPHHEIGHT, IF_2D|IF_NOMIPMAP)); + TEXASSIGN(netgraphtexture, R_AllocNewTexture("***netgraph***", NET_TIMINGS, NET_GRAPHHEIGHT, IF_UIPIC|IF_NOMIPMAP)); netgraphshader = R_RegisterShader("netgraph", "{\n" "program default2d\n" diff --git a/engine/gl/gl_rlight.c b/engine/gl/gl_rlight.c index eb3fafe67..ca41200cf 100644 --- a/engine/gl/gl_rlight.c +++ b/engine/gl/gl_rlight.c @@ -294,9 +294,9 @@ void R_RenderDlights (void) if (l->flags & LFLAG_FLASHBLEND) { //dlights emitting from the local player are not visible as flashblends - if (l->key == cl.playernum[r_refdef.currentplayernum]+1) + if (l->key == r_refdef.playerview->viewentity) continue; //was a glow - if (l->key == -(cl.playernum[r_refdef.currentplayernum]+1)) + if (l->key == -(r_refdef.playerview->viewentity)) continue; //was a muzzleflash } diff --git a/engine/gl/gl_rmain.c b/engine/gl/gl_rmain.c index 5c40c34e5..fc15fadab 100644 --- a/engine/gl/gl_rmain.c +++ b/engine/gl/gl_rmain.c @@ -230,9 +230,9 @@ void GL_SetupSceneProcessingTextures (void) void R_RotateForEntity (float *m, float *modelview, const entity_t *e, const model_t *mod) { - if (e->flags & Q2RF_WEAPONMODEL && r_refdef.currentplayernum>=0) + if ((e->flags & Q2RF_WEAPONMODEL) && r_refdef.playerview->viewentity > 0) { - entity_t *view = &cl.viewent[r_refdef.currentplayernum]; + entity_t *view = &r_refdef.playerview->viewent; float em[16]; float vm[16]; @@ -382,9 +382,9 @@ void R_SetupGL (float stereooffset) // // set up viewpoint // - x = r_refdef.vrect.x * vid.pixelwidth/(int)vid.width; - x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * vid.pixelwidth/(int)vid.width; - y = (vid.height-r_refdef.vrect.y) * vid.pixelheight/(int)vid.height; + x = r_refdef.vrect.x * (int)vid.pixelwidth/(int)vid.width; + x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * (int)vid.pixelwidth/(int)vid.width; + y = (vid.height-r_refdef.vrect.y) * (int)vid.pixelheight/(int)vid.height; y2 = ((int)vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)) * (int)vid.pixelheight/(int)vid.height; // fudge around because of frac screen scale @@ -1009,7 +1009,7 @@ void R_Clear (void) /*tbh, this entire function should be in the backend*/ GL_ForceDepthWritable(); { - if (r_clear.ival && !r_secondaryview && !r_refdef.currentplayernum && !(r_refdef.flags & Q2RDF_NOWORLDMODEL)) + if (r_clear.ival && r_refdef.grect.x == 0 && r_refdef.grect.y == 0 && r_refdef.grect.width == vid.width && r_refdef.grect.height == vid.height && !(r_refdef.flags & Q2RDF_NOWORLDMODEL)) { qglClearColor(1, 0, 0, 0); qglClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -1195,12 +1195,12 @@ qboolean R_RenderScene_Cubemap(void) facemask |= 1<<5; /*back view*/ } -//fixme: should already have the vrect somewhere. - SCR_VRectForPlayer(&vrect, r_refdef.currentplayernum); - prect.x = (vrect.x * vid.pixelwidth)/vid.width; - prect.width = (vrect.width * vid.pixelwidth)/vid.width; - prect.y = (vrect.y * vid.pixelheight)/vid.height; - prect.height = (vrect.height * vid.pixelheight)/vid.height; + vrect = r_refdef.vrect; + prect = r_refdef.pxrect; +// prect.x = (vrect.x * vid.pixelwidth)/vid.width; +// prect.width = (vrect.width * vid.pixelwidth)/vid.width; +// prect.y = (vrect.y * vid.pixelheight)/vid.height; +// prect.height = (vrect.height * vid.pixelheight)/vid.height; if (r_config.texture_non_power_of_two) { diff --git a/engine/gl/gl_screen.c b/engine/gl/gl_screen.c index 33d93c5a8..f83d32202 100644 --- a/engine/gl/gl_screen.c +++ b/engine/gl/gl_screen.c @@ -137,12 +137,6 @@ void GLSCR_UpdateScreen (void) RSpeedEnd(RSPEED_TOTALREFRESH); return; } - - // - // determine size of refresh window - // - if (vid.recalc_refdef) - SCR_CalcRefdef (); // // do 3D refresh drawing, and then update the screen @@ -191,9 +185,7 @@ void GLSCR_UpdateScreen (void) scr_con_forcedraw = true; nohud = true; - } - else if (!nohud) - SCR_TileClear (); + } SCR_DrawTwoDimensional(uimenu, nohud); diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 4554e1462..dd310e660 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -639,7 +639,12 @@ static int Shader_SetImageFlags(shader_t *shader, shaderpass_t *pass, char **nam if (!Q_strnicmp(*name, "$3d:", 4)) { *name+=4; - flags|= IF_3DMAP; + flags = (flags&~IF_TEXTYPE) | IF_3DMAP; + } + else if (!Q_strnicmp(*name, "$cube:", 6)) + { + *name+=6; + flags = (flags&~IF_TEXTYPE) | IF_CUBEMAP; } else if (!Q_strnicmp(*name, "$nearest:", 9)) { @@ -2253,10 +2258,18 @@ static void Shaderpass_Map (shader_t *shader, shaderpass_t *pass, char **ptr) flags = Shader_SetImageFlags (shader, pass, &token); if (!Shaderpass_MapGen(shader, pass, token)) { - if (flags & IF_3DMAP) - pass->texgen = T_GEN_3DMAP; - else + switch((flags & IF_TEXTYPE) >> IF_TEXTYPESHIFT) + { + case 0: pass->texgen = T_GEN_SINGLEMAP; + break; + case 1: + pass->texgen = T_GEN_3DMAP; + break; + default: + pass->texgen = T_GEN_CUBEMAP; + break; + } pass->tcgen = TC_GEN_BASE; pass->anim_frames[0] = Shader_FindImage (token, flags); @@ -2285,7 +2298,7 @@ static void Shaderpass_AnimMap (shader_t *shader, shaderpass_t *pass, char **ptr break; } - if (pass->anim_numframes < SHADER_ANIM_FRAMES_MAX) + if (pass->anim_numframes < SHADER_MAX_ANIMFRAMES) { image = Shader_FindImage (token, flags); @@ -2314,10 +2327,19 @@ static void Shaderpass_ClampMap (shader_t *shader, shaderpass_t *pass, char **pt { pass->tcgen = TC_GEN_BASE; pass->anim_frames[0] = Shader_FindImage (token, flags | IF_CLAMP); - if (flags & IF_3DMAP) - pass->texgen = T_GEN_3DMAP; - else + + switch((flags & IF_TEXTYPE) >> IF_TEXTYPESHIFT) + { + case 0: pass->texgen = T_GEN_SINGLEMAP; + break; + case 1: + pass->texgen = T_GEN_3DMAP; + break; + default: + pass->texgen = T_GEN_CUBEMAP; + break; + } if (!TEXVALID(pass->anim_frames[0])) { @@ -4688,7 +4710,7 @@ void Shader_Default2D(char *shortname, shader_t *s, const void *genargs) "}\n" ); - TEXASSIGN(s->defaulttextures.base, R_LoadHiResTexture(shortname, NULL, IF_2D|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP)); + TEXASSIGN(s->defaulttextures.base, R_LoadHiResTexture(shortname, NULL, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP)); if (!TEXVALID(s->defaulttextures.base)) { unsigned char data[4*4] = {0}; diff --git a/engine/gl/gl_shadow.c b/engine/gl/gl_shadow.c index 1e86d0e58..5d026831c 100644 --- a/engine/gl/gl_shadow.c +++ b/engine/gl/gl_shadow.c @@ -103,8 +103,14 @@ typedef struct { vbo_t *vbo; mesh_t **s; } shadowmeshbatch_t; -typedef struct shadowmesh_s { - qboolean surfonly; +typedef struct shadowmesh_s +{ + enum + { + SMT_STENCILVOLUME, + SMT_SHADOWLESS, + SMT_SURFACES + } type; unsigned int numindicies; unsigned int maxindicies; index_t *indicies; @@ -361,7 +367,7 @@ static void SH_CalcShadowBatches(model_t *mod) } } -static void SHM_BeginShadowMesh(dlight_t *dl, qboolean surfonly) +static void SHM_BeginShadowMesh(dlight_t *dl, int type) { unsigned int i; unsigned int lb; @@ -404,7 +410,7 @@ static void SHM_BeginShadowMesh(dlight_t *dl, qboolean surfonly) sh_shmesh->numverts = 0; sh_shmesh->maxindicies = 0; sh_shmesh->numindicies = 0; - sh_shmesh->surfonly = surfonly; + sh_shmesh->type = type; if (!cl.worldmodel->numshadowbatches) { @@ -622,7 +628,7 @@ static void SHM_RecursiveWorldNodeQ1_r (dlight_t *dl, mnode_t *node) if ((s*s+t*t+dot*dot) < maxdist) { SHM_Shadow_Cache_Surface(surf); - if (sh_shmesh->surfonly) + if (sh_shmesh->type != SMT_STENCILVOLUME) continue; //build a list of the edges that are to be drawn. @@ -809,7 +815,7 @@ static void SHM_RecursiveWorldNodeQ2_r (dlight_t *dl, mnode_t *node) if ((s*s+t*t+dot*dot) < maxdist) { SHM_Shadow_Cache_Surface(surf); - if (sh_shmesh->surfonly) + if (sh_shmesh->type != SMT_STENCILVOLUME) continue; //build a list of the edges that are to be drawn. @@ -1282,12 +1288,12 @@ static void SHM_ComposeVolume_BruteForce(dlight_t *dl) } } -static struct shadowmesh_s *SHM_BuildShadowMesh(dlight_t *dl, unsigned char *lvis, unsigned char *vvis, qboolean surfonly) +static struct shadowmesh_s *SHM_BuildShadowMesh(dlight_t *dl, unsigned char *lvis, unsigned char *vvis, int type) { float *v1, *v2; vec3_t v3, v4; - if (dl->worldshadowmesh && !dl->rebuildcache) + if (dl->worldshadowmesh && !dl->rebuildcache && dl->worldshadowmesh->type == type) return dl->worldshadowmesh; firstedge=0; @@ -1307,14 +1313,14 @@ static struct shadowmesh_s *SHM_BuildShadowMesh(dlight_t *dl, unsigned char *lvi } else*/ { - SHM_BeginShadowMesh(dl, surfonly); + SHM_BeginShadowMesh(dl, type); SHM_MarkLeavesQ1(dl, lvis); SHM_RecursiveWorldNodeQ1_r(dl, cl.worldmodel->nodes); } break; #ifdef Q2BSPS case fg_quake2: - SHM_BeginShadowMesh(dl, surfonly); + SHM_BeginShadowMesh(dl, type); SHM_MarkLeavesQ2(dl, lvis, vvis); SHM_RecursiveWorldNodeQ2_r(dl, cl.worldmodel->nodes); break; @@ -1325,7 +1331,7 @@ static struct shadowmesh_s *SHM_BuildShadowMesh(dlight_t *dl, unsigned char *lvi SHM_BeginShadowMesh(dl, true); sh_shadowframe++; SHM_RecursiveWorldNodeQ3_r(dl, cl.worldmodel->nodes); - if (!surfonly) + if (type == SMT_STENCILVOLUME) SHM_ComposeVolume_BruteForce(dl); break; #endif @@ -1334,8 +1340,14 @@ static struct shadowmesh_s *SHM_BuildShadowMesh(dlight_t *dl, unsigned char *lvi } /*generate edge polys for map types that need it (q1/q2)*/ - if (!surfonly) + switch (type) { + case SMT_SURFACES: + { + + } + break; + case SMT_STENCILVOLUME: SHM_BeginQuads(); while(firstedge) { @@ -1371,6 +1383,7 @@ static struct shadowmesh_s *SHM_BuildShadowMesh(dlight_t *dl, unsigned char *lvi firstedge = edge[firstedge].next; } SHM_End(); + break; } return SHM_FinishShadowMesh(dl); @@ -1441,16 +1454,9 @@ static qboolean Sh_LeafInView(qbyte *lightvis, qbyte *vvis) } #endif -typedef struct -{ - float x; - float y; - float width; - float height; - double dmin; - double dmax; -} srect_t; -static void Sh_Scissor (srect_t r) + +/* +static void Sh_Scissor (srect_t *r) { //float xs = vid.pixelwidth / (float)vid.width, ys = vid.pixelheight / (float)vid.height; switch(qrenderer) @@ -1464,15 +1470,15 @@ static void Sh_Scissor (srect_t r) case QR_OPENGL: #ifdef GLQUAKE qglScissor( - floor(r_refdef.pxrect.x + r.x*r_refdef.pxrect.width), - floor((r_refdef.pxrect.y + r.y*r_refdef.pxrect.height) - r_refdef.pxrect.height), - ceil(r.width * r_refdef.pxrect.width), - ceil(r.height * r_refdef.pxrect.height)); + floor(r_refdef.pxrect.x + r->x*r_refdef.pxrect.width), + floor((r_refdef.pxrect.y + r->y*r_refdef.pxrect.height) - r_refdef.pxrect.height), + ceil(r->width * r_refdef.pxrect.width), + ceil(r->height * r_refdef.pxrect.height)); qglEnable(GL_SCISSOR_TEST); if (qglDepthBoundsEXT) { - qglDepthBoundsEXT(r.dmin, r.dmax); + qglDepthBoundsEXT(r->dmin, r->dmax); qglEnable(GL_DEPTH_BOUNDS_TEST_EXT); } #endif @@ -1481,10 +1487,10 @@ static void Sh_Scissor (srect_t r) #ifdef D3D9QUAKE { RECT rect; - rect.left = r.x; - rect.right = r.x + r.width; - rect.top = r.y; - rect.bottom = r.y + r.height; + rect.left = r->x; + rect.right = r->x + r->width; + rect.top = r->y; + rect.bottom = r->y + r->height; IDirect3DDevice9_SetScissorRect(pD3DDev9, &rect); } #endif @@ -1510,7 +1516,7 @@ static void Sh_ScissorOff (void) break; } } - +*/ #if 0 static qboolean Sh_ScissorForSphere(vec3_t center, float radius, vrect_t *rect) { @@ -2014,16 +2020,25 @@ GL_CullFace(0); BE_SelectEntity(&r_worldentity); - for (tno = 0; tno < smesh->numbatches; tno++) +#ifdef GLQUAKE + if (qrenderer == QR_OPENGL) + GLBE_RenderShadowBuffer(smesh->numverts, smesh->vebo[0], NULL, smesh->numindicies, smesh->vebo[1], NULL); + else +#endif { - if (!smesh->batches[tno].count) - continue; - tex = cl.worldmodel->shadowbatches[tno].tex; - if (tex->shader->flags & SHADER_NODLIGHT) - continue; - BE_DrawMesh_List(tex->shader, smesh->batches[tno].count, smesh->batches[tno].s, cl.worldmodel->shadowbatches[tno].vbo, &tex->shader->defaulttextures, 0); + //FIXME: should be able to merge batches between textures+lightmaps. + for (tno = 0; tno < smesh->numbatches; tno++) + { + if (!smesh->batches[tno].count) + continue; + tex = cl.worldmodel->shadowbatches[tno].tex; + if (tex->shader->flags & SHADER_NODLIGHT) + continue; + BE_DrawMesh_List(tex->shader, smesh->batches[tno].count, smesh->batches[tno].s, cl.worldmodel->shadowbatches[tno].vbo, &tex->shader->defaulttextures, 0); + } } + //fixme: this walks through the entity lists up to 6 times per frame. switch(qrenderer) { default: @@ -2075,6 +2090,7 @@ void Sh_GenShadowMap (dlight_t *l, qbyte *lvis) float oproj[16], oview[16]; shadowmesh_t *smesh; int isspot = (l->fov != 0); + extern cvar_t temp1; if (!TEXVALID(shadowmap[isspot])) { @@ -2085,7 +2101,7 @@ void Sh_GenShadowMap (dlight_t *l, qbyte *lvis) #ifdef DBG_COLOURNOTDEPTH qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, smsize, smsize, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); #else - qglTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32_ARB, SHADOWMAP_SIZE, SHADOWMAP_SIZE, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); + qglTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16_ARB, SHADOWMAP_SIZE, SHADOWMAP_SIZE, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); #endif } else @@ -2095,7 +2111,7 @@ void Sh_GenShadowMap (dlight_t *l, qbyte *lvis) #ifdef DBG_COLOURNOTDEPTH qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, smsize*3, smsize*2, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); #else - qglTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32_ARB, SHADOWMAP_SIZE*3, SHADOWMAP_SIZE*2, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); + qglTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16_ARB, SHADOWMAP_SIZE*3, SHADOWMAP_SIZE*2, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); #endif } @@ -2117,9 +2133,11 @@ void Sh_GenShadowMap (dlight_t *l, qbyte *lvis) smesh = SHM_BuildShadowMesh(l, lvis, NULL, true); + smsize = bound(16, temp1.ival, SHADOWMAP_SIZE); + /*set framebuffer*/ GL_BeginRenderBuffer_DepthOnly(shadowmap[isspot]); - GLBE_SetupForShadowMap(shadowmap[isspot], isspot?smsize:smsize*3, isspot?smsize:smsize*2, smsize / (float)SHADOWMAP_SIZE); + GLBE_SetupForShadowMap(shadowmap[isspot], isspot?smsize:smsize*3, isspot?smsize:smsize*2, (smsize-4) / (float)SHADOWMAP_SIZE); qglViewport(0, 0, smsize*3, smsize*2); qglClear (GL_DEPTH_BUFFER_BIT); @@ -2165,8 +2183,6 @@ void Sh_GenShadowMap (dlight_t *l, qbyte *lvis) memcpy(r_refdef.m_view, oview, sizeof(r_refdef.m_view)); memcpy(r_refdef.m_projection, oproj, sizeof(r_refdef.m_projection)); - qglDisable(GL_POLYGON_OFFSET_FILL); - if (!gl_config.nofixedfunc) { qglMatrixMode(GL_PROJECTION); @@ -2234,14 +2250,14 @@ static void Sh_DrawShadowMapLight(dlight_t *l, vec3_t colour, qbyte *vvis) else lvis = NULL; - Sh_ScissorOff(); + BE_Scissor(NULL); Sh_GenShadowMap(l, lvis); bench.numlights++; //may as well use scissors - Sh_Scissor(rect); + BE_Scissor(&rect); BE_SelectEntity(&r_worldentity); @@ -2584,7 +2600,7 @@ static qboolean Sh_DrawStencilLight(dlight_t *dl, vec3_t colour, qbyte *vvis) //The backend doesn't maintain scissor state. //The backend doesn't maintain stencil test state either - it needs to be active for more than just stencils, or disabled. its awkward. - Sh_Scissor(rect); + BE_Scissor(&rect); switch(qrenderer) @@ -2818,7 +2834,7 @@ static void Sh_DrawShadowlessLight(dlight_t *dl, vec3_t colour, qbyte *vvis) } //should we actually scissor here? there's not really much point I suppose. - Sh_ScissorOff(); + BE_Scissor(NULL); bench.numlights++; @@ -2897,7 +2913,7 @@ void Sh_DrawCrepuscularLight(dlight_t *dl, float *colours) qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); } - Sh_ScissorOff(); + BE_Scissor(NULL); GLBE_RenderToTexture(r_nulltex, r_nulltex, crepuscular_texture_id, r_nulltex, false); @@ -3101,7 +3117,7 @@ void Sh_DrawLights(qbyte *vis) } } - Sh_ScissorOff(); + BE_Scissor(NULL); BE_SelectMode(BEM_STANDARD); diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index 98903cee5..295832aa2 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -1917,6 +1917,7 @@ rendererinfo_t openglrendererinfo = { GLBE_UploadAllLightmaps, GLBE_SelectEntity, GLBE_SelectDLight, + GLBE_Scissor, GLBE_LightCullModel, GLBE_VBO_Begin, diff --git a/engine/gl/gl_vidnt.c b/engine/gl/gl_vidnt.c index 1a2c2b302..8484fde8a 100644 --- a/engine/gl/gl_vidnt.c +++ b/engine/gl/gl_vidnt.c @@ -968,8 +968,6 @@ int GLVID_SetMode (rendererstate_t *info, unsigned char *palette) // fix the leftover Alt from any Alt-Tab or the like that switched us away ClearAllStates (); - vid.recalc_refdef = 1; - if (vid_desktopgamma.value) { HDC hDC = GetDC(GetDesktopWindow()); @@ -1416,60 +1414,6 @@ void OblitterateOldGamma(void) } } -void GLVID_SetPalette (unsigned char *palette) -{ - qbyte *pal; - unsigned r,g,b; - unsigned v; - unsigned short i; - unsigned *table; - extern qbyte gammatable[256]; - - // - // 8 8 8 encoding - // - if (vid_hardwaregamma.value) - { - // don't built in the gamma table - - pal = palette; - table = d_8to24rgbtable; - for (i=0 ; i<256 ; i++) - { - r = pal[0]; - g = pal[1]; - b = pal[2]; - pal += 3; - - // v = (255<<24) + (r<<16) + (g<<8) + (b<<0); - // v = (255<<0) + (r<<8) + (g<<16) + (b<<24); - v = (255<<24) + (r<<0) + (g<<8) + (b<<16); - *table++ = v; - } - d_8to24rgbtable[255] &= 0xffffff; // 255 is transparent - } - else - { -//computer has no hardware gamma (poor suckers) increase table accordingly - - pal = palette; - table = d_8to24rgbtable; - for (i=0 ; i<256 ; i++) - { - r = gammatable[pal[0]]; - g = gammatable[pal[1]]; - b = gammatable[pal[2]]; - pal += 3; - - // v = (255<<24) + (r<<16) + (g<<8) + (b<<0); - // v = (255<<0) + (r<<8) + (g<<16) + (b<<24); - v = (255<<24) + (r<<0) + (g<<8) + (b<<16); - *table++ = v; - } - d_8to24rgbtable[255] &= 0xffffff; // 255 is transparent - } -} - qboolean GLVID_ApplyGammaRamps (unsigned short *ramps) { if (ramps) @@ -1888,7 +1832,7 @@ LONG WINAPI GLMainWndProc ( case WM_COPYDATA: { COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lParam; - Sys_RunFile(cds->lpData, cds->cbData); + Host_RunFile(cds->lpData, cds->cbData, NULL); } break; case WM_KILLFOCUS: diff --git a/engine/gl/shader.h b/engine/gl/shader.h index 900d73763..befe1463d 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -6,8 +6,7 @@ typedef void (shader_gen_t)(char *name, shader_t*, const void *args); #define SHADER_PASS_MAX 8 #define SHADER_MAX_TC_MODS 8 #define SHADER_DEFORM_MAX 8 -#define SHADER_MAX_ANIMFRAMES 8 -#define SHADER_ANIM_FRAMES_MAX 16 +#define SHADER_MAX_ANIMFRAMES 16 #define SHADER_PROGPARMS_MAX 16 @@ -516,6 +515,7 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis); qboolean GLBE_LightCullModel(vec3_t org, model_t *model); void GLBE_SelectEntity(entity_t *ent); void GLBE_SelectDLight(dlight_t *dl, vec3_t colour); +void GLBE_Scissor(srect_t *rect); void GLBE_SubmitMeshes (qboolean drawworld, int start, int stop); void GLBE_RenderToTexture(texid_t sourcecol, texid_t sourcedepth, texid_t destcol, texid_t destdepth, qboolean usedepth); void GLBE_VBO_Begin(vbobctx_t *ctx, unsigned int maxsize); @@ -542,6 +542,7 @@ void D3D9BE_VBO_Begin(vbobctx_t *ctx, unsigned int maxsize); void D3D9BE_VBO_Data(vbobctx_t *ctx, void *data, unsigned int size, vboarray_t *varray); void D3D9BE_VBO_Finish(vbobctx_t *ctx, void *edata, unsigned int esize, vboarray_t *earray); void D3D9BE_VBO_Destroy(vboarray_t *vearray); +void D3D9BE_Scissor(srect_t *rect); qboolean D3D9Shader_CreateProgram (program_t *prog, char *sname, int permu, char **precompilerconstants, char *vert, char *frag); int D3D9Shader_FindUniform(union programhandle_u *h, int type, char *name); @@ -574,6 +575,7 @@ void D3D11BE_VBO_Begin(vbobctx_t *ctx, unsigned int maxsize); void D3D11BE_VBO_Data(vbobctx_t *ctx, void *data, unsigned int size, vboarray_t *varray); void D3D11BE_VBO_Finish(vbobctx_t *ctx, void *edata, unsigned int esize, vboarray_t *earray); void D3D11BE_VBO_Destroy(vboarray_t *vearray); +void D3D11BE_Scissor(srect_t *rect); #endif //Asks the backend to invoke DrawMeshChain for each surface, and to upload lightmaps as required diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c index bb632764d..8d57fa5b0 100644 --- a/engine/http/httpclient.c +++ b/engine/http/httpclient.c @@ -3,6 +3,7 @@ #include "iweb.h" #include "netinc.h" +#include "fs.h" #if defined(WEBCLIENT) @@ -277,7 +278,7 @@ void NADL_Cleanup(struct dl_download *dl) ppb_core->CallOnMainThread(1000, ccb, 0); } -qboolean HTTPDL_Decide(struct dl_download *dl) +qboolean DL_Decide(struct dl_download *dl) { const char *url = dl->redir; struct nacl_dl *ctx; @@ -314,7 +315,7 @@ qboolean HTTPDL_Decide(struct dl_download *dl) return true; } #else -qboolean HTTPDL_Decide(struct dl_download *dl); +qboolean DL_Decide(struct dl_download *dl); /* This file does one thing. Connects to servers and grabs the specified file. It doesn't do any uploading whatsoever. Live with it. @@ -334,11 +335,13 @@ struct http_dl_ctx_s { int totalreceived; //useful when we're just dumping to a file. + struct vfsfile_s *file; //if gzipping, this is a temporary file. we'll write to the real file from this after the transfer is complete. + qboolean gzip; qboolean chunking; int chunksize; int chunked; - enum {HC_REQUESTING,HC_GETTINGHEADER, HC_GETTING} state; + enum {HC_REQUESTING, HC_GETTINGHEADER, HC_GETTING, HC_DECOMPRESSING} state; int contentlength; }; @@ -421,50 +424,59 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) con->state = HC_GETTING; dl->status = DL_ACTIVE; con->contentlength = -1; //meaning end of stream. - dl->replycode = 0; + dl->replycode = 200; } else { + qboolean hcomplete = false; while(*msg) { if (*msg == '\n') { if (msg[1] == '\n') { //tut tut, not '\r'? that's not really allowed... - msg+=1; - break; - } - if (msg[2] == '\n') - { msg+=2; + hcomplete = true; break; } + if (msg[1] == '\r' && msg[2] == '\n') + { + msg+=3; + hcomplete = true; + break; + } + msg++; } - msg++; + while (*msg == ' ' || *msg == '\t') + msg++; + + nl = strchr(msg, '\n'); + if (!nl) + break;//not complete, don't bother trying to parse it. if (!strnicmp(msg, "Content-Length: ", 16)) con->contentlength = atoi(msg+16); else if (!strnicmp(msg, "Location: ", 10)) { - nl = strchr(msg, '\n'); - if (nl) - { - *nl = '\0'; - Q_strncpyz(Location, COM_TrimString(msg+10), sizeof(Location)); - *nl = '\n'; - } + *nl = '\0'; + Q_strncpyz(Location, COM_TrimString(msg+10), sizeof(Location)); + *nl = '\n'; + } + else if (!strnicmp(msg, "Content-Encoding: ", 18)) + { + char *chunk = strstr(msg, "gzip"); + if (chunk < nl) + con->gzip = true; } else if (!strnicmp(msg, "Transfer-Encoding: ", 19)) { char *chunk = strstr(msg, "chunked"); - nl = strchr(msg, '\n'); - if (nl) - if (chunk < nl) - con->chunking = true; + if (chunk < nl) + con->chunking = true; } + msg = nl; } - if (!*msg) - break;//switch - msg++; + if (!hcomplete) + break;//headers not complete. break out of switch ammount = msg - con->buffer; @@ -475,7 +487,6 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) if (!stricmp(buffer, "100")) { //http/1.1 servers can give this. We ignore it. - con->bufferused -= ammount; memmove(con->buffer, con->buffer+ammount, con->bufferused); return true; @@ -511,7 +522,7 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) } else Q_strncpyz(dl->redir, Location, sizeof(dl->redir)); - dl->poll = HTTPDL_Decide; + dl->poll = DL_Decide; dl->status = DL_PENDING; } return true; @@ -533,34 +544,41 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) dl->totalsize = con->contentlength; + memmove(con->buffer, con->buffer+ammount, con->bufferused); + } + + if (!dl->file) + { +#ifndef NPFTE + if (*dl->localname) + { + FS_CreatePath(dl->localname, FS_GAME); + dl->file = FS_OpenVFS(dl->localname, "w+b", FS_GAME); + } + else + dl->file = FS_OpenTemp(); +#endif if (!dl->file) { -#ifndef NPFTE - if (*dl->localname) - { - FS_CreatePath(dl->localname, FS_GAME); - dl->file = FS_OpenVFS(dl->localname, "w+b", FS_GAME); - } - else - dl->file = FS_OpenTemp(); -#endif - if (!dl->file) - { - Con_Printf("HTTP: Couldn't open file \"%s\"\n", dl->localname); - dl->status = DL_FAILED; - return false; - } + Con_Printf("HTTP: Couldn't open file \"%s\"\n", dl->localname); + dl->status = DL_FAILED; + return false; } - - memmove(con->buffer, con->buffer+ammount, con->bufferused); - - - con->state = HC_GETTING; - dl->status = DL_ACTIVE; - } - //Fall through + if (con->gzip) + { +#ifdef NPFTE + Con_Printf("HTTP: no support for gzipped files \"%s\"\n", dl->localname); +#else + con->file = FS_OpenTemp(); +#endif + } + else + con->file = dl->file; + con->state = HC_GETTING; + dl->status = DL_ACTIVE; + //Fall through case HC_GETTING: if (con->bufferlen - con->bufferused < 1530) ExpandBuffer(con, 1530); @@ -616,11 +634,10 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) } } - con->totalreceived+=con->chunked; - if (dl->file && con->chunked) //we've got a chunk in the buffer + if (con->file && con->chunked) //we've got a chunk in the buffer { //write it - if (VFS_WRITE(dl->file, con->buffer, con->chunked) != con->chunked) + if (VFS_WRITE(con->file, con->buffer, con->chunked) != con->chunked) { Con_Printf("Write error whilst downloading %s\nDisk full?\n", dl->localname); return false; @@ -635,9 +652,9 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) else { con->totalreceived+=ammount; - if (dl->file) //we've got a chunk in the buffer + if (con->file) //we've got a chunk in the buffer { //write it - if (VFS_WRITE(dl->file, con->buffer, con->bufferused) != con->bufferused) + if (VFS_WRITE(con->file, con->buffer, con->bufferused) != con->bufferused) { Con_Printf("Write error whilst downloading %s\nDisk full?\n", dl->localname); return false; @@ -651,7 +668,17 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) if (con->chunksize) dl->status = DL_FAILED; else - dl->status = DL_FINISHED; + { +#ifndef NPFTE + if (con->gzip) + { + VFS_SEEK(con->file, 0); + dl->file = FS_DecompressGZip(con->file, dl->file); + con->file = NULL; + } +#endif + dl->status = (dl->replycode == 200)?DL_FINISHED:DL_FAILED; + } return false; } dl->completed = con->totalreceived; @@ -730,7 +757,13 @@ void HTTPDL_Establish(struct dl_download *dl) } ExpandBuffer(con, 512*1024); - sprintf(con->buffer, "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "Connection: close\r\n" "User-Agent: "FULLENGINENAME"\r\n" "\r\n", uri, server); + sprintf(con->buffer, + "GET %s HTTP/1.1\r\n" + "Host: %s\r\n" + "Connection: close\r\n" + "Accept-Encoding: gzip\r\n" + "User-Agent: "FULLENGINENAME"\r\n" + "\r\n", uri, server); con->bufferused = strlen(con->buffer); con->contentlength = -1; } @@ -770,7 +803,7 @@ qboolean HTTPDL_Poll(struct dl_download *dl) return true; } -qboolean HTTPDL_Decide(struct dl_download *dl) +qboolean DL_Decide(struct dl_download *dl) { const char *url = dl->redir; if (!*url) @@ -844,7 +877,7 @@ struct dl_download *DL_Create(const char *url) return NULL; memset(newdl, 0, sizeof(*newdl)); Q_strncpyz(newdl->url, url, sizeof(newdl->url)); - newdl->poll = HTTPDL_Decide; + newdl->poll = DL_Decide; if (!newdl->poll(newdl)) { diff --git a/engine/http/httpserver.c b/engine/http/httpserver.c index 80a1471ec..7da66840e 100644 --- a/engine/http/httpserver.c +++ b/engine/http/httpserver.c @@ -86,6 +86,7 @@ typedef struct HTTP_active_connections_s { qboolean modeswitched; qboolean closeaftertransaction; qboolean close; + qboolean acceptgzip; char *inbuffer; unsigned int inbuffersize; @@ -129,9 +130,9 @@ void HTTP_RunExisting (void) { char *content; char *msg, *nl; - char buf2[256]; //short lived temp buffer. - char resource[256]; - char mode[8]; + char buf2[2560]; //short lived temp buffer. + char resource[2560]; + char mode[80]; qboolean hostspecified; unsigned int contentlen; @@ -219,6 +220,7 @@ cont: continue; //we need more... MORE!!! MORE I TELL YOU!!!! } + //FIXME: COM_ParseOut recognises // comments, which is not correct. msg = COM_ParseOut(msg, mode, sizeof(mode)); msg = COM_ParseOut(msg, resource, sizeof(resource)); @@ -244,6 +246,8 @@ cont: if (!strcmp(resource, "/")) strcpy(resource, "/index.html"); + cl->acceptgzip = false; + msg = COM_ParseOut(msg, buf2, sizeof(buf2)); contentlen = 0; if (!strnicmp(buf2, "HTTP/", 5)) @@ -279,6 +283,36 @@ cont: hostspecified = true; else if (!strnicmp(msg, "Content-Length: ", 16)) //parse needed header fields contentlen = strtoul(msg+16, NULL, 0); + else if (!strnicmp(msg, "Accept-Encoding:", 16)) //parse needed header fields + { + char *e; + msg += 16; + while(*msg) + { + if (*msg == '\n') + break; + while (*msg == ' ' || *msg == '\t') + msg++; + if (*msg == ',') + { + msg++; + continue; + } + e = msg; + while(*e) + { + if (*e == ',' || *e == '\r' || *e == '\n') + break; + e++; + } + while(e > msg && (e[-1] == ' ' || e[-1] == '\t')) + e--; + if (e-msg == 4 && !strncmp(msg, "gzip", 4)) + cl->acceptgzip = true; + while(*msg && *msg != '\n' && *msg != ',') + msg++; + } + } else if (!strnicmp(msg, "Transfer-Encoding: ", 18)) //parse needed header fields { cl->closeaftertransaction = true; @@ -339,6 +373,7 @@ cont: resource[1] = 0; //I'm lazy, they need to comply } IWebPrintf("Download request for \"%s\"\n", resource+1); + if (!strnicmp(mode, "P", 1)) //when stuff is posted, data is provided. Give an error message if we couldn't do anything with that data. cl->file = IWebGenerateFile(resource+1, content, contentlen); else @@ -347,12 +382,11 @@ cont: if (SV_AllowDownload(resource+1)) { char nbuf[MAX_OSPATH]; - if (HTTPmarkup >= 3 && strlen(resource+1) < sizeof(nbuf)-4) + if (cl->acceptgzip && strlen(resource+1) < sizeof(nbuf)-4) { sprintf(nbuf, "%s.gz", resource+1); - cl->file = FS_OpenVFS(nbuf, "rb", FS_GAME); + cl->file = FS_OpenVFS(nbuf, "rb", FS_GAME); } - if (cl->file) gzipped = true; else @@ -364,7 +398,6 @@ cont: cl->file = IWebGenerateFile(resource+1, content, contentlen); } } - if (!cl->file) { IWebPrintf("Download rejected\n"); diff --git a/engine/http/iweb.h b/engine/http/iweb.h index e25bcc619..ee06c152d 100644 --- a/engine/http/iweb.h +++ b/engine/http/iweb.h @@ -107,16 +107,11 @@ struct dl_download unsigned int user_num; void *user_ctx; - /*not used internally by the backend, but used by HTTP_CL_Get/thread wrapper*/ - struct dl_download *next; - void (*notify) (struct dl_download *dl); - /*stream config*/ char url[MAX_OSPATH]; /*original url*/ char redir[MAX_OSPATH]; /*current redirected url*/ - char localname[MAX_OSPATH]; - struct vfsfile_s *file; /*downloaded to, when starting will open localname if not already set*/ - qboolean (*poll) (struct dl_download *); + char localname[MAX_OSPATH]; /*leave empty for a temp file*/ + struct vfsfile_s *file; /*downloaded to, if not already set when starting will open localname or a temp file*/ /*stream status*/ enum @@ -139,6 +134,11 @@ struct dl_download #endif void *ctx; /*internal context, depending on http/ftp/etc protocol*/ void (*abort) (struct dl_download *); /*cleans up the internal context*/ + qboolean (*poll) (struct dl_download *); + + /*not used internally by the backend, but used by HTTP_CL_Get/thread wrapper*/ + struct dl_download *next; + void (*notify) (struct dl_download *dl); }; void HTTP_CL_Think(void); diff --git a/engine/http/iwebiface.c b/engine/http/iwebiface.c index 63b8d0f58..abfedf2b9 100644 --- a/engine/http/iwebiface.c +++ b/engine/http/iwebiface.c @@ -19,10 +19,10 @@ vfsfile_t *IWebGenerateFile(char *name, char *content, int contentlength) { return NULL; } -vfsfile_t *VFSSTDIO_Open(const char *osname, const char *mode); +vfsfile_t *VFSSTDIO_Open(const char *osname, const char *mode, qboolean *needsflush); vfsfile_t *FS_OpenVFS(const char *filename, const char *mode, enum fs_relative relativeto) { - return VFSSTDIO_Open(filename, mode); + return VFSSTDIO_Open(filename, mode, NULL); } void Q_strncpyz(char *d, const char *s, int n) { @@ -103,7 +103,7 @@ int main(int argc, char **argv) } -void COM_EnumerateFiles (const char *match, int (*func)(const char *, int, void *), void *parm) +void COM_EnumerateFiles (const char *match, int (*func)(const char *, int, void *, searchpathfuncs_t *f), void *parm) { HANDLE r; WIN32_FIND_DATA fd; @@ -132,12 +132,12 @@ void COM_EnumerateFiles (const char *match, int (*func)(const char *, int, void else if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //is a directory { sprintf(file, "%s%s/", apath, fd.cFileName); - go = func(file, fd.nFileSizeLow, parm); + go = func(file, fd.nFileSizeLow, parm, NULL); } else { sprintf(file, "%s%s", apath, fd.cFileName); - go = func(file, fd.nFileSizeLow, parm); + go = func(file, fd.nFileSizeLow, parm, NULL); } } while(FindNextFile(r, &fd) && go); diff --git a/engine/partcfgs/high.cfg b/engine/partcfgs/high.cfg index b42b476e3..400a98502 100644 --- a/engine/partcfgs/high.cfg +++ b/engine/partcfgs/high.cfg @@ -1,3 +1,23 @@ +/////////////////////////////// +//rain +r_part te_rain +{ + texture ball; scalefactor 1; count 1; alpha 0.4; rgb 255 255 255; die 2; veladd 2; scale 2; type texturedspark + cliptype rainsplash + clipbounce 1 + clipcount 5 +} + +r_part rainsplash +{ + randomvel 50 50 + count 1; + texture ball; scalefactor 1; alpha 0.1; rgb 255 255 255; die 0.4; scale 50; + stretchfactor 4 + veladd 50; scale 1; type texturedspark + gravity 400 +} + /////////////////////////////// //rocket trail diff --git a/engine/qclib/gui.h b/engine/qclib/gui.h index 341aedb40..1a7769abe 100644 --- a/engine/qclib/gui.h +++ b/engine/qclib/gui.h @@ -14,6 +14,7 @@ extern char parameters[16384]; extern char progssrcname[256]; extern char progssrcdir[256]; +extern pbool fl_nondfltopts; extern pbool fl_hexen2; extern pbool fl_autohighlight; extern pbool fl_compileonstart; diff --git a/engine/qclib/pr_exec.c b/engine/qclib/pr_exec.c index f0f0a8a73..0b8177abf 100644 --- a/engine/qclib/pr_exec.c +++ b/engine/qclib/pr_exec.c @@ -806,6 +806,8 @@ char *PDECL PR_EvaluateDebugString(pubprogfuncs_t *ppf, char *key) break; */ case ev_entity: + if (!EDICT_NUM(progfuncs, atoi (assignment))) + return "(invalid entity)"; *(int *)val = EDICT_TO_PROG(progfuncs, EDICT_NUM(progfuncs, atoi (assignment))); break; diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index 677379580..d46f25b44 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -648,6 +648,7 @@ enum { WARN_SYSTEMCRC, WARN_CONDITIONALTYPEMISMATCH, WARN_SELFNOTTHIS, //warned for because 'self' does not have the right type. we convert such references to 'this' instead, which is more usable. + WARN_EVILPREPROCESSOR, //exploited by nexuiz, and generally unsafe. ERR_PARSEERRORS, //caused by qcc_pr_parseerror being called. diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 6624fcee7..1c875dcc0 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -5007,25 +5007,29 @@ QCC_def_t *QCC_PR_ParseArrayPointer (QCC_def_t *d, pbool allowarrayassign) { /*emulate the array access using a function call to do the read for us*/ QCC_def_t *args[1], *funcretr; + QCC_type_t *ftype; d->references++; + ftype = QCC_PR_NewType("__arrayget", ev_function, false); + ftype->aux_type = t; + ftype->num_parms = 1; + ftype->params = qccHunkAlloc(sizeof(*ftype->params)); + ftype->params[0].arraysize=0; + ftype->params[0].type = type_float; + ftype->params[0].paramname = "__index"; + funcretr = QCC_PR_GetDef(ftype, qcva("ArrayGet*%s", d->name), NULL, true, 0, false); + /*make sure the function type that we're calling exists*/ if (d->type->type == ev_vector) { - def_parms[0].type = type_vector; - funcretr = QCC_PR_GetDef(type_function, qcva("ArrayGet*%s", d->name), NULL, true, 0, false); - args[0] = QCC_PR_Statement(&pr_opcodes[OP_DIV_F], QCC_SupplyConversion(idx, ev_float, true), QCC_MakeFloatConst(3), NULL); - d = QCC_PR_GenerateFunctionCall(NULL, funcretr, args, NULL, 1); + d = QCC_PR_GenerateFunctionCall(NULL, funcretr, args, &type_float, 1); d->type = t; } else { - def_parms[0].type = type_float; - funcretr = QCC_PR_GetDef(type_function, qcva("ArrayGet*%s", d->name), NULL, true, 0, false); - if (t->size > 1) { QCC_def_t *r; @@ -6901,6 +6905,7 @@ void QCC_PR_ParseStatement (void) return; } + //frikqcc-style labels if (QCC_PR_CheckToken(":")) { if (pr_token_type != tt_name) @@ -7067,6 +7072,41 @@ void QCC_PR_ParseStatement (void) return; } + //C-style labels. + if (pr_token_type == tt_name && pr_file_p[0] == ':' && pr_file_p[1] != ':') + { + if (pr_token_type != tt_name) + { + QCC_PR_ParseError(ERR_BADLABELNAME, "invalid label name \"%s\"", pr_token); + return; + } + + for (i = 0; i < num_labels; i++) + if (!STRNCMP(pr_labels[i].name, pr_token, sizeof(pr_labels[num_labels].name) -1)) + { + QCC_PR_ParseWarning(WARN_DUPLICATELABEL, "Duplicate label %s", pr_token); + QCC_PR_Lex(); + return; + } + + if (num_labels >= max_labels) + { + max_labels += 8; + pr_labels = realloc(pr_labels, sizeof(*pr_labels)*max_labels); + } + + strncpy(pr_labels[num_labels].name, pr_token, sizeof(pr_labels[num_labels].name) -1); + pr_labels[num_labels].lineno = pr_source_line; + pr_labels[num_labels].statementno = numstatements; + + num_labels++; + +// QCC_PR_ParseWarning("Gotos are evil"); + QCC_PR_Lex(); + QCC_PR_Expect(":"); + return; + } + // qcc_functioncalled=0; qcc_usefulstatement = false; @@ -8418,7 +8458,7 @@ QCC_def_t *QCC_PR_EmitArrayGetVector(QCC_def_t *array) QCC_Marshal_Locals(df->first_statement, numstatements); QCC_FreeTemps(); df->parm_start = locals_start; - df->numparms = locals_end - locals_start; + df->locals = locals_end - locals_start; return func; } @@ -8465,8 +8505,8 @@ void QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, char *arrayname) df->first_statement = numstatements; df->parm_size[0] = 1; df->numparms = 1; - locals_start = locals_end = FIRST_LOCAL; - index = QCC_PR_GetDef(type_float, "indexg___", def, true, 0, false); + df->parm_start = locals_start = locals_end = FIRST_LOCAL; + index = QCC_PR_GetDef(type_float, "__indexg", pr_scope, true, 0, false); G_FUNCTION(scope->ofs) = df - functions; @@ -8550,11 +8590,13 @@ void QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, char *arrayname) QCC_PR_Statement(pr_opcodes+OP_DONE, 0, 0, NULL); + df->parm_start = locals_start; QCC_WriteAsmFunction(pr_scope, df->first_statement, df->parm_start); QCC_Marshal_Locals(df->first_statement, numstatements); - QCC_FreeTemps(); df->parm_start = locals_start; - df->numparms = locals_end - locals_start; + QCC_WriteAsmFunction(pr_scope, df->first_statement, df->parm_start); + QCC_FreeTemps(); + df->locals = locals_end - locals_start; } void QCC_PR_ArraySetRecurseDivide(QCC_def_t *array, QCC_def_t *index, QCC_def_t *value, int min, int max) diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 93f260c10..b0c4bfc48 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -2363,12 +2363,21 @@ void QCC_PR_ConditionCompilation(void) // read over a newline if necessary if( s[1] == '\n' || s[1] == '\r' ) { + char *exploitcheck; s++; QCC_PR_NewLine(false); - *d++ = *s++; + s++; if( s[-1] == '\r' && s[0] == '\n' ) { - *d++ = *s++; + s++; + } + + for (exploitcheck = s; *exploitcheck && qcc_iswhite(*exploitcheck); exploitcheck++) + ; + if (*exploitcheck == '#') + { + QCC_PR_ParseWarning(WARN_EVILPREPROCESSOR, "preprocessor directive within preprocessor constant %s", cnst->name); + *d++ = '\n'; } } } diff --git a/engine/qclib/qccgui.c b/engine/qclib/qccgui.c index fddc481b7..02d97a7dc 100644 --- a/engine/qclib/qccgui.c +++ b/engine/qclib/qccgui.c @@ -1370,6 +1370,15 @@ static LONG CALLBACK OptionsWndProc(HWND hWnd,UINT message, else Button_SetCheck(optimisations[i].guiinfo, 0); } + if (!fl_nondfltopts) + { + for (i = 0; optimisations[i].enabled; i++) + { + if (optimisations[i].guiinfo) + EnableWindow(optimisations[i].guiinfo, TRUE); + } + fl_nondfltopts = true; + } break; case IDI_O_DEBUG: for (i = 0; optimisations[i].enabled; i++) @@ -1380,6 +1389,15 @@ static LONG CALLBACK OptionsWndProc(HWND hWnd,UINT message, if (optimisations[i].flags&FLAG_KILLSDEBUGGERS) Button_SetCheck(optimisations[i].guiinfo, 0); } + if (!fl_nondfltopts) + { + for (i = 0; optimisations[i].enabled; i++) + { + if (optimisations[i].guiinfo) + EnableWindow(optimisations[i].guiinfo, TRUE); + } + fl_nondfltopts = true; + } break; case IDI_O_DEFAULT: for (i = 0; optimisations[i].enabled; i++) @@ -1392,6 +1410,15 @@ static LONG CALLBACK OptionsWndProc(HWND hWnd,UINT message, else Button_SetCheck(optimisations[i].guiinfo, 0); } + if (fl_nondfltopts) + { + for (i = 0; optimisations[i].enabled; i++) + { + if (optimisations[i].guiinfo) + EnableWindow(optimisations[i].guiinfo, FALSE); + } + fl_nondfltopts = false; + } break; } break; @@ -1566,6 +1593,9 @@ void OptionsDialog(void) else Button_SetCheck(wnd, 0); + if (!fl_nondfltopts) + EnableWindow(wnd, FALSE); + num++; } diff --git a/engine/qclib/qccguistuff.c b/engine/qclib/qccguistuff.c index b9555312b..a87cec97e 100644 --- a/engine/qclib/qccguistuff.c +++ b/engine/qclib/qccguistuff.c @@ -3,6 +3,7 @@ //common gui things +pbool fl_nondfltopts; pbool fl_hexen2; pbool fl_autohighlight; pbool fl_compileonstart; @@ -82,6 +83,7 @@ void GUI_ParseCommandLine(char *args) if (!strnicmp(parameters+paramlen, "-O", 2) || !strnicmp(parameters+paramlen, "/O", 2)) { //strip out all -O + fl_nondfltopts = true; if (parameters[paramlen+2]) { if (parameters[paramlen+2] >= '0' && parameters[paramlen+2] <= '3') @@ -275,14 +277,17 @@ int GUI_BuildParms(char *args, char **argv) paramlen += strlen(param+paramlen)+1; } - for (i = 0; optimisations[i].enabled; i++) //enabled is a pointer + if (fl_nondfltopts) { - if (optimisations[i].flags & FLAG_SETINGUI) - sprintf(param+paramlen, "-O%s", optimisations[i].abbrev); - else - sprintf(param+paramlen, "-Ono-%s", optimisations[i].abbrev); - argv[argc++] = param+paramlen; - paramlen += strlen(param+paramlen)+1; + for (i = 0; optimisations[i].enabled; i++) //enabled is a pointer + { + if (optimisations[i].flags & FLAG_SETINGUI) + sprintf(param+paramlen, "-O%s", optimisations[i].abbrev); + else + sprintf(param+paramlen, "-Ono-%s", optimisations[i].abbrev); + argv[argc++] = param+paramlen; + paramlen += strlen(param+paramlen)+1; + } } for (i = 0; compiler_flag[i].enabled; i++) //enabled is a pointer diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 6565edb8a..d166d360d 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -174,6 +174,7 @@ struct { {" F300", WARN_DEADCODE}, {" F301", WARN_NOTUTF8}, {" F302", WARN_UNINITIALIZED}, + {" F303", WARN_EVILPREPROCESSOR}, //frikqcc errors //Q608: PrecacheSound: numsounds @@ -2829,7 +2830,6 @@ void QCC_SetDefaultProperties (void) ForcedCRC = 0; defaultstatic = 0; - autoprototyped = false; QCC_PR_DefineName("FTEQCC"); @@ -2911,6 +2911,7 @@ void QCC_SetDefaultProperties (void) qccwarningaction[WARN_NOTUTF8] = WA_IGNORE; qccwarningaction[WARN_UNINITIALIZED] = WA_IGNORE; //not sure about this being ignored by default. qccwarningaction[WARN_SELFNOTTHIS] = WA_IGNORE; + qccwarningaction[WARN_EVILPREPROCESSOR] = WA_WARN;//FIXME: make into WA_ERROR; if (QCC_CheckParm("-h2")) qccwarningaction[WARN_CASEINSENSATIVEFRAMEMACRO] = WA_IGNORE; @@ -3094,6 +3095,7 @@ void QCC_main (int argc, char **argv) //as part of the quake engine *compiler_flag[p].enabled = compiler_flag[p].flags & FLAG_ASDEFAULT; } + autoprototyped = autoprototype = false; QCC_SetDefaultProperties(); optres_shortenifnots = 0; @@ -3431,6 +3433,7 @@ void QCC_ContinueCompile(void) if (autoprototype) { qccmsrc = originalqccmsrc; + autoprototyped = autoprototype; QCC_SetDefaultProperties(); autoprototype = false; return; @@ -3671,8 +3674,8 @@ void new_QCC_ContinueCompile(void) pr_file_p = qccmsrc; s_file = s_file2 = QCC_CopyString (compilingfile); - QCC_SetDefaultProperties(); autoprototyped = autoprototype; + QCC_SetDefaultProperties(); autoprototype = false; QCC_PR_NewLine(false); QCC_PR_Lex(); diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 9d9c111fa..666a94d70 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -666,7 +666,6 @@ void PR_LoadGlabalStruct(void) static float writeonly; static float dimension_send_default; static float zero_default; - //static vec3_t vecwriteonly; // 523:16: warning: unused variable ‘vecwriteonly’ static float input_buttons_default; static float input_timelength_default; static float input_impulse_default; @@ -1717,7 +1716,7 @@ qboolean PR_GameCodePausedTic(float pausedtime) { //notications to the gamecode that the server is paused. globalvars_t *pr_globals; - if (!svprogfuncs || !gfuncs.ShouldPause) + if (!svprogfuncs || !gfuncs.PausedTic) return false; pr_globals = PR_globals(svprogfuncs, PR_CURRENT); @@ -3064,7 +3063,7 @@ static void QCBUILTIN PF_LocalSound(pubprogfuncs_t *prinst, struct globalvars_s if (!isDedicated) { if ((sfx = S_PrecacheSound(s))) - S_StartSound(cl.playernum[0], chan, sfx, cl.playerview[0].simorg, vol, 0.0, 0, 0); + S_StartSound(cl.playerview[0].playernum, chan, sfx, cl.playerview[0].simorg, vol, 0.0, 0, 0); } #endif }; @@ -9369,9 +9368,10 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"strpad", PF_strpad, 0, 0, 0, 225, D("string(float pad, string str1, ...)", "Pads the string with spaces, to ensure its a specific length (so long as a fixed-width font is used, anyway). If pad is negative, the spaces are added on the left. If positive the padding is on the right.")}, //will be moved {"infoadd", PF_infoadd, 0, 0, 0, 226, D("string(string old, string key, string value)", "Returns a new tempstring infostring with the named value changed (or added if it was previously unspecified). Key and value may not contain the \\ character.")}, {"infoget", PF_infoget, 0, 0, 0, 227, D("string(string info, string key)", "Reads a named value from an infostring. The returned value is a tempstring")}, - {"strncmp", PF_strncmp, 0, 0, 0, 228, D("float(string s1, string s2, float len, optional float s1ofs)", "Compares up to 'len' chars in the two strings. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\nReturns 0 if the two strings are equal, a negative value if s1 appears numerically lower, and positive if s1 appears numerically higher.")}, - {"strcasecmp", PF_strcasecmp, 0, 0, 0, 229, D("float(string s1, string s2)", "Compares the two strings without case sensitivity.\nReturns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon.")}, - {"strncasecmp", PF_strncasecmp, 0, 0, 0, 230, D("float(string s1, string s2, float len, optional float s1ofs)", "Compares up to 'len' chars in the two strings without case sensitivity. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\nReturns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon.")}, + {"strcmp", PF_strncmp, 0, 0, 0, 228, D("float(string s1, string s2)", "Compares the two strings exactly. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\nReturns 0 if the two strings are equal, a negative value if s1 appears numerically lower, and positive if s1 appears numerically higher.")}, + {"strncmp", PF_strncmp, 0, 0, 0, 228, D("float(string s1, string s2, float len, optional float s1ofs, optional float s2ofs)", "Compares up to 'len' chars in the two strings. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\nReturns 0 if the two strings are equal, a negative value if s1 appears numerically lower, and positive if s1 appears numerically higher.")}, + {"strcasecmp", PF_strncasecmp, 0, 0, 0, 229, D("float(string s1, string s2)", "Compares the two strings without case sensitivity.\nReturns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon.")}, + {"strncasecmp", PF_strncasecmp, 0, 0, 0, 230, D("float(string s1, string s2, float len, optional float s1ofs, optional float s2ofs)", "Compares up to 'len' chars in the two strings without case sensitivity. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\nReturns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon.")}, //END FTE_STRINGS //FTE_CALLTIMEOFDAY @@ -9442,12 +9442,13 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"frametoname", PF_frametoname, 0, 0, 0, 284, "string(float modidx, float framenum)"}, {"skintoname", PF_skintoname, 0, 0, 0, 285, "string(float modidx, float skin)"}, // {"cvar_setlatch", PF_cvar_setlatch, 0, 0, 0, 286, "void(string cvarname, optional string value)"}, - {"hash_createtab", PF_hash_createtab, 0, 0, 0, 287, D("float(float tabsize)", "Creates a hash table object with at least 'tabsize' slots")}, + {"hash_createtab", PF_hash_createtab, 0, 0, 0, 287, D("float(float tabsize, float stringsonly)", "Creates a hash table object with at least 'tabsize' slots. stringsonly affects the behaviour of the other hash builtins. hash table with index 0 is a game-persistant table and will NEVER be returned by this builtin (except as an error return)")}, {"hash_destroytab", PF_hash_destroytab, 0, 0, 0, 288, D("void(float table)", "Destroys a hash table object.")}, - {"hash_add", PF_hash_add, 0, 0, 0, 289, D("void(float table, string name, __variant value)", "Adds the given key with the given value to the table. value will NOT be strzoned or anything fancy like that, so don't pass temp strings there. Its okay to pass temp strings for 'name' though.")}, - {"hash_get", PF_hash_get, 0, 0, 0, 290, D("__variant(float table, string name, __variant deflt)", "looks up the specified key name in the hash table. returns deflt if key was not found.")}, + {"hash_add", PF_hash_add, 0, 0, 0, 289, D("void(float table, string name, __variant value, float replace)", "Adds the given key with the given value to the table. stringsonly=1: the value MUST be a string type, and will be internally copied. stringsonly=0: the value can be any type including vectors with the single exception that temp strings are not supported if their scope doesn't last as long as the string table. Its always okay to pass temp strings for 'name' though.\nreplace=0: Multiple values may be added for a single key, they won't overwrite.\nreplace=1: previous values with this key will be discarded first.")}, + {"hash_get", PF_hash_get, 0, 0, 0, 290, D("__variant(float table, string name, __variant deflt)", "looks up the specified key name in the hash table. returns deflt if key was not found. If stringsonly=1, the return value will be in the form of a tempstring, otherwise it'll be the original value argument exactly as it was.")}, {"hash_delete", PF_hash_delete, 0, 0, 0, 291, D("__variant(float table, string name)", "removes the named key. returns the value of the object that was destroyed, or 0 on error.")}, - {"hash_getkey", PF_hash_getkey, 0, 0, 0, 292, D("string(float table, float idx)", "gets some random key name. add+delete can change return values of this. suggested use is: while((n = hash_getkey(t,0))) strunzone(hash_delete(t,n));")}, + {"hash_getkey", PF_hash_getkey, 0, 0, 0, 292, D("string(float table, float idx)", "gets some random key name. add+delete can change return values of this, so don't blindly increment the key index if you're removing all.")}, + {"hash_getcb", PF_hash_getcb, 0, 0, 0, 293, D("void(float table, void(string keyname, __variant val) callback, optional string name)", "For each item in the table that matches the name, call the callback. if name is omitted, will enumerate ALL keys.")}, {"clearscene", PF_Fixme, 0, 0, 0, 300, D("void()", "Forgets all rentities, polygons, and temporary dlights. Resets all view properties to their default values.")},// (EXT_CSQC) @@ -9456,7 +9457,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"setproperty", PF_Fixme, 0, 0, 0, 303, D("#define setviewprop setproperty\nfloat(float property, ...)", "Allows you to override default view properties like viewport, fov, and whether the engine hud will be drawn. Different VF_ values have slightly different arguments, some are vectors, some floats.")},// (EXT_CSQC) {"renderscene", PF_Fixme, 0, 0, 0, 304, D("void()", "Draws all entities, polygons, and particles on the rentity list (which were added via addentities or addentity), using the various view properties set via setproperty. There is no ordering dependancy.\nThe scene must generally be cleared again before more entities are added, as entities will persist even over to the next frame.\nYou may call this builtin multiple times per frame, but should only be called from CSQC_UpdateView.")},// (EXT_CSQC) - {"dynamiclight_add",PF_Fixme, 0, 0, 0, 305, D("float(vector org, float radius, vector lightcolours)", "Adds a temporary dlight, ready to be drawn via addscene.")},// (EXT_CSQC) + {"dynamiclight_add",PF_Fixme, 0, 0, 0, 305, D("float(vector org, float radius, vector lightcolours, optional float style, optional string cubemapname, optional float pflags)", "Adds a temporary dlight, ready to be drawn via addscene.")},// (EXT_CSQC) //gonna expose these to ssqc as a debugging extension {"R_BeginPolygon", PF_R_PolygonBegin,0,0, 0, 306, D("void(string texturename, optional float flags)", "Specifies the shader to use for the following polygons, along with optional flags.\nIf flags&4, the polygon will be drawn as soon as the EndPolygon call is made, rather than waiting for renderscene. This allows complex 2d effects.")},// (EXT_CSQC_???) @@ -9471,22 +9472,22 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"project", PF_Fixme, 0, 0, 0, 311, D("vector (vector v)", "Transform a 3d world-space point into a 2d screen-space point, according the various origin+angle+fov etc settings set via setproperty.")},// (EXT_CSQC) //2d (immediate) operations - {"drawline", PF_Fixme, 0, 0, 0, 315, D("void(float width, vector pos1, vector pos2)", "Draws a 2d line between the 2d points")},// (EXT_CSQC) + {"drawline", PF_Fixme, 0, 0, 0, 315, D("void(float width, vector pos1, vector pos2, vector rgb, float alpha, optional float drawflag)", "Draws a 2d line between the two 2d points.")},// (EXT_CSQC) {"iscachedpic", PF_Fixme, 0, 0, 0, 316, D("float(string name)", "Checks to see if the image is currently loaded. Engines might lie, or cache between maps.")},// (EXT_CSQC) {"precache_pic", PF_Fixme, 0, 0, 0, 317, D("string(string name, optional float trywad)", "Forces the engine to load the named image. If trywad is specified, the specified name must any lack path and extension.")},// (EXT_CSQC) {"drawgetimagesize",PF_Fixme, 0, 0, 0, 318, D("#define draw_getimagesize drawgetimagesize\nvector(string picname)", "Returns the dimensions of the named image. Images specified with .lmp should give the original .lmp's dimensions even if texture replacements use a different resolution.")},// (EXT_CSQC) {"freepic", PF_Fixme, 0, 0, 0, 319, D("void(string name)", "Tells the engine that the image is no longer needed. The image will appear to be new the next time its needed.")},// (EXT_CSQC) //320 - {"drawcharacter", PF_Fixme, 0, 0, 0, 320, D("float(vector position, float character, vector size, vector rgb, float alpha, optional float flag)", "Draw the given quake character at the given position.\nIf flag&4, the function will consider the char to be a unicode char instead (or display as a ? if outside the 32-127 range).\nsize should normally be something like '8 8 0'.\nrgb should normally be '1 1 1'\nalpha normally 1.\nSoftware engines may assume the named defaults.\nNote that ALL text may be rescaled on the X axis due to variable width fonts. The X axis may even be ignored completely.")},// (EXT_CSQC, [EXT_CSQC_???]) - {"drawrawstring", PF_Fixme, 0, 0, 0, 321, D("float(vector position, string text, vector size, vector rgb, float alpha, optional float flag)", "Draws the specified string without using any markup at all, even in engines that support it.\nIf UTF-8 is globally enabled in the engine, then that encoding is used (without additional markup), otherwise it is raw quake chars.\nSoftware engines may assume a size of '8 8 0', rgb='1 1 1', alpha=1, flag&3=0, but it is not an error to draw out of the screen.")},// (EXT_CSQC, [EXT_CSQC_???]) - {"drawpic", PF_Fixme, 0, 0, 0, 322, D("float(vector position, string pic, vector size, vector rgb, float alpha, optional float flag)", "Draws an shader within the given 2d screen box. Software engines may omit support for rgb+alpha, but must support rescaling, and must clip to the screen without crashing.")},// (EXT_CSQC, [EXT_CSQC_???]) - {"drawfill", PF_Fixme, 0, 0, 0, 323, D("float(vector position, vector size, vector rgb, float alpha, optional float flags)", "Draws a solid block over the given 2d box, with given colour, alpha, and blend mode (specified via flags).\nflags&3=0 simple blend.\nflags&3=1 additive blend")},// (EXT_CSQC, [EXT_CSQC_???]) + {"drawcharacter", PF_Fixme, 0, 0, 0, 320, D("float(vector position, float character, vector size, vector rgb, float alpha, optional float drawflag)", "Draw the given quake character at the given position.\nIf flag&4, the function will consider the char to be a unicode char instead (or display as a ? if outside the 32-127 range).\nsize should normally be something like '8 8 0'.\nrgb should normally be '1 1 1'\nalpha normally 1.\nSoftware engines may assume the named defaults.\nNote that ALL text may be rescaled on the X axis due to variable width fonts. The X axis may even be ignored completely.")},// (EXT_CSQC, [EXT_CSQC_???]) + {"drawrawstring", PF_Fixme, 0, 0, 0, 321, D("float(vector position, string text, vector size, vector rgb, float alpha, optional float drawflag)", "Draws the specified string without using any markup at all, even in engines that support it.\nIf UTF-8 is globally enabled in the engine, then that encoding is used (without additional markup), otherwise it is raw quake chars.\nSoftware engines may assume a size of '8 8 0', rgb='1 1 1', alpha=1, flag&3=0, but it is not an error to draw out of the screen.")},// (EXT_CSQC, [EXT_CSQC_???]) + {"drawpic", PF_Fixme, 0, 0, 0, 322, D("float(vector position, string pic, vector size, vector rgb, float alpha, optional float drawflag)", "Draws an shader within the given 2d screen box. Software engines may omit support for rgb+alpha, but must support rescaling, and must clip to the screen without crashing.")},// (EXT_CSQC, [EXT_CSQC_???]) + {"drawfill", PF_Fixme, 0, 0, 0, 323, D("float(vector position, vector size, vector rgb, float alpha, optional float drawflag)", "Draws a solid block over the given 2d box, with given colour, alpha, and blend mode (specified via flags).\nflags&3=0 simple blend.\nflags&3=1 additive blend")},// (EXT_CSQC, [EXT_CSQC_???]) {"drawsetcliparea", PF_Fixme, 0, 0, 0, 324, D("void(float x, float y, float width, float height)", "Specifies a 2d clipping region (aka: scissor test). 2d draw calls will all be clipped to this 2d box, the area outside will not be modified by any 2d draw call (even 2d polygons).")},// (EXT_CSQC_???) {"drawresetcliparea",PF_Fixme, 0, 0, 0, 325, D("void(void)", "Reverts the scissor/clip area to the whole screen.")},// (EXT_CSQC_???) - {"drawstring", PF_Fixme, 0, 0, 0, 326, D("float(vector position, string text, vector size, vector rgb, float alpha, float flag)", "Draws a string, interpreting markup and recolouring as appropriate.")},// #326 + {"drawstring", PF_Fixme, 0, 0, 0, 326, D("float(vector position, string text, vector size, vector rgb, float alpha, float drawflag)", "Draws a string, interpreting markup and recolouring as appropriate.")},// #326 {"stringwidth", PF_Fixme, 0, 0, 0, 327, D("float(string text, float usecolours, optional vector fontsize)", "Calculates the width of the screen in virtual pixels. If usecolours is 1, markup that does not affect the string width will be ignored. Will always be decoded as UTF-8 if UTF-8 is globally enabled.\nIf the char size is not specified, '8 8 0' will be assumed.")},// EXT_CSQC_'DARKPLACES' - {"drawsubpic", PF_Fixme, 0, 0, 0, 328, D("void(vector pos, vector sz, string pic, vector srcpos, vector srcsz, vector rgb, float alpha, float flag)", "Draws a rescaled subsection of an image to the screen.")},// #328 EXT_CSQC_'DARKPLACES' + {"drawsubpic", PF_Fixme, 0, 0, 0, 328, D("void(vector pos, vector sz, string pic, vector srcpos, vector srcsz, vector rgb, float alpha, optional float drawflag)", "Draws a rescaled subsection of an image to the screen.")},// #328 EXT_CSQC_'DARKPLACES' //330 {"getstati", PF_Fixme, 0, 0, 0, 330, D("float(float stnum)", "Retrieves the numerical value of the given EV_INTEGER or EV_ENTITY stat (converted to a float).")},// (EXT_CSQC) @@ -9506,6 +9507,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"keynumtostring", PF_Fixme, 0, 0, 0, 340, D("string(float keynum)", "Returns a hunam-readable name for the given keycode, as a tempstring.")},// (EXT_CSQC) + {"keynumtostring_csqc", PF_Fixme, 0, 0, 0, 340, D("string(float keynum)", "Returns a hunam-readable name for the given keycode, as a tempstring.")},// (found in menuqc) {"stringtokeynum", PF_Fixme, 0, 0, 0, 341, D("float(string keyname)", "Looks up the key name in the same way that the bind command would, returning the keycode for that key.")},// (EXT_CSQC) {"getkeybind", PF_Fixme, 0, 0, 0, 342, D("string(float keynum)", "Finds the current binding for the given key (ignores modifiers like shift/alt/ctrl).")},// (EXT_CSQC) @@ -9628,7 +9630,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"buf_del", PF_Fixme, 0, 0, 0, 441, "void(float bufhandle)"},//DP_QC_STRINGBUFFERS {"buf_getsize", PF_Fixme, 0, 0, 0, 442, "float(float bufhandle)"},//DP_QC_STRINGBUFFERS {"buf_copy", PF_Fixme, 0, 0, 0, 443, "void(float bufhandle_from, float bufhandle_to)"},//DP_QC_STRINGBUFFERS - {"buf_sort", PF_Fixme, 0, 0, 0, 444, "void(float bufhandle, float sortpower, float backward)"},//DP_QC_STRINGBUFFERS + {"buf_sort", PF_Fixme, 0, 0, 0, 444, "void(float bufhandle, float sortprefixlen, float backward)"},//DP_QC_STRINGBUFFERS {"buf_implode", PF_Fixme, 0, 0, 0, 445, "string(float bufhandle, string glue)"},//DP_QC_STRINGBUFFERS {"bufstr_get", PF_Fixme, 0, 0, 0, 446, "string(float bufhandle, float string_index)"},//DP_QC_STRINGBUFFERS {"bufstr_set", PF_Fixme, 0, 0, 0, 447, "void(float bufhandle, float string_index, string str)"},//DP_QC_STRINGBUFFERS @@ -9680,7 +9682,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"buf_del", PF_buf_del, 0, 0, 0, 461, "void(float bufhandle)"},//DP_QC_STRINGBUFFERS {"buf_getsize", PF_buf_getsize, 0, 0, 0, 462, "float(float bufhandle)"},//DP_QC_STRINGBUFFERS {"buf_copy", PF_buf_copy, 0, 0, 0, 463, "void(float bufhandle_from, float bufhandle_to)"},//DP_QC_STRINGBUFFERS - {"buf_sort", PF_buf_sort, 0, 0, 0, 464, "void(float bufhandle, float sortpower, float backward)"},//DP_QC_STRINGBUFFERS + {"buf_sort", PF_buf_sort, 0, 0, 0, 464, "void(float bufhandle, float sortprefixlen, float backward)"},//DP_QC_STRINGBUFFERS {"buf_implode", PF_buf_implode, 0, 0, 0, 465, "string(float bufhandle, string glue)"},//DP_QC_STRINGBUFFERS {"bufstr_get", PF_bufstr_get, 0, 0, 0, 466, "string(float bufhandle, float string_index)"},//DP_QC_STRINGBUFFERS {"bufstr_set", PF_bufstr_set, 0, 0, 0, 467, "void(float bufhandle, float string_index, string str)"},//DP_QC_STRINGBUFFERS @@ -9733,14 +9735,14 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"uri_escape", PF_uri_escape, 0, 0, 0, 510, "string(string in)"},//DP_QC_URI_ESCAPE {"uri_unescape", PF_uri_unescape, 0, 0, 0, 511, "string(string in)"},//DP_QC_URI_ESCAPE {"num_for_edict", PF_num_for_edict, 0, 0, 0, 512, "float(entity ent)"},//DP_QC_NUM_FOR_EDICT - {"uri_get", PF_uri_get, 0, 0, 0, 513, "float(string uril, float id)" STUB},//DP_QC_URI_GET + {"uri_get", PF_uri_get, 0, 0, 0, 513, "float(string uril, float id)"},//DP_QC_URI_GET {"tokenize_console",PF_tokenize_console,0, 0, 0, 514, "float(string str)"}, {"argv_start_index",PF_argv_start_index,0, 0, 0, 515, "float(float idx)"}, {"argv_end_index", PF_argv_end_index, 0, 0, 0, 516, "float(float idx)"}, - {"buf_cvarlist", PF_buf_cvarlist, 0, 0, 0, 517, "void(float strbuf)" STUB}, + {"buf_cvarlist", PF_buf_cvarlist, 0, 0, 0, 517, "void(float strbuf)"}, {"cvar_description",PF_cvar_description,0, 0, 0, 518, "string(string cvarname)"}, {"gettime", PF_Fixme, 0, 0, 0, 519, "float(optional float timetype)"}, - {"keynumtostring", PF_Fixme, 0, 0, 0, 520, "string(float keynum)"}, + {"keynumtostring_omgwtf",PF_Fixme, 0, 0, 0, 520, "string(float keynum)"}, //excessive third version in dp's csqc. {"findkeysforcommand",PF_Fixme, 0, 0, 0, 521, "string(string command, optional float bindmap)"}, // {"initparticlespawner",PF_Fixme, 0, 0, 0, 522, "void(float max_themes)"}, // {"resetparticle", PF_Fixme, 0, 0, 0, 523, "void()"}, @@ -9760,8 +9762,8 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs // {"log", PF_Fixme, 0, 0, 0, 532, "float(string mname)", true}, // {"getsoundtime", VM_getsoundtime, 0, 0, 0, 533, "float(entity e, float channel)" STUB}, {"soundlength", PF_Ignore, 0, 0, 0, 534, "float(string sample)" STUB}, -// {"buf_loadfile", PF_Fixme, 0, 0, 0, 535, "float(string filename, float bufhandle)"}, -// {"buf_writefile", PF_Fixme, 0, 0, 0, 536, "float(float filehandle, float bufhandle, float startpos, float numstrings)"}, + {"buf_loadfile", PF_buf_loadfile, 0, 0, 0, 535, "float(string filename, float bufhandle)"}, + {"buf_writefile", PF_buf_writefile, 0, 0, 0, 536, "float(float filehandle, float bufhandle, optional float startpos, optional float numstrings)"}, // {"bufstr_find", PF_Fixme, 0, 0, 0, 537, "float(float bufhandle, string match, float matchrule, float startpos)"}, // {"matchpattern", PF_Fixme, 0, 0, 0, 538, "float(string s, string pattern, float matchrule)"}, // {"undefined", PF_Fixme, 0, 0, 0, 539, ""}, @@ -9777,7 +9779,9 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"writetofile", PF_writetofile, 0, 0, 0, 606, "void(float fh, entity e)"}, {"isfunction", PF_isfunction, 0, 0, 0, 607, "float(string s)"}, {"getresolution", PF_Fixme, 0, 0, 0, 608, "vector(float vidmode, optional float forfullscreen)"}, - {"keynumtostring", PF_Fixme, 0, 0, 0, 609, "string(float keynum)"}, + {"keynumtostring_menu",PF_Fixme, 0, 0, 0, 609, "string(float keynum)"}, //third copy of this builtin in dp's csqc. + {"findkeysforcommand_dp",PF_Fixme, 0, 0, 0, 610, "string(string command, optional float bindmap)"}, + {"keynumtostring", PF_Fixme, 0, 0, 0, 609, "string(float keynum)"}, //normal name is for menuqc standard. {"findkeysforcommand",PF_Fixme, 0, 0, 0, 610, "string(string command, optional float bindmap)"}, {"gethostcachevalue",PF_Fixme, 0, 0, 0, 611, "float(float type)"}, {"gethostcachestring",PF_Fixme, 0, 0, 0, 612, "string(float type, float hostnr)"}, @@ -10141,6 +10145,9 @@ typedef struct char *type; unsigned int module; + + char *desc; + int value; char *valuestr; qboolean misc; @@ -10165,20 +10172,20 @@ void PR_DumpPlatform_f(void) /*this list is here to ensure that the file can be used as a valid initial qc file (ignoring precompiler options)*/ knowndef_t knowndefs[] = { - {"self", "entity", QW|NQ|CS|MENU}, - {"other", "entity", QW|NQ|CS}, - {"world", "entity", QW|NQ|CS}, - {"time", "float", QW|NQ|CS}, - {"cltime", "float", CS}, - {"frametime", "float", QW|NQ|CS}, - {"player_localentnum", "float", CS}, - {"player_localnum", "float", CS}, - {"maxclients", "float", CS}, - {"clientcommandframe", "float", CS}, - {"servercommandframe", "float", CS}, - {"newmis", "entity", QW}, - {"force_retouch", "float", QW|NQ}, - {"mapname", "string", QW|NQ|CS}, + {"self", "entity", QW|NQ|CS|MENU, "The magic me"}, + {"other", "entity", QW|NQ|CS, "Valid in touch functions, this is the entity that we touched."}, + {"world", "entity", QW|NQ|CS, "The null entity. Hurrah. Readonly after map spawn time."}, + {"time", "float", QW|NQ|CS, "The current game time. Stops when paused."}, + {"cltime", "float", CS, "A local timer that ticks relative to local time regardless of latency, packetloss, or pause."}, + {"frametime", "float", QW|NQ|CS, "The time since the last physics/render/input frame."}, + {"player_localentnum", "float", CS, "This is entity number the player is seeing from/spectating, or the player themself, can change mid-map."}, + {"player_localnum", "float", CS, "The 0-based player index, valid for getplayerkeyvalue calls."}, + {"maxclients", "float", CS, "Maximum number of player slots on the server."}, + {"clientcommandframe", "float", CS, "This is the input-frame sequence. frames < clientcommandframe have been sent to the server. frame==clientcommandframe is still being generated and can still change."}, + {"servercommandframe", "float", CS, "This is the input-frame that was last acknowledged by the server. Input frames greater than this should be applied to the player's entity."}, + {"newmis", "entity", QW, "A named entity that should be run soon, to reduce the effects of latency."}, + {"force_retouch", "float", QW|NQ, "If positive, causes all entities to check for triggers."}, + {"mapname", "string", QW|NQ|CS, "The short name of the map."}, {"deathmatch", "float", NQ}, {"coop", "float", NQ}, {"teamplay", "float", NQ}, @@ -10218,9 +10225,9 @@ void PR_DumpPlatform_f(void) {"modelindex", ".float", QW|NQ|CS}, {"absmin, absmax", ".vector", QW|NQ|CS}, {"ltime", ".float", QW|NQ}, - {"entnum", ".float", CS}, - {"drawmask", ".float", CS}, - {"predraw", ".void()", CS}, + {"entnum", ".float", CS, "The entity number as its known on the server."}, + {"drawmask", ".float", CS, "Acts as a filter in the addentities call."}, + {"predraw", ".__variant()", CS, "Called by addentities after the filter and before the entity is actually drawn. Do your interpolation and animation in here. Return true to inhibit addition of the entity (this is defined as variant for compat with legacy code, your code should use .float instead)."}, {"lastruntime", ".float", QW}, {"movetype", ".float", QW|NQ|CS}, {"solid", ".float", QW|NQ|CS}, @@ -10235,10 +10242,10 @@ void PR_DumpPlatform_f(void) {"renderflags", ".float", CS}, {"model", ".string", QW|NQ|CS}, {"frame", ".float", QW|NQ|CS}, - {"frame1time", ".float", CS}, + {"frame1time", ".float", CS, "The absolute time into the animation/framegroup specified by .frame."}, {"frame2", ".float", CS}, - {"frame2time", ".float", CS}, - {"lerpfrac", ".float", CS}, + {"frame2time", ".float", CS, "The absolute time into the animation/framegroup specified by .frame2."}, + {"lerpfrac", ".float", CS, "If 0, use frame1 only. If 1, use frame2 only. Mix them together for values between."}, {"skin", ".float", QW|NQ|CS}, {"effects", ".float", QW|NQ|CS}, {"mins, maxs", ".vector", QW|NQ|CS}, @@ -10327,20 +10334,20 @@ void PR_DumpPlatform_f(void) #undef comfieldstring #undef comfieldfunction - {"URI_Get_Callback", "noref void(float reqid, float responsecode, string resourcebody)", QW|NQ|CS|MENU}, - {"SpectatorConnect", "noref void()", QW|NQ}, - {"SpectatorDisconnect", "noref void()", QW|NQ}, - {"SpectatorThink", "noref void()", QW|NQ}, - {"SV_ParseClientCommand", "noref void(string cmd)", QW|NQ}, - {"SV_ParseConnectionlessPacket", "noref void()", QW|NQ}, - {"SV_PausedTic", "noref void()", QW|NQ}, - {"SV_ShouldPause", "noref void()", QW|NQ}, - {"ClassChangeWeapon", "noref void()", H2}, - {"SV_RunClientCommand", "noref void()", QW|NQ}, - {"SV_AddDebugPolygons", "noref void()", QW|NQ}, - {"SV_PlayerPhysics", "noref void()", QW|NQ}, - {"EndFrame", "noref void()", QW|NQ}, - {"SV_CheckRejectConnection","noref string(string addr, string uinfo, string features) ", QW|NQ}, + {"URI_Get_Callback", "noref void(float reqid, float responsecode, string resourcebody)", QW|NQ|CS|MENU, "Called as an eventual result of the uri_get builtin."}, + {"SpectatorConnect", "noref void()", QW|NQ, "Called when a spectator joins the game."}, + {"SpectatorDisconnect", "noref void()", QW|NQ, "Called when a spectator disconnects from the game."}, + {"SpectatorThink", "noref void()", QW|NQ, "Called each frame for each spectator."}, + {"SV_ParseClientCommand", "noref void(string cmd)", QW|NQ, "Provides QC with a way to intercept 'cmd foo' commands from the client. Very handy. Self will be set to the sending client, while the 'cmd' argument can be tokenize()d and each element retrieved via argv(argno). Unrecognised cmds MUST be passed on to the clientcommand builtin."}, + {"SV_ParseConnectionlessPacket", "noref void(string sender, string body)", QW|NQ, "Provides QC with a way to communicate between servers, or with client server browsers. Sender is the sender's ip. Body is the body of the message. You'll need to add your own password/etc support as required. Self is not valid."}, + {"SV_PausedTic", "noref void(float pauseduration)", QW|NQ, "For each frame that the server is paused, this function will be called to give the gamecode a chance to unpause the server again. the pauseduration argument says how long the server has been paused for (the time global is frozen and will not increment while paused). Self is not valid."}, + {"SV_ShouldPause", "noref float(float newstatus)", QW|NQ, "Called to give the qc a change to block pause/unpause requests. Return false for the pause request to be ignored. newstatus is 1 if the user is trying to pause the game. For the duration of the call, self will be set to the player who tried to pause, or to world if it was triggered by a server-side event."}, + {"ClassChangeWeapon", "noref void()", H2, "Hexen2 support. Called when cl_playerclass changes. Self is set to the player who is changing class."}, + {"SV_RunClientCommand", "noref void()", QW|NQ, "Called each time a player movement packet was received from a client. Self is set to the player entity which should be updated, while the input_* globals specify the various properties stored within the input packet. The contents of this function should be somewaht identical to the equivelent function in CSQC, or prediction misses will occur. If you're feeling lazy, you can simply call 'runstandardplayerphysics' after modifying the inputs."}, + {"SV_AddDebugPolygons", "noref void()", QW|NQ, "Called each video frame. This is the only place where ssqc is allowed to call the R_BeginPolygon/R_PolygonVertex/R_EndPolygon builtins. This is exclusively for debugging, and will break in anything but single player as it will not be called if the engine is not running both a client and a server."}, + {"SV_PlayerPhysics", "noref void()", QW|NQ, "Legacy method to tweak player input that does not reliably work with prediction (prediction WILL break). Mods that care about prediction should use SV_RunClientCommand instead. If pr_no_playerphysics is set to 1, this function will never be called, which will either fix prediction or completely break player movement depending on whether the feature was even useful."}, + {"EndFrame", "noref void()", QW|NQ, "Called after non-player entities have been run at the end of the physics frame. Player physics is performed out of order and can/will still occur between EndFrame and BeginFrame."}, + {"SV_CheckRejectConnection","noref string(string addr, string uinfo, string features) ", QW|NQ, "Called to give the mod a chance to ignore connection requests based upon client protocol support or other properties. Use infoget to read the uinfo and features arguments."}, /* //mvdsv compat {"UserInfo_Changed", "//noref void()", QW}, {"localinfoChanged", "//noref void()", QW}, @@ -10349,23 +10356,23 @@ void PR_DumpPlatform_f(void) {"ConsoleCmd", "//noref void()", QW}, */ - {"CSQC_Init", "noref void(float apilevel, string enginename, float engineversion)", CS}, - {"CSQC_WorldLoaded", "noref void()", CS}, - {"CSQC_Shutdown", "noref void()", CS}, - {"CSQC_UpdateView", "noref void(float vwidth, float vheight, float notmenu)", CS}, - {"CSQC_Parse_StuffCmd", "noref void(string msg)", CS}, - {"CSQC_Parse_CenterPrint", "noref float(string msg)", CS}, - {"CSQC_Parse_Print", "noref void(string printmsg, float printlvl)", CS}, - {"CSQC_InputEvent", "noref float(float evtype, float scanx, float chary, float devid)", CS}, - {"CSQC_Input_Frame", "noref void()", CS}, - {"CSQC_ConsoleCommand", "noref float(string cmd)", CS}, - {"CSQC_ConsoleLink", "noref float(string text, string info)", CS}, - {"CSQC_Ent_Update", "noref void()", CS}, + {"CSQC_Init", "noref void(float apilevel, string enginename, float engineversion)", CS, "Called at startup. enginename and engineversion are arbitary hints and can take any form. enginename should be consistant between revisions, but this cannot truely be relied upon."}, + {"CSQC_WorldLoaded", "noref void()", CS, "Called after model+sound precaches have been executed. Gives a chance for the qc to read the entity lump from the bsp."}, + {"CSQC_Shutdown", "noref void()", CS, "Specifies that the csqc is going down. Save your persistant settings here."}, + {"CSQC_UpdateView", "noref void(float vwidth, float vheight, float notmenu)", CS, "Called every single video frame. The CSQC is responsible for rendering the entire screen."}, + {"CSQC_Parse_StuffCmd", "noref void(string msg)", CS, "Gives the CSQC a chance to intercept stuffcmds. Use the tokenize builtin to parse the message. Unrecognised commands would normally be localcmded, but its probably better to drop unrecognised stuffcmds completely."}, + {"CSQC_Parse_CenterPrint", "noref float(string msg)", CS, "Gives the CSQC a chance to intercept centerprints. Return true if you wish the engine to otherwise ignore the centerprint."}, + {"CSQC_Parse_Print", "noref void(string printmsg, float printlvl)", CS, "Gives the CSQC a chance to intercept sprint/bprint builtin calls. CSQC should filter by the client's current msg setting and then pass the message on to the print command, or handle them itself."}, + {"CSQC_InputEvent", "noref float(float evtype, float scanx, float chary, float devid)", CS, "Called whenever a key is pressed, the mouse is moved, etc. evtype will be one of the IE_* constants. The other arguments vary depending on the evtype. Key presses are not guarenteed to have both scan and unichar values set at the same time."}, + {"CSQC_Input_Frame", "noref void()", CS, "Called just before each time clientcommandframe is updated. You can edit the input_* globals in order to apply your own player inputs within csqc, which may allow you a convienient way to pass certain info to ssqc."}, + {"CSQC_ConsoleCommand", "noref float(string cmd)", CS, "Called if the user uses any console command registed via registercommand."}, + {"CSQC_ConsoleLink", "noref float(string text, string info)", CS, "Called if the user clicks a ^[text\\infokey\\infovalue^] link. Use infoget to read/check each supported key. Return true if you wish the engine to not attempt to handle the link itself."}, + {"CSQC_Ent_Update", "noref void(float isnew)", CS}, {"CSQC_Ent_Remove", "noref void()", CS}, {"CSQC_Event_Sound", "noref float(float entnum, float channel, float soundname, float vol, float attenuation, vector pos, float pitchmod)", CS}, // {"CSQC_ServerSound", "//void()", CS}, - {"CSQC_LoadResource", "noref float(string resname, string restype)", CS}, - {"CSQC_Parse_TempEntity", "noref void()", CS}, + {"CSQC_LoadResource", "noref float(string resname, string restype)", CS, "Called each time some resource is being loaded. CSQC can invoke various draw calls to provide a loading screen, until WorldLoaded is called."}, + {"CSQC_Parse_TempEntity", "noref float()", CS, "Please don't use this."}, {"GameCommand", "noref void(string cmdtext)", CS|MENU}, @@ -10376,311 +10383,322 @@ void PR_DumpPlatform_f(void) {"m_keyup", "void(float scan, float chr)", MENU}, {"m_toggle", "void(float mode)", MENU}, - {"physics_mode", "var float", QW|NQ|CS, 2}, - {"gamespeed", "float", CS}, - {"drawfontscale", "var vector", CS|MENU, 0, "'1 1 0'"}, - {"drawfont", "float", CS|MENU}, - {"FONT_DEFAULT", "const float", CS|MENU, 0}, + {"physics_mode", "var float", QW|NQ|CS, "0: original csqc - physics are not run\n1: DP-compat. Thinks occur, but not true movetypes.\n2: movetypes occur just as they do in ssqc.", 2}, + {"gamespeed", "float", CS, "Set by the engine, this is the value of the sv_gamespeed cvar"}, + {"numclientseats", "float", CS, "This is the number of splitscreen clients currently running on this client."}, + {"drawfontscale", "var vector", CS|MENU, "Specifies a scaler for all text rendering. There are other ways to implement this.", 0, "'1 1 0'"}, + {"drawfont", "float", CS|MENU, "Allows you to choose exactly which font is to be used to draw text. Fonts can be registered/allocated with the loadfont builtin."}, + {"FONT_DEFAULT", "const float", CS|MENU, NULL, 0}, - {"TRUE", "const float", ALL, 1}, - {"FALSE", "const float", ALL, 0}, + {"TRUE", "const float", ALL, NULL, 1}, + {"FALSE", "const float", ALL, NULL, 0}, - {"MOVETYPE_NONE", "const float", QW|NQ|CS, MOVETYPE_NONE}, - {"MOVETYPE_WALK", "const float", QW|NQ|CS, MOVETYPE_WALK}, - {"MOVETYPE_STEP", "const float", QW|NQ|CS, MOVETYPE_STEP}, - {"MOVETYPE_FLY", "const float", QW|NQ|CS, MOVETYPE_FLY}, - {"MOVETYPE_TOSS", "const float", QW|NQ|CS, MOVETYPE_TOSS}, - {"MOVETYPE_PUSH", "const float", QW|NQ|CS, MOVETYPE_PUSH}, - {"MOVETYPE_NOCLIP", "const float", QW|NQ|CS, MOVETYPE_NOCLIP}, - {"MOVETYPE_FLYMISSILE", "const float", QW|NQ|CS, MOVETYPE_FLYMISSILE}, - {"MOVETYPE_BOUNCE", "const float", QW|NQ|CS, MOVETYPE_BOUNCE}, - {"MOVETYPE_BOUNCEMISSILE", "const float", QW|NQ|CS, MOVETYPE_BOUNCEMISSILE}, - {"MOVETYPE_FOLLOW", "const float", QW|NQ|CS, MOVETYPE_FOLLOW}, - {"MOVETYPE_WALLWALK", "const float", QW|NQ|CS, MOVETYPE_WALLWALK}, - {"MOVETYPE_PHYSICS", "const float", QW|NQ|CS, MOVETYPE_PHYSICS}, + {"MOVETYPE_NONE", "const float", QW|NQ|CS, NULL, MOVETYPE_NONE}, + {"MOVETYPE_WALK", "const float", QW|NQ|CS, NULL, MOVETYPE_WALK}, + {"MOVETYPE_STEP", "const float", QW|NQ|CS, NULL, MOVETYPE_STEP}, + {"MOVETYPE_FLY", "const float", QW|NQ|CS, NULL, MOVETYPE_FLY}, + {"MOVETYPE_TOSS", "const float", QW|NQ|CS, NULL, MOVETYPE_TOSS}, + {"MOVETYPE_PUSH", "const float", QW|NQ|CS, NULL, MOVETYPE_PUSH}, + {"MOVETYPE_NOCLIP", "const float", QW|NQ|CS, NULL, MOVETYPE_NOCLIP}, + {"MOVETYPE_FLYMISSILE", "const float", QW|NQ|CS, NULL, MOVETYPE_FLYMISSILE}, + {"MOVETYPE_BOUNCE", "const float", QW|NQ|CS, NULL, MOVETYPE_BOUNCE}, + {"MOVETYPE_BOUNCEMISSILE", "const float", QW|NQ|CS, NULL, MOVETYPE_BOUNCEMISSILE}, + {"MOVETYPE_FOLLOW", "const float", QW|NQ|CS, NULL, MOVETYPE_FOLLOW}, + {"MOVETYPE_WALLWALK", "const float", QW|NQ|CS, NULL, MOVETYPE_WALLWALK}, + {"MOVETYPE_PHYSICS", "const float", QW|NQ|CS, NULL, MOVETYPE_PHYSICS}, - {"SOLID_NOT", "const float", QW|NQ|CS, SOLID_NOT}, - {"SOLID_TRIGGER", "const float", QW|NQ|CS, SOLID_TRIGGER}, - {"SOLID_BBOX", "const float", QW|NQ|CS, SOLID_BBOX}, - {"SOLID_SLIDEBOX", "const float", QW|NQ|CS, SOLID_SLIDEBOX}, - {"SOLID_BSP", "const float", QW|NQ|CS, SOLID_BSP}, - {"SOLID_CORPSE", "const float", QW|NQ|CS, SOLID_CORPSE}, - {"SOLID_LADDER", "const float", QW|NQ|CS, SOLID_LADDER}, - {"SOLID_PHYSICS_BOX", "const float", QW|NQ|CS, SOLID_PHYSICS_BOX}, - {"SOLID_PHYSICS_SPHERE", "const float", QW|NQ|CS, SOLID_PHYSICS_SPHERE}, - {"SOLID_PHYSICS_CAPSULE", "const float", QW|NQ|CS, SOLID_PHYSICS_CAPSULE}, + {"SOLID_NOT", "const float", QW|NQ|CS, NULL, SOLID_NOT}, + {"SOLID_TRIGGER", "const float", QW|NQ|CS, NULL, SOLID_TRIGGER}, + {"SOLID_BBOX", "const float", QW|NQ|CS, NULL, SOLID_BBOX}, + {"SOLID_SLIDEBOX", "const float", QW|NQ|CS, NULL, SOLID_SLIDEBOX}, + {"SOLID_BSP", "const float", QW|NQ|CS, NULL, SOLID_BSP}, + {"SOLID_CORPSE", "const float", QW|NQ|CS, NULL, SOLID_CORPSE}, + {"SOLID_LADDER", "const float", QW|NQ|CS, NULL, SOLID_LADDER}, + {"SOLID_PHYSICS_BOX", "const float", QW|NQ|CS, NULL, SOLID_PHYSICS_BOX}, + {"SOLID_PHYSICS_SPHERE", "const float", QW|NQ|CS, NULL, SOLID_PHYSICS_SPHERE}, + {"SOLID_PHYSICS_CAPSULE", "const float", QW|NQ|CS, NULL, SOLID_PHYSICS_CAPSULE}, - {"JOINTTYPE_FIXED", "const float", QW|NQ|CS, JOINTTYPE_FIXED}, - {"JOINTTYPE_POINT", "const float", QW|NQ|CS, JOINTTYPE_POINT}, - {"JOINTTYPE_HINGE", "const float", QW|NQ|CS, JOINTTYPE_HINGE}, - {"JOINTTYPE_SLIDER", "const float", QW|NQ|CS, JOINTTYPE_SLIDER}, - {"JOINTTYPE_UNIVERSAL", "const float", QW|NQ|CS, JOINTTYPE_UNIVERSAL}, - {"JOINTTYPE_HINGE2", "const float", QW|NQ|CS, JOINTTYPE_HINGE2}, + {"JOINTTYPE_FIXED", "const float", QW|NQ|CS, NULL, JOINTTYPE_FIXED}, + {"JOINTTYPE_POINT", "const float", QW|NQ|CS, NULL, JOINTTYPE_POINT}, + {"JOINTTYPE_HINGE", "const float", QW|NQ|CS, NULL, JOINTTYPE_HINGE}, + {"JOINTTYPE_SLIDER", "const float", QW|NQ|CS, NULL, JOINTTYPE_SLIDER}, + {"JOINTTYPE_UNIVERSAL", "const float", QW|NQ|CS, NULL, JOINTTYPE_UNIVERSAL}, + {"JOINTTYPE_HINGE2", "const float", QW|NQ|CS, NULL, JOINTTYPE_HINGE2}, - {"DAMAGE_NO", "const float", QW|NQ, DAMAGE_NO}, - {"DAMAGE_YES", "const float", QW|NQ, DAMAGE_YES}, - {"DAMAGE_AIM", "const float", QW|NQ, DAMAGE_AIM}, + {"DAMAGE_NO", "const float", QW|NQ, NULL, DAMAGE_NO}, + {"DAMAGE_YES", "const float", QW|NQ, NULL, DAMAGE_YES}, + {"DAMAGE_AIM", "const float", QW|NQ, NULL, DAMAGE_AIM}, - {"CONTENT_EMPTY", "const float", QW|NQ|CS, Q1CONTENTS_EMPTY}, - {"CONTENT_SOLID", "const float", QW|NQ|CS, Q1CONTENTS_SOLID}, - {"CONTENT_WATER", "const float", QW|NQ|CS, Q1CONTENTS_WATER}, - {"CONTENT_SLIME", "const float", QW|NQ|CS, Q1CONTENTS_SLIME}, - {"CONTENT_LAVA", "const float", QW|NQ|CS, Q1CONTENTS_LAVA}, - {"CONTENT_SKY", "const float", QW|NQ|CS, Q1CONTENTS_SKY}, + {"CONTENT_EMPTY", "const float", QW|NQ|CS, NULL, Q1CONTENTS_EMPTY}, + {"CONTENT_SOLID", "const float", QW|NQ|CS, NULL, Q1CONTENTS_SOLID}, + {"CONTENT_WATER", "const float", QW|NQ|CS, NULL, Q1CONTENTS_WATER}, + {"CONTENT_SLIME", "const float", QW|NQ|CS, NULL, Q1CONTENTS_SLIME}, + {"CONTENT_LAVA", "const float", QW|NQ|CS, NULL, Q1CONTENTS_LAVA}, + {"CONTENT_SKY", "const float", QW|NQ|CS, NULL, Q1CONTENTS_SKY}, - {"CHAN_AUTO", "const float", QW|NQ|CS, CHAN_AUTO}, - {"CHAN_WEAPON", "const float", QW|NQ|CS, CHAN_WEAPON}, - {"CHAN_VOICE", "const float", QW|NQ|CS, CHAN_VOICE}, - {"CHAN_ITEM", "const float", QW|NQ|CS, CHAN_ITEM}, - {"CHAN_BODY", "const float", QW|NQ|CS, CHAN_BODY}, + {"CHAN_AUTO", "const float", QW|NQ|CS, NULL, CHAN_AUTO}, + {"CHAN_WEAPON", "const float", QW|NQ|CS, NULL, CHAN_WEAPON}, + {"CHAN_VOICE", "const float", QW|NQ|CS, NULL, CHAN_VOICE}, + {"CHAN_ITEM", "const float", QW|NQ|CS, NULL, CHAN_ITEM}, + {"CHAN_BODY", "const float", QW|NQ|CS, NULL, CHAN_BODY}, - {"ATTN_NONE", "const float", QW|NQ|CS, ATTN_NONE}, - {"ATTN_NORM", "const float", QW|NQ|CS, ATTN_NORM}, - {"ATTN_IDLE", "const float", QW|NQ|CS, 2}, //including these for completeness, despite them being defined by the gamecode rather than the engine api. - {"ATTN_STATIC", "const float", QW|NQ|CS, 3}, + {"ATTN_NONE", "const float", QW|NQ|CS, NULL, ATTN_NONE}, + {"ATTN_NORM", "const float", QW|NQ|CS, NULL, ATTN_NORM}, + {"ATTN_IDLE", "const float", QW|NQ|CS, NULL, 2}, //including these for completeness, despite them being defined by the gamecode rather than the engine api. + {"ATTN_STATIC", "const float", QW|NQ|CS, NULL, 3}, - {"MSG_BROADCAST", "const float", QW|NQ, MSG_BROADCAST}, - {"MSG_ONE", "const float", QW|NQ, MSG_ONE}, - {"MSG_ALL", "const float", QW|NQ, MSG_ALL}, - {"MSG_INIT", "const float", QW|NQ, MSG_INIT}, - {"MSG_MULTICAST", "const float", QW|NQ, MSG_MULTICAST}, - {"MSG_ENTITY", "const float", QW|NQ, MSG_CSQC}, + {"MSG_BROADCAST", "const float", QW|NQ, NULL, MSG_BROADCAST}, + {"MSG_ONE", "const float", QW|NQ, NULL, MSG_ONE}, + {"MSG_ALL", "const float", QW|NQ, NULL, MSG_ALL}, + {"MSG_INIT", "const float", QW|NQ, NULL, MSG_INIT}, + {"MSG_MULTICAST", "const float", QW|NQ, NULL, MSG_MULTICAST}, + {"MSG_ENTITY", "const float", QW|NQ, NULL, MSG_CSQC}, - {"MULTICAST_ALL", "const float", QW|NQ, MULTICAST_ALL}, - {"MULTICAST_PHS", "const float", QW|NQ, MULTICAST_PHS}, - {"MULTICAST_PVS", "const float", QW|NQ, MULTICAST_PVS}, - {"MULTICAST_ONE", "const float", QW|NQ, MULTICAST_ONE}, - {"MULTICAST_ALL_R", "const float", QW|NQ, MULTICAST_ALL_R}, - {"MULTICAST_PHS_R", "const float", QW|NQ, MULTICAST_PHS_R}, - {"MULTICAST_PVS_R", "const float", QW|NQ, MULTICAST_PVS_R}, - {"MULTICAST_ONE_R", "const float", QW|NQ, MULTICAST_ONE_R}, + {"MULTICAST_ALL", "const float", QW|NQ, NULL, MULTICAST_ALL}, + {"MULTICAST_PHS", "const float", QW|NQ, NULL, MULTICAST_PHS}, + {"MULTICAST_PVS", "const float", QW|NQ, NULL, MULTICAST_PVS}, + {"MULTICAST_ONE", "const float", QW|NQ, NULL, MULTICAST_ONE}, + {"MULTICAST_ALL_R", "const float", QW|NQ, NULL, MULTICAST_ALL_R}, + {"MULTICAST_PHS_R", "const float", QW|NQ, NULL, MULTICAST_PHS_R}, + {"MULTICAST_PVS_R", "const float", QW|NQ, NULL, MULTICAST_PVS_R}, + {"MULTICAST_ONE_R", "const float", QW|NQ, NULL, MULTICAST_ONE_R}, - {"PRINT_LOW", "const float", QW, PRINT_LOW}, - {"PRINT_MEDIUM", "const float", QW, PRINT_MEDIUM}, - {"PRINT_HIGH", "const float", QW, PRINT_HIGH}, - {"PRINT_CHAT", "const float", QW, PRINT_CHAT}, + {"PRINT_LOW", "const float", QW, NULL, PRINT_LOW}, + {"PRINT_MEDIUM", "const float", QW, NULL, PRINT_MEDIUM}, + {"PRINT_HIGH", "const float", QW, NULL, PRINT_HIGH}, + {"PRINT_CHAT", "const float", QW, NULL, PRINT_CHAT}, - {"PVSF_NORMALPVS", "const float", QW|NQ, PVSF_NORMALPVS}, - {"PVSF_NOTRACECHECK", "const float", QW|NQ, PVSF_NOTRACECHECK}, - {"PVSF_USEPHS", "const float", QW|NQ, PVSF_USEPHS}, - {"PVSF_IGNOREPVS", "const float", QW|NQ, PVSF_IGNOREPVS}, - {"PVSF_NOREMOVE", "const float", QW|NQ, PVSF_NOREMOVE}, + {"PVSF_NORMALPVS", "const float", QW|NQ, "Filter first by PVS, then filter this entity using tracelines if sv_cullentities is enabled.", PVSF_NORMALPVS}, + {"PVSF_NOTRACECHECK", "const float", QW|NQ, "Filter strictly by PVS.", PVSF_NOTRACECHECK}, + {"PVSF_USEPHS", "const float", QW|NQ, "Send if we're close enough to be able to hear this entity.", PVSF_USEPHS}, + {"PVSF_IGNOREPVS", "const float", QW|NQ, "Ignores pvs. This entity is visible whereever you are on the map.", PVSF_IGNOREPVS}, + {"PVSF_NOREMOVE", "const float", QW|NQ, "Once visible to a client, this entity will remain visible. This can be useful for csqc and corpses.", PVSF_NOREMOVE}, - {"INFOKEY_P_IP", "const string", QW|NQ, 0, "\"ip\""}, - {"INFOKEY_P_REALIP", "const string", QW|NQ, 0, "\"realip\""}, - {"INFOKEY_P_CSQCACTIVE","const string", QW|NQ, 0, "\"csqcactive\""}, - {"INFOKEY_P_PING", "const string", QW|NQ, 0, "\"ping\""}, - {"INFOKEY_P_SVPING", "const string", QW|NQ, 0, "\"svping\""}, - {"INFOKEY_P_GUID", "const string", QW|NQ, 0, "\"guid\""}, - {"INFOKEY_P_CHALLENGE", "const string", QW|NQ, 0, "\"challenge\""}, - {"INFOKEY_P_USERID", "const string", QW|NQ, 0, "\"*userid\""}, - {"INFOKEY_P_DOWNLOADPCT","const string",QW|NQ, 0, "\"download\""}, - {"INFOKEY_P_TRUSTLEVEL","const string", QW|NQ, 0, "\"trustlevel\""}, - {"INFOKEY_P_PROTOCOL", "const string", QW|NQ, 0, "\"protocol\""}, + {"INFOKEY_P_IP", "const string", QW|NQ, NULL, 0, "\"ip\""}, + {"INFOKEY_P_REALIP", "const string", QW|NQ, NULL, 0, "\"realip\""}, + {"INFOKEY_P_CSQCACTIVE","const string", QW|NQ, "Client has csqc enabled. CSQC ents etc will be sent to this player.", 0, "\"csqcactive\""}, + {"INFOKEY_P_PING", "const string", CS|QW|NQ, NULL, 0, "\"ping\""}, + {"INFOKEY_P_SVPING", "const string", QW|NQ, NULL, 0, "\"svping\""}, + {"INFOKEY_P_GUID", "const string", QW|NQ, "Some hash string which should be reasonably unique to this player's quake installation.", 0, "\"guid\""}, + {"INFOKEY_P_CHALLENGE", "const string", QW|NQ, NULL, 0, "\"challenge\""}, + {"INFOKEY_P_USERID", "const string", QW|NQ, NULL, 0, "\"*userid\""}, + {"INFOKEY_P_DOWNLOADPCT","const string",QW|NQ, NULL, 0, "\"download\""}, + {"INFOKEY_P_TRUSTLEVEL","const string", QW|NQ, NULL, 0, "\"trustlevel\""}, + {"INFOKEY_P_PROTOCOL", "const string", QW|NQ, "The network protocol the client is using to connect to the server.", 0, "\"protocol\""}, + {"INFOKEY_P_MUTED", "const string", CS, "0: we can see the result of the player's say/say_team commands. 1: we see no say/say_team messages from this player. Use the ignore command to toggle this value.", 0, "\"ignored\""}, + {"INFOKEY_P_VOIP_MUTED","const string", CS, "0: we can hear this player when they speak (assuming voip is generally enabled). 1: we ignore everything this player says. Use cl_voip_mute to change the values.", 0, "\"vignored\""}, + {"INFOKEY_P_ENTERTIME", "const string", CS, NULL, 0, "\"entertime\""}, + {"INFOKEY_P_FRAGS", "const string", CS, NULL, 0, "\"frags\""}, + {"INFOKEY_P_PACKETLOSS","const string", CS, NULL, 0, "\"pl\""}, + {"INFOKEY_P_VOIPSPEAKING","const string", CS, "Boolean value that says whether the given player is currently sending voice information.", 0, "\"voipspeaking\""}, + {"INFOKEY_P_VOIPLOUDNESS","const string", CS, "Only valid for the local player. Gives a value between 0 and 1 to indicate to the user how loud their mic is.", 0, "\"voiploudness\""}, // edict.flags - {"FL_FLY", "const float", QW|NQ|CS, FL_FLY}, - {"FL_SWIM", "const float", QW|NQ|CS, FL_SWIM}, - {"FL_CLIENT", "const float", QW|NQ|CS, FL_CLIENT}, - {"FL_INWATER", "const float", QW|NQ|CS, FL_INWATER}, - {"FL_MONSTER", "const float", QW|NQ|CS, FL_MONSTER}, - {"FL_GODMODE", "const float", QW|NQ, FL_GODMODE}, - {"FL_NOTARGET", "const float", QW|NQ, FL_NOTARGET}, - {"FL_ITEM", "const float", QW|NQ|CS, FL_ITEM}, - {"FL_ONGROUND", "const float", QW|NQ|CS, FL_ONGROUND}, - {"FL_PARTIALGROUND", "const float", QW|NQ|CS, FL_PARTIALGROUND}, - {"FL_WATERJUMP", "const float", QW|NQ|CS, FL_WATERJUMP}, - {"FL_JUMPRELEASED", "const float", NQ|CS, FL_JUMPRELEASED}, - {"FL_FINDABLE_NONSOLID","const float", QW|NQ|CS, FL_FINDABLE_NONSOLID}, -// {"FL_MOVECHAIN_ANGLE", "const float", QW|NQ, FL_MOVECHAIN_ANGLE}, - {"FL_LAGGEDMOVE", "const float", QW|NQ, FLQW_LAGGEDMOVE}, -// {"FL_CLASS_DEPENDENT", "const float", QW|NQ, FL_CLASS_DEPENDENT}, + {"FL_FLY", "const float", QW|NQ|CS, NULL, FL_FLY}, + {"FL_SWIM", "const float", QW|NQ|CS, NULL, FL_SWIM}, + {"FL_CLIENT", "const float", QW|NQ|CS, NULL, FL_CLIENT}, + {"FL_INWATER", "const float", QW|NQ|CS, NULL, FL_INWATER}, + {"FL_MONSTER", "const float", QW|NQ|CS, NULL, FL_MONSTER}, + {"FL_GODMODE", "const float", QW|NQ, NULL, FL_GODMODE}, + {"FL_NOTARGET", "const float", QW|NQ, NULL, FL_NOTARGET}, + {"FL_ITEM", "const float", QW|NQ|CS, NULL, FL_ITEM}, + {"FL_ONGROUND", "const float", QW|NQ|CS, NULL, FL_ONGROUND}, + {"FL_PARTIALGROUND", "const float", QW|NQ|CS, NULL, FL_PARTIALGROUND}, + {"FL_WATERJUMP", "const float", QW|NQ|CS, NULL, FL_WATERJUMP}, + {"FL_JUMPRELEASED", "const float", NQ|CS, NULL, FL_JUMPRELEASED}, + {"FL_FINDABLE_NONSOLID","const float", QW|NQ|CS, "Allows this entity to be found with findradius", FL_FINDABLE_NONSOLID}, +// {"FL_MOVECHAIN_ANGLE", "const float", QW|NQ, NULL, FL_MOVECHAIN_ANGLE}, + {"FL_LAGGEDMOVE", "const float", QW|NQ, "Enables anti-lag on rockets etc.", FLQW_LAGGEDMOVE}, +// {"FL_CLASS_DEPENDENT", "const float", QW|NQ, NULL, FL_CLASS_DEPENDENT}, - {"MOVE_NORMAL", "const float", QW|NQ|CS, MOVE_NORMAL}, - {"MOVE_NOMONSTERS", "const float", QW|NQ|CS, MOVE_NOMONSTERS}, - {"MOVE_MISSILE", "const float", QW|NQ|CS, MOVE_MISSILE}, - {"MOVE_HITMODEL", "const float", QW|NQ|CS, MOVE_HITMODEL}, - {"MOVE_TRIGGERS", "const float", QW|NQ|CS, MOVE_TRIGGERS}, - {"MOVE_EVERYTHING", "const float", QW|NQ|CS, MOVE_EVERYTHING}, - {"MOVE_LAGGED", "const float", QW|NQ, MOVE_LAGGED}, - {"MOVE_ENTCHAIN", "const float", QW|NQ|CS, MOVE_ENTCHAIN}, + {"MOVE_NORMAL", "const float", QW|NQ|CS, NULL, MOVE_NORMAL}, + {"MOVE_NOMONSTERS", "const float", QW|NQ|CS, NULL, MOVE_NOMONSTERS}, + {"MOVE_MISSILE", "const float", QW|NQ|CS, NULL, MOVE_MISSILE}, + {"MOVE_HITMODEL", "const float", QW|NQ|CS, "Traces will impact the actual mesh of the model instead of merely their bounding box. Should generally only be used for tracelines. Note that this flag is unreliable as an object can animate through projectiles. The bounding box MUST be set to completely encompass the entity or those extra areas will be non-solid (leaving a hole for things to go through).", MOVE_HITMODEL}, + {"MOVE_TRIGGERS", "const float", QW|NQ|CS, NULL, MOVE_TRIGGERS}, + {"MOVE_EVERYTHING", "const float", QW|NQ|CS, NULL, MOVE_EVERYTHING}, + {"MOVE_LAGGED", "const float", QW|NQ, "Will use antilag based upon the player's latency. Traces will be performed against old positions for entities instead of their current origin.", MOVE_LAGGED}, + {"MOVE_ENTCHAIN", "const float", QW|NQ|CS, "Returns a list of entities impacted via the trace_ent.chain field", MOVE_ENTCHAIN}, - {"EF_BRIGHTFIELD", "const float", QW|NQ|CS, EF_BRIGHTFIELD}, - {"EF_MUZZLEFLASH", "const float", NQ|CS, EF_MUZZLEFLASH}, - {"EF_BRIGHTLIGHT", "const float", QW|NQ|CS, EF_BRIGHTLIGHT}, - {"EF_DIMLIGHT", "const float", QW|NQ|CS, EF_DIMLIGHT}, - {"EF_FLAG1", "const float", QW , QWEF_FLAG1}, - {"EF_FLAG2", "const float", QW , QWEF_FLAG2}, - {"EF_ADDITIVE", "const float", NQ|CS, NQEF_ADDITIVE}, - {"EF_BLUE", "const float", QW|NQ|CS, EF_BLUE}, - {"EF_RED", "const float", QW|NQ|CS, EF_RED}, - {"EF_FULLBRIGHT", "const float", QW|NQ|CS, EF_FULLBRIGHT}, - {"EF_NODEPTHTEST", "const float", QW|NQ|CS, EF_NODEPTHTEST}, + {"EF_BRIGHTFIELD", "const float", QW|NQ|CS, NULL, EF_BRIGHTFIELD}, + {"EF_MUZZLEFLASH", "const float", NQ|CS, NULL, EF_MUZZLEFLASH}, + {"EF_BRIGHTLIGHT", "const float", QW|NQ|CS, NULL, EF_BRIGHTLIGHT}, + {"EF_DIMLIGHT", "const float", QW|NQ|CS, NULL, EF_DIMLIGHT}, + {"EF_FLAG1", "const float", QW , NULL, QWEF_FLAG1}, + {"EF_FLAG2", "const float", QW , NULL, QWEF_FLAG2}, + {"EF_ADDITIVE", "const float", NQ|CS, NULL, NQEF_ADDITIVE}, + {"EF_BLUE", "const float", QW|NQ|CS, NULL, EF_BLUE}, + {"EF_RED", "const float", QW|NQ|CS, NULL, EF_RED}, + {"EF_FULLBRIGHT", "const float", QW|NQ|CS, NULL, EF_FULLBRIGHT}, + {"EF_NODEPTHTEST", "const float", QW|NQ|CS, NULL, EF_NODEPTHTEST}, - {"EF_NOMODELFLAGS", "const float", QW|NQ, EF_NOMODELFLAGS}, + {"EF_NOMODELFLAGS", "const float", QW|NQ, "Surpresses the normal flags specified in the model.", EF_NOMODELFLAGS}, - {"MF_ROCKET", "const float", QW|NQ, EF_MF_ROCKET>>24}, - {"MF_GRENADE", "const float", QW|NQ, EF_MF_GRENADE>>24}, - {"MF_GIB", "const float", QW|NQ, EF_MF_GIB>>24}, - {"MF_ROTATE", "const float", QW|NQ, EF_MF_ROTATE>>24}, - {"MF_TRACER", "const float", QW|NQ, EF_MF_TRACER>>24}, - {"MF_ZOMGIB", "const float", QW|NQ, EF_MF_ZOMGIB>>24}, - {"MF_TRACER2", "const float", QW|NQ, EF_MF_TRACER2>>24}, - {"MF_TRACER3", "const float", QW|NQ, EF_MF_TRACER3>>24}, + {"MF_ROCKET", "const float", QW|NQ, NULL, EF_MF_ROCKET>>24}, + {"MF_GRENADE", "const float", QW|NQ, NULL, EF_MF_GRENADE>>24}, + {"MF_GIB", "const float", QW|NQ, NULL, EF_MF_GIB>>24}, + {"MF_ROTATE", "const float", QW|NQ, NULL, EF_MF_ROTATE>>24}, + {"MF_TRACER", "const float", QW|NQ, NULL, EF_MF_TRACER>>24}, + {"MF_ZOMGIB", "const float", QW|NQ, NULL, EF_MF_ZOMGIB>>24}, + {"MF_TRACER2", "const float", QW|NQ, NULL, EF_MF_TRACER2>>24}, + {"MF_TRACER3", "const float", QW|NQ, NULL, EF_MF_TRACER3>>24}, //including these for csqc stat types. -// {"EV_VOID", "const float", QW|NQ, ev_void}, - {"EV_STRING", "const float", QW|NQ, ev_string}, - {"EV_FLOAT", "const float", QW|NQ, ev_float}, -// {"EV_VECTOR", "const float", QW|NQ, ev_vector}, - {"EV_ENTITY", "const float", QW|NQ, ev_entity}, -// {"EV_FIELD", "const float", QW|NQ, ev_field}, -// {"EV_FUNCTION", "const float", QW|NQ, ev_function}, -// {"EV_POINTER", "const float", QW|NQ, ev_pointer}, - {"EV_INTEGER", "const float", QW|NQ, ev_integer}, +// {"EV_VOID", "const float", QW|NQ, NULL, ev_void}, + {"EV_STRING", "const float", QW|NQ, NULL, ev_string}, + {"EV_FLOAT", "const float", QW|NQ, NULL, ev_float}, + {"EV_VECTOR", "const float", QW|NQ, NULL, ev_vector}, + {"EV_ENTITY", "const float", QW|NQ, NULL, ev_entity}, +// {"EV_FIELD", "const float", QW|NQ, NULL, ev_field}, +// {"EV_FUNCTION", "const float", QW|NQ, NULL, ev_function}, +// {"EV_POINTER", "const float", QW|NQ, NULL, ev_pointer}, + {"EV_INTEGER", "const float", QW|NQ, NULL, ev_integer}, +// {"EV_INTEGER", "const float", QW|NQ, NULL, ev_variant}, +// {"EV_STRUCT", "const float", QW|NQ, NULL, ev_struct}, +// {"EV_UNION", "const float", QW|NQ, NULL, ev_union}, - {"STAT_HEALTH", "const float", CS, STAT_HEALTH}, - {"STAT_WEAPON", "const float", CS, STAT_WEAPON}, - {"STAT_AMMO", "const float", CS, STAT_AMMO}, - {"STAT_ARMOR", "const float", CS, STAT_ARMOR}, - {"STAT_WEAPONFRAME", "const float", CS, STAT_WEAPONFRAME}, - {"STAT_SHELLS", "const float", CS, STAT_SHELLS}, - {"STAT_NAILS", "const float", CS, STAT_NAILS}, - {"STAT_ROCKETS", "const float", CS, STAT_ROCKETS}, - {"STAT_CELLS", "const float", CS, STAT_CELLS}, - {"STAT_ACTIVEWEAPON", "const float", CS, STAT_ACTIVEWEAPON}, - {"STAT_TOTALSECRETS", "const float", CS, STAT_TOTALSECRETS}, - {"STAT_TOTALMONSTERS", "const float", CS, STAT_TOTALMONSTERS}, - {"STAT_FOUNDSECRETS", "const float", CS, STAT_SECRETS}, - {"STAT_KILLEDMONSTERS", "const float", CS, STAT_MONSTERS}, - {"STAT_ITEMS", "const float", CS, STAT_ITEMS}, - {"STAT_VIEWHEIGHT", "const float", CS, STAT_VIEWHEIGHT}, - {"STAT_VIEW2", "const float", CS, STAT_VIEW2}, - {"STAT_VIEWZOOM", "const float", CS, STAT_VIEWZOOM}, + {"STAT_HEALTH", "const float", CS, NULL, STAT_HEALTH}, + {"STAT_WEAPON", "const float", CS, NULL, STAT_WEAPON}, + {"STAT_AMMO", "const float", CS, NULL, STAT_AMMO}, + {"STAT_ARMOR", "const float", CS, NULL, STAT_ARMOR}, + {"STAT_WEAPONFRAME", "const float", CS, NULL, STAT_WEAPONFRAME}, + {"STAT_SHELLS", "const float", CS, NULL, STAT_SHELLS}, + {"STAT_NAILS", "const float", CS, NULL, STAT_NAILS}, + {"STAT_ROCKETS", "const float", CS, NULL, STAT_ROCKETS}, + {"STAT_CELLS", "const float", CS, NULL, STAT_CELLS}, + {"STAT_ACTIVEWEAPON", "const float", CS, NULL, STAT_ACTIVEWEAPON}, + {"STAT_TOTALSECRETS", "const float", CS, NULL, STAT_TOTALSECRETS}, + {"STAT_TOTALMONSTERS", "const float", CS, NULL, STAT_TOTALMONSTERS}, + {"STAT_FOUNDSECRETS", "const float", CS, NULL, STAT_SECRETS}, + {"STAT_KILLEDMONSTERS", "const float", CS, NULL, STAT_MONSTERS}, + {"STAT_ITEMS", "const float", CS, NULL, STAT_ITEMS}, + {"STAT_VIEWHEIGHT", "const float", CS, NULL, STAT_VIEWHEIGHT}, + {"STAT_VIEW2", "const float", CS, NULL, STAT_VIEW2}, + {"STAT_VIEWZOOM", "const float", CS, NULL, STAT_VIEWZOOM}, - {"VF_MIN", "const float", CS, VF_MIN}, - {"VF_MIN_X", "const float", CS, VF_MIN_X}, - {"VF_MIN_Y", "const float", CS, VF_MIN_Y}, - {"VF_SIZE", "const float", CS, VF_SIZE}, - {"VF_SIZE_X", "const float", CS, VF_SIZE_X}, - {"VF_SIZE_Y", "const float", CS, VF_SIZE_Y}, - {"VF_VIEWPORT", "const float", CS, VF_VIEWPORT}, - {"VF_FOV", "const float", CS, VF_FOV}, - {"VF_FOVX", "const float", CS, VF_FOVX}, - {"VF_FOVY", "const float", CS, VF_FOVY}, - {"VF_ORIGIN", "const float", CS, VF_ORIGIN}, - {"VF_ORIGIN_X", "const float", CS, VF_ORIGIN_X}, - {"VF_ORIGIN_Y", "const float", CS, VF_ORIGIN_Y}, - {"VF_ORIGIN_Z", "const float", CS, VF_ORIGIN_Z}, - {"VF_ANGLES", "const float", CS, VF_ANGLES}, - {"VF_ANGLES_X", "const float", CS, VF_ANGLES_X}, - {"VF_ANGLES_Y", "const float", CS, VF_ANGLES_Y}, - {"VF_ANGLES_Z", "const float", CS, VF_ANGLES_Z}, - {"VF_DRAWWORLD", "const float", CS, VF_DRAWWORLD}, - {"VF_DRAWENGINESBAR", "const float", CS, VF_ENGINESBAR}, - {"VF_DRAWCROSSHAIR", "const float", CS, VF_DRAWCROSSHAIR}, + {"VF_MIN", "const float", CS, NULL, VF_MIN}, + {"VF_MIN_X", "const float", CS, NULL, VF_MIN_X}, + {"VF_MIN_Y", "const float", CS, NULL, VF_MIN_Y}, + {"VF_SIZE", "const float", CS, NULL, VF_SIZE}, + {"VF_SIZE_X", "const float", CS, NULL, VF_SIZE_X}, + {"VF_SIZE_Y", "const float", CS, NULL, VF_SIZE_Y}, + {"VF_VIEWPORT", "const float", CS, NULL, VF_VIEWPORT}, + {"VF_FOV", "const float", CS, "sets both fovx and fovy. consider using afov instead.", VF_FOV}, + {"VF_FOVX", "const float", CS, "horizontal field of view. does not consider aspect at all.", VF_FOVX}, + {"VF_FOVY", "const float", CS, "vertical field of view. does not consider aspect at all.", VF_FOVY}, + {"VF_ORIGIN", "const float", CS, NULL, VF_ORIGIN}, + {"VF_ORIGIN_X", "const float", CS, NULL, VF_ORIGIN_X}, + {"VF_ORIGIN_Y", "const float", CS, NULL, VF_ORIGIN_Y}, + {"VF_ORIGIN_Z", "const float", CS, NULL, VF_ORIGIN_Z}, + {"VF_ANGLES", "const float", CS, NULL, VF_ANGLES}, + {"VF_ANGLES_X", "const float", CS, NULL, VF_ANGLES_X}, + {"VF_ANGLES_Y", "const float", CS, NULL, VF_ANGLES_Y}, + {"VF_ANGLES_Z", "const float", CS, NULL, VF_ANGLES_Z}, + {"VF_DRAWWORLD", "const float", CS, NULL, VF_DRAWWORLD}, + {"VF_DRAWENGINESBAR", "const float", CS, NULL, VF_ENGINESBAR}, + {"VF_DRAWCROSSHAIR", "const float", CS, NULL, VF_DRAWCROSSHAIR}, - {"VF_CL_VIEWANGLES", "const float", CS, VF_CL_VIEWANGLES_V}, - {"VF_CL_VIEWANGLES_X", "const float", CS, VF_CL_VIEWANGLES_X}, - {"VF_CL_VIEWANGLES_Y", "const float", CS, VF_CL_VIEWANGLES_Y}, - {"VF_CL_VIEWANGLES_Z", "const float", CS, VF_CL_VIEWANGLES_Z}, + {"VF_CL_VIEWANGLES", "const float", CS, NULL, VF_CL_VIEWANGLES_V}, + {"VF_CL_VIEWANGLES_X", "const float", CS, NULL, VF_CL_VIEWANGLES_X}, + {"VF_CL_VIEWANGLES_Y", "const float", CS, NULL, VF_CL_VIEWANGLES_Y}, + {"VF_CL_VIEWANGLES_Z", "const float", CS, NULL, VF_CL_VIEWANGLES_Z}, - {"VF_PERSPECTIVE", "const float", CS, VF_PERSPECTIVE}, - {"VF_LPLAYER", "const float", CS, VF_LPLAYER}, - {"VF_AFOV", "const float", CS, VF_AFOV}, - {"VF_SCREENVSIZE", "const float", CS, VF_SCREENVSIZE}, - {"VF_SCREENPSIZE", "const float", CS, VF_SCREENPSIZE}, + {"VF_PERSPECTIVE", "const float", CS, "1: regular rendering. Fov specifies the angle. 0: isometric-style. Fov specifies the number of Quake Units each side of the viewport.", VF_PERSPECTIVE}, + {"VF_LPLAYER", "const float", CS, "The 'seat' number, used when running splitscreen.", VF_LPLAYER}, + {"VF_AFOV", "const float", CS, "Aproximate fov. Matches the 'fov' cvar. The engine handles the aspect ratio for you.", VF_AFOV}, + {"VF_SCREENVSIZE", "const float", CS, "Provides a reliable way to retrieve the current virtual screen size (even if the screen is automatically scaled to retain aspect).", VF_SCREENVSIZE}, + {"VF_SCREENPSIZE", "const float", CS, "Provides a reliable way to retrieve the current physical screen size (cvars need vid_restart for them to take effect).", VF_SCREENPSIZE}, + {"VF_VIEWENTITY", "const float", CS, "Changes the RF_EXTERNALMODEL flag on entities to match the new selection, and removes entities flaged with RF_VIEWENTITY. Requires cunning use of .entnum and typically requires calling addentities(MASK_VIEWMODEL) too.", VF_VIEWENTITY}, - {"RF_VIEWMODEL", "const float", CS, CSQCRF_VIEWMODEL}, - {"RF_EXTERNALMODEL", "const float", CS, CSQCRF_EXTERNALMODEL}, - {"RF_DEPTHHACK", "const float", CS, CSQCRF_DEPTHHACK}, - {"RF_ADDITIVE", "const float", CS, CSQCRF_ADDITIVE}, - {"RF_USEAXIS", "const float", CS, CSQCRF_USEAXIS}, - {"RF_NOSHADOW", "const float", CS, CSQCRF_NOSHADOW}, - {"RF_FRAMETIMESARESTARTTIMES","const float", CS, CSQCRF_FRAMETIMESARESTARTTIMES}, - {"RF_NOAUTOADD", "const float", CS, CSQCRF_NOAUTOADD}, + {"RF_VIEWMODEL", "const float", CS, "Specifies that the entity is a view model, and that its origin is relative to the current view position. These entities are also subject to viewweapon bob.", CSQCRF_VIEWMODEL}, + {"RF_EXTERNALMODEL", "const float", CS, "Specifies that this entity should be displayed in mirrors (and may still cast shadows), but will not otherwise be visible.", CSQCRF_EXTERNALMODEL}, + {"RF_DEPTHHACK", "const float", CS, "Hacks the depth values such that the entity uses depth values as if it were closer to the screen. This is useful when combined with viewmodels to avoid weapons poking in to walls.", CSQCRF_DEPTHHACK}, + {"RF_ADDITIVE", "const float", CS, "Shaders from this entity will temporarily be hacked to use an additive blend mode instead of their normal blend mode.", CSQCRF_ADDITIVE}, + {"RF_USEAXIS", "const float", CS, "The entity will be oriented according to the current v_forward+v_right+v_up vector values instead of the entity's .angles field.", CSQCRF_USEAXIS}, + {"RF_NOSHADOW", "const float", CS, "This entity will not cast shadows. Often useful on view models.", CSQCRF_NOSHADOW}, + {"RF_FRAMETIMESARESTARTTIMES","const float", CS, "Specifies that the frame1time, frame2time field are timestamps (denoting the start of the animation) rather than time into the animation.", CSQCRF_FRAMETIMESARESTARTTIMES}, - {"IE_KEYDOWN", "const float", CS, CSIE_KEYDOWN}, - {"IE_KEYUP", "const float", CS, CSIE_KEYUP}, - {"IE_MOUSEDELTA", "const float", CS, CSIE_MOUSEDELTA}, - {"IE_MOUSEABS", "const float", CS, CSIE_MOUSEABS}, - {"IE_ACCELEROMETER", "const float", CS, CSIE_ACCELEROMETER}, + {"IE_KEYDOWN", "const float", CS, "Specifies that a key was pressed. Second argument is the scan code. Third argument is the unicode (printable) char value. Fourth argument denotes which keyboard(or mouse, if its a mouse 'scan' key) the event came from. Note that some systems may completely separate scan codes and unicode values, with a 0 value for the unspecified argument.", CSIE_KEYDOWN}, + {"IE_KEYUP", "const float", CS, "Specifies that a key was released. Arguments are the same as IE_KEYDOWN. On some systems, this may be fired instantly after IE_KEYDOWN was fired.", CSIE_KEYUP}, + {"IE_MOUSEDELTA", "const float", CS, "Specifies that a mouse was moved (touch screens and tablets typically give IE_MOUSEABS events instead, use _windowed_mouse 0 to test code to cope with either). Second argument is the X displacement, third argument is the Y displacement. Fourth argument is which mouse or touch event triggered the event.", CSIE_MOUSEDELTA}, + {"IE_MOUSEABS", "const float", CS, "Specifies that a mouse cursor or touch event was moved to a specific location relative to the virtual screen space. Second argument is the new X position, third argument is the new Y position. Fourth argument is which mouse or touch event triggered the event.", CSIE_MOUSEABS}, + {"IE_ACCELEROMETER", "const float", CS, NULL, CSIE_ACCELEROMETER}, - {"CLIENTTYPE_DISCONNECTED","const float", QW|NQ, CLIENTTYPE_DISCONNECTED}, - {"CLIENTTYPE_REAL", "const float", QW|NQ, CLIENTTYPE_REAL}, - {"CLIENTTYPE_BOT", "const float", QW|NQ, CLIENTTYPE_BOT}, - {"CLIENTTYPE_NOTACLIENT","const float",QW|NQ, CLIENTTYPE_NOTACLIENT}, + {"CLIENTTYPE_DISCONNECTED","const float", QW|NQ, NULL, CLIENTTYPE_DISCONNECTED}, + {"CLIENTTYPE_REAL", "const float", QW|NQ, NULL, CLIENTTYPE_REAL}, + {"CLIENTTYPE_BOT", "const float", QW|NQ, NULL, CLIENTTYPE_BOT}, + {"CLIENTTYPE_NOTACLIENT","const float",QW|NQ, NULL, CLIENTTYPE_NOTACLIENT}, - {"FILE_READ", "const float", ALL, FRIK_FILE_READ}, - {"FILE_APPEND", "const float", ALL, FRIK_FILE_APPEND}, - {"FILE_WRITE", "const float", ALL, FRIK_FILE_WRITE}, - {"FILE_READNL", "const float", QW|NQ|CS, FRIK_FILE_READNL}, - {"FILE_MMAP_READ", "const float", QW|NQ|CS, FRIK_FILE_MMAP_READ}, - {"FILE_MMAP_RW", "const float", QW|NQ|CS, FRIK_FILE_MMAP_RW}, + {"FILE_READ", "const float", ALL, NULL, FRIK_FILE_READ}, + {"FILE_APPEND", "const float", ALL, NULL, FRIK_FILE_APPEND}, + {"FILE_WRITE", "const float", ALL, NULL, FRIK_FILE_WRITE}, + {"FILE_READNL", "const float", QW|NQ|CS, NULL, FRIK_FILE_READNL}, + {"FILE_MMAP_READ", "const float", QW|NQ|CS, NULL, FRIK_FILE_MMAP_READ}, + {"FILE_MMAP_RW", "const float", QW|NQ|CS, NULL, FRIK_FILE_MMAP_RW}, - {"MASK_ENGINE", "const float", CS, MASK_DELTA}, - {"MASK_VIEWMODEL", "const float", CS, MASK_STDVIEWMODEL}, + {"MASK_ENGINE", "const float", CS, "Valid as an argument for addentities. If specified, all non-csqc entities will be added to the scene.", MASK_DELTA}, + {"MASK_VIEWMODEL", "const float", CS, "Valid as an argument for addentities. If specified, the regular engine viewmodel will be added to the scene.", MASK_STDVIEWMODEL}, - {"LFIELD_ORIGIN", "const float", CS, lfield_origin}, - {"LFIELD_COLOUR", "const float", CS, lfield_colour}, - {"LFIELD_RADIUS", "const float", CS, lfield_radius}, - {"LFIELD_FLAGS", "const float", CS, lfield_flags}, - {"LFIELD_STYLE", "const float", CS, lfield_style}, - {"LFIELD_ANGLES", "const float", CS, lfield_angles}, - {"LFIELD_FOV", "const float", CS, lfield_fov}, - {"LFIELD_CORONA", "const float", CS, lfield_corona}, - {"LFIELD_CORONASCALE", "const float", CS, lfield_coronascale}, - {"LFIELD_CUBEMAPNAME", "const float", CS, lfield_cubemapname}, - {"LFIELD_AMBIENTSCALE", "const float", CS, lfield_ambientscale}, - {"LFIELD_DIFFUSESCALE", "const float", CS, lfield_diffusescale}, - {"LFIELD_SPECULARSCALE","const float", CS, lfield_specularscale}, + {"LFIELD_ORIGIN", "const float", CS, NULL, lfield_origin}, + {"LFIELD_COLOUR", "const float", CS, NULL, lfield_colour}, + {"LFIELD_RADIUS", "const float", CS, NULL, lfield_radius}, + {"LFIELD_FLAGS", "const float", CS, NULL, lfield_flags}, + {"LFIELD_STYLE", "const float", CS, NULL, lfield_style}, + {"LFIELD_ANGLES", "const float", CS, NULL, lfield_angles}, + {"LFIELD_FOV", "const float", CS, NULL, lfield_fov}, + {"LFIELD_CORONA", "const float", CS, NULL, lfield_corona}, + {"LFIELD_CORONASCALE", "const float", CS, NULL, lfield_coronascale}, + {"LFIELD_CUBEMAPNAME", "const float", CS, NULL, lfield_cubemapname}, + {"LFIELD_AMBIENTSCALE", "const float", CS, NULL, lfield_ambientscale}, + {"LFIELD_DIFFUSESCALE", "const float", CS, NULL, lfield_diffusescale}, + {"LFIELD_SPECULARSCALE","const float", CS, NULL, lfield_specularscale}, - {"LFLAG_NORMALMODE", "const float", CS, LFLAG_NORMALMODE}, - {"LFLAG_REALTIMEMODE", "const float", CS, LFLAG_REALTIMEMODE}, - {"LFLAG_LIGHTMAP", "const float", CS, LFLAG_LIGHTMAP}, - {"LFLAG_FLASHBLEND", "const float", CS, LFLAG_FLASHBLEND}, - {"LFLAG_NOSHADOWS", "const float", CS, LFLAG_NOSHADOWS}, - {"LFLAG_SHADOWMAP", "const float", CS, LFLAG_SHADOWMAP}, - {"LFLAG_CREPUSCULAR", "const float", CS, LFLAG_CREPUSCULAR}, + {"LFLAG_NORMALMODE", "const float", CS, NULL, LFLAG_NORMALMODE}, + {"LFLAG_REALTIMEMODE", "const float", CS, NULL, LFLAG_REALTIMEMODE}, + {"LFLAG_LIGHTMAP", "const float", CS, NULL, LFLAG_LIGHTMAP}, + {"LFLAG_FLASHBLEND", "const float", CS, NULL, LFLAG_FLASHBLEND}, + {"LFLAG_NOSHADOWS", "const float", CS, NULL, LFLAG_NOSHADOWS}, + {"LFLAG_SHADOWMAP", "const float", CS, NULL, LFLAG_SHADOWMAP}, + {"LFLAG_CREPUSCULAR", "const float", CS, NULL, LFLAG_CREPUSCULAR}, - {"TEREDIT_RELOAD", "const float", CS, ter_reload}, - {"TEREDIT_SAVE", "const float", CS, ter_save}, - {"TEREDIT_SETHOLE", "const float", CS, ter_sethole}, - {"TEREDIT_HEIGHT_SET", "const float", CS, ter_height_set}, - {"TEREDIT_HEIGHT_SMOOTH","const float",CS, ter_height_smooth}, - {"TEREDIT_HEIGHT_SPREAD","const float",CS, ter_height_spread}, - {"TEREDIT_HEIGHT_RAISE","const float", CS, ter_raise}, - {"TEREDIT_HEIGHT_FLATTEN","const float", CS, ter_height_flatten}, - {"TEREDIT_HEIGHT_LOWER","const float", CS, ter_lower}, - {"TEREDIT_TEX_KILL", "const float", CS, ter_tex_kill}, - {"TEREDIT_TEX_GET", "const float", CS, ter_tex_get}, - {"TEREDIT_MIX_PAINT", "const float", CS, ter_mix_paint}, - {"TEREDIT_MIX_UNIFY", "const float", CS, ter_mix_concentrate}, - {"TEREDIT_MIX_NOISE", "const float", CS, ter_mix_noise}, - {"TEREDIT_MIX_BLUR", "const float", CS, ter_mix_blur}, - {"TEREDIT_WATER_SET", "const float", CS, ter_water_set}, - {"TEREDIT_MESH_ADD", "const float", CS, ter_mesh_add}, - {"TEREDIT_MESH_KILL", "const float", CS, ter_mesh_kill}, - {"TEREDIT_TINT", "const float", CS, ter_tint}, + {"TEREDIT_RELOAD", "const float", CS, NULL, ter_reload}, + {"TEREDIT_SAVE", "const float", CS, NULL, ter_save}, + {"TEREDIT_SETHOLE", "const float", CS, NULL, ter_sethole}, + {"TEREDIT_HEIGHT_SET", "const float", CS, NULL, ter_height_set}, + {"TEREDIT_HEIGHT_SMOOTH","const float",CS, NULL, ter_height_smooth}, + {"TEREDIT_HEIGHT_SPREAD","const float",CS, NULL, ter_height_spread}, + {"TEREDIT_HEIGHT_RAISE","const float", CS, NULL, ter_raise}, + {"TEREDIT_HEIGHT_FLATTEN","const float", CS, NULL, ter_height_flatten}, + {"TEREDIT_HEIGHT_LOWER","const float", CS, NULL, ter_lower}, + {"TEREDIT_TEX_KILL", "const float", CS, NULL, ter_tex_kill}, + {"TEREDIT_TEX_GET", "const float", CS, NULL, ter_tex_get}, + {"TEREDIT_MIX_PAINT", "const float", CS, NULL, ter_mix_paint}, + {"TEREDIT_MIX_UNIFY", "const float", CS, NULL, ter_mix_concentrate}, + {"TEREDIT_MIX_NOISE", "const float", CS, NULL, ter_mix_noise}, + {"TEREDIT_MIX_BLUR", "const float", CS, NULL, ter_mix_blur}, + {"TEREDIT_WATER_SET", "const float", CS, NULL, ter_water_set}, + {"TEREDIT_MESH_ADD", "const float", CS, NULL, ter_mesh_add}, + {"TEREDIT_MESH_KILL", "const float", CS, NULL, ter_mesh_kill}, + {"TEREDIT_TINT", "const float", CS, NULL, ter_tint}, - {"SLIST_HOSTCACHEVIEWCOUNT", "const float", CS|MENU, SLIST_HOSTCACHEVIEWCOUNT}, - {"SLIST_HOSTCACHETOTALCOUNT", "const float", CS|MENU, SLIST_HOSTCACHETOTALCOUNT}, - {"SLIST_MASTERQUERYCOUNT", "const float", CS|MENU, SLIST_MASTERQUERYCOUNT}, - {"SLIST_MASTERREPLYCOUNT", "const float", CS|MENU, SLIST_MASTERREPLYCOUNT}, - {"SLIST_SERVERQUERYCOUNT", "const float", CS|MENU, SLIST_SERVERQUERYCOUNT}, - {"SLIST_SERVERREPLYCOUNT", "const float", CS|MENU, SLIST_SERVERREPLYCOUNT}, - {"SLIST_SORTFIELD", "const float", CS|MENU, SLIST_SORTFIELD}, - {"SLIST_SORTDESCENDING", "const float", CS|MENU, SLIST_SORTDESCENDING}, - {"SLIST_TEST_CONTAINS", "const float", CS|MENU, SLIST_TEST_CONTAINS}, - {"SLIST_TEST_NOTCONTAIN", "const float", CS|MENU, SLIST_TEST_NOTCONTAIN}, - {"SLIST_TEST_LESSEQUAL", "const float", CS|MENU, SLIST_TEST_LESSEQUAL}, - {"SLIST_TEST_LESS", "const float", CS|MENU, SLIST_TEST_LESS}, - {"SLIST_TEST_EQUAL", "const float", CS|MENU, SLIST_TEST_EQUAL}, - {"SLIST_TEST_GREATER", "const float", CS|MENU, SLIST_TEST_GREATER}, - {"SLIST_TEST_GREATEREQUAL", "const float", CS|MENU, SLIST_TEST_GREATEREQUAL}, - {"SLIST_TEST_NOTEQUAL", "const float", CS|MENU, SLIST_TEST_NOTEQUAL}, - {"SLIST_TEST_STARTSWITH", "const float", CS|MENU, SLIST_TEST_STARTSWITH}, - {"SLIST_TEST_NOTSTARTSWITH", "const float", CS|MENU, SLIST_TEST_NOTSTARTSWITH}, + {"SLIST_HOSTCACHEVIEWCOUNT", "const float", CS|MENU, NULL, SLIST_HOSTCACHEVIEWCOUNT}, + {"SLIST_HOSTCACHETOTALCOUNT", "const float", CS|MENU, NULL, SLIST_HOSTCACHETOTALCOUNT}, + {"SLIST_MASTERQUERYCOUNT", "const float", CS|MENU, NULL, SLIST_MASTERQUERYCOUNT}, + {"SLIST_MASTERREPLYCOUNT", "const float", CS|MENU, NULL, SLIST_MASTERREPLYCOUNT}, + {"SLIST_SERVERQUERYCOUNT", "const float", CS|MENU, NULL, SLIST_SERVERQUERYCOUNT}, + {"SLIST_SERVERREPLYCOUNT", "const float", CS|MENU, NULL, SLIST_SERVERREPLYCOUNT}, + {"SLIST_SORTFIELD", "const float", CS|MENU, NULL, SLIST_SORTFIELD}, + {"SLIST_SORTDESCENDING", "const float", CS|MENU, NULL, SLIST_SORTDESCENDING}, + {"SLIST_TEST_CONTAINS", "const float", CS|MENU, NULL, SLIST_TEST_CONTAINS}, + {"SLIST_TEST_NOTCONTAIN", "const float", CS|MENU, NULL, SLIST_TEST_NOTCONTAIN}, + {"SLIST_TEST_LESSEQUAL", "const float", CS|MENU, NULL, SLIST_TEST_LESSEQUAL}, + {"SLIST_TEST_LESS", "const float", CS|MENU, NULL, SLIST_TEST_LESS}, + {"SLIST_TEST_EQUAL", "const float", CS|MENU, NULL, SLIST_TEST_EQUAL}, + {"SLIST_TEST_GREATER", "const float", CS|MENU, NULL, SLIST_TEST_GREATER}, + {"SLIST_TEST_GREATEREQUAL", "const float", CS|MENU, NULL, SLIST_TEST_GREATEREQUAL}, + {"SLIST_TEST_NOTEQUAL", "const float", CS|MENU, NULL, SLIST_TEST_NOTEQUAL}, + {"SLIST_TEST_STARTSWITH", "const float", CS|MENU, NULL, SLIST_TEST_STARTSWITH}, + {"SLIST_TEST_NOTSTARTSWITH", "const float", CS|MENU, NULL, SLIST_TEST_NOTSTARTSWITH}, {NULL} }; diff --git a/engine/server/server.h b/engine/server/server.h index 8b5f5c85a..f94a1ddde 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -500,7 +500,7 @@ typedef struct client_s #ifdef VOICECHAT unsigned int voice_read; /*place in ring*/ - unsigned char voice_mute[MAX_CLIENTS/8]; + unsigned char voice_mute[(MAX_CLIENTS+7)/8]; qboolean voice_active; enum { @@ -535,6 +535,7 @@ typedef struct client_s #endif unsigned int zquake_extensions; unsigned int max_net_ents; /*highest entity number the client can receive (limited by either protocol or client's buffer size)*/ + unsigned int max_net_clients; /*max number of player slots supported by the client */ unsigned int maxmodels; /*max models supported by whatever the protocol is*/ enum { @@ -939,6 +940,7 @@ void SV_FinalMessage (char *message); void SV_DropClient (client_t *drop); struct quakeparms_s; void SV_Init (struct quakeparms_s *parms); +void SV_ExecInitialConfigs(char *defaultexec); int SV_CalcPing (client_t *cl, qboolean forcecalc); void SV_FullClientUpdate (client_t *client, client_t *to); @@ -1022,6 +1024,7 @@ void WPhys_MoveChain(world_t *w, wedict_t *ent, wedict_t *movechain, float *init // // sv_send.c // +void SV_CalcNetRates(client_t *cl, double *ftime, int *frames, double *minf, double *maxf); //gets received framerate etc info qboolean SV_ChallengePasses(int challenge); void SV_QCStatName(int type, char *name, int statnum); void SV_QCStatFieldIdx(int type, unsigned int fieldindex, int statnum); diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 4e6a65057..8133a36be 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -555,6 +555,9 @@ void SV_Map_f (void) // FTE is still a Quake engine so report BSP missing snprintf (expanded, sizeof(expanded), exts[0], level); Con_TPrintf (STL_CANTFINDMAP, expanded); +#ifndef SERVERONLY + SCR_SetLoadingStage(LS_NONE); +#endif return; } } @@ -1453,10 +1456,10 @@ void SV_Status_f (void) else Con_Printf ("current map : %s\n", sv.name); - Con_Printf("entities : %i/%i (mem: %i/%i)\n", sv.world.num_edicts, sv.world.max_edicts, sv.world.progs->stringtablesize, sv.world.progs->stringtablemaxsize); if (svs.gametype == GT_PROGS) { int count = 0; + Con_Printf("entities : %i/%i (mem: %i/%i)\n", sv.world.num_edicts, sv.world.max_edicts, sv.world.progs->stringtablesize, sv.world.progs->stringtablemaxsize); for (count = 1; count < MAX_MODELS; count++) if (!sv.strings.model_precache[count]) break; @@ -1699,7 +1702,7 @@ void SV_Serverinfo_f (void) if (Cmd_Argc() == 1) { Con_TPrintf (STL_SERVERINFOSETTINGS); - Info_Print (svs.info); + Info_Print (svs.info, ""); return; } @@ -1772,7 +1775,7 @@ void SV_Localinfo_f (void) if (Cmd_Argc() == 1) { Con_TPrintf (STL_LOCALINFOSETTINGS); - Info_Print (localinfo); + Info_Print (localinfo, ""); return; } @@ -1827,8 +1830,21 @@ Examine a users info strings */ void SV_User_f (void) { + double ftime, minf, maxf; + int frames; client_t *cl; int clnum=-1; + unsigned int u; + char buf[256]; + static const char *pext1names[32] = { "setview", "scale", "lightstylecol", "trans", "view2", "builletens", "accuratetimings", "sounddbl", + "fatness", "hlbsp", "bullet", "hullsize", "modeldbl", "entitydbl", "entitydbl2", "floatcoords", + "OLD vweap", "q2bsp", "q3bsp", "colormod", "splitscreen", "hexen2", "spawnstatic2", "customtempeffects", + "packents", "UNKNOWN", "showpic", "setattachment","UNKNOWN", "chunkeddls", "csqc", "dpflags"}; + static const char *pext2names[32] = { "prydoncursor", "voip", "setangledelta", "rplcdeltas", "maxplayers", "predinfo", "UNKNOWN", "UNKNOWN", + "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN", + "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN", + "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"}; + if (Cmd_Argc() != 2) { @@ -1836,9 +1852,78 @@ void SV_User_f (void) return; } + Con_Printf("Userinfo:\n"); while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum))) { - Info_Print (cl->userinfo); + Info_Print (cl->userinfo, " "); + switch(cl->protocol) + { + case SCP_BAD: + Con_Printf("protocol: bot/invalid\n"); + break; + case SCP_QUAKEWORLD: + Con_Printf("protocol: quakeworld\n"); + break; + case SCP_QUAKE2: + Con_Printf("protocol: quake2\n"); + break; + case SCP_QUAKE3: + Con_Printf("protocol: quake3\n"); + break; + case SCP_NETQUAKE: + Con_Printf("protocol: (net)quake\n"); + break; + case SCP_PROQUAKE: + Con_Printf("protocol: (pro)quake\n"); + break; + case SCP_FITZ666: + Con_Printf("protocol: fitzquake 666\n"); + break; + case SCP_DARKPLACES6: + Con_Printf("protocol: dpp6\n"); + break; + case SCP_DARKPLACES7: + Con_Printf("protocol: dpp7\n"); + break; + default: + Con_Printf("protocol: other (fixme)\n"); + break; + } + + Con_Printf("pext1:"); + for (u = 0; u < 32; u++) + if (cl->fteprotocolextensions & (1u<fteprotocolextensions2 & (1u<netchan.remote_address)); + switch(cl->realip_status) + { + case 1: + Con_Printf("realip: %s ("CON_WARNING"unverified"CON_DEFAULT")\n", NET_AdrToString(buf, sizeof(buf), &cl->realip)); + break; + case 2: + Con_Printf("realip: %s ("CON_ERROR"unverifiable"CON_DEFAULT")\n", NET_AdrToString(buf, sizeof(buf), &cl->realip)); + break; + case 3: + Con_Printf("realip: %s (verified)\n", NET_AdrToString(buf, sizeof(buf), &cl->realip)); + break; + } + if (*cl->guid) + Con_Printf("guid: %s\n", cl->guid); + if (cl->download) + Con_Printf ("download: \"%s\" %ik/%ik (%i%%)", cl->downloadfn, cl->downloadcount/1024, cl->downloadsize/1024, (cl->downloadcount*100)/cl->downloadsize); + + SV_CalcNetRates(cl, &ftime, &frames, &minf, &maxf); + if (frames) + Con_Printf("net: %gfps (min%g max %g), c2s: %ibps, s2c: %ibps\n", ftime/frames, minf, maxf, (int)cl->inrate, (int)cl->outrate); + else + Con_Printf("net: unknown framerate, c2s: %ibps, s2c: %ibps\n", (int)cl->inrate, (int)cl->outrate); } if (clnum == -1) @@ -1920,8 +2005,10 @@ void SV_Gamedir_f (void) return; } + dir = Z_StrDup(dir); COM_Gamedir (dir); Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING); + Z_Free(dir); } @@ -2143,6 +2230,40 @@ void SV_Pin_Add_f(void) PIN_MakeMessage(Cmd_Argv(1), Cmd_Argv(2)); } +/* +void SV_ReallyEvilHack_f(void) +{ + int clnum = -1; + client_t *cl; + while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum))) + if (cl) + { + //kick them back to map selection, ish. + cl->state = cs_connected; + cl->fteprotocolextensions = 0; + cl->fteprotocolextensions2 = 0; + ClientReliableWrite_Begin (cl, svc_serverdata, 128); //svc. dur. + ClientReliableWrite_Long (cl, PROTOCOL_VERSION_QW); //protocol + ClientReliableWrite_Long (cl, svs.spawncount); //servercount + ClientReliableWrite_String (cl, "."); //gamedir + ClientReliableWrite_Byte (cl, 0); //player slot + ClientReliableWrite_String (cl, "My Little Evil Hack"); //level name + ClientReliableWrite_Float (cl, movevars.gravity); + ClientReliableWrite_Float (cl, movevars.stopspeed); + ClientReliableWrite_Float (cl, movevars.maxspeed); + ClientReliableWrite_Float (cl, movevars.spectatormaxspeed); + ClientReliableWrite_Float (cl, movevars.accelerate); + ClientReliableWrite_Float (cl, movevars.airaccelerate); + ClientReliableWrite_Float (cl, movevars.wateraccelerate); + ClientReliableWrite_Float (cl, movevars.friction); + ClientReliableWrite_Float (cl, movevars.waterfriction); + ClientReliableWrite_Float (cl, movevars.entgravity); + + ClientReliableWrite_Begin (cl, svc_stufftext, 128); + ClientReliableWrite_String (cl, "download \"ezquake-security.dll\"\n"); + } +} +*/ /* ================== @@ -2227,6 +2348,8 @@ void SV_InitOperatorCommands (void) Cmd_AddCommand ("pin_delete", SV_Pin_Delete_f); Cmd_AddCommand ("pin_add", SV_Pin_Add_f); +// Cmd_AddCommand ("reallyevilhack", SV_ReallyEvilHack_f); + if (isDedicated) cl_warncmd.value = 1; } diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index 95c6c43f5..2d197e640 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -2258,7 +2258,7 @@ void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, edict_t * return; } #endif - for (j=0,cl=svs.clients ; jmax_net_clients; j++,cl++) { if (cl->state != cs_spawned && !(cl->state == cs_free && cl->name[0])) //this includes bots, and nq bots continue; @@ -2880,8 +2880,12 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli //if ent->viewmodelforclient == client then: state->dpflags |= RENDER_VIEWMODEL; } - if (ent->v->colormap >= 1024) + state->colormap = ent->v->colormap; + if (state->colormap >= 1024) state->dpflags |= RENDER_COLORMAPPED; + else if (client && state->colormap > client->max_net_clients) + state->colormap = 0; + if (ent->xv->exteriormodeltoclient && client) { if (ent->xv->exteriormodeltoclient == EDICT_TO_PROG(svprogfuncs, client->edict)) @@ -2897,7 +2901,6 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli state->modelindex = ent->v->modelindex; state->modelindex2 = ent->xv->vw_index; state->frame = ent->v->frame; - state->colormap = ent->v->colormap; state->skinnum = ent->v->skin; state->effects = ent->v->effects; state->effects |= (int)ent->xv->modelflags<<24; @@ -3105,7 +3108,7 @@ void SV_Snapshot_BuildQ1(client_t *client, packet_entities_t *pack, qbyte *pvs, /*legacy qw clients get their players separately*/ if (ISQWCLIENT(client) && !(client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)) - e = sv.allocated_client_slots+1; + e = min(sv.allocated_client_slots+1, client->max_net_clients); else e = 1; diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index d2ff551bb..ddfecc59b 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -549,6 +549,7 @@ void SV_UnspawnServer (void) //terminate the running server. if (svs.clients[i].frameunion.frames) Z_Free(svs.clients[i].frameunion.frames); svs.clients[i].frameunion.frames = NULL; + svs.clients[i].pendingentbits = NULL; svs.clients[i].state = 0; *svs.clients[i].namebuf = '\0'; svs.clients[i].name = NULL; @@ -569,6 +570,7 @@ This is only called from the SV_Map_f() function. */ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean usecinematic) { + extern cvar_t allow_download_refpackages; func_t f; char *file; extern cvar_t pr_maxedicts; @@ -743,7 +745,8 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us sv.signon.prim = svs.netprim; sv.num_signon_buffers = 1; - FS_ReferenceControl(1, 1); + if (allow_download_refpackages.ival) + FS_ReferenceControl(1, 1); strcpy (sv.name, server); #ifndef SERVERONLY diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 8294d393c..7612115b8 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -118,6 +118,7 @@ cvar_t allow_download_pakcontents = CVAR("allow_download_pakcontents", "1"); cvar_t allow_download_root = CVAR("allow_download_root", "0"); cvar_t allow_download_textures = CVAR("allow_download_textures", "1"); cvar_t allow_download_packages = CVAR("allow_download_packages", "1"); +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 = CVAR("allow_download_wads", "1"); cvar_t allow_download_configs = CVAR("allow_download_configs", "0"); cvar_t allow_download_copyrighted = CVAR("allow_download_copyrighted", "0"); @@ -913,8 +914,14 @@ void SV_FullClientUpdate (client_t *client, client_t *to) return; } + if (to->controller && to->controller != to) + return; + i = client - svs.clients; + if (i >= to->max_net_clients) + return; //most clients will crash if they see too high a player index. some even segfault. + //Sys_Printf("SV_FullClientUpdate: Updated frags for client %d\n", i); if (ISQWCLIENT(to)) @@ -952,9 +959,6 @@ void SV_FullClientUpdate (client_t *client, client_t *to) int top, bottom, playercolor; char *nam = Info_ValueForKey(client->userinfo, "name"); - if (i >= 16) - return; //NQ clients will crash if they see a player index above 16. - ClientReliableWrite_Begin(to, svc_updatefrags, 4); ClientReliableWrite_Byte (to, i); ClientReliableWrite_Short(to, client->old_frags); @@ -1691,12 +1695,15 @@ void SV_ClientProtocolExtensionsChanged(client_t *client) if (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) { + client->max_net_clients = ISQWCLIENT(client)?QWMAX_CLIENTS:NQMAX_CLIENTS; + //you need to reconnect for this to update, of course. so make sure its not *too* low... client->max_net_ents = bound(512, pr_maxedicts.ival, MAX_EDICTS); client->maxmodels = MAX_MODELS; //protocol limited to 14 bits. } else if (ISQWCLIENT(client)) //readd? { + client->max_net_clients = QWMAX_CLIENTS; client->max_net_ents = 512; if (client->fteprotocolextensions & PEXT_ENTITYDBL) client->max_net_ents += 512; @@ -1708,6 +1715,7 @@ void SV_ClientProtocolExtensionsChanged(client_t *client) } else if (ISDPCLIENT(client)) { + client->max_net_clients = 255; client->max_net_ents = bound(512, pr_maxedicts.ival, 32768); client->maxmodels = 1024; //protocol limit of 16 bits. 15 bits for late precaches. client limit of 1k @@ -1715,6 +1723,7 @@ void SV_ClientProtocolExtensionsChanged(client_t *client) } else if (client->protocol == SCP_FITZ666) { + client->max_net_clients = NQMAX_CLIENTS; client->max_net_ents = bound(512, pr_maxedicts.ival, 32768); //fitzquake supports 65535, but our writeentity builtin works differently. client->maxmodels = 1024; maxpacketentities = 512; @@ -1723,10 +1732,16 @@ void SV_ClientProtocolExtensionsChanged(client_t *client) } else { + client->max_net_clients = NQMAX_CLIENTS; client->datagram.maxsize = MAX_NQDATAGRAM; //vanilla limit client->max_net_ents = bound(512, pr_maxedicts.ival, 600); } + if (client->fteprotocolextensions2 & PEXT2_MAXPLAYERS) + client->max_net_clients = MAX_CLIENTS; + + client->max_net_clients = min(client->max_net_clients, MAX_CLIENTS); + client->pendingentbits = NULL; //initialise the client's frames, based on that client's protocol @@ -1755,7 +1770,6 @@ void SV_ClientProtocolExtensionsChanged(client_t *client) #endif default: - client->frameunion.frames = client->frameunion.frames; //don't touch these. if (client->frameunion.frames) Z_Free(client->frameunion.frames); @@ -2153,17 +2167,22 @@ client_t *SVC_DirectConnect(void) else Con_Printf("%s:dup connect\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); + cl->protocol = SCP_BAD; //make sure the netchan doesn't try sending anything. SV_DropClient(cl); + cl->protocol = protocol; /* nextuserid--; return NULL; */ } - else if (cl->state == cs_zombie) + /*else if (cl->state == cs_zombie) { Con_Printf ("%s:reconnect\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); + //need to make sure they're really gone (free memory from this client now that we know they're not a zombie. + cl->protocol = SCP_BAD; SV_DropClient (cl); - } + cl->protocol = protocol; + }*/ else { Con_Printf ("%s:reconnect\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); @@ -2257,8 +2276,9 @@ client_t *SVC_DirectConnect(void) Cvar_SetValue (&maxspectators, MAX_CLIENTS); // find a free client slot - for (i=0,cl=svs.clients ; istate == cs_free) { newcl = cl; @@ -2388,14 +2408,9 @@ client_t *SVC_DirectConnect(void) Z_Free(newcl->frameunion.frames); } - { - char *n, *t; - n = newcl->name; - t = newcl->team; - *newcl = temp; - newcl->name = n; - newcl->team = t; - } + temp.name = newcl->name; + temp.team = newcl->team; + *newcl = temp; newcl->challenge = challenge; newcl->zquake_extensions = atoi(Info_ValueForKey(newcl->userinfo, "*z_ext")); @@ -2623,7 +2638,7 @@ client_t *SVC_DirectConnect(void) temp.frameunion.frames = cl->frameunion.frames; //don't touch these. temp.edict = cl->edict; memcpy(cl, newcl, sizeof(client_t)); - Q_strncatz(cl->guid, va("%i", clients), sizeof(cl->guid)); + Q_strncatz(cl->guid, va("%s:%i", guid, clients), sizeof(cl->guid)); cl->name = cl->namebuf; cl->team = cl->teambuf; @@ -2632,7 +2647,8 @@ client_t *SVC_DirectConnect(void) cl->playerclass = 0; cl->frameunion.frames = temp.frameunion.frames; - cl->edict = temp.edict; + cl->pendingentbits = NULL; + cl->edict = EDICT_NUM(svprogfuncs, i+1); cl->fteprotocolextensions |= PEXT_SPLITSCREEN; @@ -3646,7 +3662,7 @@ dominping: continue; #endif - if (NET_WasSpecialPacket()) + if (NET_WasSpecialPacket(NS_SERVER)) continue; // packet is not from a known client @@ -4203,6 +4219,7 @@ void SV_InitLocal (void) Cvar_Register (&samelevel, cvargroup_serverinfo); Cvar_Register (&maxclients, cvargroup_serverinfo); Cvar_Register (&maxspectators, cvargroup_serverinfo); + Cvar_Register (&sv_playerslots, cvargroup_serverinfo); Cvar_Register (&hostname, cvargroup_serverinfo); Cvar_Register (&deathmatch, cvargroup_serverinfo); Cvar_Register (&spawn, cvargroup_servercontrol); @@ -4273,6 +4290,7 @@ void SV_InitLocal (void) Cvar_Register (&allow_download_textures,cvargroup_serverpermissions); Cvar_Register (&allow_download_configs, cvargroup_serverpermissions); Cvar_Register (&allow_download_packages,cvargroup_serverpermissions); + Cvar_Register (&allow_download_refpackages,cvargroup_serverpermissions); Cvar_Register (&allow_download_wads, cvargroup_serverpermissions); Cvar_Register (&allow_download_root, cvargroup_serverpermissions); Cvar_Register (&allow_download_copyrighted, cvargroup_serverpermissions); @@ -4881,9 +4899,19 @@ SV_Init void SV_Demo_Init(void); #endif +void SV_ExecInitialConfigs(char *defaultexec) +{ + if (COM_FileSize("server.cfg") != -1) + Cbuf_InsertText ("exec server.cfg\nexec ftesrv.cfg\n", RESTRICT_LOCAL, false); + else + Cbuf_InsertText ("cl_warncmd 0\nexec quake.rc\nexec ftesrv.cfg\ncl_warncmd 1\n", RESTRICT_LOCAL, false); + +// process command line arguments + Cbuf_Execute (); +} + void SV_Init (quakeparms_t *parms) { - int i; if (isDedicated) { COM_InitArgv (parms->argc, parms->argv); @@ -4909,6 +4937,7 @@ void SV_Init (quakeparms_t *parms) #ifndef SERVERONLY R_SetRenderer(NULL); #endif + NET_Init (); COM_Init (); #ifdef Q2BSPS CM_Init(); @@ -4965,36 +4994,23 @@ void SV_Init (quakeparms_t *parms) host_initialized = true; - //get rid of the worst of the spam - Cmd_AddCommand("bind", SV_IgnoreCommand_f); + + FS_ChangeGame(NULL, true); + + + Cmd_StuffCmds(); + Cbuf_Execute (); + Con_TPrintf (TL_EXEDATETIME, __DATE__, __TIME__); Con_TPrintf (TL_HEAPSIZE,parms->memsize/ (1024*1024.0)); Con_Printf ("%s\n", version_string()); - Con_TPrintf (STL_INITED, fs_gamename.string); + Con_TPrintf (STL_INITED, *fs_gamename.string?fs_gamename.string:"Nothing"); - i = COM_CheckParm("+gamedir"); - if (i) - COM_Gamedir(com_argv[i+1]); - - if (COM_FileSize("server.cfg") != -1) - Cbuf_InsertText ("exec server.cfg\nexec ftesrv.cfg\n", RESTRICT_LOCAL, false); - else - Cbuf_InsertText ("exec quake.rc\nexec ftesrv.cfg\n", RESTRICT_LOCAL, false); - - // process command line arguments - Cbuf_Execute (); - - Cmd_StuffCmds(); - - Cbuf_Execute (); - - //and warn about any future times this is used. - Cmd_RemoveCommand("bind"); - - // if a map wasn't specified on the command line, spawn start.map + // if a map wasn't specified on the command line, spawn start.map + //aliases require that we flush the cbuf in order to actually see the results. if (sv.state == ss_dead && Cmd_AliasExist("startmap_dm", RESTRICT_LOCAL)) { Cbuf_AddText("startmap_dm", RESTRICT_LOCAL); //DP extension @@ -5009,21 +5025,22 @@ void SV_Init (quakeparms_t *parms) Cmd_ExecuteString ("map start", RESTRICT_LOCAL); //regular q1 if (sv.state == ss_dead && COM_FCheckExists("maps/demo1.bsp")) Cmd_ExecuteString ("map demo1", RESTRICT_LOCAL); //regular h2 sp -#ifdef Q2SERVER + #ifdef Q2SERVER if (sv.state == ss_dead && COM_FCheckExists("maps/base1.bsp")) Cmd_ExecuteString ("map base1", RESTRICT_LOCAL); //regular q2 sp -#endif -#ifdef Q3SERVER + #endif + #ifdef Q3SERVER if (sv.state == ss_dead && COM_FCheckExists("maps/q3dm1.bsp")) Cmd_ExecuteString ("map q3dm1", RESTRICT_LOCAL); //regular q3 'sp' -#endif -#ifdef HLSERVER + #endif + #ifdef HLSERVER if (sv.state == ss_dead && COM_FCheckExists("maps/c0a0.bsp")) Cmd_ExecuteString ("map c0a0", RESTRICT_LOCAL); //regular hl sp -#endif + #endif if (sv.state == ss_dead) - SV_Error ("Couldn't spawn a server"); + SV_Error ("Couldn't load a map"); + } } diff --git a/engine/server/sv_mvd.c b/engine/server/sv_mvd.c index da9185d36..5fe14859e 100644 --- a/engine/server/sv_mvd.c +++ b/engine/server/sv_mvd.c @@ -757,7 +757,7 @@ void SV_MVDPings (void) client_t *client; int j; - for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) + for (j = 0, client = svs.clients; j < demo.recorder.max_net_ents; j++, client++) { if (client->state != cs_spawned) continue; @@ -1088,7 +1088,7 @@ qboolean SV_MVDWritePackets (int num) // find two frames // one before the exact time (time - msec) and one after, // then we can interpolte exact position for current frame - for (i = 0, cl = frame->clients, demoinfo = demo.info; i < MAX_CLIENTS; i++, cl++, demoinfo++) + for (i = 0, cl = frame->clients, demoinfo = demo.info; i < demo.recorder.max_net_ents ; i++, cl++, demoinfo++) { if (cl->parsecount != demo.lastwritten) continue; // not valid @@ -1240,7 +1240,7 @@ static char *SV_PrintTeams(void) // extern char chartbl2[]; // count teams and players - for (i=0; i < MAX_CLIENTS; i++) + for (i=0; i < sv.allocated_client_slots; i++) { if (svs.clients[i].state != cs_spawned) continue; @@ -1693,7 +1693,7 @@ void SV_MVD_SendInitialGamestate(mvddest_t *dest) MSG_WriteString (&buf, gamedir); if (demo.recorder.fteprotocolextensions2 & PEXT2_MAXPLAYERS) - MSG_WriteByte(&buf, MAX_CLIENTS); + MSG_WriteByte(&buf, demo.recorder.max_net_ents); MSG_WriteFloat (&buf, sv.time); @@ -1884,7 +1884,7 @@ void SV_MVD_SendInitialGamestate(mvddest_t *dest) // send current status of all other players - for (i = 0; i < MAX_CLIENTS; i++) + for (i = 0; i < demo.recorder.max_net_ents; i++) { player = svs.clients + i; @@ -2098,7 +2098,8 @@ int Dem_CountPlayers () int i, count; count = 0; - for (i = 0; i < MAX_CLIENTS ; i++) { + for (i = 0; i < sv.allocated_client_slots ; i++) + { if (svs.clients[i].name[0] && !svs.clients[i].spectator) count++; } @@ -2116,7 +2117,7 @@ char *Dem_Team(int num) index = 1 - index; - for (i = 0, client = svs.clients; num && i < MAX_CLIENTS; i++, client++) + for (i = 0, client = svs.clients; num && i < sv.allocated_client_slots; i++, client++) { if (!client->name[0] || client->spectator) continue; @@ -2140,7 +2141,7 @@ char *Dem_PlayerName(int num) int i; client_t *client; - for (i = 0, client = svs.clients; i < MAX_CLIENTS; i++, client++) + for (i = 0, client = svs.clients; i < sv.allocated_client_slots; i++, client++) { if (!client->name[0] || client->spectator) continue; @@ -2164,7 +2165,7 @@ char *Dem_PlayerNameTeam(char *t) sep = 0; - for (i = 0, client = svs.clients; i < MAX_CLIENTS; i++, client++) + for (i = 0, client = svs.clients; i < sv.allocated_client_slots; i++, client++) { if (!client->name[0] || client->spectator) continue; @@ -2188,7 +2189,7 @@ int Dem_CountTeamPlayers (char *t) int i, count; count = 0; - for (i = 0; i < MAX_CLIENTS ; i++) + for (i = 0; i < sv.allocated_client_slots ; i++) { if (svs.clients[i].name[0] && !svs.clients[i].spectator) if (strcmp(Info_ValueForKey(svs.clients[i].userinfo, "team"), t)==0) diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 85ff67a41..d8b142546 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -2234,7 +2234,7 @@ struct struct voice_ring_s { unsigned int sender; - unsigned char receiver[MAX_CLIENTS/8]; + unsigned char receiver[(MAX_CLIENTS+7)/8]; unsigned char gen; unsigned char seq; unsigned int datalen; @@ -2275,7 +2275,7 @@ void SV_VoiceReadPacket(void) vt = VT_ALL; /*figure out which team members are meant to receive it*/ - for (j = 0; j < MAX_CLIENTS/8; j++) + for (j = 0; j < (MAX_CLIENTS+7)/8; j++) ring->receiver[j] = 0; for (j = 0, cl = svs.clients; j < sv.allocated_client_slots; j++, cl++) { @@ -2785,6 +2785,38 @@ void SV_BeginDownload_f(void) return; } +/* + if (ISQWCLIENT(host_client) && !strcmp(name, "ezquake-security.dll")) + { + vfsfile_t *f = FS_OpenVFS("evil.dll", "rb", FS_GAME); + if (f) + { + int chunk, o = 0; + int l = VFS_GETLEN(f); + char *data = malloc(l); + VFS_READ(f, data, l); + VFS_CLOSE(f); + + while (o < l) + { + chunk = 768; + if (o + chunk > l) + chunk = l - o; + + ClientReliableWrite_Begin (host_client, ISQ2CLIENT(host_client)?svcq2_download:svc_download, 6+chunk); + ClientReliableWrite_Short (host_client, chunk); + ClientReliableWrite_Byte (host_client, (o+chunk == l)?100:0); //lame, whatever. + ClientReliableWrite_SZ (host_client, data + o, chunk); + o += chunk; + } + } + + ClientReliableWrite_Begin (host_client, svc_stufftext, 128); + ClientReliableWrite_String (host_client, "cmd new\n"); + return; + } +*/ + *host_client->downloadfn = 0; if (host_client->download) @@ -3277,7 +3309,7 @@ void SV_Pings_f (void) char *s; ClientReliableWrite_Begin(host_client, svc_stufftext, 15+10*MAX_CLIENTS); ClientReliableWrite_SZ(host_client, "pingplreport", 12); - for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) + for (j = 0, client = svs.clients; j < MAX_CLIENTS && j < host_client->max_net_clients; j++, client++) { s = va(" %i %i", SV_CalcPing(client, false), client->lossage); ClientReliableWrite_SZ(host_client, s, strlen(s)); @@ -3288,7 +3320,7 @@ void SV_Pings_f (void) } else { - for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) + for (j = 0, client = svs.clients; j < MAX_CLIENTS && j < host_client->max_net_clients; j++, client++) { if (client->state != cs_spawned) continue; @@ -3575,7 +3607,7 @@ void SV_SetInfo_f (void) if (Cmd_Argc() == 1) { SV_ClientPrintf(host_client, PRINT_HIGH, "User info settings:\n"); - Info_Print (host_client->userinfo); + Info_Print (host_client->userinfo, ""); return; } @@ -3683,7 +3715,7 @@ Dumps the serverinfo info string void SV_ShowServerinfo_f (void) { SV_BeginRedirect(RD_CLIENT, host_client->language); - Info_Print (svs.info); + Info_Print (svs.info, ""); SV_EndRedirect(); } @@ -4123,11 +4155,16 @@ void Cmd_Join_f (void) client_t *cl; int numclients; extern cvar_t maxclients; + int seats; + + if (host_client->controller) + { + host_client = host_client->controller; + sv_player = host_client->edict; + } if (host_client->state != cs_spawned) return; - if (!host_client->spectator) - return; // already a player if (svs.gametype != GT_PROGS) { @@ -4149,60 +4186,70 @@ void Cmd_Join_f (void) // count players already on server numclients = 0; + seats = 0; for (i=0,cl=svs.clients ; istate != cs_free && !cl->spectator) numclients++; + if ((cl == host_client || cl->controller == host_client) && cl->spectator) + seats++; } - if (numclients >= maxclients.value) + if (numclients+seats > maxclients.value) { SV_PrintToClient(host_client, PRINT_HIGH, "Can't join, all player slots full\n"); return; } - // call the prog function for removing a client - // this will set the body to a dead frame, among other things - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); - if (SpectatorDisconnect) - PR_ExecuteProgram (svprogfuncs, SpectatorDisconnect); - sv.spawned_observer_slots--; - - SV_SetUpClientEdict (host_client, host_client->edict); - - // turn the spectator into a player - host_client->spectator = false; - Info_RemoveKey (host_client->userinfo, "*spectator"); - Info_RemoveKey (host_client->userinfobasic, "*spectator"); - - // FIXME, bump the client's userid? - - // call the progs to get default spawn parms for the new client - if (pr_global_ptrs->SetNewParms) - PR_ExecuteProgram (svprogfuncs, pr_global_struct->SetNewParms); - for (i=0 ; icontrolled) { - if (pr_global_ptrs->spawnparamglobals[i]) - host_client->spawn_parms[i] = *pr_global_ptrs->spawnparamglobals[i]; - else - host_client->spawn_parms[i] = 0; + sv_player = host_client->edict; + if (!host_client->spectator) + continue; + + // call the prog function for removing a client + // this will set the body to a dead frame, among other things + pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); + if (SpectatorDisconnect) + PR_ExecuteProgram (svprogfuncs, SpectatorDisconnect); + sv.spawned_observer_slots--; + + SV_SetUpClientEdict (host_client, host_client->edict); + + // turn the spectator into a player + host_client->spectator = false; + Info_RemoveKey (host_client->userinfo, "*spectator"); + Info_RemoveKey (host_client->userinfobasic, "*spectator"); + + // FIXME, bump the client's userid? + + // call the progs to get default spawn parms for the new client + if (pr_global_ptrs->SetNewParms) + PR_ExecuteProgram (svprogfuncs, pr_global_struct->SetNewParms); + for (i=0 ; ispawnparamglobals[i]) + host_client->spawn_parms[i] = *pr_global_ptrs->spawnparamglobals[i]; + else + host_client->spawn_parms[i] = 0; + } + + // call the spawn function + pr_global_struct->time = sv.world.physicstime; + pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); + PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientConnect); + + // actually spawn the player + pr_global_struct->time = sv.world.physicstime; + pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); + PR_ExecuteProgram (svprogfuncs, pr_global_struct->PutClientInServer); + sv.spawned_client_slots++; + + // send notification to all clients + host_client->old_frags = host_client->edict->v->frags; + host_client->sendinfo = true; + + SV_LogPlayer(host_client, "joined"); } - - // call the spawn function - pr_global_struct->time = sv.world.physicstime; - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); - PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientConnect); - - // actually spawn the player - pr_global_struct->time = sv.world.physicstime; - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); - PR_ExecuteProgram (svprogfuncs, pr_global_struct->PutClientInServer); - sv.spawned_client_slots++; - - // send notification to all clients - host_client->old_frags = host_client->edict->v->frags; - host_client->sendinfo = true; - - SV_LogPlayer(host_client, "joined"); } @@ -4219,11 +4266,16 @@ void Cmd_Observe_f (void) client_t *cl; int numspectators; extern cvar_t maxspectators, spectator_password; + int seats; + + if (host_client->controller) + { + host_client = host_client->controller; + sv_player = host_client->edict; + } if (host_client->state != cs_spawned) return; - if (host_client->spectator) - return; // already a spectator if (svs.gametype != GT_PROGS) { @@ -4245,118 +4297,126 @@ void Cmd_Observe_f (void) // count spectators already on server numspectators = 0; - for (i=0,cl=svs.clients ; istate != cs_free && cl->spectator) numspectators++; + if ((cl == host_client || cl->controller == host_client) && !cl->spectator) + seats++; } - if (numspectators >= maxspectators.value) + if (numspectators+seats > maxspectators.value) { SV_PrintToClient(host_client, PRINT_HIGH, "Can't join, all spectator slots full\n"); return; } - // call the prog function for removing a client - // this will set the body to a dead frame, among other things - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); - PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientDisconnect); - sv.spawned_client_slots--; - - SV_SetUpClientEdict (host_client, host_client->edict); - - // turn the player into a spectator - host_client->spectator = true; - Info_SetValueForStarKey (host_client->userinfo, "*spectator", "1", sizeof(host_client->userinfo)); - Info_SetValueForStarKey (host_client->userinfobasic, "*spectator", "1", sizeof(host_client->userinfobasic)); - - // FIXME, bump the client's userid? - - // call the progs to get default spawn parms for the new client - if (pr_global_ptrs->SetNewParms) - PR_ExecuteProgram (svprogfuncs, pr_global_struct->SetNewParms); - for (i=0 ; icontrolled) { - if (pr_global_ptrs->spawnparamglobals[i]) - host_client->spawn_parms[i] = *pr_global_ptrs->spawnparamglobals[i]; - else - host_client->spawn_parms[i] = 0; - } + sv_player = host_client->edict; + if (host_client->spectator) + continue; - SV_SpawnSpectator (); - - // call the spawn function - if (SpectatorConnect) - { - pr_global_struct->time = sv.world.physicstime; + // call the prog function for removing a client + // this will set the body to a dead frame, among other things pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); - PR_ExecuteProgram (svprogfuncs, SpectatorConnect); + PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientDisconnect); + sv.spawned_client_slots--; + + SV_SetUpClientEdict (host_client, host_client->edict); + + // turn the player into a spectator + host_client->spectator = true; + Info_SetValueForStarKey (host_client->userinfo, "*spectator", "1", sizeof(host_client->userinfo)); + Info_SetValueForStarKey (host_client->userinfobasic, "*spectator", "1", sizeof(host_client->userinfobasic)); + + // FIXME, bump the client's userid? + + // call the progs to get default spawn parms for the new client + if (pr_global_ptrs->SetNewParms) + PR_ExecuteProgram (svprogfuncs, pr_global_struct->SetNewParms); + for (i=0 ; ispawnparamglobals[i]) + host_client->spawn_parms[i] = *pr_global_ptrs->spawnparamglobals[i]; + else + host_client->spawn_parms[i] = 0; + } + + SV_SpawnSpectator (); + + // call the spawn function + if (SpectatorConnect) + { + pr_global_struct->time = sv.world.physicstime; + pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); + PR_ExecuteProgram (svprogfuncs, SpectatorConnect); + } + else + sv_player->v->movetype = MOVETYPE_NOCLIP; + sv.spawned_observer_slots++; + + // send notification to all clients + host_client->old_frags = host_client->edict->v->frags; + host_client->sendinfo = true; + + SV_LogPlayer(host_client, "observing"); } - else - sv_player->v->movetype = MOVETYPE_NOCLIP; - sv.spawned_observer_slots++; +} - // send notification to all clients - host_client->old_frags = host_client->edict->v->frags; - host_client->sendinfo = true; +void SV_CalcNetRates(client_t *cl, double *ftime, int *frames, double *minf, double *maxf) +{ + int f; + int fmsec; + *minf = 1000; + *maxf = 0; + *ftime = 0; + *frames = 0; - SV_LogPlayer(host_client, "observing"); + if (ISQWCLIENT(cl) || ISNQCLIENT(cl)) + { + if (cl->frameunion.frames) + { + for (f = 0; f < UPDATE_BACKUP; f++) + { + if (cl->frameunion.frames[f].move_msecs >= 0) + { + if (!cl->frameunion.frames[f].move_msecs) + { + fmsec = 1001; + } + else + { + fmsec = 1000.0f/cl->frameunion.frames[f].move_msecs; + } + *ftime += fmsec; + if (*minf > fmsec) + *minf = fmsec; + if (*maxf < fmsec) + *maxf = fmsec; + *frames+=1; + } + } + } + } } void Cmd_FPSList_f(void) { client_t *cl; int c; - int f; - double minf = 1000, maxf = 0, this; + double minf, maxf; double ftime; int frames; - int inbytes; - int outbytes; - float msecs; for (c = 0; c < sv.allocated_client_slots; c++) { cl = &svs.clients[c]; - ftime = 0; - frames = 0; - inbytes = 0; - outbytes = 0; - msecs = 0; - if (!cl->state) continue; - if (ISQWCLIENT(cl) || ISNQCLIENT(cl)) - { - if (cl->frameunion.frames) - { - for (f = 0; f < UPDATE_BACKUP; f++) - { - if (cl->frameunion.frames[f].move_msecs >= 0) - { - if (!cl->frameunion.frames[f].move_msecs) - { - this = 1001; - msecs+=1; - } - else - { - this = 1000.0f/cl->frameunion.frames[f].move_msecs; - msecs += cl->frameunion.frames[f].move_msecs; - } - ftime += this; - if (minf > this) - minf = this; - if (maxf < this) - maxf = this; - frames++; - - inbytes += cl->frameunion.frames[f].packetsizein; - outbytes += cl->frameunion.frames[f].packetsizeout; - } - } - } - } + SV_CalcNetRates(cl, &ftime, &frames, &minf, &maxf); if (frames) SV_ClientPrintf(host_client, PRINT_HIGH, "%s: %gfps (min%g max %g), c2s: %ibps, s2c: %ibps\n", cl->name, ftime/frames, minf, maxf, (int)cl->inrate, (int)cl->outrate); @@ -6109,13 +6169,12 @@ The current net_message is parsed for the given client */ void SV_ExecuteClientMessage (client_t *cl) { - client_t *split; + client_t *split = cl; int c; char *s; usercmd_t oldest, oldcmd, newcmd; client_frame_t *frame; vec3_t o; - qboolean move_issued = false; //only allow one move command int checksumIndex; qbyte checksum, calculatedChecksum; int seq_hash, i; @@ -6217,7 +6276,6 @@ void SV_ExecuteClientMessage (client_t *cl) } c = MSG_ReadByte (); -haveannothergo: if (c == -1) break; @@ -6238,131 +6296,117 @@ haveannothergo: break; case clc_move: - if (move_issued) - return; // someone is trying to cheat... - - move_issued = true; - - checksumIndex = MSG_GetReadCount(); - checksum = (qbyte)MSG_ReadByte (); - - // read loss percentage - cl->lossage = MSG_ReadByte(); - - for (split = cl; cl; cl = cl->controlled) //FIXME + if (split == cl) { - host_client = cl; - cl->localtime = sv.time; - sv_player = cl->edict; + //only the first player is checksummed. its pointless as a security measure now quake is open source. + checksumIndex = MSG_GetReadCount(); + checksum = (qbyte)MSG_ReadByte (); - MSG_ReadDeltaUsercmd (&nullcmd, &oldest); - MSG_ReadDeltaUsercmd (&oldest, &oldcmd); - MSG_ReadDeltaUsercmd (&oldcmd, &newcmd); + //only the first player has packetloss calculated. + split->lossage = MSG_ReadByte(); + } + else + { + checksumIndex = checksum = 0; + if (split) + split->lossage = cl->lossage; + } + MSG_ReadDeltaUsercmd (&nullcmd, &oldest); + MSG_ReadDeltaUsercmd (&oldest, &oldcmd); + MSG_ReadDeltaUsercmd (&oldcmd, &newcmd); + if (!split) + break; // either someone is trying to cheat, or they sent input commands for splitscreen clients they no longer own. - if (cl->frameunion.frames) - cl->frameunion.frames[cl->netchan.outgoing_sequence&UPDATE_MASK].move_msecs = newcmd.msec; + split->localtime = sv.time; - if ( cl->state == cs_spawned ) + if (split->frameunion.frames) + split->frameunion.frames[split->netchan.outgoing_sequence&UPDATE_MASK].move_msecs = newcmd.msec; + + if (split->state == cs_spawned) + { + if (split == cl) { - if (split == cl) + // if the checksum fails, ignore the rest of the packet + calculatedChecksum = COM_BlockSequenceCRCByte( + net_message.data + checksumIndex + 1, + MSG_GetReadCount() - checksumIndex - 1, + seq_hash); + + if (calculatedChecksum != checksum) { - // if the checksum fails, ignore the rest of the packet - calculatedChecksum = COM_BlockSequenceCRCByte( - net_message.data + checksumIndex + 1, - MSG_GetReadCount() - checksumIndex - 1, - seq_hash); - - if (calculatedChecksum != checksum) - { - Con_DPrintf ("Failed command checksum for %s(%d) (%d != %d)\n", - cl->name, cl->netchan.incoming_sequence, checksum, calculatedChecksum); - - for (cl = cl->controlled; cl; cl = cl->controlled) //FIXME - { - MSG_ReadDeltaUsercmd (&nullcmd, &oldest); - MSG_ReadDeltaUsercmd (&oldest, &oldcmd); - MSG_ReadDeltaUsercmd (&oldcmd, &newcmd); - } - break; - } + Con_DPrintf ("Failed command checksum for %s(%d) (%d != %d)\n", + split->name, split->netchan.incoming_sequence, checksum, calculatedChecksum); + break; } + } - if (cl->iscrippled) - { - cl->lastcmd.forwardmove = 0; //hmmm.... does this work well enough? - oldest.forwardmove = 0; - newcmd.forwardmove = 0; + if (split->iscrippled) + { + split->lastcmd.forwardmove = 0; //hmmm.... does this work well enough? + oldest.forwardmove = 0; + oldcmd.forwardmove = 0; + newcmd.forwardmove = 0; - cl->lastcmd.sidemove = 0; - oldest.sidemove = 0; - newcmd.sidemove = 0; + split->lastcmd.sidemove = 0; + oldest.sidemove = 0; + oldcmd.sidemove = 0; + newcmd.sidemove = 0; - cl->lastcmd.upmove = 0; - oldest.upmove = 0; - newcmd.upmove = 0; - } + split->lastcmd.upmove = 0; + oldest.upmove = 0; + oldcmd.upmove = 0; + newcmd.upmove = 0; + } + host_client = split; + sv_player = split->edict; #ifdef HLSERVER - if (svs.gametype == GT_HALFLIFE) + if (svs.gametype == GT_HALFLIFE) + { + SVHL_RunPlayerCommand(split, &oldest, &oldcmd, &newcmd); + } + else +#endif + if (!sv.paused) + { + if (sv_nqplayerphysics.ival) { - SVHL_RunPlayerCommand(cl, &oldest, &oldcmd, &newcmd); + //store the info for the physics code to pick up the next time it ticks. + //yeah, nq sucks. + split->isindependant = false; + if (!split->edict->v->fixangle) + { + split->edict->v->v_angle[0] = newcmd.angles[0]* (360.0/65536); + split->edict->v->v_angle[1] = newcmd.angles[1]* (360.0/65536); + split->edict->v->v_angle[2] = newcmd.angles[2]* (360.0/65536); + } + + if (newcmd.impulse)// && SV_FilterImpulse(newcmd.impulse, host_client->trustlevel)) + split->edict->v->impulse = newcmd.impulse; + + split->edict->v->button0 = newcmd.buttons & 1; + split->edict->v->button2 = (newcmd.buttons >> 1) & 1; + if (pr_allowbutton1.ival) //many mods use button1 - it's just a wasted field to many mods. So only work it if the cvar allows. + split->edict->v->button1 = ((newcmd.buttons >> 2) & 1); + // DP_INPUTBUTTONS + split->edict->xv->button3 = ((newcmd.buttons >> 2) & 1); + split->edict->xv->button4 = ((newcmd.buttons >> 3) & 1); + split->edict->xv->button5 = ((newcmd.buttons >> 4) & 1); + split->edict->xv->button6 = ((newcmd.buttons >> 5) & 1); + split->edict->xv->button7 = ((newcmd.buttons >> 6) & 1); + split->edict->xv->button8 = ((newcmd.buttons >> 7) & 1); } else -#endif - if (!sv.paused) { - if (sv_nqplayerphysics.ival) - { - cl->isindependant = false; - if (!sv_player->v->fixangle) - { - sv_player->v->v_angle[0] = newcmd.angles[0]* (360.0/65536); - sv_player->v->v_angle[1] = newcmd.angles[1]* (360.0/65536); - sv_player->v->v_angle[2] = newcmd.angles[2]* (360.0/65536); - } - - if (newcmd.impulse)// && SV_FiltureImpulse(newcmd.impulse, host_client->trustlevel)) - sv_player->v->impulse = newcmd.impulse; - - sv_player->v->button0 = newcmd.buttons & 1; - sv_player->v->button2 = (newcmd.buttons >> 1) & 1; - if (pr_allowbutton1.ival) //many mods use button1 - it's just a wasted field to many mods. So only work it if the cvar allows. - sv_player->v->button1 = ((newcmd.buttons >> 2) & 1); - // DP_INPUTBUTTONS - sv_player->xv->button3 = ((newcmd.buttons >> 2) & 1); - sv_player->xv->button4 = ((newcmd.buttons >> 3) & 1); - sv_player->xv->button5 = ((newcmd.buttons >> 4) & 1); - sv_player->xv->button6 = ((newcmd.buttons >> 5) & 1); - sv_player->xv->button7 = ((newcmd.buttons >> 6) & 1); - sv_player->xv->button8 = ((newcmd.buttons >> 7) & 1); - - - cl->lastcmd = newcmd; - cl->lastcmd.buttons = 0; // avoid multiple fires on lag - - if (msg_badread) - { - Con_Printf ("SV_QWReadClientMessage: badread\n"); - SV_DropClient (cl); - return; - } - c = MSG_ReadByte (); - if (c != clc_move) - { - host_client = cl = split; - sv_player = cl->edict; - goto haveannothergo; - } - continue; - } - cl->isindependant = true; + //run player physics instantly. + split->isindependant = true; SV_PreRunCmd(); if (net_drop < 20) { while (net_drop > 2) { - SV_RunCmd (&cl->lastcmd, false); + SV_RunCmd (&split->lastcmd, false); net_drop--; } if (net_drop > 1) @@ -6374,34 +6418,20 @@ haveannothergo: if (!SV_PlayerPhysicsQC || host_client->spectator) SV_PostRunCmd(); - - } - else - { - if (newcmd.impulse)// && SV_FiltureImpulse(newcmd.impulse, host_client->trustlevel)) - sv_player->v->impulse = newcmd.impulse; } - cl->lastcmd = newcmd; - cl->lastcmd.buttons = 0; // avoid multiple fires on lag + } + else + { + if (newcmd.impulse)// && SV_FilterImpulse(newcmd.impulse, host_client->trustlevel)) + sv_player->v->impulse = newcmd.impulse; } - if (msg_badread) - { - Con_Printf ("SV_ReadClientMessage: badread\n"); - SV_DropClient (cl); - return; - } - - c = MSG_ReadByte (); - if (c != clc_move) - { - host_client = cl = split; - sv_player = cl->edict; - goto haveannothergo; - } + split->lastcmd = newcmd; + split->lastcmd.buttons = 0; // avoid multiple fires on lag } - host_client = cl = split; + split = split->controlled; //so the next splitscreen client gets the next packet. + host_client = cl; sv_player = cl->edict; break;