Esempio n. 1
0
/*
 * @brief The origin is typically calculated using client sided prediction, provided
 * the client is not viewing a demo, playing in 3rd person mode, or chasing
 * another player.
 */
static void Cl_UpdateOrigin(const player_state_t *from, const player_state_t *to) {

	if (Cl_UsePrediction()) {

		// use client sided prediction
		for (int32_t i = 0; i < 3; i++) {
			r_view.origin[i] = cl.predicted_state.origin[i] + cl.predicted_state.view_offset[i];
			r_view.origin[i] -= (1.0 - cl.lerp) * cl.predicted_state.error[i];
		}

		const uint32_t delta = cl.time - cl.predicted_state.step_time;
		const uint32_t interval = cl.predicted_state.step_interval;

		if (delta < interval) { // interpolate stair traversal
			const vec_t lerp = (interval - delta) / (vec_t) interval;
			r_view.origin[2] -= cl.predicted_state.step * lerp;
		}

	} else { // just use interpolated values from frame
		vec3_t origin;
		vec3_t from_offset, to_offset, offset;

		VectorLerp(from->pm_state.origin, to->pm_state.origin, cl.lerp, origin);

		UnpackVector(from->pm_state.view_offset, from_offset);
		UnpackVector(to->pm_state.view_offset, to_offset);

		VectorLerp(from_offset, to_offset, cl.lerp, offset);

		VectorAdd(origin, offset, r_view.origin);
	}

	// update the contents mask for e.g. under-water effects
	r_view.contents = Cl_PointContents(r_view.origin);
}
Esempio n. 2
0
/*
 * @brief Checks for client side prediction errors. Problems here can indicate
 * that Pm_Move or the protocol are not functioning correctly.
 */
void Cl_CheckPredictionError(void) {
	vec3_t delta;

	VectorClear(cl.predicted_state.error);

	if (!Cl_UsePrediction())
		return;

	// calculate the last user_cmd_t we sent that the server has processed
	const uint32_t frame = (cls.net_chan.incoming_acknowledged & CMD_MASK);

	// compare what the server returned with what we had predicted it to be
	VectorSubtract(cl.frame.ps.pm_state.origin, cl.predicted_state.origins[frame], delta);

	const vec_t error = VectorLength(delta);
	if (error > 1.0) {
		Com_Debug("%s\n", vtos(delta));

		if (error > 256.0) { // do not interpolate
			VectorClear(delta);
		}
	}

	VectorCopy(delta, cl.predicted_state.error);
}
Esempio n. 3
0
/*
 * @brief The angles are typically fetched from input, after factoring in client-side
 * prediction, unless the client is watching a demo or chase camera.
 */
static void Cl_UpdateAngles(const player_state_t *from, const player_state_t *to) {
	vec3_t old_angles, new_angles, angles;

	// start with the predicted angles, or interpolate the server states
	if (Cl_UsePrediction()) {
		VectorCopy(cl.predicted_state.view_angles, r_view.angles);
	} else {
		UnpackAngles(from->pm_state.view_angles, old_angles);
		UnpackAngles(to->pm_state.view_angles, new_angles);

		AngleLerp(old_angles, new_angles, cl.lerp, r_view.angles);
	}

	// add in the kick angles
	UnpackAngles(from->pm_state.kick_angles, old_angles);
	UnpackAngles(to->pm_state.kick_angles, new_angles);

	AngleLerp(old_angles, new_angles, cl.lerp, angles);
	VectorAdd(r_view.angles, angles, r_view.angles);

	// and lastly the delta angles
	UnpackAngles(from->pm_state.delta_angles, old_angles);
	UnpackAngles(to->pm_state.delta_angles, new_angles);

	VectorCopy(new_angles, angles);

	// check for small delta angles, and interpolate them
	if (!VectorCompare(old_angles, new_angles)) {
		int32_t i;

		for (i = 0; i < 3; i++) {
			const vec_t delta = fabs(new_angles[i] - old_angles[i]);
			if (delta > 5.0 && delta < 355.0) {
				break;
			}
		}

		if (i == 3) {
			AngleLerp(old_angles, new_angles, cl.lerp, angles);
		}
	}

	VectorAdd(r_view.angles, angles, r_view.angles);

	if (cl.frame.ps.pm_state.type == PM_DEAD) { // look only on x axis
		r_view.angles[0] = 0.0;
		r_view.angles[2] = 45.0;
	}

	// and finally set the view directional vectors
	AngleVectors(r_view.angles, r_view.forward, r_view.right, r_view.up);
}
Esempio n. 4
0
/*
 * @brief The angles are typically fetched from input, after factoring in client-side
 * prediction, unless the client is watching a demo or chase camera.
 */
static void Cl_UpdateAngles(const player_state_t *ps, const player_state_t *ops) {
	vec3_t old_angles, new_angles, angles;

	// start with the predicted angles, or interpolate the server states
	if (Cl_UsePrediction()) {
		VectorCopy(cl.predicted_state.view_angles, r_view.angles);
	} else {
		UnpackAngles(ops->pm_state.view_angles, old_angles);
		UnpackAngles(ps->pm_state.view_angles, new_angles);

		AngleLerp(old_angles, new_angles, cl.lerp, r_view.angles);
	}

	// add in the kick angles
	UnpackAngles(ops->pm_state.kick_angles, old_angles);
	UnpackAngles(ps->pm_state.kick_angles, new_angles);

	AngleLerp(old_angles, new_angles, cl.lerp, angles);
	VectorAdd(r_view.angles, angles, r_view.angles);

	// and lastly the delta angles
	UnpackAngles(ops->pm_state.delta_angles, old_angles);
	UnpackAngles(ps->pm_state.delta_angles, new_angles);

	VectorCopy(new_angles, angles);

	// check for small delta angles, and interpolate them
	if (!VectorCompare(new_angles, new_angles)) {

		VectorSubtract(old_angles, new_angles, angles);
		vec_t f = VectorLength(angles);

		if (f < 15.0) {
			AngleLerp(old_angles, new_angles, cl.lerp, angles);
		}
	}

	VectorAdd(r_view.angles, angles, r_view.angles);

	ClampAngles(r_view.angles);

	if (cl.frame.ps.pm_state.type == PM_DEAD) { // look only on x axis
		r_view.angles[0] = 0.0;
		r_view.angles[2] = 45.0;
	}

	// and finally set the view directional vectors
	AngleVectors(r_view.angles, r_view.forward, r_view.right, r_view.up);
}
Esempio n. 5
0
/*
 * @brief Entry point for client-side prediction. For each server frame, run
 * the player movement code with the user commands we've sent to the server
 * but have not yet received acknowledgment for. Store the resulting move so
 * that it may be interpolated into by Cl_UpdateView.
 *
 * Most of the work is passed off to the client game, which is responsible for
 * the implementation Pm_Move.
 */
void Cl_PredictMovement(void) {

	if (Cl_UsePrediction()) {

		const uint32_t current = cls.net_chan.outgoing_sequence;
		uint32_t ack = cls.net_chan.incoming_acknowledged;

		// if we are too far out of date, just freeze in place
		if (current - ack >= CMD_BACKUP) {
			Com_Debug("Exceeded CMD_BACKUP\n");
			return;
		}

		GList *cmds = NULL;

		while (++ack <= current) {
			cmds = g_list_append(cmds, &cl.cmds[ack & CMD_MASK]);
		}

		cls.cgame->PredictMovement(cmds);

		g_list_free(cmds);
	}
}