diff --git a/engine/client/cl_cam.c b/engine/client/cl_cam.c index f0cfbb688..a776dad24 100644 --- a/engine/client/cl_cam.c +++ b/engine/client/cl_cam.c @@ -49,7 +49,6 @@ cvar_t cl_chasecam = {"cl_chasecam", "0"}; //cvar_t cl_camera_maxpitch = {"cl_camera_maxpitch", "10" }; //cvar_t cl_camera_maxyaw = {"cl_camera_maxyaw", "30" }; -qboolean cam_forceview[MAX_SPLITS]; vec3_t cam_viewangles[MAX_SPLITS]; double cam_lastviewtime[MAX_SPLITS]; @@ -129,12 +128,19 @@ void Cam_Lock(int pnum, int playernum) { char st[40]; + cam_lastviewtime[pnum] = -1000; + sprintf(st, "ptrack %i", playernum); MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, st); spec_track[pnum] = playernum; - cam_forceview[pnum] = true; locked[pnum] = false; + + if (cls.demoplayback == DPB_MVD) + { + memcpy(&cl.stats[pnum], cl.players[playernum].stats, sizeof(cl.stats[pnum])); + } + Sbar_Changed(); } @@ -351,7 +357,7 @@ void Cam_Track(int pnum, usercmd_t *cmd) vec3_t vec; float len; - if (!cl.spectator) + if (!cl.spectator || !cl.worldmodel) //can happen when the server changes level return; if (cl_hightrack.value && !locked[pnum]) @@ -370,11 +376,11 @@ void Cam_Track(int pnum, usercmd_t *cmd) return; } - frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + frame = &cl.frames[cl.validsequence & UPDATE_MASK]; player = frame->playerstate + spec_track[pnum]; self = frame->playerstate + cl.playernum[pnum]; - if (/*locked[pnum] ||*/ !Cam_IsVisible(player, desired_position[pnum])) + if (!locked[pnum] || !Cam_IsVisible(player, desired_position[pnum])) { if (!locked[pnum] || realtime - cam_lastviewtime[pnum] > 0.1) { diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 81f03c395..038b2a4be 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -154,7 +154,7 @@ qboolean CL_GetDemoMessage (void) int r, i, j, tracknum; float f; float demotime; - qbyte c, msecsadded; + qbyte c, msecsadded=0; usercmd_t *pcmd; q1usercmd_t q1cmd; @@ -173,6 +173,7 @@ qboolean CL_GetDemoMessage (void) { cl.gametime = 0; cl.gametimemark = realtime; + prevtime = 0; return 0; } if (realtime<= cl.gametime && cl.gametime)// > dem_lasttime+realtime) @@ -182,6 +183,24 @@ qboolean CL_GetDemoMessage (void) realtime = cl.gametime; cls.netchan.last_received = realtime; } + + { + float f = (cl.gametime-realtime)/(cl.gametime-prevtime); + float a1; + float a2; + + for (i=0 ; i<3 ; i++) + { + a1 = cl.viewangles[2][i]; + a2 = cl.viewangles[1][i]; + if (a1 - a2 > 180) + a1 -= 360; + if (a1 - a2 < -180) + a1 += 360; + cl.simangles[0][i] = a2 + f * (a1 - a2); + } + VectorCopy(cl.simangles[0], cl.viewangles[0]); + } return 0; } } @@ -189,12 +208,16 @@ qboolean CL_GetDemoMessage (void) // VectorCopy (cl.mviewangles[0], cl.mviewangles[1]); if (cls.demoplayback == DPB_NETQUAKE) { + VectorCopy (cl.viewangles[1], cl.viewangles[2]); for (i=0 ; i<3 ; i++) { r = fread (&f, 4, 1, cls.demofile); - cl.simangles[0][i] = cl.viewangles[0][i] = LittleFloat (f); + cl.simangles[0][i] = cl.viewangles[1][i] = LittleFloat (f); } + VectorCopy (cl.viewangles[1], cl.viewangles[0]); } + + prevtime = realtime; net_message.cursize = LittleLong (net_message.cursize); if (net_message.cursize > MAX_NQMSGLEN) @@ -230,14 +253,6 @@ readnext: fread(&msecsadded, sizeof(msecsadded), 1, cls.demofile); demotime = prevtime + msecsadded*(1.0f/1000); - - if (msecsadded) - { - cls.netchan.incoming_sequence++; - cls.netchan.incoming_acknowledged++; - cls.netchan.frame_latency = 0; - cls.netchan.last_received = demotime; // just to happy timeout check - } } else { @@ -299,6 +314,17 @@ readnext: else realtime = demotime; // we're warping + if (cls.demoplayback == DPB_MVD) + { + if (msecsadded) + { + cls.netchan.incoming_sequence++; + cls.netchan.incoming_acknowledged++; + cls.netchan.frame_latency = 0; + cls.netchan.last_received = demotime; // just to happy timeout check + } + } + prevtime = demotime; if (cls.state < ca_demostart) @@ -1080,7 +1106,7 @@ void CL_PlayDemo_f (void) // // disconnect from server // - CL_Disconnect (); + CL_Disconnect_f (); // // open the demo file diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index 537f372cd..49b280e85 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -406,7 +406,7 @@ void CL_ParsePacketEntities (qboolean delta) int oldindex, newindex; int word, newnum, oldnum; qboolean full; - qbyte from; + int from; newpacket = cls.netchan.incoming_sequence&UPDATE_MASK; newp = &cl.frames[newpacket].packet_entities; @@ -417,37 +417,45 @@ void CL_ParsePacketEntities (qboolean delta) from = MSG_ReadByte (); oldpacket = cl.frames[newpacket].delta_sequence; - if (cls.demoplayback == DPB_MVD) from = oldpacket = cls.netchan.incoming_sequence - 1; - if ( (from&UPDATE_MASK) != (oldpacket&UPDATE_MASK) ) - Con_DPrintf ("WARNING: from mismatch\n"); - } - else - oldpacket = -1; - - full = false; - if (oldpacket != -1) - { - if (cls.netchan.outgoing_sequence - oldpacket >= UPDATE_BACKUP-1) - { // we can't use this, it is too old + if (cls.netchan.outgoing_sequence - cls.netchan.incoming_sequence >= UPDATE_BACKUP - 1) { + // there are no valid frames left, so drop it FlushEntityPacket (); + cl.validsequence = 0; return; } - cl.oldvalidsequence = cl.validsequence; - cl.validsequence = cls.netchan.incoming_sequence; - oldp = &cl.frames[oldpacket&UPDATE_MASK].packet_entities; + + if ((from & UPDATE_MASK) != (oldpacket & UPDATE_MASK)) { + Con_DPrintf ("WARNING: from mismatch\n"); + FlushEntityPacket (); + cl.validsequence = 0; + return; + } + + if (cls.netchan.outgoing_sequence - oldpacket >= UPDATE_BACKUP - 1) { + // we can't use this, it is too old + FlushEntityPacket (); + // don't clear cl.validsequence, so that frames can still be rendered; + // it is possible that a fresh packet will be received before + // (outgoing_sequence - incoming_sequence) exceeds UPDATE_BACKUP - 1 + return; + } + + oldp = &cl.frames[oldpacket & UPDATE_MASK].packet_entities; + full = false; } else { // this is a full update that we can start delta compressing from now oldp = &dummy; dummy.num_entities = 0; - cl.oldvalidsequence = cl.validsequence; - cl.validsequence = cls.netchan.incoming_sequence; full = true; } + cl.oldvalidsequence = cl.validsequence; + cl.validsequence = cls.netchan.incoming_sequence; + oldindex = 0; newindex = 0; newp->num_entities = 0; @@ -805,9 +813,7 @@ void CLNQ_ParseDarkPlaces5Entities(void) //the things I do.. :o( int oldi; qboolean remove; - cls.netchan.incoming_sequence++; - - cl.validsequence=1; + cl.validsequence = cls.netchan.incoming_sequence++; cl_latestframenum = MSG_ReadLong(); @@ -926,7 +932,6 @@ void CLNQ_ParseEntity(unsigned int bits) static float lasttime; packet_entities_t *pack; - cl.validsequence=1; #define NQU_MOREBITS (1<<0) #define NQU_ORIGIN1 (1<<1) #define NQU_ORIGIN2 (1<<2) @@ -1232,7 +1237,7 @@ void CL_LinkPacketEntities (void) dlight_t *dl; vec3_t angles; - pack = &cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities; + pack = &cl.frames[cl.validsequence&UPDATE_MASK].packet_entities; autorotate = anglemod(100*cl.time); @@ -1253,7 +1258,7 @@ void CL_LinkPacketEntities (void) CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand()&31), 0, 0); // if set to invisible, skip - if (s1->modelindex<0) + if (s1->modelindex<1) continue; #if 0 for (spnum = 0; spnum < cl.splitclients; spnum++) @@ -1557,7 +1562,7 @@ CL_ParseProjectiles Nails are passed as efficient temporary entities ===================== */ -void CL_ParseProjectiles (int modelindex) +void CL_ParseProjectiles (int modelindex, qboolean nails2) { int i, c, j; qbyte bits[6]; @@ -1566,6 +1571,8 @@ void CL_ParseProjectiles (int modelindex) c = MSG_ReadByte (); for (i=0 ; iorigin[0], state->origin[1], state->origin[2], 200 + (rand()&31), 0.1, 0)->noppl = (j != cl.playernum[0]); } - if (!state->modelindex) + if (state->modelindex < 1) continue; /* if (!Cam_DrawPlayer(j)) @@ -2442,10 +2449,22 @@ void CL_EmitEntities (void) - +void CL_ParseClientdata (void); void MVD_Interpolate(void) { + player_state_t *self, *oldself; + + CL_ParseClientdata(); + + self = &cl.frames[cl.parsecount & UPDATE_MASK].playerstate[cl.playernum[0]]; + oldself = &cl.frames[(cls.netchan.outgoing_sequence-1) & UPDATE_MASK].playerstate[cl.playernum[0]]; + self->messagenum = cl.parsecount; + VectorCopy(oldself->origin, self->origin); + VectorCopy(oldself->velocity, self->velocity); + VectorCopy(oldself->viewangles, self->viewangles); + + cls.netchan.outgoing_sequence = cl.parsecount+1; } diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index 95cbccae9..362b8406d 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -847,6 +847,8 @@ void CL_SendCmd (void) if (cls.demoplayback == DPB_MVD) { 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 cmd = &cl.frames[i].cmd[0]; memset(cmd, 0, sizeof(*cmd)); @@ -867,7 +869,6 @@ void CL_SendCmd (void) Cam_FinishMove(0, cmd); - cls.netchan.outgoing_sequence++; } diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 80edf5a88..18ac193a3 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1296,8 +1296,8 @@ void CL_Packet_f (void) return; } - if (Cmd_FromServer()) - { + if (Cmd_FromServer()) //some mvd servers stuffcmd a packet command which lets them know which ip the client is from. + { //unfortunatly, 50% of servers are badly configured. if (adr.type == NA_IP) if (adr.ip[0] == 127) if (adr.ip[1] == 0) @@ -1307,6 +1307,8 @@ void CL_Packet_f (void) Con_Printf ("^b^1Server is broken. Ignoring 'realip' packet request\n"); return; } + + Con_Printf ("Sending realip packet\n"); } in = Cmd_Argv(2); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 5eec97612..87532c90d 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -1258,7 +1258,8 @@ void CL_ParseServerData (void) if (cls.demoplayback == DPB_MVD) { int i; - cls.netchan.last_received = MSG_ReadFloat(); + extern float nextdemotime; + cls.netchan.last_received = nextdemotime = /*olddemotime =*/ MSG_ReadFloat(); cl.playernum[0] = MAX_CLIENTS - 1; cl.spectator = true; for (i = 0; i < UPDATE_BACKUP; i++) @@ -1732,7 +1733,7 @@ void CLNQ_ParseClientdata (int bits) CL_SetStat(0, STAT_ROCKETS, MSG_ReadByte()); CL_SetStat(0, STAT_CELLS, MSG_ReadByte()); - CL_SetStat(0, STAT_ACTIVEWEAPON, (unsigned short)MSG_ReadShort()); + CL_SetStat(0, STAT_ACTIVEWEAPON, MSG_ReadByte()); } if (bits & DPSU_VIEWZOOM) @@ -2521,6 +2522,14 @@ void CL_SetStat (int pnum, int stat, int value) return; // Host_EndGame ("CL_SetStat: %i is invalid", stat); + if (cls.demoplayback == DPB_MVD) + { + extern int cls_lastto; + cl.players[cls_lastto].stats[stat]=value; + if ( spec_track[pnum] != cls_lastto ) + return; + } + if (cl.stats[pnum][stat] != value) Sbar_Changed (); @@ -3698,7 +3707,10 @@ void CL_ParseServerMessage (void) break; case svc_nails: - CL_ParseProjectiles (cl_spikeindex); + CL_ParseProjectiles (cl_spikeindex, false); + break; + case svc_nails2: + CL_ParseProjectiles (cl_spikeindex, true); break; case svc_chokecount: // some preceding packets were choked @@ -4142,7 +4154,7 @@ void CLNQ_ParseServerMessage (void) cl.gametimemark = realtime; if (nq_dp_protocol<5) { - cls.netchan.incoming_sequence++; + cl.validsequence = cls.netchan.incoming_sequence++; // cl.frames[(cls.netchan.incoming_sequence-1)&UPDATE_MASK].packet_entities = cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities; cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities.num_entities=0; } diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index cef2dbde0..c35a634c9 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -579,7 +579,7 @@ void CL_PredictMovePNum (int pnum) return; } - if (cls.netchan.outgoing_sequence - cls.netchan.incoming_sequence >= UPDATE_BACKUP-1) + if (cls.netchan.outgoing_sequence - cl.validsequence >= UPDATE_BACKUP-1) { return; } @@ -597,7 +597,7 @@ void CL_PredictMovePNum (int pnum) } // this is the last frame received from the server - from = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + from = &cl.frames[cl.validsequence & UPDATE_MASK]; if (!cl.intermission) { @@ -637,13 +637,13 @@ void CL_PredictMovePNum (int pnum) } } #endif - if ((cl_nopred.value|| cl.fixangle)) + if (((cl_nopred.value && cls.demoplayback!=DPB_MVD)|| cl.fixangle)) { fixedorg: VectorCopy (vel, cl.simvel[pnum]); VectorCopy (org, cl.simorg[pnum]); - to = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + to = &cl.frames[cl.validsequence & UPDATE_MASK]; @@ -655,12 +655,12 @@ fixedorg: oldphysent = pmove.numphysent; CL_SetSolidPlayers (cl.playernum[pnum]); - to = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + to = &cl.frames[cl.validsequence & UPDATE_MASK]; - for (i=1 ; iplayerstate->pm_type = PM_FLY; CL_PredictUsercmd (pnum, &from->playerstate[cl.playernum[pnum]] diff --git a/engine/client/client.h b/engine/client/client.h index f27b3cd21..4372dbd60 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -156,6 +156,8 @@ typedef struct player_info_s unsigned short vweapindex; int prevcount; + + int stats[MAX_CL_STATS]; } player_info_t; @@ -703,7 +705,7 @@ void CL_SetSolidPlayers (int playernum); void CL_SetUpPlayerPrediction(qboolean dopred); void CL_EmitEntities (void); void CL_ClearProjectiles (void); -void CL_ParseProjectiles (int modelindex); +void CL_ParseProjectiles (int modelindex, qboolean nails2); void CL_ParsePacketEntities (qboolean delta); void CL_SetSolidEntities (void); void CL_ParsePlayerinfo (void); diff --git a/engine/client/fragstats.c b/engine/client/fragstats.c index 2a1c337a9..c22de2997 100644 --- a/engine/client/fragstats.c +++ b/engine/client/fragstats.c @@ -322,7 +322,7 @@ static void Stats_LoadFragFile(char *name) if (!*file) break; - Cmd_TokenizeString(file); + Cmd_TokenizeString(file, true, false); file = end+1; if (!Cmd_Argc()) continue; diff --git a/engine/client/keys.c b/engine/client/keys.c index 891832632..0860b48b8 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -869,7 +869,7 @@ void Key_Bind_f (void) if (c > 3) { - Cmd_ShiftArgs(1); + Cmd_ShiftArgs(1, Cmd_ExecLevel==RESTRICT_LOCAL); Key_SetBinding (b, modifier, Cmd_Args(), Cmd_ExecLevel); return; } diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 8b223424c..36426f3ee 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -142,6 +142,33 @@ void PF_loadfromdata (progfuncs_t *prinst, struct globalvars_s *pr_globals) G_FLOAT(OFS_RETURN) = 0; } +void PF_parseentitydata(progfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + void *ed = G_EDICT(prinst, OFS_PARM0); + char *file = PR_GetStringOfs(prinst, OFS_PARM1); + + int size; + + if (!*file) + { + G_FLOAT(OFS_RETURN) = -1; + return; + } + + if (!prinst->restoreent(prinst, file, &size, ed)) + Con_Printf("parseentitydata: missing opening data\n"); + else + { + file += size; + while(*file < ' ' && *file) + file++; + if (*file) + Con_Printf("parseentitydata: too much data\n"); + } + + G_FLOAT(OFS_RETURN) = 0; +} + void PF_mod (progfuncs_t *prinst, struct globalvars_s *pr_globals) { G_FLOAT(OFS_RETURN) = (float)(((int)G_FLOAT(OFS_PARM0))%((int)G_FLOAT(OFS_PARM1))); @@ -885,6 +912,18 @@ void PF_etof(progfuncs_t *prinst, struct globalvars_s *pr_globals) { G_FLOAT(OFS_RETURN) = G_EDICTNUM(prinst, OFS_PARM0); } +void PF_ftoe(progfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + int entnum = G_FLOAT(OFS_PARM0); + + RETURN_EDICT(prinst, EDICT_NUM(prinst, entnum)); +} + +void PF_IsNull(progfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + int str = G_INT(OFS_PARM0); + G_FLOAT(OFS_RETURN) = !str; +} builtin_t menu_builtins[] = { //0 @@ -977,7 +1016,16 @@ builtin_t menu_builtins[] = { PF_chr2str,//8 PF_etof,//9 //float etof(entity ent) = #79; //80 - skip10 + PF_ftoe,//10 + PF_IsNull, + skip1 + skip1 + skip1 + skip1 + skip1 + skip1 + skip1 + skip1 //90 skip10 //100 @@ -1033,7 +1081,7 @@ builtin_t menu_builtins[] = { PF_CL_findkeysforcommand, PF_gethostcachevalue, PF_gethostcachestring, - PF_Fixme //void parseentitydata(entity ent, string data) = #613; + PF_parseentitydata //void parseentitydata(entity ent, string data) = #613; }; int menu_numbuiltins = sizeof(menu_builtins)/sizeof(menu_builtins[0]); @@ -1084,6 +1132,8 @@ void MP_Shutdown (void) mouseusedforgui = false; + M_Init_Internal(); + if (inmenuprogs) //something in the menu caused the problem, so... { inmenuprogs = 0; @@ -1108,6 +1158,14 @@ void VARGS Menu_Abort (char *format, ...) Con_Printf("Menu_Abort: %s\nShutting down menu.dat\n", string); + +{ + static char buffer[1024*1024*8]; + int size = sizeof buffer; + menuprogs->save_ents(menuprogs, buffer, &size, 3); + COM_WriteFile("menucore.txt", buffer, size); +} + MP_Shutdown(); } diff --git a/engine/client/r_part.c b/engine/client/r_part.c index e7acc95d2..cfccc0884 100644 --- a/engine/client/r_part.c +++ b/engine/client/r_part.c @@ -368,7 +368,7 @@ void R_ParticleEffect_f(void) return; } - Cmd_TokenizeString(buf); + Cmd_TokenizeString(buf, true, true); var = Cmd_Argv(0); value = Cmd_Argv(1); diff --git a/engine/client/r_partset.c b/engine/client/r_partset.c index 46436536a..4eb7f57f3 100644 --- a/engine/client/r_partset.c +++ b/engine/client/r_partset.c @@ -2,6 +2,7 @@ char *particle_set_spikeset = +#if 0 "r_part rockettail\n" "{\n" " texture \"particles/rtrail\"\n" @@ -37,7 +38,47 @@ char *particle_set_spikeset = " blend add\n" " assoc rockettail\n" "}\n" -"\n" +#else +"r_part t_rocket\n" +"{\n" +" texture \"\"\n" +" gravity -200\n" +" step 40\n" +" scale 10\n" +" scaledelta 50\n" +" alpha 0.5\n" +" die 0.5\n" +" red 254\n" +" green 128\n" +" blue 64\n" +" blend add\n" +" isbeam\n" +" spawnmode spiral\n" +" offsetspread 5\n" +"}\n" +#endif + +//TeamFortress railgun (by model - this is also the effect used with the TE_LIGHTNING1 extension) +"r_part t_railtrail\n" +"{\n" +" texture \"particles/b_rocket3\"\n" +" step 15\n" +" scale 10\n" +" alpha 1\n" +" die 1\n" +" red 255\n" +" green 255\n" +" blue 255\n" +" blend add\n" +" isbeam\n" +" spawnmode spiral\n" +" offsetspread 100\n" +" cliptype t_railtrail\n" +" friction 0.7\n" +"}\n" +"r_trail progs/e_spike1.mdl t_railtrail\n" + + "r_part t_grenade\n" "{\n" " texture \"particles/rtrail\"\n" @@ -245,6 +286,8 @@ char *particle_set_spikeset = " emitintervalrand 0\n" "}\n" "\n" + +//fixme: 16?!?! "r_part te_explosion\n" "{\n" " texture \"particles/explosion\"\n" @@ -269,29 +312,7 @@ char *particle_set_spikeset = " scalefactor 1\n" "}\n" "\n" -"r_part cte_greenexplosion\n" -"{\n" -" texture \"particles/explosion\"\n" -" count 16\n" -" scale 100\n" -" alpha 0.7\n" -" die 4\n" -" randomvel 32\n" -" veladd 0\n" -" red 128\n" -" green 255\n" -" blue 76\n" -" reddelta 0\n" -" greendelta 0\n" -" reddelta 0\n" -" gravity 0\n" -"friction 1\n" -" stains 0\n" -" blend add\n" -" assoc shrapnal\n" -" scalefactor 1\n" -"}\n" -"\n" + "r_part empcentral\n" "{\n" " texture \"particles/emp\"\n" @@ -429,7 +450,7 @@ char *particle_set_spikeset = "r_part te_teleportsplash\n" "{\n" " texture \"particles/teleport\"\n" -" count 4192\n" +" count 4192\n" //EGAD!!!! 4192?!??! What was I thinking?!?!? (it's a very pwetty effect though...) " scale 2\n" " scalefactor 1\n" " alpha 1\n" diff --git a/engine/client/zqtp.c b/engine/client/zqtp.c index c0d6474a4..fba63744a 100644 --- a/engine/client/zqtp.c +++ b/engine/client/zqtp.c @@ -1099,7 +1099,7 @@ static void TP_LoadLocFile (char *filename, qbool quiet) } line[i] = 0; - Cmd_TokenizeString (line); + Cmd_TokenizeString (line, true, false); argc = Cmd_Argc(); if (!argc) diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 2593d56bf..62f95f661 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -965,7 +965,7 @@ Cmd_ShiftArgs Shifts Cmd_Argv results down one (killing first param) ============ */ -void Cmd_ShiftArgs (int ammount) +void Cmd_ShiftArgs (int ammount, qboolean expandstring) { int arg; while (ammount>0 && cmd_argc) @@ -984,7 +984,7 @@ void Cmd_ShiftArgs (int ammount) if (cmd_args) { - cmd_args = COM_StringParse(cmd_args); + cmd_args = COM_StringParse(cmd_args, expandstring, false); if (cmd_args) while(*cmd_args == ' ') cmd_args++; @@ -1114,7 +1114,7 @@ Cmd_TokenizeString Parses the given string into command line tokens. ============ */ -void Cmd_TokenizeString (char *text) +void Cmd_TokenizeString (char *text, qboolean expandmacros, qboolean qctokenize) { int i; @@ -1144,8 +1144,8 @@ void Cmd_TokenizeString (char *text) if (cmd_argc == 1) cmd_args = text; - - text = COM_StringParse (text); + + text = COM_StringParse (text, expandmacros, qctokenize); if (!text) return; @@ -1581,7 +1581,7 @@ void Cmd_ExecuteString (char *text, int level) Cmd_ExecLevel = level; text = Cmd_ExpandString(text, dest, sizeof(dest), level); - Cmd_TokenizeString (text); + Cmd_TokenizeString (text, level == RESTRICT_LOCAL, false); // execute the command line if (!Cmd_Argc()) diff --git a/engine/common/cmd.h b/engine/common/cmd.h index 9a3b4c186..760fbd11c 100644 --- a/engine/common/cmd.h +++ b/engine/common/cmd.h @@ -101,7 +101,7 @@ int Cmd_CheckParm (char *parm); char *Cmd_AliasExist(char *name, int restrictionlevel); void Alias_WipeStuffedAliaes(void); -void Cmd_TokenizeString (char *text); +void Cmd_TokenizeString (char *text, qboolean expandmacros, qboolean qctokenize); // Takes a null terminated string. Does not need to be /n terminated. // breaks the string up into arg tokens. @@ -131,7 +131,7 @@ void Cmd_MessageTrigger (char *message, int type); void Cmd_StuffCmds_f (void); -void Cmd_ShiftArgs (int ammount); +void Cmd_ShiftArgs (int ammount, qboolean expandstring); char *Cmd_ExpandString (char *data, char *dest, int destlen, int maxaccesslevel); diff --git a/engine/common/common.c b/engine/common/common.c index aba47550b..b665d152e 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -1428,7 +1428,7 @@ skipwhite: } //same as COM_Parse, but parses two quotes next to each other as a single quote as part of the string -char *COM_StringParse (char *data) +char *COM_StringParse (char *data, qboolean expandmacros, qboolean qctokenize) { int c; int len; @@ -1442,7 +1442,7 @@ char *COM_StringParse (char *data) // skip whitespace skipwhite: - while ( (c = *data) <= ' ') + while ( (c = *data), c <= ' ' && c != '\n') { if (c == 0) return NULL; // end of file; @@ -1501,6 +1501,54 @@ skipwhite: } } + // handle quoted strings specially + if (c == '\'' && qctokenize) + { + data++; + while (1) + { + if (len >= TOKENSIZE-1) + { + com_token[len] = '\0'; + return data; + } + + + c = *data++; + if (c=='\'') + { + c = *(data); + if (c!='\'') + { + com_token[len] = 0; + return data; + } + while (c=='\'') + { + com_token[len] = c; + len++; + data++; + c = *(data+1); + } + } + if (!c) + { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } + } + + if (qctokenize && (c == '\n' || c == '{' || c == '}' || c == ')' || c == '(' || c == ']' || c == '[' || c == '\'' || c == ':' || c == ',' || c == ';')) + { + // single character + com_token[len++] = c; + com_token[len] = 0; + return data+1; + } + // parse a regular word do { @@ -1514,16 +1562,13 @@ skipwhite: data++; len++; c = *data; - } while (c>32); + } while (c>32 && !(qctokenize && (c == '\n' || c == '{' || c == '}' || c == ')' || c == '(' || c == ']' || c == '[' || c == '\'' || c == ':' || c == ',' || c == ';'))); com_token[len] = 0; -#ifndef CLIENTONLY - { - extern redirect_t sv_redirected; - if (sv_redirected) //servers shouldn't give the values of cvars to all clients... Like password... + + if (!expandmacros) return data; - } -#endif + //now we check for macros. for (s = com_token, c= 0; c < len; c++, s++) //this isn't a quoted token by the way. { diff --git a/engine/common/common.h b/engine/common/common.h index 8c125bd3f..2ddaadad0 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -202,7 +202,7 @@ extern qboolean com_eof; char *COM_Parse (char *data); char *COM_ParseCString (char *data); -char *COM_StringParse (char *data); +char *COM_StringParse (char *data, qboolean expandmacros, qboolean qctokenize); char *COM_ParseToken (char *data); char *COM_TrimString(char *str); diff --git a/engine/http/ftpserver.c b/engine/http/ftpserver.c index 24805a534..78a5cb5a0 100644 --- a/engine/http/ftpserver.c +++ b/engine/http/ftpserver.c @@ -556,6 +556,11 @@ iwboolean FTP_ServerThinkForConnection(FTPclient_t *cl) strcpy(buffer, cl->path+1); else strcpy(buffer, cl->path); + + if (*buffer) //last characture should be a / + if (buffer[strlen(buffer)-1] != '/') + strcat(buffer, "/"); + strcat(buffer, "*"); QueueMessage (cl, "125 Opening FAKE ASCII mode data connection for file.\r\n"); diff --git a/engine/qclib/pr_edict.c b/engine/qclib/pr_edict.c index 95a617bcf..402dadfc1 100644 --- a/engine/qclib/pr_edict.c +++ b/engine/qclib/pr_edict.c @@ -121,7 +121,13 @@ struct edict_s *ED_Alloc (progfuncs_t *progfuncs) } if (i >= maxedicts-1) + { + int size; + char *buf; + buf = progfuncs->save_ents(progfuncs, NULL, &size, 0); + progfuncs->parms->WriteFile("edalloc.dump", buf, size); Sys_Error ("ED_Alloc: no free edicts"); + } } sv_num_edicts++; @@ -1348,6 +1354,99 @@ char *ED_WriteEdict(progfuncs_t *progfuncs, edictrun_t *ed, char *buffer, pbool #undef AddS } +char *SaveCallStack (progfuncs_t *progfuncs, char *s) +{ +#define AddS(str) strcpy(s, str);s+=strlen(str); + char buffer[8192]; + dfunction_t *f; + int i; + int progs; + + int arg; + int *globalbase; + + progs = -1; + + if (pr_depth == 0) + { + AddS ("\n"); + return s; + } + + globalbase = (int *)pr_globals + pr_xfunction->parm_start + pr_xfunction->locals; + + pr_stack[pr_depth].f = pr_xfunction; + for (i=pr_depth ; i>0 ; i--) + { + f = pr_stack[i].f; + + if (!f) + { + AddS ("\n"); + } + else + { + if (pr_stack[i].progsnum != progs) + { + progs = pr_stack[i].progsnum; + + sprintf(buffer, "//%i %s\n", progs, pr_progstate[progs].filename); + AddS (buffer); + } + if (!*f->s_file) + sprintf(buffer, "\t\"%i:%s\"\n", progs, f->s_name); + else + sprintf(buffer, "\t\"%i:%s\" //%s\n", progs, f->s_name, f->s_file); + AddS (buffer); + + AddS ("\t{\n"); + for (arg = 0; arg < f->locals; arg++) + { + ddef16_t *local; + local = ED_GlobalAtOfs16(progfuncs, f->parm_start+arg); + if (!local) + sprintf(buffer, "\t\tofs%i %i // %f\n", f->parm_start+arg, *(int *)(globalbase - f->locals+arg), *(float *)(globalbase - f->locals+arg) ); + else + { +__try +{ + if (local->type == ev_entity) + { //go safly. + int n; + sprintf(buffer, "\t\t\"%s\"\t\"entity INVALID POINTER\"\n", local->s_name, n); + for (n = 0; n < sv_num_edicts; n++) + { + if (prinst->edicttable[n] == (struct edict_s *)PROG_TO_EDICT(((eval_t*)(globalbase - f->locals+arg))->edict)) + { + sprintf(buffer, "\t\t\"%s\" \"entity %i\"\n", local->s_name, n); + break; + } + } + } + else + sprintf(buffer, "\t\t\"%s\"\t\"%s\"\n", local->s_name, PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase - f->locals+arg))); +} +__except(EXCEPTION_EXECUTE_HANDLER) +{ + sprintf(buffer, "\t\t\"%s\" \"ILLEGAL POINTER\"\n", local->s_name); +} + if (local->type == ev_vector) + arg+=2; + } + AddS (buffer); + } + AddS ("\t}\n"); + + if (i == pr_depth) + globalbase = localstack + localstack_used - f->locals; + else + globalbase -= f->locals; + } + } + return s; +#undef AddS +} + //there are two ways of saving everything. //0 is to save just the entities. //1 is to save the entites, and all the progs info so that all the variables are saved off, and it can be reloaded to exactly how it was (provided no files or data has been changed outside, like the progs.dat for example) @@ -1413,7 +1512,6 @@ char *SaveEnts(progfuncs_t *progfuncs, char *mem, int *len, int alldata) if (alldata) { - AddS("general {\n"); AddS(qcva("\"maxprogs\" \"%i\"\n", maxprogs)); // AddS(qcva("\"maxentities\" \"%i\"\n", maxedicts)); @@ -1438,6 +1536,14 @@ char *SaveEnts(progfuncs_t *progfuncs, char *mem, int *len, int alldata) } } + if (alldata == 3) + { + //include callstack + AddS("stacktrace {\n"); + s = SaveCallStack(progfuncs, s); + AddS("}\n"); + } + for (a = 0; a < maxprogs; a++) //I would mix, but external functions rely on other progs being loaded { if (!pr_progstate[a].progs) @@ -2003,8 +2109,6 @@ struct edict_s *RestoreEnt (progfuncs_t *progfuncs, char *buf, int *size, struct ent = (edictrun_t *)ed; ent->isfree = false; - ED_ClearEdict(progfuncs, ent); - buf = ED_ParseEdict(progfuncs, buf, ent); *size = buf - start; diff --git a/engine/qclib/pr_exec.c b/engine/qclib/pr_exec.c index 4aba8602a..ab418323a 100644 --- a/engine/qclib/pr_exec.c +++ b/engine/qclib/pr_exec.c @@ -241,9 +241,14 @@ int PR_EnterFunction (progfuncs_t *progfuncs, dfunction_t *f, int progsnum) pr_depth++; if (pr_depth == MAX_STACK_DEPTH) { - printf ("stack overflow on call to %s", f->s_name); pr_depth--; PR_StackTrace (progfuncs); + + printf ("stack overflow on call to %s\n", f->s_name); + + //comment this out if you want the progs to try to continue anyway (could cause infinate loops) + Abort("Stack Overflow\n"); + PR_AbortStack(progfuncs); return pr_xstatement; } diff --git a/engine/qclib/progsint.h b/engine/qclib/progsint.h index 2d802a048..e99550c2e 100644 --- a/engine/qclib/progsint.h +++ b/engine/qclib/progsint.h @@ -363,7 +363,7 @@ vars(prstack_t, pr_stack, MAX_STACK_DEPTH); var(int, pr_depth); #define pr_depth prinst->pr_depth -#define LOCALSTACK_SIZE 16384 +#define LOCALSTACK_SIZE 4096 vars(int, localstack, LOCALSTACK_SIZE); #define localstack prinst->localstack var(int, localstack_used); diff --git a/engine/qux/x_reqs.c b/engine/qux/x_reqs.c index 1aff5c5ce..a14f04156 100644 --- a/engine/qux/x_reqs.c +++ b/engine/qux/x_reqs.c @@ -2704,7 +2704,7 @@ void Draw_CharToDrawable (int num, unsigned int *drawable, int x, int y, int wid if (s > e) return; - if (y >= height) + if (y >= height-e) return; if (y < -8) return; @@ -2819,13 +2819,13 @@ void XR_PolyText(xclient_t *cl, xReq *request) while(1) { charnum = 0; + charnum |= *str++; if (req->reqType == X_ImageText16) charnum |= (*str++)<<8; - charnum |= *str++; if (!charnum) return; - Draw_CharToDrawable(charnum, (unsigned int *)drbuffer, xpos, ypos, drwidth, drheight, gc); + Draw_CharToDrawable(charnum&255, (unsigned int *)drbuffer, xpos, ypos, drwidth, drheight, gc); xpos += 8; } @@ -2877,6 +2877,8 @@ void XR_QueryFont(xclient_t *cl, xReq *request) //basically ignored. We only sup { // xResourceReq *req = (xResourceReq *)request; char buffer[8192]; + int i; + xCharInfo *ci; xQueryFontReply *rep = (xQueryFontReply *)buffer; rep->type = X_Reply; @@ -2910,13 +2912,23 @@ void XR_QueryFont(xclient_t *cl, xReq *request) //basically ignored. We only sup rep->drawDirection = 0; rep->minByte1 = 0; rep->maxByte1 = 0; - rep->allCharsExist = 1; + rep->allCharsExist = 0; rep->fontAscent = 4; rep->fontDescent = 4; - rep->nCharInfos = 0; /* followed by this many xCharInfo structures */ + rep->nCharInfos = 255; /* followed by this many xCharInfo structures */ rep->length = ((sizeof(xQueryFontReply) - sizeof(xGenericReply)) + rep->nFontProps*sizeof(xFontProp) + rep->nCharInfos*sizeof(xCharInfo))/4; + ci = (xCharInfo*)(rep+1); + for (i = 0; i < rep->nCharInfos; i++) + { + ci[i].leftSideBearing = 0; + ci[i].rightSideBearing = 0; + ci[i].characterWidth = 8; + ci[i].ascent = 4; + ci[i].descent = 4; + } + X_SendData(cl, rep, sizeof(xGenericReply)+rep->length*4); } diff --git a/engine/server/net_preparse.c b/engine/server/net_preparse.c index dfc581246..89cef1259 100644 --- a/engine/server/net_preparse.c +++ b/engine/server/net_preparse.c @@ -1294,18 +1294,18 @@ void NPP_MVDFlush(void) if (1) { int entnum, i; - entity_state_t *ent; + mvdentity_state_t *ent; - if (!sv.demostate) - sv.demostate = BZ_Malloc(sizeof(entity_state_t)*MAX_EDICTS); - - sv.demostatevalid = true; + if (!sv.demobaselines) + { + sv.demobaselines = (mvdentity_state_t*)BZ_Malloc(sizeof(mvdentity_state_t)*MAX_EDICTS); + sv.demostatevalid = true; + } entnum = buffer[1] + (buffer[2]<<8); // if (entnum < MAX_CLIENTS) // break; - ent = &sv.demostate[entnum]; + ent = &sv.demobaselines[entnum]; - ent->number = entnum; ent->modelindex = buffer[3]; ent->frame = buffer[4]; ent->colormap = buffer[5]; @@ -1314,7 +1314,7 @@ void NPP_MVDFlush(void) for (i=0 ; i<3 ; i++) { ent->origin[i] = (short)(buffer[7+i*3] + (buffer[8+i*3]<<8))/8.0f; - ent->angles[i] = buffer[9+i*3]*360.0/256; + ent->angles[i] = buffer[9+i*3]; } } break; @@ -1412,11 +1412,14 @@ void NPP_MVDFlush(void) ignoreprotocol=true; //a bug exists in that the delta MUST have been reliably recorded. { int i; - entity_state_t *ents; + int entnum; + mvdentity_state_t *ents; unsigned short s; if (!sv.demostate) - sv.demostate = BZ_Malloc(sizeof(entity_state_t)*MAX_EDICTS); - sv.demostatevalid = true; + { + sv.demostate = BZ_Malloc(sizeof(mvdentity_state_t)*MAX_EDICTS); + sv.demostatevalid = true; + } i = majortype-svc_packetentities+1; while (1) { @@ -1428,14 +1431,25 @@ void NPP_MVDFlush(void) } else { - ents = &sv.demostate[s&511]; - ents->number = s&511; + entnum = s&511; s &= ~511; + + if (entnum > sv.demomaxents) + sv.demomaxents = entnum; + + ents = &sv.demostate[entnum]; if (s & U_REMOVE) - { + { //this entity went from the last packet ents->modelindex = 0; + ents->effects = 0; + continue; } + if (!ents->modelindex && !ents->effects && sv.demobaselines) + { //new entity, reset to baseline + memcpy(ents, &sv.demobaselines[entnum], sizeof(mvdentity_state_t)); + } + if (s & U_MOREBITS) { s |= buffer[i]; @@ -1480,7 +1494,7 @@ void NPP_MVDFlush(void) if (s & U_ANGLE1) { - ents->angles[0] = (unsigned char)(buffer[i]) * (360.0/256); + ents->angles[0] = (unsigned char)(buffer[i]);// * (360.0/256); i++; } @@ -1492,7 +1506,7 @@ void NPP_MVDFlush(void) if (s & U_ANGLE2) { - ents->angles[1] = (unsigned char)(buffer[i]) * (360.0/256); + ents->angles[1] = (unsigned char)(buffer[i]);// * (360.0/256); i++; } @@ -1504,7 +1518,7 @@ void NPP_MVDFlush(void) if (s & U_ANGLE3) { - ents->angles[2] = (unsigned char)(buffer[i]) * (360.0/256); + ents->angles[2] = (unsigned char)(buffer[i]);// * (360.0/256); i++; } } @@ -1516,7 +1530,7 @@ void NPP_MVDFlush(void) { int i, j; unsigned short flags; - entity_state_t *ents; + mvdentity_state_t *ents; int wframe, playernum; vec3_t oldorg; vec3_t oldang; @@ -1549,7 +1563,8 @@ void NPP_MVDFlush(void) for (j=0 ; j<3 ; j++) if (flags & (DF_ANGLES << j)) { - ents->angles[j] = (int)(buffer[i] + (buffer[i+1]<<8))*360.0f/(256*256); + //FIXME: angle truncation here. + ents->angles[j] = (int)(buffer[i] + (buffer[i+1]<<8))/256; i+=2; } @@ -1620,7 +1635,7 @@ void NPP_MVDFlush(void) case svc_stufftext: ignoreprotocol = true; - Cmd_TokenizeString(buffer+1); + Cmd_TokenizeString(buffer+1, false, false); if (!stricmp(Cmd_Argv(0), "fullserverinfo")) { Q_strncpyz(sv.demoinfo, Cmd_Argv(1), sizeof(sv.demoinfo)); diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index a71b75f20..3a6e4d9d2 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -3474,7 +3474,14 @@ void PF_walkmove (progfuncs_t *prinst, struct globalvars_s *pr_globals) // oldf = pr_xfunction; oldself = pr_global_struct->self; - G_FLOAT(OFS_RETURN) = SV_movestep(ent, move, true, false, settrace); + if (!SV_TestEntityPosition(ent)) + { + G_FLOAT(OFS_RETURN) = SV_movestep(ent, move, true, false, settrace); + if (SV_TestEntityPosition(ent)) + Con_Printf("Entity became stuck\n"); + } + else + Con_Printf("Ent is stuck - sorry\n"); // restore program state @@ -3502,7 +3509,7 @@ void PF_droptofloor (progfuncs_t *prinst, struct globalvars_s *pr_globals) end[2] -= 512; VectorCopy (ent->v.origin, start); - trace = SV_Move (start, ent->v.mins, ent->v.maxs, end, false, ent); + trace = SV_Move (start, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent); if (trace.fraction == 1 || trace.allsolid) G_FLOAT(OFS_RETURN) = 0; @@ -3510,7 +3517,7 @@ void PF_droptofloor (progfuncs_t *prinst, struct globalvars_s *pr_globals) { VectorCopy (trace.endpos, ent->v.origin); SV_LinkEdict (ent, false); -// ent->v.flags = (int)ent->v.flags | FL_ONGROUND; + ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(prinst, trace.ent); G_FLOAT(OFS_RETURN) = 1; } @@ -5396,9 +5403,9 @@ void PF_fgets (progfuncs_t *prinst, struct globalvars_s *pr_globals) pf_fopen_files[fnum].ofs = s - pf_fopen_files[fnum].data; if (!pr_string_temp[0] && !*s) - G_INT(OFS_PARM0) = 0; //EOF + G_INT(OFS_RETURN) = 0; //EOF else - RETURN_SSTRING(pr_string_temp); + G_INT(OFS_RETURN) = (int)pr_string_temp - prinst->stringtable; } void PF_fputs (progfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -5776,7 +5783,7 @@ void PF_ArgC (progfuncs_t *prinst, struct globalvars_s *pr_globals) //85 / //KRIMZON_SV_PARSECLIENTCOMMAND added these two. void PF_Tokenize (progfuncs_t *prinst, struct globalvars_s *pr_globals) //84 //void(string str) tokanize; { - Cmd_TokenizeString(PR_GetStringOfs(prinst, OFS_PARM0)); + Cmd_TokenizeString(PR_GetStringOfs(prinst, OFS_PARM0), false, true); G_FLOAT(OFS_RETURN) = Cmd_Argc(); } void PF_ArgV (progfuncs_t *prinst, struct globalvars_s *pr_globals) //86 //string(float num) argv; diff --git a/engine/server/server.h b/engine/server/server.h index 467fbe869..fa9f64a15 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -80,6 +80,23 @@ typedef struct { #define CTE_CHANNELFADE 16 #define CTE_ISBEAM 128 + + +typedef struct +{ + vec3_t origin; + qbyte angles[3]; + qbyte modelindex; + qbyte frame; + qbyte colormap; + qbyte skinnum; + qbyte effects; + + qbyte scale; + qbyte trans; + qbyte fatness; +} mvdentity_state_t; + typedef struct { qboolean active; // false when server is going down @@ -175,7 +192,9 @@ typedef struct qboolean mvdrecording; +//==================================================== //this lot is for playback of demos + qboolean mvdplayback; float realtime; FILE *demofile; //also signifies playing the thing. @@ -183,6 +202,7 @@ typedef struct int lasttype; int lastto; +//playback spikes (svc_nails/nails2) int numdemospikes; struct { vec3_t org; @@ -191,9 +211,14 @@ typedef struct qbyte yaw; qbyte modelindex; } demospikes[255]; - entity_state_t *demostate; + +//playback of entities (svc_nails/nails2) + mvdentity_state_t *demostate; + mvdentity_state_t *demobaselines; + int demomaxents; qboolean demostatevalid; - char demoinfo[MAX_SERVERINFO_STRING]; + +//players struct { int stats[MAX_CL_STATS]; int pl; @@ -207,11 +232,14 @@ typedef struct float updatetime; } recordedplayer[MAX_CLIENTS]; +//gamestate + char demoinfo[MAX_SERVERINFO_STRING]; char demmodel_precache[MAX_MODELS][MAX_QPATH]; // NULL terminated char demsound_precache[MAX_SOUNDS][MAX_QPATH]; // NULL terminated char demgamedir[64]; char demname[64]; // map name - qboolean democausesreconnect; //make players load the level. + + qboolean democausesreconnect; //this makes current clients go through the connection process (and when the demo ends too) sizebuf_t demosignon; int num_demosignon_buffers; int demosignon_buffer_size[MAX_SIGNON_BUFFERS]; @@ -219,6 +247,7 @@ typedef struct char demfullmapname[64]; char *demolightstyles[MAX_LIGHTSTYLES]; +//==================================================== entity_state_t extendedstatics[MAX_STATIC_ENTITIES]; int numextrastatics; diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 18fb92de8..f02a64705 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -751,12 +751,12 @@ void SV_StuffToClient_f(void) char *c; char *key; - Cmd_ShiftArgs(1); + Cmd_ShiftArgs(1, Cmd_ExecLevel==RESTRICT_LOCAL); if (!strcmp(Cmd_Argv(1), "bind")) { key = Z_Malloc(strlen(Cmd_Argv(2))+1); strcpy(key, Cmd_Argv(2)); - Cmd_ShiftArgs(2); + Cmd_ShiftArgs(2, Cmd_ExecLevel==RESTRICT_LOCAL); } else key = NULL; @@ -1169,7 +1169,7 @@ void SV_SaveInfo(FILE *f, char *info, char *commandname) command = info+1; value = strchr(command, '\\'); info = strchr(value+1, '\\'); - if (!*info) //eot.. + if (!info) //eot.. info = value+strlen(value); if (*command == '*') //unsettable, so don't write it for later setting. @@ -1496,7 +1496,7 @@ void SV_SetTimer_f(void) return; } - Cmd_ShiftArgs(2); //strip the two vars + Cmd_ShiftArgs(2, Cmd_ExecLevel==RESTRICT_LOCAL); //strip the two vars command = Cmd_Args(); timercommand = Cvar_Get("sv_timer", "", CVAR_NOSET, NULL); diff --git a/engine/server/sv_demo.c b/engine/server/sv_demo.c index 8dc6c963f..568a7449e 100644 --- a/engine/server/sv_demo.c +++ b/engine/server/sv_demo.c @@ -225,6 +225,14 @@ void SV_LoadClientDemo_f (void) SV_ReadMVD(); } + if (!sv.state) + Cmd_ExecuteString("map start\n", Cmd_ExecLevel); //go for the start map + if (!sv.state) + { + Con_Printf("Could not activate server\n"); + return; + } + demoname = Cmd_Argv(1); com_filesize = COM_FOpenFile(demoname, &svd.demofile); @@ -312,7 +320,7 @@ qboolean SV_RunDemo (void) float demotime; qbyte c; // usercmd_t *pcmd; - usercmd_t emptycmd; +// usercmd_t emptycmd; static float prevtime = 0.0; qbyte newtime; @@ -376,11 +384,18 @@ readnext: switch (c & 7) { case dem_cmd : + + Con_Printf ("dem_cmd not supported\n"); + fclose(svd.demofile); + svd.demofile = NULL; + return false; + + // user sent input // i = svd.netchan.outgoing_sequence & UPDATE_MASK; // pcmd = &cl.frames[i].cmd; - if ((r = fread (&emptycmd, sizeof(emptycmd), 1, svd.demofile)) != 1) - SV_Error ("Corrupted demo"); + // if ((r = fread (&emptycmd, sizeof(emptycmd), 1, svd.demofile)) != 1) + // SV_Error ("Corrupted demo"); /* // qbyte order stuff for (j = 0; j < 3; j++) @@ -393,7 +408,7 @@ readnext: cl.frames[i].receivedtime = -1; // we haven't gotten a reply yet svd.netchan.outgoing_sequence++; */ - fread (&emptycmd, 12, 1, svd.demofile); + // fread (&emptycmd, 12, 1, svd.demofile); /* for (j = 0; j < 3; j++) cl.viewangles[i] = LittleFloat (cl.viewangles[i]); if (cl.spectator) diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index 83d7ea50a..9051b4e8c 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -278,6 +278,10 @@ void SV_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qb int i; float miss; + static entity_state_t defaultbaseline; + if (from == &((edict_t*)NULL)->baseline) + from = &defaultbaseline; + // send an update bits = 0; @@ -1028,6 +1032,9 @@ void SV_WritePlayersToClient (client_t *client, edict_t *clent, qbyte *pvs, size client_t *cl; edict_t *ent, *vent; int pflags; + vec3_t lerpedang; + + int splitnum = 0; demo_frame_t *demo_frame; demo_client_t *dcl; @@ -1194,7 +1201,10 @@ void SV_WritePlayersToClient (client_t *client, edict_t *clent, qbyte *pvs, size clst.zext = client->zquake_extensions; clst.cl = NULL; - VectorMA(sv.demostate[i+1].angles, realtime - sv.recordedplayer[i].updatetime, sv.recordedplayer[i].avelocity, ang); + ang[0] = sv.demostate[i+1].angles[0]*360.0f/256+ (realtime - sv.recordedplayer[i].updatetime)*sv.recordedplayer[i].avelocity[0]; + ang[1] = sv.demostate[i+1].angles[1]*360.0f/256+ (realtime - sv.recordedplayer[i].updatetime)*sv.recordedplayer[i].avelocity[1]; + ang[2] = sv.demostate[i+1].angles[2]*360.0f/256+ (realtime - sv.recordedplayer[i].updatetime)*sv.recordedplayer[i].avelocity[2]; + VectorMA(sv.demostate[i+1].origin, realtime - sv.recordedplayer[i].updatetime, sv.recordedplayer[i].velocity, org); ang[0] *= -3; @@ -1221,7 +1231,19 @@ void SV_WritePlayersToClient (client_t *client, edict_t *clent, qbyte *pvs, size SV_WritePlayerToClient(msg, &clst); } - if (ent != clent) + splitnum = 0; + if (cl->controlled) + { //hrm. splitscreen. + client_t *s; + for (s = cl; s; s = s->controlled, splitnum++) + { + if (s->edict == ent) + break; + } + if (!s) + continue; + } + else if (ent != clent) continue; } @@ -1314,7 +1336,7 @@ void SV_WritePlayersToClient (client_t *client, edict_t *clent, qbyte *pvs, size clst.health = 100; if (sv.demostatevalid) - clst.playernum = MAX_CLIENTS-1; + clst.playernum = MAX_CLIENTS-1-splitnum; clst.isself = false; if ((cl == client || cl->controller == client) && !sv.demostatevalid) @@ -1343,7 +1365,10 @@ void SV_WritePlayersToClient (client_t *client, edict_t *clent, qbyte *pvs, size { clst.origin = sv.demostate[client->spec_track].origin; clst.velocity = sv.recordedplayer[client->spec_track-1].velocity; - clst.angles = sv.demostate[client->spec_track].angles; + clst.angles = lerpedang; + lerpedang[0] = sv.demostate[client->spec_track].angles[0]*360.0/256; + lerpedang[1] = sv.demostate[client->spec_track].angles[1]*360.0/256; + lerpedang[2] = sv.demostate[client->spec_track].angles[2]*360.0/256; } clst.spectator = 2; } @@ -1704,7 +1729,7 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore vec3_t org; edict_t *ent; packet_entities_t *pack; - entity_state_t *dement; + mvdentity_state_t *dement; edict_t *clent; client_frame_t *frame; entity_state_t *state; @@ -1779,7 +1804,7 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore if (sv.demostatevalid) //generate info from demo stats { - for (e=1, dement=&sv.demostate[e] ; emodelindex) continue; @@ -1805,7 +1830,9 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore state->number = e; state->flags = EF_DIMLIGHT; VectorCopy (dement->origin, state->origin); - VectorCopy (dement->angles, state->angles); + state->angles[0] = dement->angles[0]*360.0f/256; + state->angles[1] = dement->angles[1]*360.0f/256; + state->angles[2] = dement->angles[2]*360.0f/256; state->modelindex = dement->modelindex; state->frame = dement->frame; state->colormap = dement->colormap; @@ -1817,8 +1844,6 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore #endif #ifdef PEXT_TRANS state->trans = dement->trans; - if (!state->trans) - state->trans = 1; #endif #ifdef PEXT_FATNESS state->fatness = dement->fatness; diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 1ee867c6a..e6d7b4524 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -649,7 +649,7 @@ void SVC_Status (void) int slots=0; - Cmd_TokenizeString ("status"); + Cmd_TokenizeString ("status", false, false); SV_BeginRedirect (RD_PACKET); Con_Printf ("%s\n", svs.info); for (i=0 ; idata, msg->cursize, 1, demo.dest); - if (demo.disk) - fflush (demo.file); - else if (demo.size - demo_size > demo_max_size) + if (demo.file) { - demo_size = demo.size; - demo.mfile -= 0x80000; - fwrite(svs.demomem, 1, 0x80000, demo.file); - fflush(demo.file); - memmove(svs.demomem, svs.demomem + 0x80000, demo.size - 0x80000); + if (demo.disk) + fflush (demo.file); + else if (demo.size - demo_size > demo_max_size) + { + demo_size = demo.size; + demo.mfile -= 0x80000; + fwrite(svs.demomem, 1, 0x80000, demo.file); + fflush(demo.file); + memmove(svs.demomem, svs.demomem + 0x80000, demo.size - 0x80000); + } } + else + demo_size = demo.size; } @@ -736,6 +743,17 @@ size_t memwrite ( const void *buffer, size_t size, size_t count, qbyte **mem) return c; } +size_t streamwrite ( const void *buffer, size_t size, size_t count, qbyte **mem) +{ + int sent; + sent = send(demo.tcpsocket, buffer, size*count, 0); + + if (sent <= 0) + return 0; + + return count; +} + static char chartbl[256]; void CleanName_Init (); @@ -745,17 +763,17 @@ void MVD_Init (void) #define MVDVARGROUP "Server MVD cvars" - Cvar_Register (&sv_demofps, MVDVARGROUP); + Cvar_Register (&sv_demofps, MVDVARGROUP); Cvar_Register (&sv_demoPings, MVDVARGROUP); Cvar_Register (&sv_demoNoVis, MVDVARGROUP); Cvar_Register (&sv_demoUseCache, MVDVARGROUP); Cvar_Register (&sv_demoCacheSize, MVDVARGROUP); Cvar_Register (&sv_demoMaxSize, MVDVARGROUP); Cvar_Register (&sv_demoMaxDirSize, MVDVARGROUP); - Cvar_Register (&sv_demoDir, MVDVARGROUP); + Cvar_Register (&sv_demoDir, MVDVARGROUP); Cvar_Register (&sv_demoPrefix, MVDVARGROUP); Cvar_Register (&sv_demoSuffix, MVDVARGROUP); - Cvar_Register (&sv_demotxt , MVDVARGROUP); + Cvar_Register (&sv_demotxt, MVDVARGROUP); Cvar_Register (&sv_demoExtraNames, MVDVARGROUP); @@ -765,7 +783,7 @@ void MVD_Init (void) if (p < com_argc-1) size = Q_atoi (com_argv[p+1]) * 1024; else - Sys_Error ("Memory_Init: you must specify a size in KB after -democache"); + Sys_Error ("MVD_Init: you must specify a size in KB after -democache"); } if (size < MIN_MVD_MEMORY) @@ -806,6 +824,25 @@ qboolean SV_InitRecord(void) return true; } +qboolean SV_InitStream(void) +{ + if (!USACACHE) + { + dwrite = (void *)&streamwrite; + demo.dest = &demo.tcpsocket; + demo.disk = false; + } else + { + dwrite = (void *)&memwrite; + demo.mfile = svs.demomem; + demo.dest = &demo.mfile; + } + + demo_size = 0; + + return true; +} + /* ==================== SV_Stop @@ -1031,6 +1068,8 @@ static qboolean SV_MVD_Record (char *name, int tcpsocket) char *gamedir; int seq = 1; +//okay, this is lame. We're going to allow a new mvd to 'reconnect' if we start recording the other... + memset(&demo, 0, sizeof(demo)); demo.recorder.frames = demo_frames; for (i = 0; i < UPDATE_BACKUP; i++) @@ -1093,9 +1132,11 @@ static qboolean SV_MVD_Record (char *name, int tcpsocket) } else { + SV_InitStream(); + if (demo.tcpsocket) return false; - SV_BroadcastPrintf (PRINT_CHAT, "Server starts recording to network client\n"); + SV_BroadcastPrintf (PRINT_CHAT, "Server starts recording to QWTV\n"); demo.tcpsocket = tcpsocket; } @@ -1308,15 +1349,15 @@ static qboolean SV_MVD_Record (char *name, int tcpsocket) MSG_WriteByte (&buf, svc_updatefrags); MSG_WriteByte (&buf, i); MSG_WriteShort (&buf, player->old_frags); - + MSG_WriteByte (&buf, svc_updateping); MSG_WriteByte (&buf, i); MSG_WriteShort (&buf, SV_CalcPing(player)); - + MSG_WriteByte (&buf, svc_updatepl); MSG_WriteByte (&buf, i); MSG_WriteByte (&buf, player->lossage); - + MSG_WriteByte (&buf, svc_updateentertime); MSG_WriteByte (&buf, i); MSG_WriteFloat (&buf, realtime - player->connection_started); @@ -1750,6 +1791,139 @@ void SV_MVDEasyRecord_f (void) SV_MVD_Record (name2, 0); } + +#ifdef _WIN32 + +#define EWOULDBLOCK WSAEWOULDBLOCK +#define EMSGSIZE WSAEMSGSIZE +#define ECONNRESET WSAECONNRESET +#define ECONNABORTED WSAECONNABORTED +#define ECONNREFUSED WSAECONNREFUSED +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL + +#define qerrno WSAGetLastError() +#else +#define qerrno errno + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef sun +#include +#endif + +#ifdef NeXT +#include +#endif + +#define closesocket close +#define ioctlsocket ioctl +#endif + + +int MVD_StreamStartListening(int port) +{ + char name[256]; + int sock; + struct hostent *hent; + + struct sockaddr_in address; +// int fromlen; + + unsigned int nonblocking = true; + + address.sin_family = AF_INET; + if (gethostname(name, sizeof(name)) == -1) + return INVALID_SOCKET; + hent = gethostbyname(name); + if (!hent) + return INVALID_SOCKET; + address.sin_addr.s_addr = *(int *)(hent->h_addr_list[0]); + address.sin_port = htons((u_short)port); + + + + if ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) + { + Sys_Error ("MVD_StreamStartListening: socket:", strerror(qerrno)); + } + + if (ioctlsocket (sock, FIONBIO, &nonblocking) == -1) + { + Sys_Error ("FTP_TCP_OpenSocket: ioctl FIONBIO:", strerror(qerrno)); + } + + if( bind (sock, (void *)&address, sizeof(address)) == -1) + { + closesocket(sock); + return INVALID_SOCKET; + } + + listen(sock, 2); + + return sock; +} + +void SV_MVDStream_Poll(void) +{ + static int listensocket=INVALID_SOCKET; + static int listenport; + int client; + netadr_t na; + struct sockaddr_qstorage addr; + int addrlen; + qboolean wanted; + if (!sv.state) + wanted = false; + else if (listenport && (int)mvd_streamport.value != listenport) //easy way to switch... disable for a frame. :) + { + listenport = mvd_streamport.value; + wanted = false; + } + else + { + listenport = mvd_streamport.value; + wanted = true; + } + + if (wanted && listensocket==INVALID_SOCKET) + listensocket = MVD_StreamStartListening(listenport); + else if (!wanted && listensocket!=INVALID_SOCKET) + { + closesocket(listensocket); + listensocket = INVALID_SOCKET; + return; + } + if (listensocket==INVALID_SOCKET) + return; + + addrlen = sizeof(addr); + client = accept(listensocket, (struct sockaddr *)&addr, &addrlen); + + if (client == INVALID_SOCKET) + return; + + if (sv.mvdrecording) + { //sorry + closesocket(client); + return; + } + + SockadrToNetadr(&addr, &na); + Con_Printf("MVD streaming client connected from %s\n", NET_AdrToString(na)); + + SV_MVD_Record (NULL, client); +} + void SV_MVDList_f (void) { dir_t dir; @@ -2122,6 +2296,8 @@ void SV_MVDInit(void) Cmd_AddCommand ("demolist", SV_MVDList_f); Cmd_AddCommand ("rmdemo", SV_MVDRemove_f); Cmd_AddCommand ("rmdemonum", SV_MVDRemoveNum_f); + + Cvar_Register(&mvd_streamport, "MVD Streaming"); } #endif diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 689236a77..a377e09ea 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -270,7 +270,7 @@ int SV_FlyMove (edict_t *ent, float time, trace_t *steptrace) trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent); - if (trace.allsolid) + if (trace.startsolid) { // entity is trapped in another solid VectorCopy (vec3_origin, ent->v.velocity); return 3; diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 966ad79a3..34923cb3e 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -109,6 +109,7 @@ void SV_New_f (void) { char *gamedir; int playernum; + int splitnum; client_t *split; if (host_client->state == cs_spawned) @@ -166,6 +167,7 @@ void SV_New_f (void) MSG_WriteByte (&host_client->netchan.message, 0); MSG_WriteString (&host_client->netchan.message, gamedir); + splitnum = 0; for (split = host_client; split; split = split->controlled) { #ifdef Q2SERVER @@ -175,7 +177,9 @@ void SV_New_f (void) #endif playernum = NUM_FOR_EDICT(svprogfuncs, split->edict)-1; if (sv.demostate) - playernum = (MAX_CLIENTS-1)|128; + { + playernum = (MAX_CLIENTS-1-splitnum)|128; + } else if (split->spectator) playernum |= 128; @@ -192,6 +196,7 @@ void SV_New_f (void) #ifdef SVRANKING split->stats_started = realtime; #endif + splitnum++; } if (host_client->fteprotocolextensions & PEXT_SPLITSCREEN) MSG_WriteByte (&host_client->netchan.message, 128); @@ -620,7 +625,7 @@ void SV_Soundlist_f (void) { int i; //char **s; - int n; + unsigned n; if (host_client->state != cs_connected) { @@ -646,9 +651,10 @@ void SV_Soundlist_f (void) SZ_Clear(&host_client->netchan.message); } - if (n < 0) + if (n >= MAX_SOUNDS) { - Con_Printf ("SV_Soundlist_f: %s tried to crash us\n", host_client->name); + SV_EndRedirect(); + Con_Printf ("SV_Soundlist_f: %s send an invalid index\n", host_client->name); SV_DropClient(host_client); return; } @@ -690,7 +696,7 @@ SV_Modellist_f void SV_Modellist_f (void) { int i; - int n; + unsigned n; if (host_client->state != cs_connected) { @@ -716,9 +722,10 @@ void SV_Modellist_f (void) SZ_Clear(&host_client->netchan.message); } - if (n < 0) + if (n >= MAX_MODELS) { - Con_Printf ("SV_Modellist_f: %s tried to crash us\n", host_client->name); + SV_EndRedirect(); + Con_Printf ("SV_Modellist_f: %s send an invalid index\n", host_client->name); SV_DropClient(host_client); return; } @@ -795,9 +802,12 @@ void SV_PreSpawn_f (void) buf = atoi(Cmd_Argv(2)); if (buf >= bufs+statics+sv.num_edicts+255) - buf = 0; - if (buf < 0) - buf = 0; + { + SV_EndRedirect(); + Con_Printf ("SV_Modellist_f: %s send an invalid index\n", host_client->name); + SV_DropClient(host_client); + return; + } if (!buf) { @@ -2748,7 +2758,7 @@ void SV_ExecuteUserCommand (char *s, qboolean fromQC) Con_DPrintf("Client command: %s\n", s); - Cmd_TokenizeString (s); + Cmd_TokenizeString (s, false, false); sv_player = host_client->edict; Cmd_ExecLevel=1; @@ -2766,7 +2776,7 @@ void SV_ExecuteUserCommand (char *s, qboolean fromQC) } } sv_player = host_client->edict; - Cmd_ShiftArgs(1); + Cmd_ShiftArgs(1, false); } #ifdef Q2SERVER @@ -3103,7 +3113,7 @@ void SVNQ_PreSpawn_f (void) } void SVNQ_NQInfo_f (void) { - Cmd_TokenizeString(va("setinfo \"%s\" \"%s\"\n", Cmd_Argv(0), Cmd_Argv(1))); + Cmd_TokenizeString(va("setinfo \"%s\" \"%s\"\n", Cmd_Argv(0), Cmd_Argv(1)), false, false); SV_SetInfo_f(); } @@ -3209,7 +3219,7 @@ void SVNQ_ExecuteUserCommand (char *s) client_t *oldhost = host_client; ucmd_t *u; - Cmd_TokenizeString (s); + Cmd_TokenizeString (s, false, false); sv_player = host_client->edict; Cmd_ExecLevel=1; diff --git a/engine/server/svmodel.c b/engine/server/svmodel.c index 396cc4f22..002fc0545 100644 --- a/engine/server/svmodel.c +++ b/engine/server/svmodel.c @@ -1197,8 +1197,8 @@ void Mod_LoadPlanes (lump_t *l) } void Q1BSP_FatPVS (vec3_t org, qboolean add); -qboolean Q1BSP_EdictInFatPVS(edict_t *ent); -void Q1BSP_FindTouchedLeafs(edict_t *ent); +qboolean Q1BSP_EdictInFatPVS(struct edict_s *ent); +void Q1BSP_FindTouchedLeafs(struct edict_s *ent); /* ================= diff --git a/engine/server/world.c b/engine/server/world.c index 7c8395bb1..38387d88f 100644 --- a/engine/server/world.c +++ b/engine/server/world.c @@ -1847,6 +1847,9 @@ trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, e #endif SV_ClipToLinks ( sv_areanodes, &clip ); + if (clip.trace.startsolid) + clip.trace.fraction = 0; + return clip.trace; } #ifdef Q2SERVER