From 4cf1d7fe10ad2dee57c59679a74e1148c38f3779 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sun, 5 Nov 2023 13:52:23 +0100
Subject: [PATCH] Cache and reuse removed mobjs when spawning mobjs

---
 src/d_think.h |  1 +
 src/p_local.h |  1 +
 src/p_mobj.c  | 17 +++++++++++++++--
 src/p_setup.c |  1 +
 src/p_tick.c  | 12 +++++++++++-
 5 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/src/d_think.h b/src/d_think.h
index efc1589bf6..5891245878 100644
--- a/src/d_think.h
+++ b/src/d_think.h
@@ -51,6 +51,7 @@ typedef struct thinker_s
 	// killough 11/98: count of how many other objects reference
 	// this one using pointers. Used for garbage collection.
 	INT32 references;
+	boolean cachable;
 
 #ifdef PARANOIA
 	INT32 debug_mobjtype;
diff --git a/src/p_local.h b/src/p_local.h
index c26c098600..70eb3435e0 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -71,6 +71,7 @@ typedef enum
 	NUM_THINKERLISTS
 } thinklistnum_t; /**< Thinker lists. */
 extern thinker_t thlist[];
+extern mobj_t *mobjcache;
 
 void P_InitThinkers(void);
 void P_AddThinker(const thinklistnum_t n, thinker_t *thinker);
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 7a5aaf4249..f424fba06c 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -45,6 +45,8 @@ actioncache_t actioncachehead;
 
 static mobj_t *overlaycap = NULL;
 
+mobj_t *mobjcache = NULL;
+
 void P_InitCachedActions(void)
 {
 	actioncachehead.prev = actioncachehead.next = &actioncachehead;
@@ -10659,7 +10661,16 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 		type = MT_RAY;
 	}
 
-	mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL);
+	if (mobjcache != NULL)
+	{
+		mobj = mobjcache;
+		mobjcache = mobjcache->hnext;
+		memset(mobj, 0, sizeof(*mobj));
+	}
+	else
+	{
+		mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL);
+	}
 
 	// this is officially a mobj, declared as soon as possible.
 	mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker;
@@ -11214,7 +11225,9 @@ void P_RemoveMobj(mobj_t *mobj)
 		INT32 prevreferences;
 		if (!mobj->thinker.references)
 		{
-			Z_Free(mobj); // No refrrences? Can be removed immediately! :D
+			// no references, dump it directly in the mobj cache
+			mobj->hnext = mobjcache;
+			mobjcache = mobj;
 			return;
 		}
 
diff --git a/src/p_setup.c b/src/p_setup.c
index 7f6fcd36cd..ab6b68bd11 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -7806,6 +7806,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
 	Patch_FreeTag(PU_PATCH_LOWPRIORITY);
 	Patch_FreeTag(PU_PATCH_ROTATED);
 	Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1);
+	mobjcache = NULL;
 
 	R_InitializeLevelInterpolators();
 
diff --git a/src/p_tick.c b/src/p_tick.c
index 444b68d2fd..c19f901e3e 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -217,6 +217,7 @@ void P_AddThinker(const thinklistnum_t n, thinker_t *thinker)
 	thlist[n].prev = thinker;
 
 	thinker->references = 0;    // killough 11/98: init reference counter to 0
+	thinker->cachable = n == THINK_MOBJ;
 
 #ifdef PARANOIA
 	thinker->debug_mobjtype = MT_NULL;
@@ -319,7 +320,16 @@ void P_RemoveThinkerDelayed(thinker_t *thinker)
 	(next->prev = currentthinker = thinker->prev)->next = next;
 
 	R_DestroyLevelInterpolators(thinker);
-	Z_Free(thinker);
+	if (thinker->cachable)
+	{
+		// put cachable thinkers in the mobj cache, so we can avoid allocations
+		((mobj_t *)thinker)->hnext = mobjcache;
+		mobjcache = (mobj_t *)thinker;
+	}
+	else
+	{
+		Z_Free(thinker);
+	}
 }
 
 //
-- 
GitLab