diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index 82688547c..4caf99533 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -502,11 +502,16 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * Con_Printf("%3i: Update %4i 0x%x\n", msg_readcount, entnum, bits); if (bits & UF_RESET) + { +// Con_Printf("%3i: Reset %i @ %i\n", msg_readcount, entnum, cls.netchan.incoming_sequence); *news = *baseline; + } else if (!olds) { - Con_DPrintf("New entity without reset\n"); - *news = *baseline; + /*reset got lost, probably the data will be filled in later - FIXME: we should probably ignore this entity*/ +// Con_DPrintf("New entity without reset\n"); + memset(news, 0, sizeof(*news)); +// *news = *baseline; } else *news = *olds; @@ -600,12 +605,15 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * news->u.q1.msec = MSG_ReadByte(); else news->u.q1.msec = 0; + if (predbits & UFP_WEAPONFRAME) + { + news->u.q1.weaponframe = MSG_ReadByte(); + if (news->u.q1.weaponframe & 0x80) + news->u.q1.weaponframe = (news->u.q1.weaponframe & 127) | (MSG_ReadByte()<<7); + } } else - { - news->u.q1.pmovetype = 0; news->u.q1.msec = 0; - } if (bits & UF_MODEL) { @@ -667,18 +675,12 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * if (bits & UF_FATNESS) news->fatness = MSG_ReadByte(); - - - - /*update the prediction info if needed*/ - if ((bits & UF_PREDINFO) && (news->number-1) < cl.allocated_client_slots) - { - frame_t *fram; - fram = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; - CL_PlayerFrameUpdated(&fram->playerstate[news->number-1], news, cls.netchan.incoming_sequence); - } } +/* +Note: strictly speaking, you don't need multiple frames, just two and flip between them. +FTE retains the full 64 frames because its interpolation will go multiple packets back in time to cover packet loss. +*/ void CLFTE_ParseEntities(void) { int oldpacket, newpacket; @@ -686,6 +688,7 @@ void CLFTE_ParseEntities(void) unsigned short newnum, oldnum; int oldindex; qboolean isvalid = false; + entity_state_t *e; // int i; // for (i = cl.validsequence+1; i < cls.netchan.incoming_sequence; i++) @@ -700,7 +703,7 @@ void CLFTE_ParseEntities(void) cl.frames[newpacket].invalid = true; - if (cls.netchan.incoming_sequence >= cl.validsequence + UPDATE_BACKUP) + if (!cl.validsequence || cls.netchan.incoming_sequence-cl.validsequence >= UPDATE_BACKUP-1) { oldp = &nullp; oldp->num_entities = 0; @@ -738,6 +741,7 @@ void CLFTE_ParseEntities(void) } if (newnum == 0x8000) { + /*removal of world - means forget all entities*/ if (cl_shownet.ival >= 3) Con_Printf("%3i: Reset all\n", msg_readcount); newp->num_entities = 0; @@ -763,7 +767,7 @@ void CLFTE_ParseEntities(void) if (newnum & 0x8000) { if (cl_shownet.ival >= 3) - Con_Printf("%3i: Remove %i\n", msg_readcount, (newnum&32767)); + Con_Printf("%3i: Remove %i @ %i\n", msg_readcount, (newnum&32767), cls.netchan.incoming_sequence); if (oldnum == (newnum&0x7fff)) oldindex++; continue; @@ -786,6 +790,21 @@ void CLFTE_ParseEntities(void) } } + for (oldindex = 0; oldindex < newp->num_entities; oldindex++) + { + e = newp->entities + oldindex; + if (e->number > cl.allocated_client_slots) + break; + + /*update the prediction info if needed*/ + if (e->u.q1.pmovetype) + { + frame_t *fram; + fram = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + CL_PlayerFrameUpdated(&fram->playerstate[e->number-1], e, cls.netchan.incoming_sequence); + } + } + if (isvalid) { cl.oldvalidsequence = cl.validsequence; @@ -794,7 +813,10 @@ void CLFTE_ParseEntities(void) cl.frames[newpacket].invalid = false; } else + { + newp->num_entities = 0; cl.validsequence = 0; + } /*ackedinputsequence is updated when we have new player prediction info*/ cl.ackedinputsequence = cls.netchan.incoming_sequence; @@ -3170,6 +3192,17 @@ void CL_ParsePlayerinfo (void) TP_ParsePlayerInfo(oldstate, state, info); + cl.players[num].stats[STAT_WEAPONFRAME] = state->weaponframe; + cl.players[num].statsf[STAT_WEAPONFRAME] = state->weaponframe; + for (i = 0; i < cl.splitclients; i++) + { + if (cl.playernum[i] == num) + { + cl.stats[i][STAT_WEAPONFRAME] = state->weaponframe; + cl.statsf[i][STAT_WEAPONFRAME] = state->weaponframe; + } + } + if (cl.splitclients < MAX_SPLITS) { extern cvar_t cl_splitscreen; @@ -3382,6 +3415,15 @@ guess_pm_type: TP_ParsePlayerInfo(oldstate, state, info); + for (i = 0; i < cl.splitclients; i++) + { + if (cl.playernum[i] == num) + { + cl.stats[i][STAT_WEAPONFRAME] = state->weaponframe; + cl.statsf[i][STAT_WEAPONFRAME] = state->weaponframe; + } + } + if (cl.worldmodel && cl_lerp_players.ival) { player_state_t exact; @@ -3938,7 +3980,7 @@ void CL_SetSolidEntities (void) if (!state->solid && !state->skinnum) continue; - if (state->solid == 31) + if (state->solid == ES_SOLID_BSP) { /*bsp model size*/ if (state->modelindex <= 0) continue; @@ -4129,6 +4171,7 @@ void CL_SetSolidPlayers (void) memset(pent, 0, sizeof(physent_t)); VectorCopy(pplayer->origin, pent->origin); + pent->info = j+1; VectorCopy(player_mins, pent->mins); VectorCopy(player_maxs, pent->maxs); if (++pmove.numphysent == MAX_PHYSENTS) //we just hit 88 miles per hour. diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index 088750f3e..df8a7ea59 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -520,7 +520,7 @@ void CL_AdjustAngles (int pnum, double frametime) quant *= speed; in_rotate -= quant; if (ruleset_allow_frj.ival) - cl.viewangles[pnum][YAW] += quant; + cl.viewanglechange[pnum][YAW] += quant; } if (!(in_strafe.state[pnum] & 1)) @@ -528,9 +528,8 @@ void CL_AdjustAngles (int pnum, double frametime) quant = cl_yawspeed.ival; if (cl.fpd & FPD_LIMIT_YAW || !ruleset_allow_frj.ival) quant = bound(-900, quant, 900); - cl.viewangles[pnum][YAW] -= speed*quant * CL_KeyState (&in_right, pnum); - cl.viewangles[pnum][YAW] += speed*quant * CL_KeyState (&in_left, pnum); - cl.viewangles[pnum][YAW] = anglemod(cl.viewangles[pnum][YAW]); + cl.viewanglechange[pnum][YAW] -= speed*quant * CL_KeyState (&in_right, pnum); + cl.viewanglechange[pnum][YAW] += speed*quant * CL_KeyState (&in_left, pnum); } if (in_klook.state[pnum] & 1) { @@ -538,8 +537,8 @@ void CL_AdjustAngles (int pnum, double frametime) quant = cl_pitchspeed.ival; if (cl.fpd & FPD_LIMIT_PITCH || !ruleset_allow_frj.ival) quant = bound(-700, quant, 700); - cl.viewangles[pnum][PITCH] -= speed*quant * CL_KeyState (&in_forward, pnum); - cl.viewangles[pnum][PITCH] += speed*quant * CL_KeyState (&in_back, pnum); + cl.viewanglechange[pnum][PITCH] -= speed*quant * CL_KeyState (&in_forward, pnum); + cl.viewanglechange[pnum][PITCH] += speed*quant * CL_KeyState (&in_back, pnum); } up = CL_KeyState (&in_lookup, pnum); @@ -548,19 +547,11 @@ void CL_AdjustAngles (int pnum, double frametime) quant = cl_pitchspeed.ival; if (!ruleset_allow_frj.ival) quant = bound(-700, quant, 700); - cl.viewangles[pnum][PITCH] -= speed*cl_pitchspeed.ival * up; - cl.viewangles[pnum][PITCH] += speed*cl_pitchspeed.ival * down; + cl.viewanglechange[pnum][PITCH] -= speed*cl_pitchspeed.ival * up; + cl.viewanglechange[pnum][PITCH] += speed*cl_pitchspeed.ival * down; if (up || down) - V_StopPitchDrift (pnum); - - CL_ClampPitch(pnum); - - if (cl.viewangles[pnum][ROLL] > 50) - cl.viewangles[pnum][ROLL] = 50; - if (cl.viewangles[pnum][ROLL] < -50) - cl.viewangles[pnum][ROLL] = -50; - + V_StopPitchDrift (pnum); } /* @@ -612,10 +603,60 @@ int MakeChar (int i) void CL_ClampPitch (int pnum) { + vec3_t view[4]; + vec3_t impact, norm; + float mat[16], mat2[16]; + static float oldtime; + float timestep = realtime - oldtime; + oldtime = realtime; + + if (1) + { + AngleVectors(cl.viewangles[pnum], view[0], view[1], view[2]); + Matrix4x4_RM_FromVectors(mat, view[0], view[1], view[2], vec3_origin); + + Matrix4_Multiply(Matrix4x4_CM_NewRotation(-cl.viewanglechange[pnum][PITCH], 0, 1, 0), mat, mat2); + Matrix4_Multiply(Matrix4x4_CM_NewRotation(cl.viewanglechange[pnum][YAW], 0, 0, 1), mat2, mat); + + Matrix3x4_RM_ToVectors(mat, view[0], view[1], view[2], view[3]); + + VectorMA(cl.simorg[pnum], -48, view[2], view[3]); + if (!TraceLineN(cl.simorg[pnum], view[3], impact, norm)) + { + norm[0] = 0; + norm[1] = 0; + norm[2] = 1; + } + + { + vec3_t cross; + float roll; + float dot; + /*keep the roll relative to the 'ground'*/ + CrossProduct(norm, view[2], cross); + dot = DotProduct(view[0], cross); + roll = timestep * 720/M_PI * -(dot); + Con_Printf("%f %f\n", dot, roll); + Matrix4_Multiply(Matrix4x4_CM_NewRotation(roll, 1, 0, 0), mat, mat2); + Matrix3x4_RM_ToVectors(mat2, view[0], view[1], view[2], view[3]); + } + + VectorAngles(view[0], view[2], cl.viewangles[pnum]); + cl.viewangles[pnum][PITCH]=360 - cl.viewangles[pnum][PITCH]; + VectorClear(cl.viewanglechange[pnum]); + + return; + } + + cl.viewangles[pnum][PITCH] += cl.viewanglechange[pnum][PITCH]; + cl.viewangles[pnum][YAW] += cl.viewanglechange[pnum][YAW]; + cl.viewangles[pnum][ROLL] += cl.viewanglechange[pnum][ROLL]; + VectorClear(cl.viewanglechange[pnum]); + #ifdef Q2CLIENT - float pitch; if (cls.protocol == CP_QUAKE2) { + float pitch; pitch = SHORT2ANGLE(cl.q2frame.playerstate.pmove.delta_angles[PITCH]); if (pitch > 180) pitch -= 360; @@ -647,6 +688,11 @@ void CL_ClampPitch (int pnum) if (cl.viewangles[pnum][PITCH] < cl.minpitch) cl.viewangles[pnum][PITCH] = cl.minpitch; } + + if (cl.viewangles[pnum][ROLL] > 50) + cl.viewangles[pnum][ROLL] = 50; + if (cl.viewangles[pnum][ROLL] < -50) + cl.viewangles[pnum][ROLL] = -50; } /* @@ -659,6 +705,8 @@ void CL_FinishMove (usercmd_t *cmd, int msecs, int pnum) int i; int bits; + CL_ClampPitch(pnum); + // // always dump the first two message, because it may contain leftover inputs // from the last level @@ -1375,6 +1423,7 @@ void CL_SendCmd (double frametime, qboolean mainloop) if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) { extern cvar_t cl_splitscreen; + cl.ackedinputsequence = cls.netchan.outgoing_sequence; i = cls.netchan.outgoing_sequence & UPDATE_MASK; cl.frames[i].senttime = realtime; // we haven't gotten a reply yet // cl.frames[i].receivedtime = -1; // we haven't gotten a reply yet diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index c4878d267..095108012 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -2781,7 +2781,7 @@ void CL_ReadPackets (void) // check timeout // if (cls.state >= ca_connected - && realtime - cls.netchan.last_received > cl_timeout.value) + && realtime - cls.netchan.last_received > cl_timeout.value && !cls.demoplayback) { #ifndef CLIENTONLY /*don't timeout when we're the actual server*/ diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index 0de3a538c..9198e57ae 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -24,8 +24,6 @@ cvar_t cl_nopred = SCVAR("cl_nopred","0"); extern cvar_t cl_lerp_players; cvar_t cl_pushlatency = SCVAR("pushlatency","-999"); -extern frame_t *view_frame; - extern float pm_airaccelerate; extern usercmd_t independantphysics[MAX_SPLITS]; @@ -768,7 +766,7 @@ static void CL_DecodeStateSize(unsigned short solid, int modelindex, vec3_t mins void CL_PlayerFrameUpdated(player_state_t *plstate, entity_state_t *state, int sequence) { /*update the prediction info*/ - int pmtype; + int pmtype, i; if (state->u.q1.pmovetype == MOVETYPE_NOCLIP) { if (cls.z_ext & Z_EXT_PM_TYPE_NEW) @@ -790,6 +788,17 @@ void CL_PlayerFrameUpdated(player_state_t *plstate, entity_state_t *state, int s VectorScale(state->u.q1.velocity, 1/8.0, plstate->velocity); plstate->messagenum = sequence; + cl.players[state->number-1].stats[STAT_WEAPONFRAME] = state->u.q1.weaponframe; + 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) + { + cl.stats[i][STAT_WEAPONFRAME] = state->u.q1.weaponframe; + cl.statsf[i][STAT_WEAPONFRAME] = state->u.q1.weaponframe; + } + } + CL_DecodeStateSize(state->solid, state->modelindex, plstate->szmins, plstate->szmaxs); } @@ -891,7 +900,6 @@ void CL_PredictMovePNum (int pnum) { return; } - CL_ClampPitch(pnum); if (cls.netchan.outgoing_sequence - cl.ackedinputsequence >= UPDATE_BACKUP-1) { //lagging like poo. if (!cl.intermission) //keep the angles working though. @@ -977,7 +985,7 @@ fixedorg: goto fixedorg; } - to = &cl.frames[cl.ackedinputsequence & UPDATE_MASK]; + to = &cl.frames[cl.validsequence & UPDATE_MASK]; from = &cl.frames[cl.oldvalidsequence & UPDATE_MASK]; //figure out the lerp factor @@ -1030,8 +1038,6 @@ fixedorg: CL_PredictUsercmd (pnum, &from->playerstate[cl.playernum[pnum]] , &to->playerstate[cl.playernum[pnum]], &to->cmd[pnum]); - cl.onground[pnum] = pmove.onground; - if (to->senttime >= realtime) break; from = to; @@ -1046,9 +1052,8 @@ fixedorg: to->senttime = realtime; CL_PredictUsercmd (pnum, &from->playerstate[cl.playernum[pnum]] , &to->playerstate[cl.playernum[pnum]], &to->cmd[pnum]); - - cl.onground[pnum] = pmove.onground; } + cl.onground[pnum] = pmove.onground; stepheight = to->playerstate[cl.playernum[pnum]].origin[2] - from->playerstate[cl.playernum[pnum]].origin[2]; if (cl.nolocalplayer[pnum]) diff --git a/engine/client/client.h b/engine/client/client.h index cb3eee302..cdf34c5d4 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -538,6 +538,7 @@ typedef struct // sent to the server each frame. And only reset at level change // and teleport times vec3_t viewangles[MAX_SPLITS]; + vec3_t viewanglechange[MAX_SPLITS]; // the client simulates or interpolates movement to get these values double time; // this is the time value that the client diff --git a/engine/client/console.c b/engine/client/console.c index 70916bb46..883455e37 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -901,10 +901,10 @@ void Con_DrawInput (int left, int right, int y) rhs = x + left; if (cursorframe) { - extern cvar_t com_parseutf8; - if (com_parseutf8.ival) - Font_DrawChar(rhs, y, (*cursor&~(CON_BGMASK|CON_FGMASK)) | (COLOR_BLUE<0 && (key_lines[edit_line][key_linepos] & 0xc0) != 0x80) { while((key_lines[edit_line][key_linepos+charlen] & 0xc0) == 0x80) @@ -577,7 +577,7 @@ void Key_Console (unsigned int unicode, int key) if (key_linepos > 1) { int charlen = 1; - if (com_parseutf8.ival) + if (com_parseutf8.ival>0) { while (key_linepos > charlen && (key_lines[edit_line][key_linepos-charlen] & 0xc0) == 0x80) charlen++; @@ -730,10 +730,10 @@ void Key_Console (unsigned int unicode, int key) unsigned char c2; unsigned char c3; - if (unicode > 127) + if (unicode > ((com_parseutf8.ival<0)?255:127)) { extern cvar_t com_parseutf8; - if (com_parseutf8.ival) + if (com_parseutf8.ival>0) { if (unicode > 0xffff) { diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 25cd3afe3..178dad9ab 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -1113,9 +1113,6 @@ static void QCBUILTIN PF_cs_unproject (progfuncs_t *prinst, struct globalvars_s //clear scene, and set up the default stuff. static void QCBUILTIN PF_R_ClearScene (progfuncs_t *prinst, struct globalvars_s *pr_globals) { - extern frame_t *view_frame; - extern player_state_t *view_message; - if (*prinst->callargc > 0) CSQC_ChangeLocalPlayer(G_FLOAT(OFS_PARM0)); @@ -1133,12 +1130,6 @@ static void QCBUILTIN PF_R_ClearScene (progfuncs_t *prinst, struct globalvars_s skel_dodelete(csqcprogs); CL_SwapEntityLists(); - view_frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; - view_message = &view_frame->playerstate[cl.playernum[csqc_lplayernum]]; -#ifdef NQPROT - if (cls.protocol == CP_NETQUAKE || !view_message->messagenum) - view_message->weaponframe = cl.stats[csqc_lplayernum][STAT_WEAPONFRAME]; -#endif V_CalcRefdef(csqc_lplayernum); //set up the defaults (for player 0) csqc_addcrosshair = false; @@ -4682,9 +4673,11 @@ void CSQC_World_GetFrameState(world_t *w, wedict_t *win, framestate_t *out) void CSQC_Shutdown(void) { - search_close_progs(csqcprogs, false); if (csqcprogs) { + search_close_progs(csqcprogs, false); + PR_fclose_progs(csqcprogs); + CSQC_ForgetThreads(); CloseProgs(csqcprogs); } diff --git a/engine/client/view.c b/engine/client/view.c index 04eb7ffb8..ec47c1a02 100644 --- a/engine/client/view.c +++ b/engine/client/view.c @@ -101,7 +101,6 @@ extern cvar_t cl_chasecam; float v_dmg_time[MAX_SPLITS], v_dmg_roll[MAX_SPLITS], v_dmg_pitch[MAX_SPLITS]; -frame_t *view_frame; player_state_t *view_message; /* @@ -1111,14 +1110,14 @@ void V_CalcRefdef (int pnum) else if (scr_viewsize.value == 80) view->origin[2] += 0.5; - if (!view_message || view_message->flags & (PF_GIB|PF_DEAD) || (unsigned int)cl.stats[pnum][STAT_WEAPON] >= MAX_MODELS) + if (cl.stats[pnum][STAT_HEALTH] > 0 && (unsigned int)cl.stats[pnum][STAT_WEAPON] >= MAX_MODELS) view->model = NULL; else view->model = cl.model_precache[cl.stats[pnum][STAT_WEAPON]]; #ifdef HLCLIENT if (!CLHL_AnimateViewEntity(view)) #endif - view->framestate.g[FS_REG].frame[0] = view_message?view_message->weaponframe:0; + view->framestate.g[FS_REG].frame[0] = cl.stats[pnum][STAT_WEAPONFRAME]; // set up the refresh position if (v_gunkick.value) @@ -1309,12 +1308,7 @@ void V_RenderPlayerViews(int plnum) int viewnum; #endif SCR_VRectForPlayer(&r_refdef.vrect, plnum); - view_message = &view_frame->playerstate[cl.playernum[plnum]]; -#ifdef NQPROT - if (cls.protocol == CP_NETQUAKE) - view_message->weaponframe = cl.stats[0][STAT_WEAPONFRAME]; -#endif - cl.simangles[plnum][ROLL] = 0; // FIXME @@@ +// cl.simangles[plnum][ROLL] = 0; // FIXME @@@ DropPunchAngle (plnum); @@ -1485,8 +1479,6 @@ void V_RenderView (void) RSpeedEnd(RSPEED_LINKENTITIES); } - view_frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; - R_PushDlights (); r_secondaryview = 0; diff --git a/engine/common/common.c b/engine/common/common.c index 401c412d1..c267ee625 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -95,7 +95,8 @@ cvar_t gameversion_max = CVARD("gameversion_max","", "gamecode version for serve cvar_t fs_gamename = CVARFD("fs_gamename", "", CVAR_NOSET, "The filesystem is trying to run this game"); 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 parse. 2 parse, but stop parsing that string if a char was malformed. +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. +cvar_t com_highlightcolor = CVARD("com_highlightcolor", STRINGIFY(COLOR_RED), "ANSI colour to be used for highlighted text, used when com_parseutf8 is active."); qboolean com_modified; // set true if using non-id files @@ -1973,7 +1974,7 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t if (*str == 1 || *str == 2) { if (com_parseutf8.ival) - defaultflags = (defaultflags&~CON_FGMASK) | (COLOR_MAGENTA< 0) { //check for utf-8 //uc is the output unicode char @@ -3363,6 +3364,7 @@ void COM_Init (void) Cvar_Register (&gameversion_min, "Gamecode"); Cvar_Register (&gameversion_max, "Gamecode"); Cvar_Register (&com_parseutf8, "Internationalisation"); + Cvar_Register (&com_highlightcolor, "Internationalisation"); com_parseutf8.ival = 1; diff --git a/engine/common/fs.c b/engine/common/fs.c index 8476a5198..1a363b2e3 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -1740,7 +1740,7 @@ void COM_Gamedir (const char *dir) /*some modern non-compat settings*/ #define DMFCFG "set com_parseutf8 1\npm_airstep 1\nsv_demoExtensions 1\n" /*set some stuff so our regular qw client appears more like hexen2*/ -#define HEX2CFG "set_calc cl_playerclass int (random * 5) + 1\nset r_particlesdesc \"spikeset tsshaft h2part\"\nset sv_maxspeed 640\nset watervis 1\nset r_wateralpha 0.5\nset sv_pupglow 1\nset cl_model_bobbing 1\nsv_sound_land \"fx/thngland.wav\"\n" +#define HEX2CFG "set com_parseutf8 -1\nset gl_font gfx/hexen2\nset in_builtinkeymap 0\nset_calc cl_playerclass int (random * 5) + 1\nset r_particlesdesc \"spikeset tsshaft h2part\"\nset sv_maxspeed 640\nset watervis 1\nset r_wateralpha 0.5\nset sv_pupglow 1\nset cl_model_bobbing 1\nsv_sound_land \"fx/thngland.wav\"\n" /*Q3's ui doesn't like empty model/headmodel/handicap cvars, even if the gamecode copes*/ #define Q3CFG "gl_overbright 2\nseta model sarge\nseta headmodel sarge\nseta handicap 100\n" diff --git a/engine/common/pmove.c b/engine/common/pmove.c index 076983f8f..4254aaf3d 100644 --- a/engine/common/pmove.c +++ b/engine/common/pmove.c @@ -270,11 +270,10 @@ int PM_StepSlideMove (qboolean in_air) if (!(blocked & BLOCKED_STEP)) return blocked; - org = (originalvel[2] < 0) ? pmove.origin : original; - VectorCopy (org, dest); - dest[2] -= movevars.stepheight; + org = (-DotProduct(pmove.gravitydir, originalvel) < 0) ? pmove.origin : original; + VectorMA (org, movevars.stepheight, pmove.gravitydir, dest); trace = PM_PlayerTrace (org, dest); - if (trace.fraction == 1 || trace.plane.normal[2] < MIN_STEP_NORMAL) + if (trace.fraction == 1 || -DotProduct(pmove.gravitydir, trace.plane.normal) < MIN_STEP_NORMAL) return blocked; // adjust stepsize, otherwise it would be possible to walk up a @@ -291,8 +290,7 @@ int PM_StepSlideMove (qboolean in_air) VectorCopy (originalvel, pmove.velocity); // move up a stair height - VectorCopy (pmove.origin, dest); - dest[2] += stepsize; + VectorMA (pmove.origin, -stepsize, pmove.gravitydir, dest); trace = PM_PlayerTrace (pmove.origin, dest); if (!trace.startsolid && !trace.allsolid) { @@ -300,31 +298,32 @@ int PM_StepSlideMove (qboolean in_air) } if (in_air && originalvel[2] < 0) - pmove.velocity[2] = 0; + VectorMA(pmove.velocity, -DotProduct(pmove.velocity, pmove.gravitydir), pmove.gravitydir, pmove.velocity); //z=0 PM_SlideMove (); // press down the stepheight - VectorCopy (pmove.origin, dest); - dest[2] -= stepsize; + VectorMA (pmove.origin, stepsize, pmove.gravitydir, dest); trace = PM_PlayerTrace (pmove.origin, dest); - if (trace.fraction != 1 && trace.plane.normal[2] < MIN_STEP_NORMAL) + if (trace.fraction != 1 && -DotProduct(pmove.gravitydir, trace.plane.normal) < MIN_STEP_NORMAL) goto usedown; if (!trace.startsolid && !trace.allsolid) { VectorCopy (trace.endpos, pmove.origin); } - if (pmove.origin[2] < original[2]) + if (-DotProduct(pmove.gravitydir, pmove.origin) < -DotProduct(pmove.gravitydir, original)) goto usedown; VectorCopy (pmove.origin, up); - // decide which one went farther - downdist = (down[0] - original[0])*(down[0] - original[0]) - + (down[1] - original[1])*(down[1] - original[1]); - updist = (up[0] - original[0])*(up[0] - original[0]) - + (up[1] - original[1])*(up[1] - original[1]); + // decide which one went farther (in the forwards direction regardless of step values) + VectorSubtract(down, original, dest); + VectorMA(dest, -DotProduct(dest, pmove.gravitydir), pmove.gravitydir, dest); //z=0 + downdist = DotProduct(dest, dest); + VectorSubtract(up, original, dest); + VectorMA(dest, -DotProduct(dest, pmove.gravitydir), pmove.gravitydir, dest); //z=0 + updist = DotProduct(dest, dest); if (downdist >= updist) { @@ -335,13 +334,14 @@ usedown: } // copy z value from slide move - pmove.velocity[2] = downvel[2]; + VectorMA(pmove.velocity, DotProduct(downvel, pmove.gravitydir)-DotProduct(pmove.velocity, pmove.gravitydir), pmove.gravitydir, pmove.velocity); //z=downvel if (!pmove.onground && pmove.waterlevel < 2 && (blocked & BLOCKED_STEP)) { float scale; // in pm_airstep mode, walking up a 16 unit high step // will kill 16% of horizontal velocity scale = 1 - 0.01*(pmove.origin[2] - original[2]); + //FIXME gravitydir pmove.velocity[0] *= scale; pmove.velocity[1] *= scale; } @@ -517,9 +517,13 @@ void PM_WaterMove (void) wishvel[i] = forward[i]*pmove.cmd.forwardmove + right[i]*pmove.cmd.sidemove; if (pmove.pm_type != PM_FLY && !pmove.cmd.forwardmove && !pmove.cmd.sidemove && !pmove.cmd.upmove && !pmove.onladder) - wishvel[2] -= 60; // drift towards bottom + { + VectorMA(wishvel, 60, pmove.gravitydir, wishvel); + } else - wishvel[2] += pmove.cmd.upmove; + { + VectorMA(wishvel, -pmove.cmd.upmove, pmove.gravitydir, wishvel); + } VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); @@ -551,7 +555,7 @@ void PM_FlyMove (void) for (i=0 ; i<3 ; i++) wishvel[i] = forward[i]*pmove.cmd.forwardmove + right[i]*pmove.cmd.sidemove; - wishvel[2] += pmove.cmd.upmove; + VectorMA(wishvel, -pmove.cmd.upmove, pmove.gravitydir, wishvel); VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); @@ -585,7 +589,9 @@ void PM_LadderMove (void) wishvel[2]*=10; if (pmove.cmd.buttons & 2) - wishvel[2]+=movevars.maxspeed; + { + VectorMA(wishvel, -movevars.maxspeed, pmove.gravitydir, wishvel); + } VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); @@ -600,8 +606,7 @@ void PM_LadderMove (void) // assume it is a stair or a slope, so press down from stepheight above VectorMA (pmove.origin, frametime, pmove.velocity, dest); - VectorCopy (dest, start); - start[2] += movevars.stepheight + 1; + VectorMA(dest, -(movevars.stepheight + 1), pmove.gravitydir, start); trace = PM_PlayerTrace (start, dest); if (!trace.startsolid && !trace.allsolid) // FIXME: check steep slope? { // walked up the step @@ -628,15 +633,14 @@ void PM_AirMove (void) fmove = pmove.cmd.forwardmove; smove = pmove.cmd.sidemove; - - forward[2] = 0; - right[2] = 0; + VectorMA(forward, -DotProduct(forward, pmove.gravitydir), pmove.gravitydir, forward); //z=0 + VectorMA(right, -DotProduct(right, pmove.gravitydir), pmove.gravitydir, right); //z=0 VectorNormalize (forward); VectorNormalize (right); - for (i=0 ; i<2 ; i++) + for (i=0 ; i<3 ; i++) wishdir[i] = forward[i]*fmove + right[i]*smove; - wishdir[2] = 0; + VectorMA(wishdir, -DotProduct(wishdir, pmove.gravitydir), pmove.gravitydir, wishdir); //z=0 wishspeed = VectorNormalize(wishdir); @@ -655,21 +659,24 @@ void PM_AirMove (void) pmove.velocity[2] = min(pmove.velocity[2], 0); // bound above by 0 PM_Accelerate (wishdir, wishspeed, movevars.accelerate); // add gravity - pmove.velocity[2] -= movevars.entgravity * movevars.gravity * frametime; + VectorMA(pmove.velocity, movevars.entgravity * movevars.gravity * frametime, pmove.gravitydir, pmove.velocity); } else { - pmove.velocity[2] = 0; + VectorMA(pmove.velocity, -DotProduct(pmove.velocity, pmove.gravitydir), pmove.gravitydir, pmove.velocity); //z=0 PM_Accelerate (wishdir, wishspeed, movevars.accelerate); } - if (!pmove.velocity[0] && !pmove.velocity[1] && !movevars.slidyslopes) + //clear the z out, so we can test if we're moving horizontally relative to gravity + VectorMA(pmove.velocity, -DotProduct(pmove.velocity, pmove.gravitydir), pmove.gravitydir, wishdir); + if (!DotProduct(wishdir, wishdir) && !movevars.slidyslopes) { - pmove.velocity[2] = 0; + //clear z if we're not moving + VectorClear(pmove.velocity); return; } else if (!movevars.slidefix && !movevars.slidyslopes) - pmove.velocity[2] = 0; + VectorMA(pmove.velocity, -DotProduct(pmove.velocity, pmove.gravitydir), pmove.gravitydir, pmove.velocity); //z=0 PM_StepSlideMove(false); } @@ -681,7 +688,7 @@ void PM_AirMove (void) PM_AirAccelerate (wishdir, wishspeed, movevars.accelerate); // add gravity - pmove.velocity[2] -= movevars.entgravity * movevars.gravity * frametime; + VectorMA(pmove.velocity, movevars.entgravity * movevars.gravity * frametime, pmove.gravitydir, pmove.velocity); if (movevars.airstep) blocked = PM_StepSlideMove (true); @@ -698,29 +705,45 @@ cplane_t groundplane; PM_CategorizePosition ============= */ +trace_t PM_TraceLine (vec3_t start, vec3_t end); void PM_CategorizePosition (void) { vec3_t point; int cont; trace_t trace; + pmove.gravitydir[0] = 0; + pmove.gravitydir[1] = 0; + pmove.gravitydir[2] = -1; + if (pmove.pm_type == PM_WALLWALK) + { + vec3_t tmin,tmax; + VectorCopy(player_mins, tmin); + VectorCopy(player_maxs, tmax); + VectorMA(pmove.origin, -48, up, point); + trace = PM_TraceLine(pmove.origin, point); + VectorCopy(tmin, player_mins); + VectorCopy(tmax, player_maxs); + + if (trace.fraction < 1) + VectorNegate(trace.plane.normal, pmove.gravitydir); + } + // if the player hull point one unit down is solid, the player // is on ground // see if standing on something solid - point[0] = pmove.origin[0]; - point[1] = pmove.origin[1]; - point[2] = pmove.origin[2] - 1; + VectorAdd(pmove.origin, pmove.gravitydir, point); trace.startsolid = trace.allsolid = true; VectorClear(trace.endpos); - if (pmove.velocity[2] > 180) + if (DotProduct(pmove.gravitydir, pmove.velocity) > 180) { pmove.onground = false; } else { trace = PM_PlayerTrace (pmove.origin, point); - if (trace.fraction == 1 || trace.plane.normal[2] < MIN_STEP_NORMAL) + if (trace.fraction == 1 || -DotProduct(pmove.gravitydir, trace.plane.normal) < MIN_STEP_NORMAL) pmove.onground = false; else { @@ -843,14 +866,17 @@ void PM_CheckJump (void) if (pmove.waterlevel >= 2) { // swimming, not jumping + float speed; pmove.onground = false; if (pmove.watertype == FTECONTENTS_WATER) - pmove.velocity[2] = 100; + speed = 100; else if (pmove.watertype == FTECONTENTS_SLIME) - pmove.velocity[2] = 80; + speed = 80; else - pmove.velocity[2] = 50; + speed = 50; + + VectorMA(pmove.velocity, -speed-DotProduct(pmove.velocity, pmove.gravitydir), pmove.gravitydir, pmove.velocity); return; } @@ -862,16 +888,16 @@ void PM_CheckJump (void) // check for jump bug // groundplane normal was set in the call to PM_CategorizePosition - if (pmove.velocity[2] < 0 && DotProduct(pmove.velocity, groundplane.normal) < -0.1) + if (-DotProduct(pmove.gravitydir, pmove.velocity) < 0 && DotProduct(pmove.velocity, groundplane.normal) < -0.1) { // pmove.velocity is pointing into the ground, clip it PM_ClipVelocity (pmove.velocity, groundplane.normal, pmove.velocity, 1); } pmove.onground = false; - pmove.velocity[2] += 270; + VectorMA(pmove.velocity, -270, pmove.gravitydir, pmove.velocity); - if (movevars.ktjump > 0) + if (movevars.ktjump > 0 && pmove.pm_type != PM_WALLWALK) { if (movevars.ktjump > 1) movevars.ktjump = 1; @@ -1064,6 +1090,9 @@ void PM_PlayerMove (float gamespeed) frametime = pmove.cmd.msec * 0.001*gamespeed; pmove.numtouch = 0; + //TEMP + pmove.pm_type = PM_WALLWALK; + if (pmove.pm_type == PM_NONE || pmove.pm_type == PM_FREEZE) { PM_CategorizePosition (); return; diff --git a/engine/common/pmove.h b/engine/common/pmove.h index 99a2790bf..c61547f1c 100644 --- a/engine/common/pmove.h +++ b/engine/common/pmove.h @@ -28,7 +28,8 @@ typedef enum { PM_DEAD, // no acceleration PM_FLY, // fly, bump into walls PM_NONE, // can't move - PM_FREEZE // can't move or look around (TODO) + PM_FREEZE, // can't move or look around (TODO) + PM_WALLWALK // sticks to walls. on ground while near one } pmtype_t; #define PMF_JUMP_HELD 1 @@ -56,6 +57,7 @@ typedef struct vec3_t angles; vec3_t velocity; vec3_t basevelocity; + vec3_t gravitydir; qboolean jump_held; int jump_msec; // msec since last jump float waterjumptime; diff --git a/engine/common/protocol.h b/engine/common/protocol.h index fe65f949f..01c442e9f 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -563,6 +563,7 @@ enum clcq2_ops_e #define UFP_VELOCITYXY (1u<<4) #define UFP_VELOCITYZ (1u<<5) #define UFP_MSEC (1u<<6) +#define UFP_WEAPONFRAME (1u<<7) #define UF_REMOVE UF_16BIT /*special flag, slightly more compact (we can reuse the 16bit flag as its not important)*/ @@ -818,6 +819,7 @@ typedef struct entity_state_s /*info to predict other players, so I don't get yelled at if fte were to stop supporting it*/ qbyte pmovetype; qbyte msec; + unsigned short weaponframe; short movement[3]; short velocity[3]; // 1/8th } q1; diff --git a/engine/gl/gl_font.c b/engine/gl/gl_font.c index 059e505a6..466680ff5 100644 --- a/engine/gl/gl_font.c +++ b/engine/gl/gl_font.c @@ -751,7 +751,7 @@ static texid_t Font_LoadQuakeConchars(void) return r_nulltex; } -static texid_t Font_LoadHexen2Conchars(void) +static texid_t Font_LoadHexen2Conchars(qboolean iso88591) { //gulp... so it's come to this has it? rework the hexen2 conchars into the q1 system. texid_t tex; @@ -759,51 +759,52 @@ static texid_t Font_LoadHexen2Conchars(void) unsigned char *tempchars; unsigned char *in, *out, *outbuf; FS_LoadFile("gfx/menu/conchars.lmp", (void**)&tempchars); + + /*hexen2's conchars are arranged 32-wide, 16 high. + the upper 8 rows are 256 8859-1 chars + the lower 8 rows are a separate set of recoloured 8859-1 chars. + + if we're loading for the fallback then we're loading this data for quake compatibility, + so we grab only the first 4 rows of each set of chars (128 low chars, 128 high chars). + + if we're loading a proper charset, then we load only the first set of chars, we can recolour the rest anyway (com_parseutf8 will do so anyway). + as a final note, parsing iso8859-1 french/german/etc as utf8 will generally result in decoding errors which can gracefully revert to 8859-1 safely. If this premise fails too much, we can always change the parser for different charsets - the engine always uses unicode and thus 8859-1 internally. + */ if (tempchars) { outbuf = BZ_Malloc(8*8*256*8); out = outbuf; - for (i = 0; i < 8*8; i+=1) + /*read the low chars*/ + for (i = 0; i < 8*8*(iso88591?2:1); i+=1) { - if ((i/8)&1) - { - in = tempchars + ((i)/8)*16*8*8+(i&7)*32*8 - 256*4+128; - for (x = 0; x < 16*8; x++) - *out++ = *in++; - } + if (i&(1<<3)) + in = tempchars + (i>>3)*16*8*8+(i&7)*32*8 - 256*4+128; else - { - in = tempchars + (i/8)*16*8*8+(i&7)*32*8; - for (x = 0; x < 16*8; x++) - *out++ = *in++; - } + in = tempchars + (i>>3)*16*8*8+(i&7)*32*8; + for (x = 0; x < 16*8; x++) + *out++ = *in++; } - for (i = 0; i < 8*8; i+=1) + /*read the high chars*/ + for (; i < 8*8*2; i+=1) { - if ((i/8)&1) - { - in = tempchars+128*128 + ((i)/8)*16*8*8+(i&7)*32*8 - 256*4+128; - for (x = 0; x < 16*8; x++) - *out++ = *in++; - } + if (i&(1<<3)) + in = tempchars+128*128 + ((i>>3)&15)*16*8*8+(i&7)*32*8 - 256*4+128; else - { - in = tempchars+128*128 + (i/8)*16*8*8+(i&7)*32*8; - for (x = 0; x < 16*8; x++) - *out++ = *in++; - } + in = tempchars+128*128 + ((i>>3)&15)*16*8*8+(i&7)*32*8; + for (x = 0; x < 16*8; x++) + *out++ = *in++; } FS_FreeFile(tempchars); // add ocrana leds - if (con_ocranaleds.value && con_ocranaleds.value != 2) + if (!iso88591 && con_ocranaleds.value && con_ocranaleds.value != 2) AddOcranaLEDsIndexed (outbuf, 128, 128); for (i=0 ; i<128*128 ; i++) if (outbuf[i] == 0) outbuf[i] = 255; // proper transparent color - tex = R_LoadTexture8 ("charset", 128, 128, outbuf, IF_NOMIPMAP|IF_NOGAMMA, 1); + tex = R_LoadTexture8 (iso88591?"gfx/menu/8859-1.lmp":"charset", 128, 128, outbuf, IF_NOMIPMAP|IF_NOGAMMA, 1); Z_Free(outbuf); return tex; } @@ -844,7 +845,7 @@ static texid_t Font_LoadDefaultConchars(void) tex = Font_LoadQuakeConchars(); if (TEXVALID(tex)) return tex; - tex = Font_LoadHexen2Conchars(); + tex = Font_LoadHexen2Conchars(false); if (TEXVALID(tex)) return tex; tex = Font_LoadFallbackConchars(); @@ -899,6 +900,7 @@ struct font_s *Font_LoadFont(int height, char *fontfilename) { struct font_s *f; int i = 0; + int defaultplane; f = Z_Malloc(sizeof(*f)); f->charheight = height; @@ -1045,12 +1047,19 @@ struct font_s *Font_LoadFont(int height, char *fontfilename) } } + defaultplane = BITMAPPLANE;/*assume the bitmap plane - don't use the fallback as people don't think to use com_parseutf8*/ if (!TEXVALID(f->singletexture)) { if (!TEXVALID(f->singletexture) && !TEXVALID(fontplanes.defaultfont)) fontplanes.defaultfont = Font_LoadDefaultConchars(); - f->singletexture = fontplanes.defaultfont; + if (!strcmp(fontfilename, "gfx/hexen2")) + { + f->singletexture = Font_LoadHexen2Conchars(true); + defaultplane = DEFAULTPLANE; + } + if (!TEXVALID(f->singletexture)) + f->singletexture = fontplanes.defaultfont; } for (; i < FONTCHARS; i++) @@ -1070,7 +1079,7 @@ struct font_s *Font_LoadFont(int height, char *fontfilename) f->chars[i].top = 0; f->chars[i].nextchar = 0; //these chars are not linked in f->chars[i].pad = 0; - f->chars[i].texplane = BITMAPPLANE; /*if its a 'raster' font, don't use the default chars, always use the raster images*/ + f->chars[i].texplane = defaultplane; } return f; } diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index c4d8d06c8..4e574d54a 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -4155,13 +4155,14 @@ void Shader_Readpass (shader_t *shader, char **ptr) break; } - if ((shader->flags & SHADER_SKY) && (shader->flags & SHADER_DEPTHWRITE)) + /*if ((shader->flags & SHADER_SKY) && (shader->flags & SHADER_DEPTHWRITE)) { #ifdef warningmsg #pragma warningmsg("is this valid?") #endif pass->shaderbits &= ~SBITS_MISC_DEPTHWRITE; } + */ } static qboolean Shader_Parsetok (shader_t *shader, shaderpass_t *pass, shaderkey_t *keys, char *token, char **ptr) @@ -5118,6 +5119,7 @@ void Shader_DefaultBSPQ1(char *shortname, shader_t *s, const void *args) "map $diffuse\n" "tcmod scale 10 10\n" "tcmod scroll 0.04 0.04\n" + "depthwrite\n" "}\n" "{\n" "map $fullbright\n" diff --git a/engine/gl/gl_viddroid.c b/engine/gl/gl_viddroid.c index 517f59eb9..55eca44da 100644 --- a/engine/gl/gl_viddroid.c +++ b/engine/gl/gl_viddroid.c @@ -307,14 +307,14 @@ void IN_Move (float *movements, int pnum) } else { - cl.viewangles[pnum][YAW] -= m_yaw.value * mouse_x; + cl.viewanglechange[pnum][YAW] -= m_yaw.value * mouse_x; } if (in_mlook.state[pnum] & 1) V_StopPitchDrift (pnum); - if ( (in_mlook.state[pnum] & 1) && !(in_strafe.state[pnum] & 1)) { - cl.viewangles[pnum][PITCH] += m_pitch.value * mouse_y; - CL_ClampPitch(pnum); + if ( (in_mlook.state[pnum] & 1) && !(in_strafe.state[pnum] & 1)) + { + cl.viewanglechange[pnum][PITCH] += m_pitch.value * mouse_y; } else { diff --git a/engine/gl/gl_vidlinuxglx.c b/engine/gl/gl_vidlinuxglx.c index 5e2a7534b..46c60e03b 100644 --- a/engine/gl/gl_vidlinuxglx.c +++ b/engine/gl/gl_vidlinuxglx.c @@ -1149,14 +1149,14 @@ void IN_MouseMove (float *movements, int pnum) if ( (in_strafe.state[pnum] & 1) || (lookstrafe.value && (in_mlook.state[pnum] & 1) )) movements[1] += m_side.value * mouse_x; else - cl.viewangles[pnum][YAW] -= m_yaw.value * mouse_x; + cl.viewanglechange[pnum][YAW] -= m_yaw.value * mouse_x; if (in_mlook.state[pnum] & 1) V_StopPitchDrift (pnum); if ( (in_mlook.state[pnum] & 1) && !(in_strafe.state[pnum] & 1)) { - cl.viewangles[pnum][PITCH] += m_pitch.value * mouse_y; + cl.viewanglechange[pnum][PITCH] += m_pitch.value * mouse_y; CL_ClampPitch(pnum); } else diff --git a/engine/server/progdefs.h b/engine/server/progdefs.h index fa4ed7b72..a2e63b3d3 100644 --- a/engine/server/progdefs.h +++ b/engine/server/progdefs.h @@ -247,6 +247,7 @@ and the extension fields are added on the end and can have extra vm-specific stu comfieldfloat(SendFlags)/*EXT_CSQC_1 (one of the DP guys came up with it)*/\ comfieldfloat(Version)/*EXT_CSQC (obsolete)*/\ comfieldfloat(pvsflags)/*EXT_CSQC_1*/\ + comfieldfloat(modelflags)\ comfieldfloat(uniquespawnid)/*FTE_ENT_UNIQUESPAWNID*/\ comfieldfunction(customizeentityforclient, ".float()") diff --git a/engine/server/server.h b/engine/server/server.h index 9ad681343..8d0ba9bea 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -293,6 +293,7 @@ typedef struct vec3_t playerpositions[MAX_CLIENTS]; //where each player was in this frame, for antilag qboolean playerpresent[MAX_CLIENTS]; //whether the player was actually present packet_entities_t entities; //package containing entity states that were sent in this frame, for deltaing + unsigned short *resendentnum; //the number of each entity that was sent in this frame unsigned int *resendentbits; //the bits of each entity that were sent in this frame } client_frame_t; diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index 7a509589c..139e50fd4 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -399,26 +399,27 @@ void SV_CSQC_DroppedPacket(client_t *client, int sequence) //skip it if we never generated that frame, to avoid pulling in stale data if (client->frameunion.frames[sequence & UPDATE_MASK].sequence != sequence) { - Con_Printf("SV: Stale %i\n", sequence); +// Con_Printf("SV: Stale %i\n", sequence); return; } if (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) { unsigned int *f = client->frameunion.frames[sequence & UPDATE_MASK].resendentbits; - Con_Printf("SV: Resend %i\n", sequence); - i = client->max_net_ents; - if (i > sv.world.num_edicts) - i = sv.world.num_edicts; + unsigned short *n = client->frameunion.frames[sequence & UPDATE_MASK].resendentnum; +// Con_Printf("SV: Resend %i\n", sequence); + i = client->frameunion.frames[sequence & UPDATE_MASK].entities.num_entities; while (i > 0) { - if (f[i]) - { - client->pendingentbits[i] |= f[i]; - f[i] = 0; - } i--; + +// if (f[i] & UF_RESET) +// Con_Printf("Resend %i @ %i\n", i, sequence); +// if (f[i] & UF_REMOVE) +// Con_Printf("Remove %i @ %i\n", i, sequence); + client->pendingentbits[n[i]] |= f[i]; } + client->frameunion.frames[sequence & UPDATE_MASK].entities.num_entities = 0; } if (!(client->csqcactive)) //we don't need this, but it might be a little faster. @@ -714,23 +715,58 @@ void SVQW_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, } /*special flags which are slightly more compact. these are 'wasted' as part of the delta itself*/ -#define UF_REMOVE UF_16BIT /*special flag, slightly more compact (we can reuse the 16bit flag as its not important)*/ -#define UF_MOVETYPE UF_EFFECTS2 -//#define UF_WASTED3 UF_EXTEND1 -//#define UF_WASTED2 UF_EXTEND2 -//#define UF_WASTED1 UF_EXTEND3 +#define UF_REMOVE UF_16BIT /*says we removed the entity in this frame*/ +#define UF_MOVETYPE UF_EFFECTS2 /*this flag isn't present in the header itself*/ +#define UF_RESET2 UF_EXTEND1 /*so new ents are reset 3 times to avoid weird baselines*/ +#define UF_UNUSED UF_EXTEND2 /**/ +#define UF_WEAPONFRAME UF_EXTEND3 + +static unsigned int SVFTE_DeltaPredCalcBits(entity_state_t *from, entity_state_t *to) +{ + unsigned int bits = 0; + if (from && from->u.q1.pmovetype != to->u.q1.pmovetype) + bits |= UFP_MOVETYPE; + + if (to->u.q1.movement[0]) + bits |= UFP_FORWARD; + if (to->u.q1.movement[1]) + bits |= UFP_SIDE; + if (to->u.q1.movement[2]) + bits |= UFP_UP; + if (to->u.q1.velocity[0]) + bits |= UFP_VELOCITYXY; + if (to->u.q1.velocity[1]) + bits |= UFP_VELOCITYXY; + if (to->u.q1.velocity[2]) + bits |= UFP_VELOCITYZ; + if (to->u.q1.msec) + bits |= UFP_MSEC; + + return bits; +} static unsigned int SVFTE_DeltaCalcBits(entity_state_t *from, entity_state_t *to) { unsigned int bits = 0; + if (from->u.q1.pmovetype != to->u.q1.pmovetype) + bits |= UF_PREDINFO|UF_MOVETYPE; + if (from->u.q1.weaponframe != to->u.q1.weaponframe) + bits |= UF_PREDINFO|UF_WEAPONFRAME; if (to->u.q1.pmovetype) { - bits |= UF_PREDINFO; - /*if we've got player movement then always resend this extra stuff to avoid any weird loss*/ - bits |= UF_ORIGINXY | UF_ORIGINZ | UF_ANGLESXZ | UF_ANGLESY; - if (from->u.q1.pmovetype != to->u.q1.pmovetype) - bits |= UF_MOVETYPE; + if (SVFTE_DeltaPredCalcBits(from, to)) + bits |= UF_PREDINFO; + + /*moving players get extra data forced upon them which is not deltatracked*/ + if ((bits & UF_PREDINFO) && (from->u.q1.velocity[0] || from->u.q1.velocity[1] || from->u.q1.velocity[2])) + { + /*if we've got player movement then write the origin anyway*/ + bits |= UF_ORIGINXY | UF_ORIGINZ; + /*and force angles too, if its not us*/ + if (host_client != svs.clients + to->number-1) + bits |= UF_ANGLESXZ | UF_ANGLESY; + } } if (to->origin[0] != from->origin[0]) @@ -798,6 +834,11 @@ static void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_ bits &= ~UF_MOVETYPE; predbits |= UFP_MOVETYPE; } + if (bits & UF_WEAPONFRAME) + { + bits &= ~UF_WEAPONFRAME; + predbits |= UFP_WEAPONFRAME; + } /*check if we need more precision*/ if ((bits & UF_MODEL) && state->modelindex > 255) @@ -876,20 +917,7 @@ static void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_ if (bits & UF_PREDINFO) { /*movetype is set above somewhere*/ - if (state->u.q1.movement[0]) - predbits |= UFP_FORWARD; - if (state->u.q1.movement[1]) - predbits |= UFP_SIDE; - if (state->u.q1.movement[2]) - predbits |= UFP_UP; - if (state->u.q1.velocity[0]) - predbits |= UFP_VELOCITYXY; - if (state->u.q1.velocity[1]) - predbits |= UFP_VELOCITYXY; - if (state->u.q1.velocity[2]) - predbits |= UFP_VELOCITYZ; - if (state->u.q1.msec) - predbits |= UFP_MSEC; + predbits |= SVFTE_DeltaPredCalcBits(NULL, state); MSG_WriteByte(msg, predbits); if (predbits & UFP_FORWARD) @@ -909,6 +937,16 @@ static void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_ MSG_WriteShort(msg, state->u.q1.velocity[2]); if (predbits & UFP_MSEC) MSG_WriteByte(msg, state->u.q1.msec); + if (predbits & UFP_WEAPONFRAME) + { + if (state->u.q1.weaponframe > 127) + { + MSG_WriteByte(msg, 128 | (state->u.q1.weaponframe & 127)); + MSG_WriteByte(msg, state->u.q1.weaponframe>>7); + } + else + MSG_WriteByte(msg, state->u.q1.weaponframe); + } } if (bits & UF_MODEL) @@ -980,12 +1018,17 @@ Only what changed is tracked, via bitmask, its previous value is never tracked. */ void SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizebuf_t *msg) { + edict_t *e; entity_state_t *o, *n; unsigned int i; unsigned int j; - unsigned int *resend; + unsigned int bits; + unsigned int *resendbits; + unsigned short *resendnum; + unsigned int outno, outmax; qboolean reset = (client->delta_sequence == -1) || (client->pendingentbits[0] & UF_REMOVE); + //if we're clearing the list and starting from scratch, just wipe all lingering state if (reset) { for (j = 0; j < client->sentents.max_entities; j++) @@ -995,7 +1038,7 @@ void SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizebuf_t } } - j = 0; + //expand client's entstate list if (to->num_entities) { j = to->entities[to->num_entities-1].number+1; @@ -1007,7 +1050,7 @@ void SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizebuf_t } } - /*figure out the bits that changed*/ + /*figure out the entitys+bits that changed (removed and active)*/ for (i = 0, j = 0; i < to->num_entities; i++) { n = &to->entities[i]; @@ -1017,20 +1060,24 @@ void SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizebuf_t o = &client->sentents.entities[j]; if (o->number) { - client->pendingentbits[j] = UF_REMOVE; - o->number = 0; /*dead*/ + e = EDICT_NUM(svprogfuncs, o->number); + if (!((int)e->xv->pvsflags & PVSF_NOREMOVE)) + { + client->pendingentbits[j] = UF_REMOVE; + o->number = 0; /*dead*/ + } } } o = &client->sentents.entities[j]; if (!o->number) { - /*forget any remove bits*/ - client->pendingentbits[j] = UF_RESET | SVFTE_DeltaCalcBits(&EDICT_NUM(svprogfuncs, n->number)->baseline, n); + /*flag it for reset, we can add the extra bits later once we get around to sending it*/ + client->pendingentbits[j] = UF_RESET | UF_RESET2; } else { - client->pendingentbits[j] |= SVFTE_DeltaCalcBits(o, n);; + client->pendingentbits[j] |= SVFTE_DeltaCalcBits(o, n); } *o = *n; j++; @@ -1041,42 +1088,72 @@ void SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizebuf_t o = &client->sentents.entities[j]; if (o->number) { - client->pendingentbits[j] = UF_REMOVE; - o->number = 0; /*dead*/ + e = EDICT_NUM(svprogfuncs, o->number); + if (!((int)e->xv->pvsflags & PVSF_NOREMOVE)) + { + client->pendingentbits[j] = UF_REMOVE; + o->number = 0; /*dead*/ + } } } - resend = client->frameunion.frames[client->netchan.incoming_sequence & UPDATE_MASK].resendentbits; + /*cache frame info*/ + resendbits = client->frameunion.frames[client->netchan.incoming_sequence & UPDATE_MASK].resendentbits; + resendnum = client->frameunion.frames[client->netchan.incoming_sequence & UPDATE_MASK].resendentnum; + outno = 0; + outmax = client->frameunion.frames[client->netchan.incoming_sequence & UPDATE_MASK].entities.max_entities; + /*start writing the packet*/ MSG_WriteByte (msg, svcfte_updateentities); MSG_WriteFloat(msg, sv.world.physicstime); - if (reset) + if (reset) /*if we're resetting, the client will see 'remove world' which should be handled as a special case*/ MSG_WriteShort(msg, 0x8000); - memset(resend, 0, client->sentents.max_entities*sizeof(*resend)); - for(j = 0; j < client->sentents.max_entities; j++) { - if (!client->pendingentbits[j]) + bits = client->pendingentbits[j]; + if (!(bits & ~UF_RESET2)) //skip while there's nothing to send (skip reset2 if there's no other changes, its only to reduce chances of the client getting 'new' entities containing just an origin)*/ continue; if (msg->cursize + 50 > msg->maxsize) break; /*give up if it gets full*/ + if (outno >= outmax) + break; + client->pendingentbits[j] = 0; - if (client->pendingentbits[j] & UF_REMOVE) + if (bits & UF_REMOVE) { MSG_WriteShort(msg, j | 0x8000); - resend[j] = UF_REMOVE; + resendbits[outno] = UF_REMOVE; +// Con_Printf("REMOVE %i @ %i\n", j, client->netchan.incoming_sequence); } - else + else if (client->sentents.entities[j].number) /*only send a new copy of the ent if they actually have one already*/ { + if (bits & UF_RESET2) + { + /*if reset2, then this is the second packet sent to the client and should have a forced reset (but which isn't tracked)*/ + resendbits[outno] = bits & ~UF_RESET2; + bits = UF_RESET | SVFTE_DeltaCalcBits(&EDICT_NUM(svprogfuncs, j)->baseline, &client->sentents.entities[j]); +// Con_Printf("RESET2 %i @ %i\n", j, client->netchan.incoming_sequence); + } + else if (bits & UF_RESET) + { + /*flag the entity for the next packet, so we always get two resets when it appears, to reduce the effects of packetloss on seeing rockets etc*/ + client->pendingentbits[j] = UF_RESET2; + bits = UF_RESET | SVFTE_DeltaCalcBits(&EDICT_NUM(svprogfuncs, j)->baseline, &client->sentents.entities[j]); + resendbits[outno] = UF_RESET; +// Con_Printf("RESET %i @ %i\n", j, client->netchan.incoming_sequence); + } + else + resendbits[outno] = bits; MSG_WriteShort(msg, j); - SVFTE_WriteUpdate(client->pendingentbits[j], &client->sentents.entities[j], msg); - resend[j] = client->pendingentbits[j]; + SVFTE_WriteUpdate(bits, &client->sentents.entities[j], msg); } - client->pendingentbits[j] = 0; + resendnum[outno++] = j; } - MSG_WriteShort(msg, 0); + + client->frameunion.frames[client->netchan.incoming_sequence & UPDATE_MASK].entities.num_entities = outno; + client->frameunion.frames[client->netchan.incoming_sequence & UPDATE_MASK].sequence = client->netchan.incoming_sequence; } /* @@ -2590,7 +2667,6 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli //note that client can be null, for building baselines. int i; - state->number = NUM_FOR_EDICT(svprogfuncs, ent); state->u.q1.msec = 0; @@ -2601,13 +2677,18 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli state->u.q1.velocity[0] = 0; state->u.q1.velocity[1] = 0; state->u.q1.velocity[2] = 0; + + if (client == &svs.clients[state->number-1]) + state->u.q1.weaponframe = ent->v->weaponframe; + else + state->u.q1.weaponframe = 0; if ((state->number-1) < (unsigned short)sv.allocated_client_slots && ent->v->movetype) { client_t *cl = &svs.clients[state->number-1]; if (cl->isindependant) { state->u.q1.pmovetype = ent->v->movetype; - if (cl != client) + if (cl != client && client) { /*only generate movement values if the client doesn't already know them...*/ state->u.q1.movement[0] = ent->xv->movement[0]; state->u.q1.movement[1] = ent->xv->movement[1]; @@ -2662,6 +2743,7 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli state->colormap = ent->v->colormap; state->skinnum = ent->v->skin; state->effects = ent->v->effects; + state->effects |= (int)ent->xv->modelflags<<24; state->hexen2flags = ent->xv->drawflags; state->abslight = (int)(ent->xv->abslight*255) & 255; state->tagentity = ent->xv->tag_entity; @@ -2733,9 +2815,18 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli i = ent->xv->colormod[1]*(256/8); state->colormod[1] = bound(0, i, 255); i = ent->xv->colormod[2]*(256/8); state->colormod[2] = bound(0, i, 255); } - state->glowmod[0] = ent->xv->glowmod[0]*(256/8); - state->glowmod[1] = ent->xv->glowmod[1]*(256/8); - state->glowmod[2] = ent->xv->glowmod[2]*(256/8); + if (!ent->xv->glowmod[0] && !ent->xv->glowmod[1] && !ent->xv->glowmod[2]) + { + state->glowmod[0] = (256/8); + state->glowmod[1] = (256/8); + state->glowmod[2] = (256/8); + } + else + { + state->glowmod[0] = ent->xv->glowmod[0]*(256/8); + state->glowmod[1] = ent->xv->glowmod[1]*(256/8); + state->glowmod[2] = ent->xv->glowmod[2]*(256/8); + } state->glowsize = ent->xv->glow_size*0.25; state->glowcolour = ent->xv->glow_color; if (ent->xv->glow_trail) diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index b3126e4bc..eb278c26c 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -218,82 +218,7 @@ baseline will be transmitted } */ -void SV_EdictToEntState (int num, edict_t *ent, entity_state_t *state) -{ - int i; - - state->number = num; - state->flags = 0; - VectorCopy (ent->v->origin, state->origin); - VectorCopy (ent->v->angles, state->angles); - state->modelindex = ent->v->modelindex; - state->frame = ent->v->frame; - state->colormap = ent->v->colormap; - state->skinnum = ent->v->skin; - state->effects = ent->v->effects; - state->hexen2flags = ent->xv->drawflags; - state->abslight = (int)(ent->xv->abslight*255) & 255; - state->tagentity = ent->xv->tag_entity; - state->tagindex = ent->xv->tag_index; - - state->light[0] = ent->xv->color[0]*255; - state->light[1] = ent->xv->color[1]*255; - state->light[2] = ent->xv->color[2]*255; - state->light[3] = ent->xv->light_lev; - state->lightstyle = ent->xv->style; - state->lightpflags = ent->xv->pflags; - -/* if ((int)ent->v->flags & FL_CLASS_DEPENDENT && client->playerclass) //hexen2 wierdness. - { - char modname[MAX_QPATH]; - Q_strncpyz(modname, sv.strings.model_precache[state->modelindex], sizeof(modname)); - if (strlen(modname)>5) - { - modname[strlen(modname)-5] = client->playerclass+'0'; - state->modelindex = SV_ModelIndex(modname); - } - }*/ - if (/*progstype == PROG_H2 &&*/ ent->v->solid == SOLID_BSP) - state->angles[0]*=-1; - - if (ent->v->solid == SOLID_BSP) - state->solid = ES_SOLID_BSP; - - if (state->effects & EF_FULLBRIGHT) - { - state->hexen2flags |= MLS_FULLBRIGHT; - } - - if (!ent->xv->alpha) - state->trans = 255; - else - state->trans = ent->xv->alpha*255; - - if (!ent->xv->colormod[0] && !ent->xv->colormod[1] && !ent->xv->colormod[2]) - { - state->colormod[0] = (256)/8; - state->colormod[1] = (256)/8; - state->colormod[2] = (256)/8; - } - else - { - i = ent->xv->colormod[0]*(256/8); state->colormod[0] = bound(0, i, 255); - i = ent->xv->colormod[1]*(256/8); state->colormod[1] = bound(0, i, 255); - i = ent->xv->colormod[2]*(256/8); state->colormod[2] = bound(0, i, 255); - } - state->glowsize = ent->xv->glow_size*0.25; - state->glowcolour = ent->xv->glow_color; -#define RENDER_GLOWTRAIL 2 - if (ent->xv->glow_trail) - state->dpflags |= RENDER_GLOWTRAIL; - - if (!ent->xv->scale) - state->scale = 1*16; - else - state->scale = ent->xv->scale*16; - - state->fatness = ent->xv->fatness*16; -} +void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *client); void SVNQ_CreateBaseline (void) { @@ -320,7 +245,7 @@ void SVNQ_CreateBaseline (void) // // create entity baseline // - SV_EdictToEntState(entnum, svent, &svent->baseline); + SV_Snapshot_BuildStateQ1(&svent->baseline, svent, NULL); if (entnum > 0 && entnum <= sv.allocated_client_slots) { @@ -329,7 +254,10 @@ void SVNQ_CreateBaseline (void) else svent->baseline.colormap = 0; //this would crash NQ. - svent->baseline.modelindex = playermodel; + if (!svent->baseline.solid) + svent->baseline.solid = (2 | (3<<5) | (4<<10)); + if (!svent->baseline.modelindex) + svent->baseline.modelindex = playermodel; } svent->baseline.modelindex&=255; } diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 0316d7089..bb2cb08e1 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -2268,12 +2268,24 @@ client_t *SVC_DirectConnect(void) if ((temp.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))// || ISDPCLIENT(&temp)) { - temp.frameunion.frames = Z_Malloc(sizeof(client_frame_t)*UPDATE_BACKUP+sizeof(unsigned int)*temp.max_net_ents*UPDATE_BACKUP + sizeof(unsigned int)*temp.max_net_ents); + char *ptr; + int maxents = maxpacketentities; /*this is the max number of ents updated per frame. we can't track more, so...*/ + ptr = Z_Malloc( sizeof(client_frame_t)*UPDATE_BACKUP+ + sizeof(*temp.pendingentbits)*temp.max_net_ents+ + sizeof(unsigned int)*maxents*UPDATE_BACKUP+ + sizeof(unsigned short)*maxents*UPDATE_BACKUP); + temp.frameunion.frames = (void*)ptr; + ptr += sizeof(*temp.frameunion.frames)*UPDATE_BACKUP; + temp.pendingentbits = (void*)ptr; + ptr += sizeof(*temp.pendingentbits)*temp.max_net_ents; for (i = 0; i < UPDATE_BACKUP; i++) { - temp.frameunion.frames[i].resendentbits = (unsigned int*)(temp.frameunion.frames+UPDATE_BACKUP) + i*temp.max_net_ents; + temp.frameunion.frames[i].entities.max_entities = maxents; + temp.frameunion.frames[i].resendentnum = (void*)ptr; + ptr += sizeof(*temp.frameunion.frames[i].resendentnum)*maxents; + temp.frameunion.frames[i].resendentbits = (void*)ptr; + ptr += sizeof(*temp.frameunion.frames[i].resendentbits)*maxents; } - temp.pendingentbits = (unsigned int*)(temp.frameunion.frames+UPDATE_BACKUP) + UPDATE_BACKUP*temp.max_net_ents; } else { diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index e39520d39..eedd802f9 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -1532,7 +1532,7 @@ void SV_CalcClientStats(client_t *client, int statsi[MAX_CL_STATS], float statsf if (!client->spectator) { statsf[STAT_ACTIVEWEAPON] = ent->v->weapon; - if (client->csqcactive || client->protocol != SCP_QUAKEWORLD) + if ((client->csqcactive && !(client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)) || client->protocol != SCP_QUAKEWORLD) statsf[STAT_WEAPONFRAME] = ent->v->weaponframe; } diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index d1cd4130f..b48b8d2a7 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -6193,7 +6193,6 @@ void SV_ExecuteClientMessage (client_t *cl) // save time for ping calculations if (cl->frameunion.frames) { //split screen doesn't always have frames. - cl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].sequence = cl->netchan.outgoing_sequence; cl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].senttime = realtime; cl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].ping_time = -1; cl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].move_msecs = -1;