Esempio n. 1
0
static void CL_DemoSeekMs(double ms, int exactServerTime)  // server time in milliseconds
{
	double wantedTime;

	if (!clc.demoplaying)
	{
		Com_FuncDPrinf("not playing demo can't seek\n");
		return;
	}

	wantedTime = ms;

	if (exactServerTime > 0)
	{
		wantedTime = exactServerTime;
	}

	DEMODEBUG("seek want %f\n", wantedTime);

	if (wantedTime > (double)cl.serverTime + di.Overf)
	{
		CL_DemoFastForward(wantedTime);
	}
	else
	{
		CL_RewindDemo(wantedTime);
	}
}
Esempio n. 2
0
static void CL_RewindDemo(double wantedTime)
{
	int             i;
	rewindBackups_t *rb;

	if (!IS_LEGACY_MOD)
	{
		Com_FuncPrinf("Rewind is only supported on legacy mod, sorry\n");
		return;
	}

	if (wantedTime < (double)di.firstServerTime)
	{
		wantedTime = di.firstServerTime;
	}

	if (!rewindBackups[0].valid || di.snapCount == 0)
	{
		CL_DemoFastForward(wantedTime);
		return;
	}

	rb = NULL;
	for (i = di.snapCount - 1; i >= 0; i--)
	{
		rb = &rewindBackups[i];
		// go back a second before wanted time in order to have snapshot backups available for screen matching
		if ((double)rb->cl.snap.serverTime < wantedTime - 1000.0)
		{
			break;
		}
	}
	if (rb == NULL || i < 0)
	{
		if (rb == NULL)
		{
			Com_FuncDPrinf("FIXME rewind couldn't find valid snap  rb:%p  i:%d  rb serverTime %d   wanted %f\n", rb, i, rewindBackups[0].cl.snap.serverTime, wantedTime);
		}
		rb = &rewindBackups[0];
		i  = 0;
	}

	DEMODEBUG("seeking to index %d %d   cl.serverTime:%d  cl.snap.serverTime:%d, new clc.lastExecutedServercommand %d  clc.serverCommandSequence %d\n", i, rb->seekPoint, cl.serverTime, cl.snap.serverTime, rb->clc.lastExecutedServerCommand, rb->clc.serverCommandSequence);
	FS_Seek(clc.demofile, rb->seekPoint, FS_SEEK_SET);

	// TODO: take a look at these hacks
	di.numSnaps  = rb->numSnaps;
	di.snapCount = i + 1;

	memcpy(&cl, &rb->cl, sizeof(clientActive_t));
	memcpy(&clc, &rb->clc, sizeof(clientConnection_t));
	memcpy(&cls, &rb->cls, sizeof(clientStatic_t));
	di.Overf = 0;

	// TODO: this is a hack to set the state to something valid
	cls.state = CA_ACTIVE;

	CL_DemoFastForward(wantedTime);
}
Esempio n. 3
0
void CL_SeekNext_f(void)
{
	snapshot_t snapshot;
	qboolean   r;
	int        i;

	if (cl.snap.serverTime == di.lastServerTime)
	{
		Com_FuncDPrinf("at last snap\n");
		return;
	}

	// takes into account offline demos with snaps with same server time
	if (cl.serverTime < cl.snap.serverTime)
	{
		CL_DemoSeekMs(0, cl.snap.serverTime);
		return;
	}

	i = 1;
	while (1)
	{
		r = CL_PeekSnapshot(cl.snap.messageNum + i, &snapshot);
		if (!r)
		{
			Com_FuncDPrinf("couldn't get next snapshot\n");
			return;
		}

		if (snapshot.serverTime > cl.serverTime)
		{
			break;
		}
		i++;
	}

	CL_DemoSeekMs(0, snapshot.serverTime);
}
Esempio n. 4
0
/*
==================
CL_NextDemo

Called when a demo or cinematic finishes
If the "nextdemo" cvar is set, that command will be issued
==================
*/
void CL_NextDemo(void)
{
	char v[MAX_STRING_CHARS];

	Q_strncpyz(v, Cvar_VariableString("nextdemo"), sizeof(v));
	v[MAX_STRING_CHARS - 1] = 0;
	Com_FuncDPrinf("CL_NextDemo: %s\n", v);
	if (!v[0])
	{
		return;
	}

	Cvar_Set("nextdemo", "");
	Cbuf_AddText(v);
	Cbuf_AddText("\n");
	Cbuf_Execute();
}
Esempio n. 5
0
void CL_SeekPrev_f(void)
{
	clSnapshot_t *clSnap;
	int          i = 0;

	if (cl.snap.serverTime == di.firstServerTime)
	{
		Com_FuncDPrinf("at first snap\n");
		return;
	}

	// takes into account offline demos with snapshots having same server time
	while (qtrue)
	{
		clSnap = &cl.snapshots[(cl.snap.messageNum - i) & PACKET_MASK];
		if (clSnap->serverTime < cl.serverTime)
		{
			break;
		}
		i++;
	}

	CL_DemoSeekMs(0, clSnap->serverTime);
}
Esempio n. 6
0
static void CL_DemoFastForward(double wantedTime)
{
	int loopCount;

	if (cls.state < CA_CONNECTED)
	{
		return;
	}

	if (wantedTime >= di.lastServerTime)
	{
		return;
	}

	DEMODEBUG("fast_forward %f\n", wantedTime);

	if (wantedTime >= (double)cl.serverTime  &&  wantedTime < (double)cl.snap.serverTime)
	{
		cl.serverTime      = floor(wantedTime);
		cls.realtime       = floor(wantedTime);
		di.Overf           = wantedTime - floor(wantedTime);
		cl.serverTimeDelta = 0;
		return;
	}

	if (wantedTime < (double)cl.snap.serverTime)
	{
		Com_FuncPrinf("This should not happen %f < %d\n", wantedTime, cl.snap.serverTime);
		return;
	}

	if (wantedTime > (double)di.lastServerTime)
	{
		Com_FuncPrinf("would seek past end of demo (%f > %d)\n", wantedTime, di.lastServerTime);
		return;
	}

	DEMODEBUG("fastfowarding from %f to %f\n", (double)cl.serverTime + di.Overf, wantedTime);

	loopCount = 0;
	while ((double)cl.snap.serverTime <= wantedTime)
	{
		DEMODEBUG("Servertime: %d wanted time %lf\n", cl.snap.serverTime, wantedTime);
		CL_ReadDemoMessage();
		while (clc.lastExecutedServerCommand < clc.serverCommandSequence)
		{
			if (clc.lastExecutedServerCommand + 1 <= clc.serverCommandSequence - MAX_RELIABLE_COMMANDS)
			{
				if (cl.snap.serverTime <= di.firstServerTime)
				{
					clc.lastExecutedServerCommand = clc.serverCommandSequence - 1;
					Com_FuncPrinf("setting clc.lastExecutedServerCommand %d (%d)\n", clc.lastExecutedServerCommand, loopCount);
				}
				else
				{
					Com_FuncDPrinf("FIXME %i  (%i) + 1 <= (%i) - MAX_RELIABLE_COMMANDS (%i)\n", clc.lastExecutedServerCommand, clc.serverCommandSequence, cl.snap.serverTime, di.firstServerTime);
					break;
				}
			}
			CL_GetServerCommand(clc.lastExecutedServerCommand + 1);
		}
		loopCount++;
	}

	DEMODEBUG("read %d demo messages, cl.snap.serverTime %d, wantedTime %f\n", loopCount, cl.snap.serverTime, wantedTime);

	cl.serverTime      = floor(wantedTime);
	cls.realtime       = floor(wantedTime);
	di.Overf           = wantedTime - floor(wantedTime);
	cl.serverTimeDelta = 0;

	// TODO: fix this
	//VM_Call(cgvm, CG_TIME_CHANGE, cl.serverTime, (int)(Overf * SUBTIME_RESOLUTION));

	di.seeking                        = qtrue;
	di.firstNonDeltaMessageNumWritten = -1;
}
Esempio n. 7
0
// Do very shallow parse of the demo (could be extended) just to get times and snapshot count
static void CL_ParseDemo(void)
{
	int tstart   = 0;
	int demofile = 0;

	// Reset our demo data
	memset(&di, 0, sizeof(di));

	// Parse start
	di.gameStartTime = -1;
	di.gameEndTime   = -1;
	FS_Seek(clc.demofile, 0, FS_SEEK_SET);
	tstart = Sys_Milliseconds();

	while (qtrue)
	{
		int   r;
		msg_t buf;
		msg_t *msg;
		byte  bufData[MAX_MSGLEN];
		int   s;
		int   cmd;

		di.demoPos = FS_FTell(clc.demofile);

		// get the sequence number
		r = FS_Read(&s, 4, clc.demofile);
		if (r != 4)
		{
			CL_DemoCompleted();
			return;
		}

		clc.serverMessageSequence = LittleLong(s);

		// init the message
		MSG_Init(&buf, bufData, sizeof(bufData));

		// get the length
		r = FS_Read(&buf.cursize, 4, clc.demofile);

		if (r != 4)
		{
			break;
		}

		buf.cursize = LittleLong(buf.cursize);
		if (buf.cursize == -1)
		{
			break;
		}

		if (buf.cursize > buf.maxsize)
		{
			Com_FuncDPrinf("demoMsglen > MAX_MSGLEN");
			break;
		}

		r = FS_Read(buf.data, buf.cursize, clc.demofile);
		if (r != buf.cursize)
		{
			Com_FuncDPrinf("Demo file was truncated.\n");
			break;
		}

		clc.lastPacketTime = cls.realtime;
		buf.readcount      = 0;

		// parse
		msg = &buf;
		MSG_Bitstream(msg);

		// get the reliable sequence acknowledge number
		clc.reliableAcknowledge = MSG_ReadLong(msg);

		if (clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS)
		{
			clc.reliableAcknowledge = clc.reliableSequence;
		}

		// parse the message
		while (qtrue)
		{
			if (msg->readcount > msg->cursize)
			{
				Com_FuncDrop("read past end of server message");
				break;
			}

			cmd = MSG_ReadByte(msg);

			if (cmd == svc_EOF)
			{
				break;
			}

			// other commands
			switch (cmd)
			{
			default:
				Com_FuncDrop("Illegible server message %d", cmd);
				break;
			case svc_nop:
				break;
			case svc_serverCommand:
				MSG_ReadLong(msg);
				MSG_ReadString(msg);
				break;
			case svc_gamestate:
				clc.serverCommandSequence = MSG_ReadLong(msg);
				cl.gameState.dataCount    = 1;
				while (qtrue)
				{
					int cmd2 = MSG_ReadByte(msg);
					if (cmd2 == svc_EOF)
					{
						break;
					}
					if (cmd2 == svc_configstring)
					{
						MSG_ReadShort(msg);
						MSG_ReadBigString(msg);
					}
					else if (cmd2 == svc_baseline)
					{
						entityState_t s1, s2;
						memset(&s1, 0, sizeof(s1));
						memset(&s2, 0, sizeof(s2));
						MSG_ReadBits(msg, GENTITYNUM_BITS);
						MSG_ReadDeltaEntity(msg, &s1, &s2, 0);
					}
					else
					{
						Com_FuncDrop("bad command byte");
					}
				}
				MSG_ReadLong(msg);
				MSG_ReadLong(msg);
				break;
			case svc_snapshot:
				CL_ParseDemoSnapShotSimple(msg);
				break;
			case svc_download:
				MSG_ReadShort(msg);

				break;
			}
		}

		if (!cl.snap.valid)
		{
			Com_FuncDPrinf("!cl.snap.valid\n");
			continue;
		}

		if (cl.snap.serverTime < cl.oldFrameServerTime)
		{
			// ignore snapshots that don't have entities
			if (cl.snap.snapFlags & SNAPFLAG_NOT_ACTIVE)
			{
				continue;
			}
			cls.state = CA_ACTIVE;

			// set the timedelta so we are exactly on this first frame
			cl.serverTimeDelta = cl.snap.serverTime - cls.realtime;
			cl.oldServerTime   = cl.snap.serverTime;

			clc.timeDemoBaseTime = cl.snap.serverTime;
		}
		cl.oldFrameServerTime = cl.snap.serverTime;

		if (cl.newSnapshots)
		{
			CL_AdjustTimeDelta();
		}

		di.lastServerTime = cl.snap.serverTime;
		if (di.firstServerTime == 0)
		{
			di.firstServerTime = cl.snap.serverTime;
			Com_FuncPrinf("firstServerTime %d\n", di.firstServerTime);
		}

		di.snapsInDemo++;
	}

	Com_FuncDPrinf("Snaps in demo: %i\n", di.snapsInDemo);
	Com_FuncDPrinf("last serverTime %d   total %f minutes\n", cl.snap.serverTime, (cl.snap.serverTime - di.firstServerTime) / 1000.0 / 60.0);
	Com_FuncDPrinf("parse time %f seconds\n", (float)(Sys_Milliseconds() - tstart) / 1000.0);
	FS_Seek(clc.demofile, 0, FS_SEEK_SET);
	clc.demoplaying = qfalse;
	demofile        = clc.demofile;
	CL_ClearState();
	Com_Memset(&clc, 0, sizeof(clc));
	clc.demofile             = demofile;
	cls.state                = CA_DISCONNECTED;
	cl_connectedToPureServer = qfalse;

	dpi.firstTime = di.firstServerTime;
	dpi.lastTime  = di.lastServerTime;
}
Esempio n. 8
0
static void CL_ParseDemoSnapShotSimple(msg_t *msg)
{
	int          len;
	clSnapshot_t *old;
	clSnapshot_t newSnap;
	int          deltaNum;
	int          oldMessageNum;
	int          i, packetNum;

	memset(&newSnap, 0, sizeof(newSnap));
	newSnap.serverCommandNum = clc.serverCommandSequence;
	newSnap.serverTime       = MSG_ReadLong(msg);
	newSnap.messageNum       = clc.serverMessageSequence;
	deltaNum                 = MSG_ReadByte(msg);
	if (!deltaNum)
	{
		newSnap.deltaNum = -1;
	}
	else
	{
		newSnap.deltaNum = newSnap.messageNum - deltaNum;
	}
	newSnap.snapFlags = MSG_ReadByte(msg);

	if (newSnap.deltaNum <= 0)
	{
		newSnap.valid = qtrue;      // uncompressed frame
		old           = NULL;
	}
	else
	{
		old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK];
		if (!old->valid)
		{
			// should never happen
			Com_FuncPrinf("Delta from invalid frame (not supposed to happen!).\n");
		}
		else if (old->messageNum != newSnap.deltaNum)
		{
			// The frame that the server did the delta from
			// is too old, so we can't reconstruct it properly.
			Com_FuncDPrinf("Delta frame too old.\n");
		}
		else if (cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES - 128)
		{
			Com_FuncDPrinf("Delta parseEntitiesNum too old.\n");
		}
		else
		{
			newSnap.valid = qtrue;  // valid delta parse
		}
	}

	// read areamask
	len = MSG_ReadByte(msg);

	if (len > sizeof(newSnap.areamask))
	{
		Com_FuncDrop("Invalid size %d for areamask.", len);
		return;
	}

	MSG_ReadData(msg, &newSnap.areamask, len);

	if (old)
	{
		MSG_ReadDeltaPlayerstate(msg, &old->ps, &newSnap.ps);
	}
	else
	{
		MSG_ReadDeltaPlayerstate(msg, NULL, &newSnap.ps);
	}

	// read packet entities
	CL_ParsePacketEntities(msg, old, &newSnap);

	// if not valid, dump the entire thing now that it has
	// been properly read
	if (!newSnap.valid)
	{
		return;
	}

	// clear the valid flags of any snapshots between the last
	// received and this one, so if there was a dropped packet
	// it won't look like something valid to delta from next
	// time we wrap around in the buffer
	oldMessageNum = cl.snap.messageNum + 1;

	if (newSnap.messageNum - oldMessageNum >= PACKET_BACKUP)
	{
		oldMessageNum = newSnap.messageNum - (PACKET_BACKUP - 1);
	}
	for (; oldMessageNum < newSnap.messageNum; oldMessageNum++)
	{
		cl.snapshots[oldMessageNum & PACKET_MASK].valid = qfalse;
	}

	// copy to the current good spot
	cl.snap      = newSnap;
	cl.snap.ping = 999;
	// calculate ping time
	for (i = 0; i < PACKET_BACKUP; i++)
	{
		packetNum = (clc.netchan.outgoingSequence - 1 - i) & PACKET_MASK;
		if (cl.snap.ps.commandTime >= cl.outPackets[packetNum].p_serverTime)
		{
			cl.snap.ping = cls.realtime - cl.outPackets[packetNum].p_realtime;
			break;
		}
	}
	// save the frame off in the backup array for later delta comparisons
	cl.snapshots[cl.snap.messageNum & PACKET_MASK] = cl.snap;
	cl.newSnapshots                                = qtrue;
}
Esempio n. 9
0
qboolean CL_PeekSnapshot(int snapshotNumber, snapshot_t *snapshot)
{
	clSnapshot_t *clSnap;
	clSnapshot_t csn;
	int          i, count;
	int          origPosition;
	int          cmd;
	//char         *s;
	char     buffer[16];
	qboolean success = qfalse;
	int      r;
	msg_t    buf;
	byte     bufData[MAX_MSGLEN];
	int      j;
	int      lastPacketTimeOrig;
	int      parseEntitiesNumOrig;
	int      currentSnapNum;
	//int      serverMessageSequence;

	clSnap = &csn;

	if (!clc.demoplaying)
	{
		return qfalse;
	}

	if (snapshotNumber <= cl.snap.messageNum)
	{
		success = CL_GetSnapshot(snapshotNumber, snapshot);
		if (!success)
		{
			Com_FuncDPrinf("snapshot number outside of backup buffer\n");
			return qfalse;
		}
		return qtrue;
	}

	if (snapshotNumber > cl.snap.messageNum + 1)
	{
		Com_FuncDPrinf("FIXME CL_PeekSnapshot  %d >  cl.snap.messageNum + 1 (%d)\n", snapshotNumber, cl.snap.messageNum);
		//return qfalse;
	}

	parseEntitiesNumOrig = cl.parseEntitiesNum;
	lastPacketTimeOrig   = clc.lastPacketTime;
	// CL_ReadDemoMessage()
	origPosition = FS_FTell(clc.demofile);

	currentSnapNum = cl.snap.messageNum;

	for (j = 0; j < snapshotNumber - currentSnapNum; j++)
	{
		// get the sequence number
		memset(buffer, 0, sizeof(buffer));
		r = FS_Read(&buffer, 4, clc.demofile);
		if (r != 4)
		{
			Com_FuncDPrinf("couldn't read sequence number\n");
			FS_Seek(clc.demofile, origPosition, FS_SEEK_SET);
			clc.lastPacketTime  = lastPacketTimeOrig;
			cl.parseEntitiesNum = parseEntitiesNumOrig;
			return qfalse;
		}
		//serverMessageSequence = LittleLong(*((int *)buffer));

		// init the message
		memset(&buf, 0, sizeof(msg_t));
		MSG_Init(&buf, bufData, sizeof(bufData));

		// get the length
		r = FS_Read(&buf.cursize, 4, clc.demofile);
		if (r != 4)
		{
			Com_FuncDPrinf("couldn't get length\n");
			FS_Seek(clc.demofile, origPosition, FS_SEEK_SET);
			clc.lastPacketTime  = lastPacketTimeOrig;
			cl.parseEntitiesNum = parseEntitiesNumOrig;
			return qfalse;
		}

		buf.cursize = LittleLong(buf.cursize);
		if (buf.cursize == -1)
		{
			Com_FuncDPrinf("buf.cursize == -1\n");
			FS_Seek(clc.demofile, origPosition, FS_SEEK_SET);
			clc.lastPacketTime  = lastPacketTimeOrig;
			cl.parseEntitiesNum = parseEntitiesNumOrig;
			return qfalse;
		}

		if (buf.cursize > buf.maxsize)
		{
			Com_FuncDrop("demoMsglen > MAX_MSGLEN");
		}

		r = FS_Read(buf.data, buf.cursize, clc.demofile);
		if (r != buf.cursize)
		{
			Com_FuncDPrinf("Demo file was truncated.\n");
			FS_Seek(clc.demofile, origPosition, FS_SEEK_SET);
			clc.lastPacketTime  = lastPacketTimeOrig;
			cl.parseEntitiesNum = parseEntitiesNumOrig;
			return qfalse;
		}

		clc.lastPacketTime = cls.realtime;
		buf.readcount      = 0;

		MSG_Bitstream(&buf);
		// get the reliable sequence acknowledge number
		MSG_ReadLong(&buf);

		// parse the message
		while (qtrue)
		{
			if (buf.readcount > buf.cursize)
			{
				Com_FuncDrop("read past end of server message");
				break;
			}

			cmd = MSG_ReadByte(&buf);

			if (cmd == svc_EOF)
			{
				break;
			}
			success = qfalse;

			switch (cmd)
			{
			default:
				Com_FuncDrop("Illegible server message");
				break;
			case svc_nop:
				break;
			case svc_serverCommand:
				MSG_ReadLong(&buf);  // seq
				//s = MSG_ReadString(&buf);
				MSG_ReadString(&buf);
				break;
			case svc_gamestate:
				Com_FuncDPrinf("FIXME gamestate\n");
				goto alldone;
				break;
			case svc_snapshot:
				// TODO: changed this check if it works
				CL_ParseSnapshot(&buf);
				if (cl.snap.valid)
				{
					success = qtrue;
				}
				break;
			case svc_download:
				Com_FuncDPrinf("FIXME download\n");
				goto alldone;
				break;
			}
		}

alldone:

		if (!success)
		{
			Com_FuncDPrinf("failed\n");
			FS_Seek(clc.demofile, origPosition, FS_SEEK_SET);
			clc.lastPacketTime  = lastPacketTimeOrig;
			cl.parseEntitiesNum = parseEntitiesNumOrig;
			return success;
		}

		// FIXME other ents not supported yet

		// if the entities in the frame have fallen out of their
		// circular buffer, we can't return it
		if (cl.parseEntitiesNum - clSnap->parseEntitiesNum >= MAX_PARSE_ENTITIES)
		{
			Com_FuncDPrinf("cl.parseEntitiesNum - clSnap->parseEntitiesNum >= MAX_PARSE_ENTITIES");
			FS_Seek(clc.demofile, origPosition, FS_SEEK_SET);
			clc.lastPacketTime  = lastPacketTimeOrig;
			cl.parseEntitiesNum = parseEntitiesNumOrig;
			return qtrue;  // FIXME if you fix other ents
		}

		// write the snapshot
		snapshot->snapFlags             = clSnap->snapFlags;
		snapshot->serverCommandSequence = clSnap->serverCommandNum;
		snapshot->ping                  = clSnap->ping;
		snapshot->serverTime            = clSnap->serverTime;
		Com_Memcpy(snapshot->areamask, clSnap->areamask, sizeof(snapshot->areamask));
		snapshot->ps = clSnap->ps;
		count        = clSnap->numEntities;

		if (count > MAX_ENTITIES_IN_SNAPSHOT)
		{
			Com_FuncDPrinf("truncated %i entities to %i\n", count, MAX_ENTITIES_IN_SNAPSHOT);
			count = MAX_ENTITIES_IN_SNAPSHOT;
		}

		snapshot->numEntities = count;
		for (i = 0; i < count; i++)
		{
			snapshot->entities[i] = cl.parseEntities[(clSnap->parseEntitiesNum + i) & (MAX_PARSE_ENTITIES - 1)];
		}

	}

	FS_Seek(clc.demofile, origPosition, FS_SEEK_SET);
	clc.lastPacketTime  = lastPacketTimeOrig;
	cl.parseEntitiesNum = parseEntitiesNumOrig;
	// TODO: configstring changes and server commands!!!

	return qtrue;
}