From: divverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Date: Sat, 26 Sep 2009 18:15:41 +0000 (+0000)
Subject: network CSQC and network entities separately (should prevent bugs when an entity... 
X-Git-Tag: xonotic-v0.1.0preview~1390
X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=027f95c8315bd7ec0c4b9351ba2b32e7beebd460;p=xonotic%2Fdarkplaces.git

network CSQC and network entities separately (should prevent bugs when an entity suddenly gets SendEntity assigned, which can happen during entity reuse)


git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@9243 d7cf8633-e32d-0410-b094-e92efae38249
---

diff --git a/protocol.c b/protocol.c
index f6fce7a0..9cc93d5d 100644
--- a/protocol.c
+++ b/protocol.c
@@ -37,7 +37,7 @@ entity_state_t defaultstate =
 	0,//unsigned short nodrawtoclient; // !
 	0,//unsigned short drawonlytoclient; // !
 	{0,0,0,0},//unsigned short light[4]; // color*256 (0.00 to 255.996), and radius*1
-	0,//unsigned char active; // true if a valid state
+	ACTIVE_NOT,//unsigned char active; // true if a valid state
 	0,//unsigned char lightstyle;
 	0,//unsigned char lightpflags;
 	0,//unsigned char colormap;
@@ -165,7 +165,7 @@ void EntityFrameQuake_ReadEntity(int bits)
 	else
 	{
 		s = ent->state_baseline;
-		s.active = true;
+		s.active = ACTIVE_NETWORK;
 	}
 
 	cl.isquakeentity[num] = true;
@@ -226,7 +226,7 @@ void EntityFrameQuake_ReadEntity(int bits)
 
 	ent->state_previous = ent->state_current;
 	ent->state_current = s;
-	if (ent->state_current.active)
+	if (ent->state_current.active == ACTIVE_NETWORK)
 	{
 		CL_MoveLerpEntityStates(ent);
 		cl.entities_active[ent->state_current.number] = true;
@@ -255,7 +255,7 @@ void EntityFrameQuake_ISeeDeadEntities(void)
 			else
 			{
 				cl.isquakeentity[num] = false;
-				cl.entities_active[num] = false;
+				cl.entities_active[num] = ACTIVE_NOT;
 				cl.entities[num].state_current = defaultstate;
 				cl.entities[num].state_current.number = num;
 			}
@@ -430,11 +430,11 @@ static void EntityFrameCSQC_DeallocFrame(client_t *client, int framenum)
 //[515]: we use only one array per-client for SendEntity feature
 // TODO: add some handling for entity send priorities, to better deal with huge
 // amounts of csqc networked entities
-qboolean EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numstates, const entity_state_t *states, int framenum)
+qboolean EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numnumbers, const unsigned short *numbers, int framenum)
 {
 	int num, number, end, sendflags;
 	qboolean sectionstarted = false;
-	const entity_state_t *n;
+	const unsigned short *n;
 	prvm_edict_t *ed;
 	prvm_eval_t *val;
 	client_t *client = svs.clients + sv.writeentitiestoclient_clientnumber;
@@ -456,9 +456,9 @@ qboolean EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numstates,
 		client->csqcnumedicts = prog->num_edicts;
 
 	number = 1;
-	for (num = 0, n = states;num < numstates;num++, n++)
+	for (num = 0, n = numbers;num < numnumbers;num++, n++)
 	{
-		end = n->number;
+		end = *n;
 		for (;number < end;number++)
 		{
 			if (client->csqcentityscope[number])
@@ -494,9 +494,9 @@ qboolean EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numstates,
 		if (client->csqcentityscope[number])
 			client->csqcentityscope[number] = 1;
 	// keep visible entities
-	for (i = 0, n = states;i < numstates;i++, n++)
+	for (i = 0, n = numbers;i < numnumbers;i++, n++)
 	{
-		number = n->number;
+		number = *n;
 		ed = prog->edicts + number;
 		val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.SendEntity);
 		if (val->function)
@@ -851,7 +851,7 @@ int EntityState_DeltaBits(const entity_state_t *o, const entity_state_t *n)
 {
 	unsigned int bits;
 	// if o is not active, delta from default
-	if (!o->active)
+	if (o->active != ACTIVE_NETWORK)
 		o = &defaultstate;
 	bits = 0;
 	if (fabs(n->origin[0] - o->origin[0]) > (1.0f / 256.0f))
@@ -1030,7 +1030,7 @@ void EntityState_WriteUpdate(const entity_state_t *ent, sizebuf_t *msg, const en
 {
 	unsigned int bits;
 	ENTITYSIZEPROFILING_START(msg, ent->number);
-	if (ent->active)
+	if (ent->active == ACTIVE_NETWORK)
 	{
 		// entity is active, check for changes from the delta
 		if ((bits = EntityState_DeltaBits(delta, ent)))
@@ -1044,7 +1044,7 @@ void EntityState_WriteUpdate(const entity_state_t *ent, sizebuf_t *msg, const en
 	else
 	{
 		// entity is inactive, check if the delta was active
-		if (delta->active)
+		if (delta->active == ACTIVE_NETWORK)
 		{
 			// write the remove number
 			MSG_WriteShort(msg, ent->number | 0x8000);
@@ -1483,7 +1483,7 @@ void EntityFrame_CL_ReadFrame(void)
 					CL_ExpandEntities(number);
 			}
 			cl.entities_active[number] = true;
-			e->active = true;
+			e->active = ACTIVE_NETWORK;
 			e->time = cl.mtime[0];
 			e->number = number;
 			EntityState_ReadFields(e, EntityState_ReadExtendBits());
@@ -1507,7 +1507,7 @@ void EntityFrame_CL_ReadFrame(void)
 			if (cl.entities_active[number])
 			{
 				cl.entities_active[number] = false;
-				cl.entities[number].state_current.active = false;
+				cl.entities[number].state_current.active = ACTIVE_NOT;
 			}
 		}
 		if (number >= cl.num_entities)
@@ -1526,7 +1526,7 @@ void EntityFrame_CL_ReadFrame(void)
 		if (cl.entities_active[number])
 		{
 			cl.entities_active[number] = false;
-			cl.entities[number].state_current.active = false;
+			cl.entities[number].state_current.active = ACTIVE_NOT;
 		}
 	}
 }
@@ -1651,7 +1651,7 @@ int EntityFrame4_AckFrame(entityframe4_database_t *d, int framenum, int servermo
 							entity_state_t *s = EntityFrame4_GetReferenceEntity(d, commit->entity[j].number);
 							if (commit->entity[j].active != s->active)
 							{
-								if (commit->entity[j].active)
+								if (commit->entity[j].active == ACTIVE_NETWORK)
 									Con_Printf("commit entity %i has become active (modelindex %i)\n", commit->entity[j].number, commit->entity[j].modelindex);
 								else
 									Con_Printf("commit entity %i has become inactive (modelindex %i)\n", commit->entity[j].number, commit->entity[j].modelindex);
@@ -1779,20 +1779,20 @@ void EntityFrame4_CL_ReadFrame(void)
 					// read the changes
 					if (developer_networkentities.integer >= 2)
 						Con_Printf("entity %i: update\n", enumber);
-					s->active = true;
+					s->active = ACTIVE_NETWORK;
 					EntityState_ReadFields(s, EntityState_ReadExtendBits());
 				}
 			}
 			else if (developer_networkentities.integer >= 4)
 				Con_Printf("entity %i: copy\n", enumber);
 			// set the cl.entities_active flag
-			cl.entities_active[enumber] = s->active;
+			cl.entities_active[enumber] = (s->active == ACTIVE_NETWORK);
 			// set the update time
 			s->time = cl.mtime[0];
 			// fix the number (it gets wiped occasionally by copying from defaultstate)
 			s->number = enumber;
 			// check if we need to update the lerp stuff
-			if (s->active)
+			if (s->active == ACTIVE_NETWORK)
 				CL_MoveLerpEntityStates(&cl.entities[enumber]);
 			// add this to the commit entry whether it is modified or not
 			if (d->currentcommit)
@@ -1800,7 +1800,7 @@ void EntityFrame4_CL_ReadFrame(void)
 			// print extra messages if desired
 			if (developer_networkentities.integer >= 2 && cl.entities[enumber].state_current.active != cl.entities[enumber].state_previous.active)
 			{
-				if (cl.entities[enumber].state_current.active)
+				if (cl.entities[enumber].state_current.active == ACTIVE_NETWORK)
 					Con_Printf("entity #%i has become active\n", enumber);
 				else if (cl.entities[enumber].state_previous.active)
 					Con_Printf("entity #%i has become inactive\n", enumber);
@@ -1885,7 +1885,7 @@ qboolean EntityFrame4_WriteFrame(sizebuf_t *msg, int maxsize, entityframe4_datab
 		{
 			inactiveentitystate.number = n;
 			s = &inactiveentitystate;
-			if (e->active)
+			if (e->active == ACTIVE_NETWORK)
 			{
 				// entity used to exist but doesn't anymore, send remove
 				MSG_WriteShort(&buf, n | 0x8000);
@@ -1985,7 +1985,7 @@ static int EntityState5_Priority(entityframe5_database_t *d, int stateindex)
 	if (stateindex <= svs.maxclients)
 		priority++;
 	// remove dead entities very quickly because they are just 2 bytes
-	if (!d->states[stateindex].active)
+	if (d->states[stateindex].active != ACTIVE_NETWORK)
 	{
 		priority++;
 		return bound(1, priority, ENTITYFRAME5_PRIORITYLEVELS - 1);
@@ -2026,7 +2026,7 @@ void EntityState5_WriteUpdate(int number, const entity_state_t *s, int changedbi
 	if(val && val->function)
 		return;
 
-	if (!s->active)
+	if (s->active != ACTIVE_NETWORK)
 		MSG_WriteShort(msg, number | 0x8000);
 	else
 	{
@@ -2180,7 +2180,7 @@ static void EntityState5_ReadUpdate(entity_state_t *s, int number)
 	if (bits & E5_FULLUPDATE)
 	{
 		*s = defaultstate;
-		s->active = true;
+		s->active = ACTIVE_NETWORK;
 	}
 	if (bits & E5_FLAGS)
 		s->flags = MSG_ReadByte();
@@ -2332,9 +2332,9 @@ static void EntityState5_ReadUpdate(entity_state_t *s, int number)
 static int EntityState5_DeltaBits(const entity_state_t *o, const entity_state_t *n)
 {
 	unsigned int bits = 0;
-	if (n->active)
+	if (n->active == ACTIVE_NETWORK)
 	{
-		if (!o->active)
+		if (o->active != ACTIVE_NETWORK)
 			bits |= E5_FULLUPDATE;
 		if (!VectorCompare(o->origin, n->origin))
 			bits |= E5_ORIGIN;
@@ -2366,7 +2366,7 @@ static int EntityState5_DeltaBits(const entity_state_t *o, const entity_state_t
 			bits |= E5_COLORMOD;
 	}
 	else
-		if (o->active)
+		if (o->active == ACTIVE_NETWORK)
 			bits |= E5_FULLUPDATE;
 	return bits;
 }
@@ -2414,18 +2414,18 @@ void EntityFrame5_CL_ReadFrame(void)
 			EntityState5_ReadUpdate(s, enumber);
 		}
 		// set the cl.entities_active flag
-		cl.entities_active[enumber] = s->active;
+		cl.entities_active[enumber] = (s->active == ACTIVE_NETWORK);
 		// set the update time
 		s->time = cl.mtime[0];
 		// fix the number (it gets wiped occasionally by copying from defaultstate)
 		s->number = enumber;
 		// check if we need to update the lerp stuff
-		if (s->active)
+		if (s->active == ACTIVE_NETWORK)
 			CL_MoveLerpEntityStates(&cl.entities[enumber]);
 		// print extra messages if desired
 		if (developer_networkentities.integer >= 2 && cl.entities[enumber].state_current.active != cl.entities[enumber].state_previous.active)
 		{
-			if (cl.entities[enumber].state_current.active)
+			if (cl.entities[enumber].state_current.active == ACTIVE_NETWORK)
 				Con_Printf("entity #%i has become active\n", enumber);
 			else if (cl.entities[enumber].state_previous.active)
 				Con_Printf("entity #%i has become inactive\n", enumber);
@@ -2757,7 +2757,7 @@ void EntityStateQW_ReadPlayerUpdate(void)
 	// read the update
 	s = &ent->state_current;
 	*s = defaultstate;
-	s->active = true;
+	s->active = ACTIVE_NETWORK;
 	s->number = enumber;
 	s->colormap = enumber;
 	playerflags = MSG_ReadShort();
@@ -2860,18 +2860,18 @@ void EntityStateQW_ReadPlayerUpdate(void)
 	}
 
 	// set the cl.entities_active flag
-	cl.entities_active[enumber] = s->active;
+	cl.entities_active[enumber] = (s->active == ACTIVE_NETWORK);
 	// set the update time
 	s->time = cl.mtime[0] - msec * 0.001; // qw has no clock
 	// check if we need to update the lerp stuff
-	if (s->active)
+	if (s->active == ACTIVE_NETWORK)
 		CL_MoveLerpEntityStates(&cl.entities[enumber]);
 }
 
 static void EntityStateQW_ReadEntityUpdate(entity_state_t *s, int bits)
 {
 	int qweffects = 0;
-	s->active = true;
+	s->active = ACTIVE_NETWORK;
 	s->number = bits & 511;
 	bits &= ~511;
 	if (bits & QW_U_MOREBITS)
@@ -3071,7 +3071,7 @@ void EntityFrameQW_CL_ReadFrame(qboolean delta)
 			if (cl.entities_active[number])
 			{
 				cl.entities_active[number] = false;
-				cl.entities[number].state_current.active = false;
+				cl.entities[number].state_current.active = ACTIVE_NOT;
 			}
 		}
 		if (number >= cl.num_entities)
diff --git a/protocol.h b/protocol.h
index 7e01d764..cfb2084e 100644
--- a/protocol.h
+++ b/protocol.h
@@ -337,6 +337,14 @@ void Protocol_Names(char *buffer, size_t buffersize);
 #define RENDER_NOSELFSHADOW 262144 // render lighting on this entity before its own shadow is added to the scene
 // (note: all RENDER_NOSELFSHADOW entities are grouped together and rendered in a batch before their shadows are rendered, so they can not shadow eachother either)
 
+typedef enum entity_state_active_e
+{
+	ACTIVE_NOT = 0,
+	ACTIVE_NETWORK = 1,
+	ACTIVE_SHARED = 2
+}
+entity_state_active_t;
+
 // this is 96 bytes
 typedef struct entity_state_s
 {
@@ -969,7 +977,7 @@ void EntityFrameQW_CL_ReadFrame(qboolean delta);
 
 struct client_s;
 void EntityFrameCSQC_LostFrame(struct client_s *client, int framenum);
-qboolean EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numstates, const entity_state_t *states, int framenum);
+qboolean EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numnumbers, const unsigned short *numbers, int framenum);
 
 #endif
 
diff --git a/server.h b/server.h
index e2f02618..7e7124e3 100644
--- a/server.h
+++ b/server.h
@@ -146,6 +146,7 @@ typedef struct server_s
 	int writeentitiestoclient_pvsbytes;
 	unsigned char writeentitiestoclient_pvs[MAX_MAP_LEAFS/8];
 	entity_state_t writeentitiestoclient_sendstates[MAX_EDICTS];
+	unsigned short writeentitiestoclient_csqcsendstates[MAX_EDICTS];
 
 	int numsendentities;
 	entity_state_t sendentities[MAX_EDICTS];
diff --git a/sv_main.c b/sv_main.c
index 476be285..86820bbd 100644
--- a/sv_main.c
+++ b/sv_main.c
@@ -1103,7 +1103,7 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c
 		return false;
 
 	*cs = defaultstate;
-	cs->active = true;
+	cs->active = ACTIVE_NETWORK;
 	cs->number = enumber;
 	VectorCopy(ent->fields.server->origin, cs->origin);
 	VectorCopy(ent->fields.server->angles, cs->angles);
@@ -1268,6 +1268,8 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c
 		if (sendflags)
 			for (i = 0;i < svs.maxclients;i++)
 				svs.clients[i].csqcentitysendflags[enumber] |= sendflags;
+		// mark it as inactive for non-csqc networking
+		cs->active = ACTIVE_SHARED;
 	}
 
 	return true;
@@ -1449,7 +1451,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
 void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int maxsize)
 {
 	qboolean need_empty = false;
-	int i, numsendstates;
+	int i, numsendstates, numcsqcsendstates;
 	entity_state_t *s;
 	prvm_edict_t *camera;
 	qboolean success;
@@ -1482,14 +1484,22 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
 		SV_MarkWriteEntityStateToClient(sv.sendentities + i);
 
 	numsendstates = 0;
+	numcsqcsendstates = 0;
 	for (i = 0;i < sv.numsendentities;i++)
 	{
 		if (sv.sententities[sv.sendentities[i].number] == sv.sententitiesmark)
 		{
-			s = &sv.writeentitiestoclient_sendstates[numsendstates++];
-			*s = sv.sendentities[i];
-			if (s->exteriormodelforclient && s->exteriormodelforclient == sv.writeentitiestoclient_cliententitynumber)
-				s->flags |= RENDER_EXTERIORMODEL;
+			if(sv.sendentities[i].active == ACTIVE_NETWORK)
+			{
+				s = &sv.writeentitiestoclient_sendstates[numsendstates++];
+				*s = sv.sendentities[i];
+				if (s->exteriormodelforclient && s->exteriormodelforclient == sv.writeentitiestoclient_cliententitynumber)
+					s->flags |= RENDER_EXTERIORMODEL;
+			}
+			else if(sv.sendentities[i].active == ACTIVE_SHARED)
+				sv.writeentitiestoclient_csqcsendstates[numcsqcsendstates++] = sv.sendentities[i].number;
+			else
+				Con_Printf("entity %d is in sv.sendentities and marked, but not active, please breakpoint me\n", sv.sendentities[i].number);
 		}
 	}
 
@@ -1497,9 +1507,9 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
 		Con_Printf("client \"%s\" entities: %d total, %d visible, %d culled by: %d pvs %d trace\n", client->name, sv.writeentitiestoclient_stats_totalentities, sv.writeentitiestoclient_stats_visibleentities, sv.writeentitiestoclient_stats_culled_pvs + sv.writeentitiestoclient_stats_culled_trace, sv.writeentitiestoclient_stats_culled_pvs, sv.writeentitiestoclient_stats_culled_trace);
 
 	if(client->entitydatabase5)
-		need_empty = EntityFrameCSQC_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates, client->entitydatabase5->latestframenum + 1);
+		need_empty = EntityFrameCSQC_WriteFrame(msg, maxsize, numcsqcsendstates, sv.writeentitiestoclient_csqcsendstates, client->entitydatabase5->latestframenum + 1);
 	else
-		EntityFrameCSQC_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates, 0);
+		EntityFrameCSQC_WriteFrame(msg, maxsize, numcsqcsendstates, sv.writeentitiestoclient_csqcsendstates, 0);
 
 	if(client->num_skippedentityframes >= 10)
 		need_empty = true; // force every 10th frame to be not empty (or cl_movement replay takes too long)