shithub: qk1

Download patch

ref: 24f1de7539d431c959ef3531a959ece279834ba0
parent: 471fd5859e08f9f5cc163a3394397dd7627124e7
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Sun Nov 26 19:10:35 EST 2023

fix the usual SZ_GetSpace overflow on huge maps

This seems to fix maps like 'telefragged' (did not even spawn before)
and 'ad_tears' (would break in the middle of the game).

Keep an array of signons and move data to the newly allocated sizebuf
if it doesn't fully fit in the old one. A bit better(?) implementation
compared to other engines as you don't have to figure out the size of
the frame by yourself.

--- a/common.c
+++ b/common.c
@@ -335,8 +335,17 @@
 SZ_GetSpace(sizebuf_t *buf, int length)
 {
 	void *data;
+	sizebuf_t *next;
 
+again:
 	if(buf->cursize + length > buf->maxsize){
+		if(buf->overflow_cb != nil){
+			next = buf->overflow_cb(buf);
+			if(next != buf){
+				buf = next;
+				goto again;
+			}
+		}
 		if(!buf->allowoverflow)
 			Host_Error("SZ_GetSpace: %s: overflow without allowoverflow set", buf->name);
 		if(length > buf->maxsize)
--- a/common.h
+++ b/common.h
@@ -1,7 +1,8 @@
 void *Arr_AllocExtra(void *arr, int *nel, int needextra);
 
-typedef struct sizebuf_s
-{
+typedef struct sizebuf_t sizebuf_t;
+
+struct sizebuf_t {
 	bool	allowoverflow;	// if false, do a fatal
 	bool	overflowed;		// set to true if the buffer size failed
 	char	*name;
@@ -8,7 +9,8 @@
 	byte	*data;
 	int		maxsize;
 	int		cursize;
-} sizebuf_t;
+	sizebuf_t *(*overflow_cb)(sizebuf_t *s);
+};
 
 void SZ_Alloc (sizebuf_t *buf, int startsize);
 void SZ_Clear (sizebuf_t *buf);
--- a/host_cmd.c
+++ b/host_cmd.c
@@ -645,22 +645,17 @@
 */
 void Host_PreSpawn_f (void)
 {
-	if (cmd_source == src_command)
-	{
-		Con_Printf ("prespawn is not valid from the console\n");
+	if(cmd_source == src_command){
+		Con_Printf("prespawn is not valid from the console\n");
 		return;
 	}
-
-	if (host_client->spawned)
-	{
-		Con_Printf ("prespawn not valid -- allready spawned\n");
+	if(host_client->spawned){
+		Con_Printf("prespawn not valid -- already spawned\n");
 		return;
 	}
 
-	SZ_Write (&host_client->message, sv.signon.data, sv.signon.cursize);
-	MSG_WriteByte (&host_client->message, svc_signonnum);
-	MSG_WriteByte (&host_client->message, 2);
-	host_client->sendsignon = true;
+	host_client->signon.state = SIGNON_SENDING;
+	host_client->signon.id = 0;
 }
 
 /*
@@ -783,7 +778,7 @@
 
 	MSG_WriteByte (&host_client->message, svc_signonnum);
 	MSG_WriteByte (&host_client->message, 3);
-	host_client->sendsignon = true;
+	host_client->signon.state = SIGNON_KICK;
 }
 
 /*
--- a/pr_cmds.c
+++ b/pr_cmds.c
@@ -514,18 +514,20 @@
 	if(soundnum >= sv.protocol->limit_sound)
 		return;
 
+	SV_SignonFrame();
+
 	MSG_WriteByte(
-		&sv.signon,
+		sv.signon,
 		soundnum < sv.protocol->large_sound ?
 			svc_spawnstaticsound :
 			svc_spawnstaticsound2
 	);
 	for (i=0 ; i<3 ; i++)
-		sv.protocol->MSG_WriteCoord(&sv.signon, pos[i]);
+		sv.protocol->MSG_WriteCoord(sv.signon, pos[i]);
 
-	(soundnum < sv.protocol->large_sound ? MSG_WriteByte : MSG_WriteShort)(&sv.signon, soundnum);
-	MSG_WriteByte(&sv.signon, vol*255);
-	MSG_WriteByte(&sv.signon, attenuation*64);
+	(soundnum < sv.protocol->large_sound ? MSG_WriteByte : MSG_WriteShort)(sv.signon, soundnum);
+	MSG_WriteByte(sv.signon, vol*255);
+	MSG_WriteByte(sv.signon, attenuation*64);
 
 }
 
@@ -1411,7 +1413,7 @@
 		return &sv.reliable_datagram;
 
 	case MSG_INIT:
-		return &sv.signon;
+		return sv.signon;
 
 	default:
 		PR_RunError (pr, "WriteDest: bad destination");
@@ -1490,26 +1492,28 @@
 	if(!defalpha(ent->alpha))
 		bits |= sv.protocol->fl_baseline_alpha;
 
-	MSG_WriteByte (&sv.signon, bits ? svc_spawnstatic2 : svc_spawnstatic);
+	SV_SignonFrame();
+
+	MSG_WriteByte(sv.signon, bits ? svc_spawnstatic2 : svc_spawnstatic);
 	if(bits)
-		MSG_WriteByte(&sv.signon, bits);
+		MSG_WriteByte(sv.signon, bits);
 
 	((bits & sv.protocol->fl_large_baseline_model) ? MSG_WriteShort : MSG_WriteByte)
-		(&sv.signon, model);
+		(sv.signon, model);
 	((bits & sv.protocol->fl_large_baseline_frame) ? MSG_WriteShort : MSG_WriteByte)
-		(&sv.signon, frame);
-	MSG_WriteByte (&sv.signon, ent->v.colormap);
-	MSG_WriteByte (&sv.signon, ent->v.skin);
+		(sv.signon, frame);
+	MSG_WriteByte(sv.signon, ent->v.colormap);
+	MSG_WriteByte(sv.signon, ent->v.skin);
 	for (i=0 ; i<3 ; i++)
 	{
-		sv.protocol->MSG_WriteCoord(&sv.signon, ent->v.origin[i]);
-		sv.protocol->MSG_WriteAngle(&sv.signon, ent->v.angles[i]);
+		sv.protocol->MSG_WriteCoord(sv.signon, ent->v.origin[i]);
+		sv.protocol->MSG_WriteAngle(sv.signon, ent->v.angles[i]);
 	}
 	if(bits & sv.protocol->fl_baseline_alpha)
-		MSG_WriteByte(&sv.signon, ent->alpha);
+		MSG_WriteByte(sv.signon, ent->alpha);
 
 	// throw the entity away now
-	ED_Free (ent);
+	ED_Free(ent);
 }
 
 //=============================================================================
--- a/pr_edict.c
+++ b/pr_edict.c
@@ -929,6 +929,7 @@
 			continue;
 		}
 
+		SV_SignonFrame();
 		pr->global_struct->self = EDICT_TO_PROG(pr, ent);
 		PR_ExecuteProgram (pr, func - pr->functions);
 	}
--- a/server.h
+++ b/server.h
@@ -39,8 +39,10 @@
 	sizebuf_t	reliable_datagram;	// copied to all clients at end of frame
 	byte		reliable_datagram_buf[MAX_DATAGRAM];
 
-	sizebuf_t	signon;
-	byte		signon_buf[NET_MAXMESSAGE];
+	sizebuf_t *signon;
+	int signon_frame;
+	sizebuf_t *signons[256];
+	int numsignons;
 
 	pr_t *pr;
 } server_t;
@@ -48,13 +50,24 @@
 
 #define	NUM_PING_TIMES		16
 
+enum {
+	SIGNON_INIT,
+	SIGNON_SENDING,
+	SIGNON_DONE,
+	SIGNON_KICK,
+};
+
 typedef struct client_s
 {
 	bool		active;				// false = client is free
 	bool		spawned;			// false = don't send datagrams
 	bool		dropasap;			// has been told to go to another level
-	bool		sendsignon;			// only valid before spawned
 
+	struct {
+		int state;
+		int id;
+	}signon;
+
 	double			last_message;		// reliable messages must be sent
 										// periodically
 
@@ -162,6 +175,8 @@
 extern	edict_t		*sv_player;
 
 //===========================================================
+
+void SV_SignonFrame(void);
 
 void SV_Init (void);
 
--- a/sv_main.c
+++ b/sv_main.c
@@ -1,5 +1,9 @@
 #include "quakedef.h"
 
+enum {
+	MAX_SIGNON_SIZE = 32000,
+};
+
 server_t		sv;
 server_static_t	svs;
 
@@ -10,6 +14,41 @@
 
 static char	localmodels[MAX_MODELS][8];			// inline model names for precache
 
+static void SV_AllocSignon(void);
+
+static sizebuf_t *
+signon_overflow_sb(sizebuf_t *s)
+{
+	int frame;
+
+	if(s == sv.signon){
+		frame = sv.signon_frame;
+		SV_AllocSignon();
+		SZ_Write(sv.signon, s->data + frame, s->cursize - frame);
+		s->cursize = frame;
+	}
+	return sv.signon;
+}
+
+static void
+SV_AllocSignon(void)
+{
+	if(sv.numsignons == 256)
+		Host_Error("too many signons");
+	sv.signon = sv.signons[sv.numsignons++] = Hunk_Alloc(sizeof(*sv.signon) + MAX_SIGNON_SIZE);
+	sv.signon->data = (byte*)(sv.signon + 1);
+	sv.signon->maxsize = MAX_SIGNON_SIZE;
+	sv.signon->name = "sv.signon";
+	sv.signon->overflow_cb = signon_overflow_sb;
+	sv.signon_frame = 0;
+}
+
+void
+SV_SignonFrame(void)
+{
+	sv.signon_frame = sv.signon->cursize;
+}
+
 //============================================================================
 
 static void
@@ -241,7 +280,7 @@
 	MSG_WriteByte (&client->message, svc_signonnum);
 	MSG_WriteByte (&client->message, 1);
 
-	client->sendsignon = true;
+	client->signon.state = SIGNON_KICK;
 	client->spawned = false;		// need prespawn, spawn, etc
 }
 
@@ -849,7 +888,8 @@
 */
 void SV_SendClientMessages (void)
 {
-	int			i;
+	int i;
+	sizebuf_t *s;
 
 	// update frags, names, etc
 	SV_UpdateToReliableMessages ();
@@ -872,12 +912,27 @@
 		// send a full message when the next signon stage has been requested
 		// some other message data (name changes, etc) may accumulate
 		// between signon stages
-			if (!host_client->sendsignon)
-			{
-				if (realtime - host_client->last_message > 5)
+			if(host_client->signon.state == SIGNON_INIT){
+				if(realtime - host_client->last_message > 5)
 					SV_SendNop (host_client);
 				continue;	// don't send out non-signon messages
 			}
+			if(host_client->signon.state == SIGNON_SENDING){
+				if(host_client->signon.id < sv.numsignons){
+					s = sv.signons[host_client->signon.id];
+					if(host_client->message.cursize+s->cursize <= host_client->message.maxsize){
+						SZ_Write(&host_client->message, s->data, s->cursize);
+						host_client->signon.id++;
+					}
+				}
+				if(host_client->signon.id == sv.numsignons)
+					host_client->signon.state = SIGNON_DONE;
+			}
+			if(host_client->signon.state == SIGNON_DONE && host_client->message.cursize+2 < host_client->message.maxsize){
+					MSG_WriteByte(&host_client->message, svc_signonnum);
+					MSG_WriteByte(&host_client->message, 2);
+					host_client->signon.state = SIGNON_KICK;
+			}
 		}
 
 		// check for an overflowed message.  Should only happen
@@ -902,12 +957,12 @@
 				SV_DropClient (false);	// went to another level
 			else
 			{
-				if (NET_SendMessage (host_client->netconnection
-				, &host_client->message) == -1)
-					SV_DropClient (true);	// if the message couldn't send, kick off
-				SZ_Clear (&host_client->message);
+				if(NET_SendMessage(host_client->netconnection, &host_client->message) < 0)
+					SV_DropClient(true);	// if the message couldn't send, kick off
+				SZ_Clear(&host_client->message);
 				host_client->last_message = realtime;
-				host_client->sendsignon = false;
+				if(host_client->signon.state == SIGNON_KICK)
+					host_client->signon.state = SIGNON_INIT;
 			}
 		}
 	}
@@ -1000,24 +1055,24 @@
 			bits |= sv.protocol->fl_baseline_alpha;
 
 		// add to the message
-		MSG_WriteByte (&sv.signon, bits ? svc_spawnbaseline2 : svc_spawnbaseline);
-		MSG_WriteShort (&sv.signon, entnum);
+		SV_SignonFrame();
+		MSG_WriteByte(sv.signon, bits ? svc_spawnbaseline2 : svc_spawnbaseline);
+		MSG_WriteShort(sv.signon, entnum);
 		if(bits != 0)
-			MSG_WriteByte(&sv.signon, bits);
+			MSG_WriteByte(sv.signon, bits);
 
 		((bits & sv.protocol->fl_large_baseline_model) ? MSG_WriteShort : MSG_WriteByte)
-			(&sv.signon, svent->baseline.modelindex);
+			(sv.signon, svent->baseline.modelindex);
 		((bits & sv.protocol->fl_large_baseline_frame) ? MSG_WriteShort : MSG_WriteByte)
-			(&sv.signon, svent->baseline.frame);
-		MSG_WriteByte (&sv.signon, svent->baseline.colormap);
-		MSG_WriteByte (&sv.signon, svent->baseline.skin);
-		for (i=0 ; i<3 ; i++)
-		{
-			sv.protocol->MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]);
-			sv.protocol->MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]);
+			(sv.signon, svent->baseline.frame);
+		MSG_WriteByte(sv.signon, svent->baseline.colormap);
+		MSG_WriteByte(sv.signon, svent->baseline.skin);
+		for(i = 0; i < 3; i++){
+			sv.protocol->MSG_WriteCoord(sv.signon, svent->baseline.origin[i]);
+			sv.protocol->MSG_WriteAngle(sv.signon, svent->baseline.angles[i]);
 		}
 		if(bits & sv.protocol->fl_baseline_alpha)
-			MSG_WriteByte(&sv.signon, svent->baseline.alpha);
+			MSG_WriteByte(sv.signon, svent->baseline.alpha);
 	}
 }
 
@@ -1138,10 +1193,7 @@
 	sv.reliable_datagram.data = sv.reliable_datagram_buf;
 	sv.reliable_datagram.name = "sv.reliable_datagram";
 
-	sv.signon.maxsize = sizeof sv.signon_buf;
-	sv.signon.cursize = 0;
-	sv.signon.data = sv.signon_buf;
-	sv.signon.name = "sv.signon";
+	SV_AllocSignon();
 
 	// leave slots at start for clients only
 	sv.pr->num_edicts = svs.maxclients+1;