From 9e3cfe2bb82e7af14e4bfa9959eb01e84716d081 Mon Sep 17 00:00:00 2001 From: temx Date: Sun, 16 Apr 2023 23:34:51 +0200 Subject: [PATCH] Restore part of "Trim edict list on load" in order to fix 'autofastload' Otherwise autofastload leaks edicts, eventually aborting game when max_edicts is reached BEWARE: this _may_ break old savegames ! --- Quake/host_cmd.c | 38 +++++++++++----------------- Quake/pr_edict.c | 64 ++++++++++++++++++++++++++++++++---------------- Quake/progs.h | 1 + 3 files changed, 58 insertions(+), 45 deletions(-) diff --git a/Quake/host_cmd.c b/Quake/host_cmd.c index 2caf61a1..ad40d7cf 100644 --- a/Quake/host_cmd.c +++ b/Quake/host_cmd.c @@ -1306,7 +1306,7 @@ static void Host_Loadgame_f (void) const char *data; int i; edict_t *ent; - int entnum; + int entnum, lastusedent; int version; float spawn_parms[NUM_TOTAL_SPAWN_PARMS]; qboolean was_recording = cls.demorecording; @@ -1446,7 +1446,8 @@ static void Host_Loadgame_f (void) PR_ClearEdictStrings (); // load the edicts out of the savegame file - entnum = -1; // -1 is the globals + qcvm->time = 0; // mark freed edicts for immediate reuse + entnum = lastusedent = -1; // -1 is the globals while (*data) { while (*data == ' ' || *data == '\r' || *data == '\n') @@ -1571,22 +1572,7 @@ static void Host_Loadgame_f (void) if (entnum < qcvm->num_edicts) { if (ent->free) - { - if (qcvm->free_edicts_head == ent) - { - assert (!ent->prev_free); - qcvm->free_edicts_head = ent->next_free; - } - if (qcvm->free_edicts_tail == ent) - { - assert (!ent->next_free); - qcvm->free_edicts_tail = ent->prev_free; - } - if (ent->prev_free) - ent->prev_free->next_free = ent->next_free; - if (ent->next_free) - ent->next_free->prev_free = ent->prev_free; - } + ED_RemoveFromFreeList (ent); ent->free = false; ent->next_free = NULL; ent->prev_free = NULL; @@ -1601,15 +1587,23 @@ static void Host_Loadgame_f (void) // link it into the bsp tree if (!ent->free) + { SV_LinkEdict (ent, false); + lastusedent = entnum; + } } entnum++; } - qcvm->time = time; - for (i = entnum; i < qcvm->num_edicts; i++) + for (i = lastusedent + 1; i < q_max (qcvm->num_edicts, entnum); i++) + { ED_Free (EDICT_NUM (i)); + ED_RemoveFromFreeList (EDICT_NUM (i)); + memset (EDICT_NUM (i), 0, qcvm->edict_size); + } + qcvm->time = time; + qcvm->num_edicts = lastusedent + 1; if (fastload) { @@ -1627,10 +1621,6 @@ static void Host_Loadgame_f (void) Send_Spawn_Info (svs.clients, true); } - else if (entnum < qcvm->num_edicts) - Con_Warning ("Save game had less entities than map (%d < %d)\n", entnum, qcvm->num_edicts); // should be Host_Error, but try to recover - - qcvm->num_edicts = q_max (qcvm->num_edicts, entnum); Mem_Free (start); start = NULL; diff --git a/Quake/pr_edict.c b/Quake/pr_edict.c index 42e7da5d..57cf3be3 100644 --- a/Quake/pr_edict.c +++ b/Quake/pr_edict.c @@ -142,6 +142,33 @@ void ED_Free (edict_t *ed) } } +/* +================= +ED_RemoveFromFreeList + +Used at load time to place edicts at a specifit spot, and to trim qcvm->num_edicts +================= +*/ +void ED_RemoveFromFreeList (edict_t *ed) +{ + assert (ed->free); + + if (qcvm->free_edicts_head == ed) + { + assert (!ed->prev_free); + qcvm->free_edicts_head = ed->next_free; + } + if (qcvm->free_edicts_tail == ed) + { + assert (!ed->next_free); + qcvm->free_edicts_tail = ed->prev_free; + } + if (ed->prev_free) + ed->prev_free->next_free = ed->next_free; + if (ed->next_free) + ed->next_free->prev_free = ed->prev_free; +} + //=========================================================================== /* @@ -738,42 +765,37 @@ For savegames */ void ED_Write (FILE *f, edict_t *ed) { - ddef_t *d; - int *v; - int i, j; - const char *name; - int type; - - fprintf (f, "{\n"); + ddef_t *d; + int *v; + int i; + int type; if (ed->free) { - fprintf (f, "}\n"); + fprintf (f, "{\n}\n"); return; } + fprintf (f, "{\n"); + for (i = 1; i < qcvm->progs->numfielddefs; i++) { d = &qcvm->fielddefs[i]; - name = PR_GetString (d->s_name); - j = strlen (name); - if (j > 1 && name[j - 2] == '_') - continue; // skip _x, _y, _z vars + type = d->type; + assert (!!(type & DEF_SAVEGLOBAL) == (strlen (PR_GetString (d->s_name)) > 1 && PR_GetString (d->s_name)[strlen (PR_GetString (d->s_name)) - 2] == '_')); + if (type & DEF_SAVEGLOBAL) + continue; v = (int *)((char *)&ed->v + d->ofs * 4); // if the value is still all 0, skip the field - type = d->type & ~DEF_SAVEGLOBAL; - for (j = 0; j < type_size[type]; j++) - { - if (v[j]) - break; - } - if (j == type_size[type]) + assert (type < 8 && ((type == ev_vector && type_size[type] == 3) || (type != ev_vector && type_size[type] == 1))); + if (type != ev_vector && !v[0]) + continue; + if (type == ev_vector && !v[0] && !v[1] && !v[2]) continue; - fprintf (f, "\"%s\" ", name); - fprintf (f, "\"%s\"\n", PR_UglyValueString (d->type, (eval_t *)v)); + fprintf (f, "\"%s\" \"%s\"\n", PR_GetString (d->s_name), PR_UglyValueString (d->type, (eval_t *)v)); } // johnfitz -- save entity alpha manually when progs.dat doesn't know about alpha diff --git a/Quake/progs.h b/Quake/progs.h index b5b9173e..e74b28ab 100644 --- a/Quake/progs.h +++ b/Quake/progs.h @@ -111,6 +111,7 @@ void PR_Profile_f (void); edict_t *ED_Alloc (void); void ED_Free (edict_t *ed); +void ED_RemoveFromFreeList (edict_t *ed); void ED_Print (edict_t *ed); void ED_Write (FILE *f, edict_t *ed);