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;