From 7d08a8ba9d7925d79b903a087b97357968e011de Mon Sep 17 00:00:00 2001 From: havoc Date: Sun, 26 Feb 2006 03:00:47 +0000 Subject: [PATCH] qw support is 99% working git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@6033 d7cf8633-e32d-0410-b094-e92efae38249 --- cl_input.c | 8 ++- cl_main.c | 46 +++++++++++-- cl_parse.c | 179 +++++++++++++++++++++++++++++++++------------------ cl_screen.c | 23 +++++++ client.h | 12 +++- cmd.c | 5 +- common.c | 12 ++-- host_cmd.c | 44 +++++++++---- netconn.c | 22 +++++-- progs.h | 4 +- protocol.c | 182 ++++++++++++++++++++++++++++++++-------------------- protocol.h | 2 + sbar.c | 51 +++++++++++---- sv_user.c | 2 +- view.c | 16 +++-- 15 files changed, 427 insertions(+), 181 deletions(-) diff --git a/cl_input.c b/cl_input.c index 62451ea6..e6d05362 100644 --- a/cl_input.c +++ b/cl_input.c @@ -858,9 +858,9 @@ void QW_MSG_WriteDeltaUsercmd(sizebuf_t *buf, qw_usercmd_t *from, qw_usercmd_t * if (bits & QW_CM_UP) MSG_WriteShort(buf, to->upmove); if (bits & QW_CM_BUTTONS) - MSG_WriteShort(buf, to->buttons); + MSG_WriteByte(buf, to->buttons); if (bits & QW_CM_IMPULSE) - MSG_WriteShort(buf, to->impulse); + MSG_WriteByte(buf, to->impulse); MSG_WriteByte(buf, to->msec); } @@ -986,6 +986,7 @@ void CL_SendMove(void) // PROTOCOL_DARKPLACES5 clc_move = 19 bytes total // PROTOCOL_DARKPLACES6 clc_move = 52 bytes total // PROTOCOL_DARKPLACES7 clc_move = 56 bytes total + // PROTOCOL_QUAKEWORLD clc_move = 34 bytes total (typically, but can reach 43 bytes, or even 49 bytes with roll) if (cls.protocol == PROTOCOL_QUAKEWORLD) { int checksumindex; @@ -1042,9 +1043,12 @@ void CL_SendMove(void) // request delta compression if appropriate if (cl.qw_validsequence && !cl_nodelta.integer && cls.state == ca_connected && !cls.demorecording) { + cl.qw_deltasequence[cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK] = cl.qw_validsequence; MSG_WriteByte(&buf, qw_clc_delta); MSG_WriteByte(&buf, cl.qw_validsequence & 255); } + else + cl.qw_deltasequence[cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK] = -1; } else if (cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEDP || cls.protocol == PROTOCOL_NEHAHRAMOVIE) { diff --git a/cl_main.c b/cl_main.c index db35eaca..46db3607 100644 --- a/cl_main.c +++ b/cl_main.c @@ -124,6 +124,7 @@ CL_ClearState void CL_ClearState(void) { int i; + entity_t *ent; if (cl_entities) Mem_Free(cl_entities);cl_entities = NULL; if (cl_csqcentities) Mem_Free(cl_csqcentities);cl_csqcentities = NULL; //[515]: csqc @@ -138,6 +139,7 @@ void CL_ClearState(void) if (cl_brushmodel_entities) Mem_Free(cl_brushmodel_entities);cl_brushmodel_entities = NULL; if (cl.entitydatabase) EntityFrame_FreeDatabase(cl.entitydatabase);cl.entitydatabase = NULL; if (cl.entitydatabase4) EntityFrame4_FreeDatabase(cl.entitydatabase4);cl.entitydatabase4 = NULL; + if (cl.entitydatabaseqw) EntityFrameQW_FreeDatabase(cl.entitydatabaseqw);cl.entitydatabaseqw = NULL; if (cl.scores) Mem_Free(cl.scores);cl.scores = NULL; if (!sv.active) @@ -145,6 +147,9 @@ void CL_ClearState(void) // wipe the entire cl structure memset (&cl, 0, sizeof(cl)); + + S_StopAllSounds(); + // reset the view zoom interpolation cl.mviewzoom[0] = cl.mviewzoom[1] = 1; @@ -213,6 +218,27 @@ void CL_ClearState(void) VectorSet(cl_playercrouchmaxs, 16, 16, 24); } + // disable until we get textures for it + R_ResetSkyBox(); + + ent = &cl_entities[0]; + // entire entity array was cleared, so just fill in a few fields + ent->state_current.active = true; + ent->render.model = cl.worldmodel = NULL; // no world model yet + ent->render.scale = 1; // some of the renderer still relies on scale + ent->render.alpha = 1; + ent->render.colormap = -1; // no special coloring + ent->render.flags = RENDER_SHADOW | RENDER_LIGHT; + Matrix4x4_CreateFromQuakeEntity(&ent->render.matrix, 0, 0, 0, 0, 0, 0, 1); + Matrix4x4_Invert_Simple(&ent->render.inversematrix, &ent->render.matrix); + CL_BoundingBoxForEntity(&ent->render); + + // noclip is turned off at start + noclip_anglehack = false; + + // mark all frames invalid for delta + memset(cl.qw_deltasequence, -1, sizeof(cl.qw_deltasequence)); + CL_Screen_NewMap(); CL_Particles_Clear(); CL_CGVM_Clear(); @@ -307,13 +333,22 @@ void CL_Disconnect(void) if (cls.demorecording) CL_Stop_f(); - // send clc_disconnect 3 times to improve chances of server receiving - // it (but it still fails sometimes) - Con_DPrint("Sending clc_disconnect\n"); + // send disconnect message 3 times to improve chances of server + // receiving it (but it still fails sometimes) memset(&buf, 0, sizeof(buf)); buf.data = bufdata; buf.maxsize = sizeof(bufdata); - MSG_WriteByte(&buf, clc_disconnect); + if (cls.protocol == PROTOCOL_QUAKEWORLD) + { + Con_DPrint("Sending drop command\n"); + MSG_WriteByte(&buf, qw_clc_stringcmd); + MSG_WriteString(&buf, "drop"); + } + else + { + Con_DPrint("Sending clc_disconnect\n"); + MSG_WriteByte(&buf, clc_disconnect); + } NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol); NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol); NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol); @@ -723,7 +758,7 @@ void CL_AddQWCTFFlagModel(entity_t *player, int skin) VectorSet(flag->render.colormod, 1, 1, 1); // attach the flag to the player matrix Matrix4x4_CreateFromQuakeEntity(&flagmatrix, -f, -22, 0, 0, 0, -45, 1); - Matrix4x4_Concat(&flag->render.matrix, &flagmatrix, &player->render.matrix); + Matrix4x4_Concat(&flag->render.matrix, &player->render.matrix, &flagmatrix); Matrix4x4_Invert_Simple(&flag->render.inversematrix, &flag->render.matrix); R_LerpAnimation(&flag->render); CL_BoundingBoxForEntity(&flag->render); @@ -1604,6 +1639,7 @@ int CL_ReadFromServer(void) CL_RelinkStaticEntities(); CL_RelinkBeams(); CL_RelinkEffects(); + CL_RelinkQWNails(); } else csqc_frame = true; diff --git a/cl_parse.c b/cl_parse.c index d7e0f0f6..d1009147 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -388,6 +388,7 @@ static qboolean QW_CL_CheckOrDownloadFile(const char *filename) return false; } +static void QW_CL_ProcessUserInfo(int slot); static void QW_CL_RequestNextDownload(void) { int i; @@ -400,7 +401,31 @@ static void QW_CL_RequestNextDownload(void) case dl_single: break; case dl_skin: - // TODO + if (cls.qw_downloadnumber == 0) + Con_Printf("Checking skins...\n"); + for (;cls.qw_downloadnumber < cl.maxclients;cls.qw_downloadnumber++) + { + if (!cl.scores[cls.qw_downloadnumber].name[0]) + continue; + // check if we need to download the file, and return if so + if (!QW_CL_CheckOrDownloadFile(va("skins/%s.pcx", cl.scores[cls.qw_downloadnumber].qw_skin))) + return; + } + + cls.qw_downloadtype = dl_none; + + // load any newly downloaded skins + for (i = 0;i < cl.maxclients;i++) + QW_CL_ProcessUserInfo(i); + + // if we're still in signon stages, request the next one + if (cls.signon != SIGNONS) + { + cls.signon = SIGNONS-1; + // we'll go to SIGNONS when the first entity update is received + MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd); + MSG_WriteString(&cls.netcon->message, va("begin %i", cl.qw_servercount)); + } break; case dl_model: if (cls.qw_downloadnumber == 0) @@ -409,7 +434,6 @@ static void QW_CL_RequestNextDownload(void) cls.qw_downloadnumber = 1; } - cls.qw_downloadtype = dl_model; for (;cls.qw_downloadnumber < MAX_MODELS && cl.model_name[cls.qw_downloadnumber][0];cls.qw_downloadnumber++) { // skip submodels @@ -428,6 +452,8 @@ static void QW_CL_RequestNextDownload(void) return; } + cls.qw_downloadtype = dl_none; + // touch all of the precached models that are still loaded so we can free // anything that isn't needed Mod_ClearUsed(); @@ -452,9 +478,20 @@ static void QW_CL_RequestNextDownload(void) if ((cl.model_precache[i] = Mod_ForName(cl.model_name[i], false, false, false))->Draw == NULL) Con_Printf("Model %s could not be found or downloaded\n", cl.model_name[i]); + // check memory integrity + Mem_CheckSentinelsGlobal(); + + // now that we have a world model, set up the world entity, renderer + // modules and csqc + cl_entities[0].render.model = cl.worldmodel = cl.model_precache[1]; + CL_BoundingBoxForEntity(&cl_entities[0].render); + + R_Modules_NewMap(); + CL_CGVM_Start(); + // done checking sounds and models, send a prespawn command now MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd); - MSG_WriteString(&cls.netcon->message, va("prespawn %i 0 %i", cl.qw_servercount, cl.worldmodel ? cl.worldmodel->brush.qw_md4sum2 : 0)); + MSG_WriteString(&cls.netcon->message, va("prespawn %i 0 %i", cl.qw_servercount, cl.model_precache[1]->brush.qw_md4sum2)); if (cls.qw_downloadmemory) { @@ -469,17 +506,15 @@ static void QW_CL_RequestNextDownload(void) cls.qw_downloadnumber = 1; } - cls.qw_downloadtype = dl_sound; for (;cl.sound_name[cls.qw_downloadnumber][0];cls.qw_downloadnumber++) { - // skip subsounds - if (cl.sound_name[cls.qw_downloadnumber][0] == '*') - continue; // check if we need to download the file, and return if so if (!QW_CL_CheckOrDownloadFile(va("sound/%s", cl.sound_name[cls.qw_downloadnumber]))) return; } + cls.qw_downloadtype = dl_none; + // load new sounds and unload old ones // FIXME: S_ServerSounds does not know about cl.sfx_ sounds S_ServerSounds(cl.sound_name, cls.qw_downloadnumber); @@ -500,6 +535,9 @@ static void QW_CL_RequestNextDownload(void) cl.sound_precache[i] = S_PrecacheSound(cl.sound_name[i], true, false); } + // check memory integrity + Mem_CheckSentinelsGlobal(); + // done with sound downloads, next we check models MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd); MSG_WriteString(&cls.netcon->message, va("modellist %i %i", cl.qw_servercount, 0)); @@ -512,9 +550,11 @@ static void QW_CL_RequestNextDownload(void) static void QW_CL_ParseDownload(void) { - int size = MSG_ReadShort(); + int size = (signed short)MSG_ReadShort(); int percent = MSG_ReadByte(); + //Con_Printf("download %i %i%% (%i/%i)\n", size, percent, cls.qw_downloadmemorycursize, cls.qw_downloadmemorymaxsize); + // skip the download fragment if playing a demo if (!cls.netcon) { @@ -602,6 +642,7 @@ static void QW_CL_ParseModelList(void) return; } + cls.signon = 2; cls.qw_downloadnumber = 0; cls.qw_downloadtype = dl_model; QW_CL_RequestNextDownload(); @@ -636,11 +677,30 @@ static void QW_CL_ParseSoundList(void) return; } + cls.signon = 2; cls.qw_downloadnumber = 0; cls.qw_downloadtype = dl_sound; QW_CL_RequestNextDownload(); } +static void QW_CL_Skins_f(void) +{ + cls.qw_downloadnumber = 0; + cls.qw_downloadtype = dl_skin; + QW_CL_RequestNextDownload(); +} + +static void QW_CL_Changing_f(void) +{ + if (cls.qw_downloadmemory) // don't change when downloading + return; + + S_StopAllSounds(); + cl.intermission = 0; + cls.signon = 1; // not active anymore, but not disconnected + Con_Printf("\nChanging map...\n"); +} + void QW_CL_NextUpload(void) { int r, percent, size; @@ -716,9 +776,11 @@ static void QW_CL_ProcessUserInfo(int slot) cl.scores[slot].colors = topcolor * 16 + bottomcolor; InfoString_GetValue(cl.scores[slot].qw_userinfo, "*spectator", temp, sizeof(temp)); cl.scores[slot].qw_spectator = temp[0] != 0; + InfoString_GetValue(cl.scores[slot].qw_userinfo, "team", cl.scores[slot].qw_team, sizeof(cl.scores[slot].qw_team)); InfoString_GetValue(cl.scores[slot].qw_userinfo, "skin", cl.scores[slot].qw_skin, sizeof(cl.scores[slot].qw_skin)); - // LordHavoc: abusing Draw_CachePic for caching skins... - cl.scores[slot].qw_skin_cachepic = Draw_CachePic(cl.scores[slot].qw_skin, true); + if (!cl.scores[slot].qw_skin[0]) + strlcpy(cl.scores[slot].qw_skin, "base", sizeof(cl.scores[slot].qw_skin)); + // TODO: skin cache } static void QW_CL_UpdateUserInfo(void) @@ -760,10 +822,13 @@ static void QW_CL_ServerInfo(void) { char key[2048]; char value[2048]; + char temp[32]; strlcpy(key, MSG_ReadString(), sizeof(key)); strlcpy(value, MSG_ReadString(), sizeof(value)); Con_DPrintf("SERVERINFO: %s=%s\n", key, value); InfoString_SetValue(cl.qw_serverinfo, sizeof(cl.qw_serverinfo), key, value); + InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp)); + cl.qw_teamplay = atoi(temp); } static void QW_CL_ParseNails(void) @@ -772,16 +837,17 @@ static void QW_CL_ParseNails(void) int numnails = MSG_ReadByte(); vec_t *v; unsigned char bits[6]; - cl.qw_num_nails = 0; for (i = 0;i < numnails;i++) { - v = cl.qw_nails[cl.qw_num_nails++]; for (j = 0;j < 6;j++) bits[j] = MSG_ReadByte(); + if (cl.qw_num_nails > 255) + continue; + v = cl.qw_nails[cl.qw_num_nails++]; v[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096; v[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096; v[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096; - v[3] = 360*(bits[4]>>4)/16; + v[3] = -360*(bits[4]>>4)/16; v[4] = 360*bits[5]/256; v[5] = 0; } @@ -888,6 +954,10 @@ void CL_ParseServerInfo (void) Con_DPrint("Serverinfo packet received.\n"); + // if server is active, we already began a loading plaque + if (!sv.active) + SCR_BeginLoadingPlaque(); + // check memory integrity Mem_CheckSentinelsGlobal(); @@ -910,6 +980,8 @@ void CL_ParseServerInfo (void) cls.protocol = protocol; Con_DPrintf("Server protocol is %s\n", Protocol_NameForEnum(cls.protocol)); + cl_num_entities = 1; + if (protocol == PROTOCOL_QUAKEWORLD) { cl.qw_servercount = MSG_ReadLong(); @@ -963,23 +1035,15 @@ void CL_ParseServerInfo (void) // check memory integrity Mem_CheckSentinelsGlobal(); - S_StopAllSounds(); - // if server is active, we already began a loading plaque - if (!sv.active) - SCR_BeginLoadingPlaque(); - - // disable until we get textures for it - R_ResetSkyBox(); - - memset(cl.csqc_model_precache, 0, sizeof(cl.csqc_model_precache)); //[515]: csqc - memset(cl.model_precache, 0, sizeof(cl.model_precache)); - memset(cl.sound_precache, 0, sizeof(cl.sound_precache)); - MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd); MSG_WriteString(&cls.netcon->message, va("soundlist %i %i", cl.qw_servercount, 0)); cls.state = ca_connected; cls.signon = 1; + + // note: on QW protocol we can't set up the gameworld until after + // downloads finish... + // (we don't even know the name of the map yet) } else { @@ -1006,18 +1070,6 @@ void CL_ParseServerInfo (void) // check memory integrity Mem_CheckSentinelsGlobal(); - S_StopAllSounds(); - // if server is active, we already began a loading plaque - if (!sv.active) - SCR_BeginLoadingPlaque(); - - // disable until we get textures for it - R_ResetSkyBox(); - - memset(cl.csqc_model_precache, 0, sizeof(cl.csqc_model_precache)); //[515]: csqc - memset(cl.model_precache, 0, sizeof(cl.model_precache)); - memset(cl.sound_precache, 0, sizeof(cl.sound_precache)); - // parse model precache list for (nummodels=1 ; ; nummodels++) { @@ -1088,32 +1140,16 @@ void CL_ParseServerInfo (void) for (i=1 ; istate_current.active = true; - ent->render.model = cl.worldmodel = cl.model_precache[1]; - ent->render.scale = 1; // some of the renderer still relies on scale - ent->render.alpha = 1; - ent->render.colormap = -1; // no special coloring - ent->render.flags = RENDER_SHADOW | RENDER_LIGHT; - Matrix4x4_CreateFromQuakeEntity(&ent->render.matrix, 0, 0, 0, 0, 0, 0, 1); - Matrix4x4_Invert_Simple(&ent->render.inversematrix, &ent->render.matrix); - CL_BoundingBoxForEntity(&ent->render); - - cl_num_entities = 1; - - R_Modules_NewMap(); - CL_CGVM_Start(); - - // noclip is turned off at start - noclip_anglehack = false; + // we now have the worldmodel so we can set up the game world + ent->render.model = cl.worldmodel = cl.model_precache[1]; + CL_BoundingBoxForEntity(&ent->render); + R_Modules_NewMap(); + CL_CGVM_Start(); + } // check memory integrity Mem_CheckSentinelsGlobal(); @@ -2172,6 +2208,17 @@ void CL_ParseServerMessage(void) { cl.mtime[1] = cl.mtime[0]; cl.mtime[0] = realtime; // qw has no clock + cl.movement_needupdate = true; + + // slightly kill qw player entities each frame + for (i = 1;i < cl.maxclients;i++) + cl_entities_active[i] = false; + + // kill all qw nails + cl.qw_num_nails = 0; + + // fade weapon view kick + cl.qw_weaponkick = min(cl.qw_weaponkick + 10 * cl.frametime, 0); while (1) { @@ -2236,7 +2283,7 @@ void CL_ParseServerMessage(void) CL_NextDemo(); else CL_Disconnect(); - break; + return; case qw_svc_print: i = MSG_ReadByte(); @@ -2389,10 +2436,10 @@ void CL_ParseServerMessage(void) break; case qw_svc_smallkick: - Con_Printf("TODO: qw_svc_smallkick\n"); + cl.qw_weaponkick = -2; break; case qw_svc_bigkick: - Con_Printf("TODO: qw_svc_bigkick\n"); + cl.qw_weaponkick = -4; break; case qw_svc_muzzleflash: @@ -2476,6 +2523,12 @@ void CL_ParseServerMessage(void) break; } } + + // fully kill the still slightly dead qw player entities each frame + for (i = 1;i < cl.maxclients;i++) + if (!cl_entities_active[i]) + cl_entities[i].state_current.active = false; + QW_CL_UpdateItemsAndWeapon(); } else @@ -2918,6 +2971,8 @@ void CL_Parse_Init(void) Cmd_AddCommand("nextul", QW_CL_NextUpload, "sends next fragment of current upload buffer (screenshot for example)"); Cmd_AddCommand("stopul", QW_CL_StopUpload, "aborts current upload (screenshot for example)"); + Cmd_AddCommand("skins", QW_CL_Skins_f, "downloads missing qw skins from server"); + Cmd_AddCommand("changing", QW_CL_Changing_f, "sent by qw servers to tell client to wait for level change"); } void CL_Parse_Shutdown(void) diff --git a/cl_screen.c b/cl_screen.c index f4e11ba7..5b5c622a 100644 --- a/cl_screen.c +++ b/cl_screen.c @@ -391,6 +391,27 @@ void SCR_DrawBrand (void) DrawQ_Pic (x, y, "gfx/brand", 0, 0, 1, 1, 1, 1, 0); } +/* +============== +SCR_DrawDownload +============== +*/ +static void SCR_DrawDownload(void) +{ + int len; + float x, y; + float size = 8; + char temp[256]; + if (!cls.qw_downloadname[0]) + return; + dpsnprintf(temp, sizeof(temp), "Downloading %s ... %3i%%\n", cls.qw_downloadname, cls.qw_downloadpercent); + len = strlen(temp); + x = (vid_conwidth.integer - len*size) / 2; + y = vid_conheight.integer - size; + DrawQ_Fill(0, y, vid_conwidth.integer, size, 0, 0, 0, 0.5, 0); + DrawQ_String(x, y, temp, len, size, size, 1, 1, 1, 1, 0); +} + //============================================================================= @@ -1530,6 +1551,8 @@ void CL_UpdateScreen(void) SCR_DrawBrand(); + SCR_DrawDownload(); + SCR_UpdateScreen(); } diff --git a/client.h b/client.h index b87be426..5b485c0e 100644 --- a/client.h +++ b/client.h @@ -376,8 +376,8 @@ typedef struct scoreboard_s int qw_ping; int qw_packetloss; int qw_spectator; + char qw_team[8]; char qw_skin[MAX_QPATH]; - cachepic_t *qw_skin_cachepic; // skins are loaded as cachepics } scoreboard_t; typedef struct cshift_s @@ -712,9 +712,15 @@ typedef struct client_state_s // local copy of the server infostring char qw_serverinfo[MAX_SERVERINFO_STRING]; + // time of last qw "pings" command sent to server while showing scores + double last_ping_request; + // used during connect int qw_servercount; + // updated from serverinfo + int qw_teamplay; + // indicates whether the player is spectating qboolean qw_spectator; @@ -743,9 +749,13 @@ typedef struct client_state_s int qw_num_nails; vec_t qw_nails[255][6]; + float qw_weaponkick; + int qw_validsequence; qw_usercmd_t qw_moves[QW_UPDATE_BACKUP]; + + int qw_deltasequence[QW_UPDATE_BACKUP]; } client_state_t; diff --git a/cmd.c b/cmd.c index 2af8f84c..b042cd28 100644 --- a/cmd.c +++ b/cmd.c @@ -1036,7 +1036,10 @@ void Cmd_ForwardStringToServer (const char *s) // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my // attention, it has been eradicated from here, its only (former) use in // all of darkplaces. - MSG_WriteByte(&cls.netcon->message, clc_stringcmd); + if (cls.protocol == PROTOCOL_QUAKEWORLD) + MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd); + else + MSG_WriteByte(&cls.netcon->message, clc_stringcmd); SZ_Write(&cls.netcon->message, (const unsigned char *)s, (int)strlen(s) + 1); } diff --git a/common.c b/common.c index 570b6f43..efea4851 100644 --- a/common.c +++ b/common.c @@ -338,7 +338,7 @@ void MSG_WriteCoord32f (sizebuf_t *sb, float f) void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol) { - if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE) + if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_QUAKEWORLD) MSG_WriteCoord13i (sb, f); else if (protocol == PROTOCOL_DARKPLACES1) MSG_WriteCoord32f (sb, f); @@ -379,7 +379,7 @@ void MSG_WriteAngle32f (sizebuf_t *sb, float f) void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol) { - if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4) + if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD) MSG_WriteAngle8i (sb, f); else MSG_WriteAngle16i (sb, f); @@ -479,7 +479,7 @@ char *MSG_ReadString (void) { static char string[MAX_INPUTLINE]; int l,c; - for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadChar()) != -1 && c != 0;l++) + for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++) string[l] = c; string[l] = 0; return string; @@ -488,7 +488,7 @@ char *MSG_ReadString (void) int MSG_ReadBytes (int numbytes, unsigned char *out) { int l, c; - for (l = 0;l < numbytes && (c = MSG_ReadChar()) != -1;l++) + for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++) out[l] = c; return l; } @@ -510,7 +510,7 @@ float MSG_ReadCoord32f (void) float MSG_ReadCoord (protocolversion_t protocol) { - if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE) + if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_QUAKEWORLD) return MSG_ReadCoord13i(); else if (protocol == PROTOCOL_DARKPLACES1) return MSG_ReadCoord32f(); @@ -545,7 +545,7 @@ float MSG_ReadAngle32f (void) float MSG_ReadAngle (protocolversion_t protocol) { - if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4) + if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD) return MSG_ReadAngle8i (); else return MSG_ReadAngle16i (); diff --git a/host_cmd.c b/host_cmd.c index a3292429..b0d86868 100644 --- a/host_cmd.c +++ b/host_cmd.c @@ -371,22 +371,39 @@ This is sent just before a server changes levels */ void Host_Reconnect_f (void) { - if (cmd_source == src_command) - { - Con_Print("reconnect is not valid from the console\n"); - return; - } - if (Cmd_Argc() != 1) + if (cls.protocol == PROTOCOL_QUAKEWORLD) { - Con_Print("reconnect : wait for signon messages again\n"); - return; + if (cls.qw_downloadmemory) // don't change when downloading + return; + + S_StopAllSounds(); + + if (cls.netcon) + { + if (cls.state == ca_connected && cls.signon < SIGNONS) + { + Con_Printf("reconnecting...\n"); + MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd); + MSG_WriteString(&cls.netcon->message, "new"); + } + else + Con_Printf("Please use connect instead (reconnect not implemented)\n"); + } } - if (!cls.signon) + else { - //Con_Print("reconnect: no signon, ignoring reconnect\n"); - return; + if (Cmd_Argc() != 1) + { + Con_Print("reconnect : wait for signon messages again\n"); + return; + } + if (!cls.signon) + { + Con_Print("reconnect: no signon, ignoring reconnect\n"); + return; + } + cls.signon = 0; // need new connection messages } - cls.signon = 0; // need new connection messages } /* @@ -2077,6 +2094,7 @@ Sent by server when serverinfo changes // TODO: shouldn't this be a cvar instead? void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld { + char temp[512]; if (Cmd_Argc() != 2) { Con_Printf ("usage: fullserverinfo \n"); @@ -2084,6 +2102,8 @@ void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld } strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo)); + InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp)); + cl.qw_teamplay = atoi(temp); } /* diff --git a/netconn.c b/netconn.c index 0bb32463..66129781 100755 --- a/netconn.c +++ b/netconn.c @@ -456,34 +456,44 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers sendreliable = true; } // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1) - *((int *)(sendbuffer + 0)) = LittleLong(conn->qw.outgoing_sequence | (sendreliable<<31)); + *((int *)(sendbuffer + 0)) = LittleLong((unsigned int)conn->qw.outgoing_sequence | ((unsigned int)sendreliable<<31)); // last received unreliable packet number, and last received reliable packet number (0 or 1) - *((int *)(sendbuffer + 4)) = LittleLong(conn->qw.incoming_sequence | (conn->qw.incoming_reliable_sequence<<31)); + *((int *)(sendbuffer + 4)) = LittleLong((unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31)); packetLen = 8; + conn->qw.outgoing_sequence++; // client sends qport in every packet if (conn == cls.netcon) { *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport); packetLen += 2; } - if (packetLen + (sendreliable ? conn->sendMessageLength : 0) + data->cursize > (int)sizeof(sendbuffer)) + if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400) { Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize); return -1; } + // add the reliable message if there is one if (sendreliable) { memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength); packetLen += conn->sendMessageLength; + conn->qw.last_reliable_sequence = conn->qw.outgoing_sequence; + } + // add the unreliable message if possible + if (packetLen + data->cursize <= 1400) + { + memcpy(sendbuffer + packetLen, data->data, data->cursize); + packetLen += data->cursize; } - memcpy(sendbuffer + packetLen, data->data, data->cursize); - packetLen += data->cursize; - conn->qw.outgoing_sequence++; NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress); packetsSent++; unreliableMessagesSent++; + + // delay later packets to obey rate limit + conn->qw.cleartime = max(conn->qw.cleartime, realtime) + packetLen * conn->qw.rate; + return 0; } else diff --git a/progs.h b/progs.h index 63f40578..7005deec 100644 --- a/progs.h +++ b/progs.h @@ -53,7 +53,7 @@ typedef struct edict_engineprivate_s // we should avoid extensive checking on entities already encountered int areagridmarknumber; - // PROTOCOL_QUAKE, PROTOCOL_QUAKEDP, PROTOCOL_NEHAHRAMOVIE + // PROTOCOL_QUAKE, PROTOCOL_QUAKEDP, PROTOCOL_NEHAHRAMOVIE, PROTOCOL_QUAKEWORLD // baseline values entity_state_t baseline; @@ -186,7 +186,7 @@ typedef struct edict_engineprivate_s // we should avoid extensive checking on entities already encountered int areagridmarknumber; - // PROTOCOL_QUAKE, PROTOCOL_QUAKEDP, PROTOCOL_NEHAHRAMOVIE + // PROTOCOL_QUAKE, PROTOCOL_QUAKEDP, PROTOCOL_NEHAHRAMOVIE, PROTOCOL_QUAKEWORLD // baseline values entity_state_t baseline; diff --git a/protocol.c b/protocol.c index 69b1c52b..91e0b7cb 100644 --- a/protocol.c +++ b/protocol.c @@ -2340,6 +2340,44 @@ void EntityFrame5_WriteFrame(sizebuf_t *msg, entityframe5_database_t *d, int num } +static int QW_TranslateEffects(int qweffects, int number) +{ + int effects = 0; + if (qweffects & QW_EF_BRIGHTFIELD) + effects |= EF_BRIGHTFIELD; + if (qweffects & QW_EF_MUZZLEFLASH) + effects |= EF_MUZZLEFLASH; + if (qweffects & QW_EF_FLAG1) + { + // mimic FTEQW's interpretation of EF_FLAG1 as EF_NODRAW on non-player entities + if (number > cl.maxclients) + effects |= EF_NODRAW; + else + effects |= EF_FLAG1QW; + } + if (qweffects & QW_EF_FLAG2) + { + // mimic FTEQW's interpretation of EF_FLAG2 as EF_ADDITIVE on non-player entities + if (number > cl.maxclients) + effects |= EF_ADDITIVE; + else + effects |= EF_FLAG2QW; + } + if (qweffects & QW_EF_RED) + { + if (qweffects & QW_EF_BLUE) + effects |= EF_RED | EF_BLUE; + else + effects |= EF_RED; + } + else if (qweffects & QW_EF_BLUE) + effects |= EF_BLUE; + else if (qweffects & QW_EF_BRIGHTLIGHT) + effects |= EF_BRIGHTLIGHT; + else if (qweffects & QW_EF_DIMLIGHT) + effects |= EF_DIMLIGHT; + return effects; +} void EntityStateQW_ReadPlayerUpdate(void) { @@ -2362,9 +2400,14 @@ void EntityStateQW_ReadPlayerUpdate(void) s = &ent->state_current; *s = defaultstate; s->active = true; + s->colormap = enumber; playerflags = MSG_ReadShort(); MSG_ReadVector(s->origin, cls.protocol); s->frame = MSG_ReadByte(); + + VectorClear(viewangles); + VectorClear(velocity); + if (playerflags & QW_PF_MSEC) { // time difference between last update this player sent to the server, @@ -2379,23 +2422,23 @@ void EntityStateQW_ReadPlayerUpdate(void) { bits = MSG_ReadByte(); if (bits & QW_CM_ANGLE1) - viewangles[0] = MSG_ReadAngle16i(); + viewangles[0] = MSG_ReadAngle16i(); // cmd->angles[0] if (bits & QW_CM_ANGLE2) - viewangles[1] = MSG_ReadAngle16i(); + viewangles[1] = MSG_ReadAngle16i(); // cmd->angles[1] if (bits & QW_CM_ANGLE3) - viewangles[2] = MSG_ReadAngle16i(); + viewangles[2] = MSG_ReadAngle16i(); // cmd->angles[2] if (bits & QW_CM_FORWARD) - MSG_ReadShort(); + MSG_ReadShort(); // cmd->forwardmove if (bits & QW_CM_SIDE) - MSG_ReadShort(); + MSG_ReadShort(); // cmd->sidemove if (bits & QW_CM_UP) - MSG_ReadShort(); + MSG_ReadShort(); // cmd->upmove if (bits & QW_CM_BUTTONS) - MSG_ReadByte(); + MSG_ReadByte(); // cmd->buttons if (bits & QW_CM_IMPULSE) - MSG_ReadByte(); + MSG_ReadByte(); // cmd->impulse + MSG_ReadByte(); // cmd->msec } - VectorClear(velocity); if (playerflags & QW_PF_VELOCITY1) velocity[0] = MSG_ReadShort(); if (playerflags & QW_PF_VELOCITY2) @@ -2409,9 +2452,17 @@ void EntityStateQW_ReadPlayerUpdate(void) if (playerflags & QW_PF_SKINNUM) s->skin = MSG_ReadByte(); if (playerflags & QW_PF_EFFECTS) - s->effects = MSG_ReadByte(); + s->effects = QW_TranslateEffects(MSG_ReadByte(), enumber); if (playerflags & QW_PF_WEAPONFRAME) weaponframe = MSG_ReadByte(); + else + weaponframe = 0; + + if (enumber == cl.playerentity) + { + // if this is an update on our player, update the angles + VectorCopy(cl.viewangles, viewangles); + } // calculate the entity angles from the viewangles s->angles[0] = viewangles[0] * -0.0333; @@ -2441,6 +2492,12 @@ void EntityStateQW_ReadPlayerUpdate(void) VectorCopy(velocity, cl.mvelocity[0]); cl.stats[STAT_WEAPONFRAME] = weaponframe; + if (playerflags & QW_PF_GIB) + cl.stats[STAT_VIEWHEIGHT] = 8; + else if (playerflags & QW_PF_DEAD) + cl.stats[STAT_VIEWHEIGHT] = -16; + else + cl.stats[STAT_VIEWHEIGHT] = 22; } // set the cl_entities_active flag @@ -2474,43 +2531,7 @@ static void EntityStateQW_ReadEntityUpdate(entity_state_t *s, int bits) if (bits & QW_U_SKIN) s->skin = MSG_ReadByte(); if (bits & QW_U_EFFECTS) - { - s->effects = 0; - qweffects = MSG_ReadByte(); - if (qweffects & QW_EF_BRIGHTFIELD) - s->effects |= EF_BRIGHTFIELD; - if (qweffects & QW_EF_MUZZLEFLASH) - s->effects |= EF_MUZZLEFLASH; - if (qweffects & QW_EF_FLAG1) - { - // mimic FTEQW's interpretation of EF_FLAG1 as EF_NODRAW on non-player entities - if (s->number > cl.maxclients) - s->effects |= EF_NODRAW; - else - s->effects |= EF_FLAG1QW; - } - if (qweffects & QW_EF_FLAG2) - { - // mimic FTEQW's interpretation of EF_FLAG2 as EF_ADDITIVE on non-player entities - if (s->number > cl.maxclients) - s->effects |= EF_ADDITIVE; - else - s->effects |= EF_FLAG2QW; - } - if (qweffects & QW_EF_RED) - { - if (qweffects & QW_EF_BLUE) - s->effects |= EF_RED | EF_BLUE; - else - s->effects |= EF_RED; - } - else if (qweffects & QW_EF_BLUE) - s->effects |= EF_BLUE; - else if (qweffects & QW_EF_BRIGHTLIGHT) - s->effects |= EF_BRIGHTLIGHT; - else if (qweffects & QW_EF_DIMLIGHT) - s->effects |= EF_DIMLIGHT; - } + s->effects = QW_TranslateEffects(qweffects = MSG_ReadByte(), s->number); if (bits & QW_U_ORIGIN1) s->origin[0] = MSG_ReadCoord13i(); if (bits & QW_U_ANGLE1) @@ -2538,49 +2559,70 @@ static void EntityStateQW_ReadEntityUpdate(entity_state_t *s, int bits) if (bits & QW_U_EFFECTS) Con_Printf(" U_EFFECTS %i", qweffects); if (bits & QW_U_ORIGIN1) - Con_Printf(" U_ORIGIN1 %i", s->origin[0]); + Con_Printf(" U_ORIGIN1 %f", s->origin[0]); if (bits & QW_U_ANGLE1) - Con_Printf(" U_ANGLE1 %i", s->angles[0]); + Con_Printf(" U_ANGLE1 %f", s->angles[0]); if (bits & QW_U_ORIGIN2) - Con_Printf(" U_ORIGIN2 %i", s->origin[1]); + Con_Printf(" U_ORIGIN2 %f", s->origin[1]); if (bits & QW_U_ANGLE2) - Con_Printf(" U_ANGLE2 %i", s->angles[1]); + Con_Printf(" U_ANGLE2 %f", s->angles[1]); if (bits & QW_U_ORIGIN3) - Con_Printf(" U_ORIGIN3 %i", s->origin[2]); + Con_Printf(" U_ORIGIN3 %f", s->origin[2]); if (bits & QW_U_ANGLE3) - Con_Printf(" U_ANGLE3 %i", s->angles[2]); + Con_Printf(" U_ANGLE3 %f", s->angles[2]); if (bits & QW_U_SOLID) Con_Printf(" U_SOLID"); Con_Print("\n"); } } +entityframeqw_database_t *EntityFrameQW_AllocDatabase(mempool_t *pool) +{ + entityframeqw_database_t *d; + d = (entityframeqw_database_t *)Mem_Alloc(pool, sizeof(*d)); + return d; +} + +void EntityFrameQW_FreeDatabase(entityframeqw_database_t *d) +{ + Mem_Free(d); +} + void EntityFrameQW_CL_ReadFrame(qboolean delta) { qboolean invalid = false; int number, oldsnapindex, newsnapindex, oldindex, newindex, oldnum, newnum; entity_t *ent; - entityframeqw_database_t *d = cl.entitydatabaseqw; + entityframeqw_database_t *d; entityframeqw_snapshot_t *oldsnap, *newsnap; + if (!cl.entitydatabaseqw) + cl.entitydatabaseqw = EntityFrameQW_AllocDatabase(cl_mempool); + d = cl.entitydatabaseqw; + newsnapindex = cls.netcon->qw.incoming_sequence & QW_UPDATE_MASK; newsnap = d->snapshot + newsnapindex; memset(newsnap, 0, sizeof(*newsnap)); + oldsnapindex = -1; + oldsnap = NULL; if (delta) { - oldsnapindex = MSG_ReadByte() & QW_UPDATE_MASK; - oldsnap = d->snapshot + oldsnapindex; - if (cls.netcon->qw.outgoing_sequence - oldsnapindex >= QW_UPDATE_BACKUP-1) + number = MSG_ReadByte(); + oldsnapindex = cl.qw_deltasequence[newsnapindex]; + if ((number & QW_UPDATE_MASK) != (oldsnapindex & QW_UPDATE_MASK)) + Con_DPrintf("WARNING: from mismatch\n"); + if (oldsnapindex != -1) { - Con_DPrintf("delta update too old\n"); - newsnap->invalid = invalid = true; // too old - delta = false; + if (cls.netcon->qw.outgoing_sequence - oldsnapindex >= QW_UPDATE_BACKUP-1) + { + Con_DPrintf("delta update too old\n"); + newsnap->invalid = invalid = true; // too old + delta = false; + } + oldsnap = d->snapshot + (oldsnapindex & QW_UPDATE_MASK); } - } - else - { - oldsnapindex = -1; - oldsnap = NULL; + else + delta = false; } // read the number of this frame to echo back in next input packet @@ -2638,10 +2680,13 @@ void EntityFrameQW_CL_ReadFrame(qboolean delta) { if (newsnap->num_entities >= QW_MAX_PACKET_ENTITIES) Host_Error("EntityFrameQW_CL_ReadFrame: newsnap->num_entities == MAX_PACKETENTITIES"); - newsnap->entities[newsnap->num_entities] = (newnum == oldnum) ? oldsnap->entities[oldindex++] : cl_entities[newnum].state_baseline; + newsnap->entities[newsnap->num_entities] = (newnum == oldnum) ? oldsnap->entities[oldindex] : cl_entities[newnum].state_baseline; EntityStateQW_ReadEntityUpdate(newsnap->entities + newsnap->num_entities, word); newsnap->num_entities++; } + + if (newnum == oldnum) + oldindex++; } // expand cl_num_entities to include every entity we've seen this game @@ -2653,8 +2698,8 @@ void EntityFrameQW_CL_ReadFrame(qboolean delta) CL_ExpandEntities(newnum); } - // now update the entities from the snapshot states - number = 1; + // now update the non-player entities from the snapshot states + number = cl.maxclients + 1; for (newindex = 0;;newindex++) { newnum = newindex >= newsnap->num_entities ? cl_num_entities : newsnap->entities[newindex].number; @@ -2673,6 +2718,7 @@ void EntityFrameQW_CL_ReadFrame(qboolean delta) ent = &cl_entities[number]; ent->state_previous = ent->state_current; ent->state_current = newsnap->entities[newindex]; + ent->state_current.time = cl.mtime[0]; CL_MoveLerpEntityStates(ent); // the entity lives again... cl_entities_active[number] = true; diff --git a/protocol.h b/protocol.h index e92337b9..46a9ea52 100644 --- a/protocol.h +++ b/protocol.h @@ -931,6 +931,8 @@ typedef struct entityframeqw_database_s } entityframeqw_database_t; +entityframeqw_database_t *EntityFrameQW_AllocDatabase(mempool_t *pool); +void EntityFrameQW_FreeDatabase(entityframeqw_database_t *d); void EntityStateQW_ReadPlayerUpdate(void); void EntityFrameQW_CL_ReadFrame(qboolean delta); diff --git a/sbar.c b/sbar.c index f2d1da54..79b0cf91 100644 --- a/sbar.c +++ b/sbar.c @@ -564,7 +564,7 @@ void Sbar_SortFrags (void) strcpy(teams[teamlines-1].name, "^3Yellow Team"); else strcpy(teams[teamlines-1].name, "Total Team Score"); - + teams[teamlines-1].frags = 0; teams[teamlines-1].colors = color + 16 * color; } @@ -1391,16 +1391,31 @@ Sbar_DeathmatchOverlay */ float Sbar_PrintScoreboardItem(scoreboard_t *s, float x, float y) { + int minutes; unsigned char *c; - // draw colors behind score - c = (unsigned char *)&palette_complete[(s->colors & 0xf0) + 8]; - DrawQ_Fill(x + 8, y+1, 32, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), c[3] * (1.0f / 255.0f) * sbar_alpha_fg.value, 0); - c = (unsigned char *)&palette_complete[((s->colors & 15)<<4) + 8]; - DrawQ_Fill(x + 8, y+4, 32, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), c[3] * (1.0f / 255.0f) * sbar_alpha_fg.value, 0); - // print the text - //DrawQ_String(x, y, va("%c%4i %s", (s - cl.scores) == cl.playerentity - 1 ? 13 : ' ', (int) s->frags, s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0); - // FIXME: use a constant for this color tag instead - DrawQ_ColoredString(x, y, va("%c%4i %s" STRING_COLOR_DEFAULT_STR, (s - cl.scores) == cl.playerentity - 1 ? 13 : ' ', (int) s->frags, s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL ); + if (cls.protocol == PROTOCOL_QUAKEWORLD) + { + // draw colors behind score + c = (unsigned char *)&palette_complete[(s->colors & 0xf0) + 8]; + DrawQ_Fill(x + 14*8, y+1, 32, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), c[3] * (1.0f / 255.0f) * sbar_alpha_fg.value, 0); + c = (unsigned char *)&palette_complete[((s->colors & 15)<<4) + 8]; + DrawQ_Fill(x + 14*8, y+4, 32, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), c[3] * (1.0f / 255.0f) * sbar_alpha_fg.value, 0); + // print the text + //DrawQ_String(x, y, va("%c%4i %s", (s - cl.scores) == cl.playerentity - 1 ? 13 : ' ', (int) s->frags, s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0); + DrawQ_ColoredString(x, y, va("%c%4i %2i %4i %4i %-4s %s", (s - cl.scores) == cl.playerentity - 1 ? 13 : ' ', bound(0, s->qw_ping, 9999), bound(0, s->qw_packetloss, 99), minutes,(int) s->frags, cl.qw_teamplay ? s->qw_team : "", s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL ); + } + else + { + minutes = (int)((cl.intermission ? cl.completed_time - s->qw_entertime : realtime - s->qw_entertime) / 60.0); + // draw colors behind score + c = (unsigned char *)&palette_complete[(s->colors & 0xf0) + 8]; + DrawQ_Fill(x + 1*8, y+1, 32, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), c[3] * (1.0f / 255.0f) * sbar_alpha_fg.value, 0); + c = (unsigned char *)&palette_complete[((s->colors & 15)<<4) + 8]; + DrawQ_Fill(x + 1*8, y+4, 32, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), c[3] * (1.0f / 255.0f) * sbar_alpha_fg.value, 0); + // print the text + //DrawQ_String(x, y, va("%c%4i %s", (s - cl.scores) == cl.playerentity - 1 ? 13 : ' ', (int) s->frags, s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0); + DrawQ_ColoredString(x, y, va("%c%4i %s", (s - cl.scores) == cl.playerentity - 1 ? 13 : ' ', (int) s->frags, s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL ); + } return 8; } @@ -1409,13 +1424,27 @@ void Sbar_DeathmatchOverlay (void) int i, x, y; cachepic_t *pic; + // request new ping times every two second + if (cl.last_ping_request < realtime - 2) + { + cl.last_ping_request = realtime; + if (cls.protocol == PROTOCOL_QUAKEWORLD) + { + MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd); + MSG_WriteString(&cls.netcon->message, "pings"); + } + } + pic = Draw_CachePic ("gfx/ranking", true); DrawQ_Pic ((vid_conwidth.integer - pic->width)/2, 8, "gfx/ranking", 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0); // scores Sbar_SortFrags (); // draw the text - x = (vid_conwidth.integer - (6 + 15) * 8) / 2; + if (cls.protocol == PROTOCOL_QUAKEWORLD) + x = (vid_conwidth.integer - (6 + 17 + 15) * 8) / 2; + else + x = (vid_conwidth.integer - (6 + 15) * 8) / 2; y = 40; if (Sbar_IsTeammatch ()) diff --git a/sv_user.c b/sv_user.c index 966c0551..e152d424 100644 --- a/sv_user.c +++ b/sv_user.c @@ -815,7 +815,7 @@ void SV_ReadClientMessage(void) return; } - cmd = MSG_ReadChar (); + cmd = MSG_ReadByte (); if (cmd == -1) { // end of message diff --git a/view.c b/view.c index be9bea79..2a9629c3 100644 --- a/view.c +++ b/view.c @@ -334,10 +334,14 @@ void V_CalcRefdef (void) if (cl.intermission) { // entity is a fixed camera, just copy the matrix - Matrix4x4_Copy(&r_refdef.viewentitymatrix, &ent->render.matrix); - Matrix4x4_Copy(&viewmodelmatrix, &ent->render.matrix); - r_refdef.viewentitymatrix.m[2][3] += cl.stats[STAT_VIEWHEIGHT]; - viewmodelmatrix.m[2][3] += cl.stats[STAT_VIEWHEIGHT]; + if (cls.protocol == PROTOCOL_QUAKEWORLD) + Matrix4x4_CreateFromQuakeEntity(&r_refdef.viewentitymatrix, cl.qw_intermission_origin[0], cl.qw_intermission_origin[1], cl.qw_intermission_origin[2], cl.qw_intermission_angles[0], cl.qw_intermission_angles[1], cl.qw_intermission_angles[2], 1); + else + { + r_refdef.viewentitymatrix = ent->render.matrix; + r_refdef.viewentitymatrix.m[2][3] += cl.stats[STAT_VIEWHEIGHT]; + } + viewmodelmatrix = r_refdef.viewentitymatrix; } else { @@ -346,6 +350,10 @@ void V_CalcRefdef (void) Matrix4x4_OriginFromMatrix(&ent->render.matrix, vieworg); VectorCopy(cl.viewangles, viewangles); + // apply qw weapon recoil effect (this did not work in QW) + // TODO: add a cvar to disable this + viewangles[PITCH] += cl.qw_weaponkick; + if (cl.onground) { if (!cl.oldonground) -- 2.39.2