Exemple #1
0
/**
 * @brief Decides which entities are going to be visible to the client, and
 * copies off the player state and area_bits.
 */
void Sv_BuildClientFrame(sv_client_t *client) {
	vec3_t org, off;

	g_entity_t *cent = client->entity;
	if (!cent->client) {
		return;    // not in game yet
	}

	// this is the frame we are creating
	sv_frame_t *frame = &client->frames[sv.frame_num & PACKET_MASK];
	frame->sent_time = quetoo.ticks; // timestamp for ping calculation

	// grab the current player_state_t
	frame->ps = cent->client->ps;

	// find the client's PVS
	const pm_state_t *pm = &cent->client->ps.pm_state;
	UnpackVector(pm->view_offset, off);
	VectorAdd(pm->origin, off, org);

	const int32_t leaf = Cm_PointLeafnum(org, 0);
	const int32_t area = Cm_LeafArea(leaf);

	// calculate the visible areas
	frame->area_bytes = Cm_WriteAreaBits(area, frame->area_bits);

	// resolve the visibility data
	byte pvs[MAX_BSP_LEAFS >> 3], phs[MAX_BSP_LEAFS >> 3];
	Sv_ClientVisibility(org, pvs, phs);

	// build up the list of relevant entities
	frame->num_entities = 0;
	frame->entity_state = svs.next_entity_state;

	for (uint16_t e = 1; e < svs.game->num_entities; e++) {
		g_entity_t *ent = ENTITY_FOR_NUM(e);

		// ignore entities that are local to the server
		if (ent->sv_flags & SVF_NO_CLIENT) {
			continue;
		}

		// ignore entities without visible presence unless they have an effect
		if (!ent->s.event && !ent->s.effects && !ent->s.trail && !ent->s.model1 && !ent->s.sound) {
			continue;
		}

		// ignore entities not in PVS / PHS
		if (ent != cent) {
			const sv_entity_t *sent = &sv.entities[e];

			// by first checking area
			if (!Cm_AreasConnected(area, sent->areas[0])) {
				if (!sent->areas[1] || !Cm_AreasConnected(area, sent->areas[1])) {
					continue;
				}
			}

			const byte *vis = ent->s.sound || ent->s.event ? phs : pvs;

			if (sent->num_clusters == -1) { // use top_node
				if (!Cm_HeadnodeVisible(sent->top_node, vis)) {
					continue;
				}
			} else { // or check individual leafs
				int32_t i;
				for (i = 0; i < sent->num_clusters; i++) {
					const int32_t c = sent->clusters[i];
					if (vis[c >> 3] & (1 << (c & 7))) {
						break;
					}
				}
				if (i == sent->num_clusters) {
					continue;    // not visible
				}
			}
		}

		// copy it to the circular entity_state_t array
		entity_state_t *s = &svs.entity_states[svs.next_entity_state % svs.num_entity_states];
		if (ent->s.number != e) {
			Com_Warn("Fixing entity number: %d -> %d\n", ent->s.number, e);
			ent->s.number = e;
		}
		*s = ent->s;

		// don't mark our own missiles as solid for prediction
		if (ent->owner == client->entity) {
			s->solid = SOLID_NOT;
		}

		svs.next_entity_state++;
		frame->num_entities++;
	}
}
Exemple #2
0
/*
 * Sv_LinkEdict
 *
 * Called whenever an entity changes origin, mins, maxs, or solid to add it to
 * the clipping hull.
 */
void Sv_LinkEdict(g_edict_t *ent) {
	sv_area_node_t *node;
	int leafs[MAX_TOTAL_ENT_LEAFS];
	int clusters[MAX_TOTAL_ENT_LEAFS];
	int num_leafs;
	int i, j, k;
	int area;
	int top_node;

	if (ent == svs.game->edicts) // never bother with the world
		return;

	if (ent->area.prev) // unlink from its previous area
		Sv_UnlinkEdict(ent);

	if (!ent->in_use) // and if its free, we're done
		return;

	// set the size
	VectorSubtract(ent->maxs, ent->mins, ent->size);

	// encode the size into the entity_state for client prediction
	if (ent->solid == SOLID_BOX) { // assume that x/y are equal and symetric
		i = ent->maxs[0] / 8;
		if (i < 1)
			i = 1;
		if (i > 31)
			i = 31;

		// z is not symmetric
		j = (-ent->mins[2]) / 8;
		if (j < 1)
			j = 1;
		if (j > 31)
			j = 31;

		// and z maxs can be negative...
		k = (ent->maxs[2] + 32) / 8;
		if (k < 1)
			k = 1;
		if (k > 63)
			k = 63;

		ent->s.solid = (k << 10) | (j << 5) | i;
	} else if (ent->solid == SOLID_BSP) {
		ent->s.solid = 31; // a solid_bbox will never create this value
	} else
		ent->s.solid = 0;

	// set the absolute bounding box
	if (ent->solid == SOLID_BSP && (ent->s.angles[0] || ent->s.angles[1]
			|| ent->s.angles[2])) { // expand for rotation
		float max, v;
		int i;

		max = 0;
		for (i = 0; i < 3; i++) {
			v = fabsf(ent->mins[i]);
			if (v > max)
				max = v;
			v = fabsf(ent->maxs[i]);
			if (v > max)
				max = v;
		}
		for (i = 0; i < 3; i++) {
			ent->abs_mins[i] = ent->s.origin[i] - max;
			ent->abs_maxs[i] = ent->s.origin[i] + max;
		}
	} else { // normal
		VectorAdd(ent->s.origin, ent->mins, ent->abs_mins);
		VectorAdd(ent->s.origin, ent->maxs, ent->abs_maxs);
	}

	// because movement is clipped an epsilon away from an actual edge,
	// we must fully check even when bounding boxes don't quite touch
	ent->abs_mins[0] -= 1;
	ent->abs_mins[1] -= 1;
	ent->abs_mins[2] -= 1;
	ent->abs_maxs[0] += 1;
	ent->abs_maxs[1] += 1;
	ent->abs_maxs[2] += 1;

	// link to PVS leafs
	ent->num_clusters = 0;
	ent->area_num = 0;
	ent->area_num2 = 0;

	// get all leafs, including solids
	num_leafs = Cm_BoxLeafnums(ent->abs_mins, ent->abs_maxs, leafs,
			MAX_TOTAL_ENT_LEAFS, &top_node);

	// set areas
	for (i = 0; i < num_leafs; i++) {
		clusters[i] = Cm_LeafCluster(leafs[i]);
		area = Cm_LeafArea(leafs[i]);
		if (area) { // doors may legally occupy two areas,
			// but nothing should ever need more than that
			if (ent->area_num && ent->area_num != area) {
				if (ent->area_num2 && ent->area_num2 != area && sv.state
						== SV_LOADING) {
					Com_Debug("Object touching 3 areas at %f %f %f\n",
							ent->abs_mins[0], ent->abs_mins[1],
							ent->abs_mins[2]);
				}
				ent->area_num2 = area;
			} else
				ent->area_num = area;
		}
	}

	if (num_leafs >= MAX_TOTAL_ENT_LEAFS) { // assume we missed some leafs, and mark by head_node
		ent->num_clusters = -1;
		ent->head_node = top_node;
	} else {
		ent->num_clusters = 0;
		for (i = 0; i < num_leafs; i++) {

			if (clusters[i] == -1)
				continue; // not a visible leaf

			for (j = 0; j < i; j++)
				if (clusters[j] == clusters[i])
					break;

			if (j == i) {
				if (ent->num_clusters == MAX_ENT_CLUSTERS) { // assume we missed some leafs, and mark by head_node
					ent->num_clusters = -1;
					ent->head_node = top_node;
					break;
				}

				ent->cluster_nums[ent->num_clusters++] = clusters[i];
			}
		}
	}

	// if first time, make sure old_origin is valid
	if (!ent->link_count) {
		VectorCopy(ent->s.origin, ent->s.old_origin);
	}
	ent->link_count++;

	if (ent->solid == SOLID_NOT)
		return;

	// find the first node that the ent's box crosses
	node = sv_world.area_nodes;
	while (true) {

		if (node->axis == -1)
			break;

		if (ent->abs_mins[node->axis] > node->dist)
			node = node->children[0];
		else if (ent->abs_maxs[node->axis] < node->dist)
			node = node->children[1];
		else
			break; // crosses the node
	}

	// link it in
	if (ent->solid == SOLID_TRIGGER)
		Sv_InsertLink(&ent->area, &node->trigger_edicts);
	else
		Sv_InsertLink(&ent->area, &node->solid_edicts);
}
Exemple #3
0
/**
 * @brief Called whenever an entity changes origin, mins, maxs, or solid to add it to
 * the clipping hull.
 */
void Sv_LinkEntity(g_entity_t *ent) {
	int32_t leafs[MAX_ENT_LEAFS];
	int32_t clusters[MAX_ENT_LEAFS];
	size_t i, j;
	int32_t top_node;

	if (ent == svs.game->entities) { // never bother with the world
		return;
	}

	// remove it from its current sector
	Sv_UnlinkEntity(ent);

	if (!ent->in_use) { // and if its free, we're done
		return;
	}

	// set the size
	VectorSubtract(ent->maxs, ent->mins, ent->size);

	// encode the size into the entity state for client prediction
	ent->s.solid = ent->solid;
	switch (ent->s.solid) {
		case SOLID_TRIGGER:
		case SOLID_PROJECTILE:
		case SOLID_DEAD:
		case SOLID_BOX:
			PackBounds(ent->mins, ent->maxs, &ent->s.bounds);
			break;
		default:
			PackBounds(vec3_origin, vec3_origin, &ent->s.bounds);
			break;
	}

	// set the absolute bounding box; ensure it is symmetrical
	Cm_EntityBounds(ent->solid, ent->s.origin, ent->s.angles, ent->mins, ent->maxs, ent->abs_mins, ent->abs_maxs);

	sv_entity_t *sent = &sv.entities[NUM_FOR_ENTITY(ent)];

	// link to PVS leafs
	sent->num_clusters = 0;
	sent->areas[0] = sent->areas[1] = 0;

	// get all leafs, including solids
	const size_t len = Cm_BoxLeafnums(ent->abs_mins, ent->abs_maxs, leafs, lengthof(leafs),
	                                  &top_node, 0);

	// set areas, allowing entities (doors) to occupy up to two
	for (i = 0; i < len; i++) {
		clusters[i] = Cm_LeafCluster(leafs[i]);
		const int32_t area = Cm_LeafArea(leafs[i]);
		if (area) {
			if (sent->areas[0] && sent->areas[0] != area) {
				if (sent->areas[1] && sent->areas[1] != area && sv.state == SV_LOADING) {
					Com_Warn("Object touching 3 areas at %s\n", vtos(ent->abs_mins));
				}
				sent->areas[1] = area;
			} else {
				sent->areas[0] = area;
			}
		}
	}

	if (len == MAX_ENT_LEAFS) { // use top_node
		sent->num_clusters = -1;
		sent->top_node = top_node;
	} else {
		sent->num_clusters = 0;
		for (i = 0; i < len; i++) {

			if (clusters[i] == -1) {
				continue;    // not a visible leaf
			}

			for (j = 0; j < i; j++)
				if (clusters[j] == clusters[i]) {
					break;
				}

			if (j == i) {
				if (sent->num_clusters == MAX_ENT_CLUSTERS) { // use top_node
					Com_Debug(DEBUG_SERVER, "%s exceeds MAX_ENT_CLUSTERS\n", etos(ent));
					sent->num_clusters = -1;
					sent->top_node = top_node;
					break;
				}

				sent->clusters[sent->num_clusters++] = clusters[i];
			}
		}
	}

	if (ent->solid == SOLID_NOT) {
		return;
	}

	// find the first sector that the ent's box crosses
	sv_sector_t *sector = sv_world.sectors;
	while (true) {

		if (sector->axis == -1) {
			break;
		}

		if (ent->abs_mins[sector->axis] > sector->dist) {
			sector = sector->children[0];
		} else if (ent->abs_maxs[sector->axis] < sector->dist) {
			sector = sector->children[1];
		} else {
			break;    // crosses the node
		}
	}

	// add it to the sector
	sent->sector = sector;
	sector->entities = g_list_prepend(sector->entities, ent);

	// and update its clipping matrices
	const vec_t *angles = ent->solid == SOLID_BSP ? ent->s.angles : vec3_origin;

	Matrix4x4_CreateFromEntity(&sent->matrix, ent->s.origin, angles, 1.0);
	Matrix4x4_Invert_Simple(&sent->inverse_matrix, &sent->matrix);
}