diff --git a/src/Sourcefile b/src/Sourcefile
index 4a81308c850ee706b75d9fb751f09f36b1786938..75023900f7ceb01efadaad4ae73dbff5629cc76d 100755
--- a/src/Sourcefile
+++ b/src/Sourcefile
@@ -42,6 +42,7 @@ p_map.c
 p_maputl.c
 p_mobj.c
 p_polyobj.c
+hashtable.c
 p_saveg.c
 p_savenetrb.c
 p_setup.c
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index f7150ed526bd33603d044b310292e666fd6a71f1..024ba6f0e688e3072e79099ae40b66fa584af103 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -5784,7 +5784,6 @@ void MakeNetDebugString()
 		}
 	}
 
-	static tic_t lastSim = 0;
 	sprintf(&netDebugText[strlen(netDebugText)], "\n\nCanSimulate: %d", canSimulate);
 	sprintf(&netDebugText[strlen(netDebugText)], "\nJitter: %d", serverJitter);
 	sprintf(&netDebugText[strlen(netDebugText)], "\nRTTJitter: %d", rttJitter);
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 282a5b7f9e4df0fe48188bc9a96ead0b74bc071e..8d2df5a1ad0da4d4a9b1da3ea9592ec9b1ec5a81 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -106,9 +106,9 @@ static void TimeFudge_OnChange(void);
 void Command_Autotimefudge(void);
 static void AutoUpdateTimeFudge_OnChange(void);
 
-// #ifdef NETGAME_DEVMODE
+#ifdef NETGAME_DEVMODE
 static void Fishcake_OnChange(void);
-// #endif
+#endif
 
 static void Command_Playdemo_f(void);
 static void Command_Timedemo_f(void);
@@ -203,9 +203,9 @@ static CV_PossibleValue_t pause_cons_t[] = {{0, "Server"}, {1, "All"}, {0, NULL}
 
 consvar_t cv_showinputjoy = CVAR_INIT ("showinputjoy", "Off", 0, CV_OnOff, NULL);
 
-// #ifdef NETGAME_DEVMODE
+#ifdef NETGAME_DEVMODE
 static consvar_t cv_fishcake = CVAR_INIT ("fishcake", "Off", CV_CALL|CV_NOSHOWHELP|CV_RESTRICT, CV_OnOff, Fishcake_OnChange);
-// #endif
+#endif
 static consvar_t cv_dummyconsvar = CVAR_INIT ("dummyconsvar", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, DummyConsvar_OnChange);
 
 consvar_t cv_restrictskinchange = CVAR_INIT ("restrictskinchange", "Yes", CV_SAVE|CV_NETVAR|CV_CHEAT, CV_YesNo, NULL);
@@ -387,7 +387,7 @@ consvar_t cv_simmisstics = { "simmisstics", "Yes", 0, CV_YesNo, NULL, 0, NULL, N
 consvar_t cv_jittersmoothing = { "jittersmoothing", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL };
 
 static CV_PossibleValue_t simulateculldistance_cons_t[] = { {0, "MIN"}, {10000, "MAX"}, {0, NULL} };
-consvar_t cv_simulateculldistance = { "simcull", "MIN", 0, simulateculldistance_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL };
+consvar_t cv_simulateculldistance = { "simcull", "2", 0, simulateculldistance_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL };
 
 static CV_PossibleValue_t siminaccuracy_cons_t[] = { {1, "MIN"}, {10, "MAX"}, {0, NULL} };
 consvar_t cv_siminaccuracy = { "siminaccuracy", "MIN", 0, siminaccuracy_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL };
@@ -846,9 +846,9 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_netticbuffer);
 	CV_RegisterVar(&cv_netsimstat);
 
-// #ifdef NETGAME_DEVMODE
+#ifdef NETGAME_DEVMODE
 	CV_RegisterVar(&cv_fishcake);
-// #endif
+#endif
 
 	// HUD
 	CV_RegisterVar(&cv_timetic);
@@ -4473,7 +4473,7 @@ void Command_Retry_f(void)
 	}
 }
 
-// #ifdef NETGAME_DEVMODE
+#ifdef NETGAME_DEVMODE
 // Allow the use of devmode in netgames.
 static void Fishcake_OnChange(void)
 {
@@ -4488,7 +4488,7 @@ static void Fishcake_OnChange(void)
 	else if (cv_debug != cv_fishcake.value)
 		CV_SetValue(&cv_fishcake, cv_debug);
 }
-// #endif
+#endif
 
 /** Reports to the console whether or not the game has been modified.
   *
diff --git a/src/hashtable.c b/src/hashtable.c
new file mode 100644
index 0000000000000000000000000000000000000000..7b26ad4998e249f27b669435957d5c34036f6ad6
--- /dev/null
+++ b/src/hashtable.c
@@ -0,0 +1,377 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// John FrostFox (john.frostfox@gmail.com)
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  hashtable.c
+/// \brief Hashtable abstraction implementation
+
+#include "hashtable.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define HT_NUMLISTS UINT8_MAX
+
+typedef struct mobjnum_linkedList_s{
+    thinker_t* thinker;
+    struct mobjnum_linkedList_s* prev;
+    struct mobjnum_linkedList_s* next;
+}
+mobjnum_linkedList;
+
+mobjnum_linkedList mobjnum_Hashtable[HT_NUMLISTS]; //dumb and idiotic, there are up to UINT8_MAX linked lists.
+//Wish I could just simply make a mobj_t array with UINT64_MAX entries, LOL
+
+void mobjnum_ht_linkedList_Wipe();
+/** Initializes simple mobjnum hashtable linked list.*/
+void mobjnum_ht_linkedList_Init()
+{
+    mobjnum_ht_linkedList_Wipe();
+    for (uint8_t i = 0; i < HT_NUMLISTS; i++)
+    {
+        mobjnum_Hashtable[i].thinker = NULL;
+        mobjnum_Hashtable[i].prev = NULL;
+        mobjnum_Hashtable[i].next = NULL;
+    }
+    
+}
+
+/** Adds a mobj's address, determines in which list to add automatically
+  *
+  * \param mobj     Which mobj's memory address to add 
+  */
+void mobjnum_ht_linkedList_AddEntry (thinker_t* thinker)
+{
+
+    mobjnum_linkedList* currentEntry; // = &mobjnum_Hashtable[(UINT8)(mobj->mobjnum % HT_NUMLISTS)];
+    mobjnum_linkedList* next;
+
+    currentEntry = &mobjnum_Hashtable[(UINT8)(((mobj_t*)thinker)->mobjnum % HT_NUMLISTS)];
+
+    if (!currentEntry->next) // check for the first entry
+    {
+        currentEntry->thinker = thinker;
+        // CONS_Printf("next not exists, create\n");
+        currentEntry->next = malloc(sizeof(mobjnum_linkedList));
+        currentEntry->next->thinker = NULL;
+        currentEntry->next->next = NULL;
+        currentEntry->next->prev = currentEntry;
+        return;
+    }
+    
+    for (currentEntry = &mobjnum_Hashtable[(UINT8)(((mobj_t*)thinker)->mobjnum % HT_NUMLISTS)]; currentEntry != NULL; currentEntry = next)
+    {
+        if (!currentEntry->thinker)
+        {
+            // CONS_Printf("mobj not exists, adding\n");
+            currentEntry->thinker = thinker;
+            break;
+        }
+        else
+        {
+            // CONS_Printf("mobj exists, go next\n");
+            if (!currentEntry->next)
+            {
+                // CONS_Printf("next not exists, create\n");
+                currentEntry->next = malloc(sizeof(mobjnum_linkedList));
+                currentEntry->next->thinker = NULL;
+                currentEntry->next->next = NULL;
+                currentEntry->next->prev = currentEntry;
+            }
+            else
+                // CONS_Printf("next exists\n");
+            next = currentEntry->next;
+        }
+    }
+}
+
+/** Removes a mobj's address by its mobjnum, determines from which list to remove automatically
+  *
+  * \param mobjnum     Which mobj's memory address to remove by itsmobjnum 
+  */
+// void mobjnum_ht_linkedList_RemoveEntry (uint8_t mobjnumber)
+// {
+//     mobjnum_linkedList* currentEntry = &mobjnum_Hashtable[(UINT8)(mobjnumber % HT_NUMLISTS)];
+//     if (currentEntry->next == &currentEntry)
+//         currentEntry->mobj = &mobj;
+//     else
+//     {
+//         currentEntry->next = malloc(sizeof(mobjnum_linkedList));
+
+//     }
+// }
+
+/** Looks for a mobj by its mobjnum.
+  *
+  * \param mobjnumber mobj's mobjnum
+  * \return mobj_t* if an object is found, otherwise NULL.
+  */
+thinker_t* mobjnum_ht_linkedList_Find (uint32_t mobjnumber)
+{
+    // mobjnum_linkedList* currentEntry = &mobjnum_Hashtable[(UINT8)(mobjnumber % HT_NUMLISTS)];
+
+    mobjnum_linkedList* currentEntry; // = &mobjnum_Hashtable[(UINT8)(mobj->mobjnum % HT_NUMLISTS)];
+    mobjnum_linkedList* next;
+
+    currentEntry = &mobjnum_Hashtable[(UINT8)(mobjnumber % HT_NUMLISTS)];
+
+    if (!currentEntry->next) // check for the first entry
+    {
+        if (!currentEntry->thinker)
+            return NULL;
+
+        if (((mobj_t *)currentEntry->thinker)->mobjnum == mobjnumber)
+            return currentEntry->thinker;
+    }
+
+    for (currentEntry = &mobjnum_Hashtable[(UINT8)(mobjnumber % HT_NUMLISTS)]; currentEntry != NULL; currentEntry = next)
+    {
+        if (!currentEntry->thinker)
+            return NULL;
+
+        if (((mobj_t *)currentEntry->thinker)->mobjnum == mobjnumber)
+            return currentEntry->thinker;
+        else
+        {
+            next = currentEntry->next;
+        }
+    }
+    return NULL;
+}
+
+/** Wipes the entire list by iterating everything it has*/
+void mobjnum_ht_linkedList_Wipe()
+{
+    mobjnum_linkedList* currentEntry; // = &mobjnum_Hashtable[(UINT8)(mobj->mobjnum % HT_NUMLISTS)];
+    mobjnum_linkedList* next;
+
+    for (uint8_t i = 0; i < HT_NUMLISTS; i++)
+    {
+        if (!mobjnum_Hashtable[i].next)
+            continue;
+        for (currentEntry = &mobjnum_Hashtable[i]; currentEntry != NULL; currentEntry = next)
+        {
+            // bye!
+            if (currentEntry->next)
+                next = currentEntry->next;
+            else
+                next = NULL;
+            if (currentEntry->next)
+            {
+                currentEntry->next = NULL;
+                // CONS_Printf("Has Next\n");
+            }
+            if (currentEntry->thinker)
+            {
+                // CONS_Printf("Has mobj\n");
+                currentEntry->thinker = NULL;
+            }
+            if (currentEntry->prev)
+            {
+                // CONS_Printf("Has Prev\n");
+                currentEntry->prev = NULL;
+                free(currentEntry);
+            }
+        }
+    }
+    return;
+}
+
+// Simple hash table implemented in C.
+
+// Hash table entry (slot may be filled or empty).
+typedef struct {
+    const char* key;  // key is NULL if this slot is empty
+    void* value;
+} ht_entry;
+
+// Hash table structure: create with ht_create, free with ht_destroy.
+struct hashtable {
+    ht_entry* entries;  // hash slots
+    size_t capacity;    // size of _entries array
+    size_t length;      // number of items in hash table
+};
+
+#define INITIAL_CAPACITY 16  // must not be zero
+
+hashtable* hashtable_Create(void) {
+    // Allocate space for hash table struct.
+    hashtable* table = malloc(sizeof(hashtable));
+    if (table == NULL) {
+        return NULL;
+    }
+    table->length = 0;
+    table->capacity = INITIAL_CAPACITY;
+
+    // Allocate (zero'd) space for entry buckets.
+    table->entries = calloc(table->capacity, sizeof(ht_entry));
+    if (table->entries == NULL) {
+        free(table); // error, free table before we return!
+        return NULL;
+    }
+    return table;
+}
+
+void hashtable_Destroy(hashtable* table) {
+    // First free allocated keys.
+    for (size_t i = 0; i < table->capacity; i++) {
+        if (table->entries[i].key != NULL) {
+            free((void*)table->entries[i].key);
+        }
+    }
+
+    // Then free entries array and table itself.
+    free(table->entries);
+    free(table);
+}
+
+#define FNV_OFFSET 14695981039346656037UL
+#define FNV_PRIME 1099511628211UL
+
+// Return 64-bit FNV-1a hash for key (NUL-terminated). See description:
+// https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function
+static uint64_t hash_key(const char* key) {
+    uint64_t hash = FNV_OFFSET;
+    for (const char* p = key; *p; p++) {
+        hash ^= (uint64_t)(unsigned char)(*p);
+        hash *= FNV_PRIME;
+    }
+    return hash;
+}
+
+void* hashtable_Get(hashtable* table, const UINT32* key) {
+    // AND hash with capacity-1 to ensure it's within entries array.
+    uint64_t hash = hash_key(key);
+    size_t index = (size_t)(hash & (uint64_t)(table->capacity - 1));
+
+    // Loop till we find an empty entry.
+    while (table->entries[index].key != NULL) {
+        if (strcmp(key, table->entries[index].key) == 0) {
+            // Found key, return value.
+            return table->entries[index].value;
+        }
+        // Key wasn't in this slot, move to next (linear probing).
+        index++;
+        if (index >= table->capacity) {
+            // At end of entries array, wrap around.
+            index = 0;
+        }
+    }
+    return NULL;
+}
+
+// Internal function to set an entry (without expanding table).
+static const char* ht_set_entry(ht_entry* entries, size_t capacity,
+        const char* key, void* value, size_t* plength) {
+    // AND hash with capacity-1 to ensure it's within entries array.
+    uint64_t hash = hash_key(key);
+    size_t index = (size_t)(hash & (uint64_t)(capacity - 1));
+
+    // Loop till we find an empty entry.
+    while (entries[index].key != NULL) {
+        if (strcmp(key, entries[index].key) == 0) {
+            // Found key (it already exists), update value.
+            entries[index].value = value;
+            return entries[index].key;
+        }
+        // Key wasn't in this slot, move to next (linear probing).
+        index++;
+        if (index >= capacity) {
+            // At end of entries array, wrap around.
+            index = 0;
+        }
+    }
+
+    // Didn't find key, allocate+copy if needed, then insert it.
+    if (plength != NULL) {
+        key = strdup(key);
+        if (key == NULL) {
+            return NULL;
+        }
+        (*plength)++;
+    }
+    entries[index].key = (char*)key;
+    entries[index].value = value;
+    return key;
+}
+
+// Expand hash table to twice its current size. Return true on success,
+// false if out of memory.
+static boolean ht_expand(hashtable* table) {
+    // Allocate new entries array.
+    size_t new_capacity = table->capacity * 2;
+    if (new_capacity < table->capacity) {
+        return false;  // overflow (capacity would be too big)
+    }
+    ht_entry* new_entries = calloc(new_capacity, sizeof(ht_entry));
+    if (new_entries == NULL) {
+        return false;
+    }
+
+    // Iterate entries, move all non-empty ones to new table's entries.
+    for (size_t i = 0; i < table->capacity; i++) {
+        ht_entry entry = table->entries[i];
+        if (entry.key != NULL) {
+            ht_set_entry(new_entries, new_capacity, entry.key,
+                         entry.value, NULL);
+        }
+    }
+
+    // Free old entries array and update this table's details.
+    free(table->entries);
+    table->entries = new_entries;
+    table->capacity = new_capacity;
+    return true;
+}
+
+const char* hashtable_Set(hashtable* table, const UINT32* key, void* value) {
+    assert(value != NULL);
+    if (value == NULL) {
+        return NULL;
+    }
+
+    // If length will exceed half of current capacity, expand it.
+    if (table->length >= table->capacity / 2) {
+        if (!ht_expand(table)) {
+            return NULL;
+        }
+    }
+
+    // Set entry and update length.
+    return ht_set_entry(table->entries, table->capacity, key, value,
+                        &table->length);
+}
+
+size_t hashtable_Length(hashtable* table) {
+    return table->length;
+}
+
+hashtable_iterator ht_iterator(hashtable* table) {
+    hashtable_iterator it;
+    it._table = table;
+    it._index = 0;
+    return it;
+}
+
+boolean hashtable_Next(hashtable_iterator* it) {
+    // Loop till we've hit end of entries array.
+    hashtable* table = it->_table;
+    while (it->_index < table->capacity) {
+        size_t i = it->_index;
+        it->_index++;
+        if (table->entries[i].key != NULL) {
+            // Found next non-empty item, update iterator key and value.
+            ht_entry entry = table->entries[i];
+            it->key = entry.key;
+            it->value = entry.value;
+            return true;
+        }
+    }
+    return false;
+}
\ No newline at end of file
diff --git a/src/hashtable.h b/src/hashtable.h
new file mode 100644
index 0000000000000000000000000000000000000000..c32a19fb0b6252a5c5cfa5c7ad909b2bcf0cc434
--- /dev/null
+++ b/src/hashtable.h
@@ -0,0 +1,56 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// John FrostFox (john.frostfox@gmail.com)
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  hashtable.h
+/// \brief Hashtable abstraction
+
+#include "doomdef.h"
+#include "p_mobj.h"
+#include "d_think.h"
+
+// extern uint16_t	 hashHits;
+// extern uint16_t	 hashMiss;
+
+void mobjnum_ht_linkedList_Init();
+void mobjnum_ht_linkedList_AddEntry (thinker_t* thinker);
+thinker_t* mobjnum_ht_linkedList_Find (uint32_t mobjnumber);
+// void mobjnum_ht_linkedList_Wipe();
+
+// Hash table iterator: create with ht_iterator, iterate with ht_next.
+typedef struct hashtable hashtable;
+
+typedef struct {
+    const UINT32* key;
+    void* value;
+
+    hashtable* _table; // reference to hash table being iterated
+    size_t _index;     // current index into ht._entries
+} hashtable_iterator;
+
+hashtable* hashtable_Create(void); //creates a hashtable and returns the pointer
+void* hashtable_Get(hashtable* table, const UINT32* key);
+
+// Set item with given key (NUL-terminated) to value (which must not
+// be NULL). If not already present in table, key is copied to newly
+// allocated memory (keys are freed automatically when ht_destroy is
+// called). Return address of copied key, or NULL if out of memory.
+// const char* hashtable_Set(hashtable* table, UINT32* key, void* value);
+const char* hashtable_Set(hashtable* table, const UINT32* key, void* value);
+
+size_t hashtable_Length(hashtable* table);
+
+// Free memory allocated for hash table, including allocated keys.
+void hashtable_Destroy(hashtable* table);
+
+// Return new hash table iterator (for use with ht_next).
+hashtable_iterator hashtable_Iterator(hashtable_iterator* table);
+
+// Move iterator to next item in hash table, update iterator's key
+// and value to current item, and return true. If there are no more
+// items, return false. Don't call ht_set during iteration.
+boolean hashtable_Next(hashtable_iterator* it);
\ No newline at end of file
diff --git a/src/lua_script.c b/src/lua_script.c
index aa1db3aebaa78c3ac75bcdc3c5db19ed2f34bdac..d32c9c611d796b242b92669dd88408fe7f377f48 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -37,6 +37,8 @@
 #include "doomstat.h"
 #include "g_state.h"
 
+#include "hashtable.h"
+
 lua_State *gL = NULL;
 
 // List of internal libraries to load from SRB2
@@ -1655,17 +1657,46 @@ void LUA_UnArchive(void)
 		UnArchiveExtVars(&players[i]);
 	}
 
-	do {
-		mobjnum = READUINT32(save_p); // read a mobjnum
-		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
+	mobjnum = READUINT32(save_p); // read a mobjnum
+	while(mobjnum != UINT32_MAX) // repeat until end of mobjs marker.
+	{
+		th = mobjnum_ht_linkedList_Find(mobjnum);
+		if (th && ((mobj_t *)th)->mobjnum == mobjnum)
 		{
-			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
-				continue;
-			if (((mobj_t *)th)->mobjnum != mobjnum) // find matching mobj
-				continue;
-			UnArchiveExtVars(th); // apply variables
+			// hashHits++;
+			UnArchiveExtVars(th);
 		}
-	} while(mobjnum != UINT32_MAX); // repeat until end of mobjs marker.
+		else
+		{
+			// hashmiss++;
+			
+			for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
+			{
+				if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+					continue;
+				if (((mobj_t *)th)->mobjnum != mobjnum) // find matching mobj
+					continue;
+				UnArchiveExtVars(th); // apply variables
+			}
+		}
+		mobjnum = READUINT32(save_p); // read a mobjnum
+	} 
+	
+	// mobjnum = READUINT32(save_p);
+	// while(mobjnum != UINT32_MAX) // repeat until end of mobjs marker.
+	// {
+	// 	{
+	// 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
+	// 		{
+	// 			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+	// 				continue;
+	// 			if (((mobj_t *)th)->mobjnum != mobjnum) // find matching mobj
+	// 				continue;
+	// 			UnArchiveExtVars(th); // apply variables
+	// 		}
+	// 	}
+	// 	mobjnum = READUINT32(save_p); // read a mobjnum
+	// }
 
 	LUAh_NetArchiveHook(NetUnArchive); // call the NetArchive hook in unarchive mode
 	UnArchiveTables();
diff --git a/src/p_savenetrb.c b/src/p_savenetrb.c
index 04822b1e38d091bef5c98f419c1e403df60659ab..34bd7bdf46836e381ae959008b9e6da96716e5e0 100755
--- a/src/p_savenetrb.c
+++ b/src/p_savenetrb.c
@@ -39,10 +39,7 @@
 #include "p_setup.h"
 #include "p_slopes.h"
 #include "console.h"
-
-
-
-
+#include "hashtable.h"
 
 // Block UINT32s to attempt to ensure that the correct data is
 // being preserved
@@ -69,6 +66,27 @@ typedef enum
 	DRONE      = 0x80,
 } player_saveflags;
 
+mobj_t *P_FindNewPosition_Hashtable(UINT32 oldposition)
+{
+	thinker_t *th;
+	mobj_t *mobj;
+	th = mobjnum_ht_linkedList_Find(oldposition);
+	if (th && ((mobj_t *)th)->mobjnum == oldposition && !(th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed))
+		return (mobj_t *)th;
+	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
+	{
+		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+			continue;
+
+		mobj = (mobj_t *)th;
+		if (mobj->mobjnum != oldposition)
+			continue;
+		return mobj;
+	}
+	CONS_Debug(DBG_GAMELOGIC, "mobj not found\n");
+	return NULL;
+}
+
 static inline void P_ArchivePlayer(void)
 {
 	const player_t *player = &players[consoleplayer];
@@ -808,7 +826,7 @@ static void P_NetUnArchiveWaypoints(void)
 		for (j = 0; j < numwaypoints[i]; j++)
 		{
 			mobjnum = READUINT32(save_p);
-			waypoints[i][j] = (mobjnum == 0) ? NULL : P_FindNewPosition(mobjnum);
+			waypoints[i][j] = (mobjnum == 0) ? NULL : P_FindNewPosition_Hashtable(mobjnum);
 		}
 	}
 }
@@ -4881,7 +4899,11 @@ static void P_NetUnArchiveThinkers(void)
 					I_Error("P_UnarchiveSpecials: Unknown tclass %d in savegame", tclass);
 			}
 			if (th)
+			{
 				P_AddThinker(i, th);
+				if (i == THINK_MOBJ && tclass == tc_mobj)
+					mobjnum_ht_linkedList_AddEntry(th);
+			}
 		}
 
 		CONS_Debug(DBG_NETPLAY, "%u thinkers loaded in list %d\n", numloaded, i);
@@ -4898,7 +4920,7 @@ static void P_NetUnArchiveThinkers(void)
 			delay = (void *)currentthinker;
 			if (!(mobjnum = (UINT32)(size_t)delay->caller))
 				continue;
-			delay->caller = P_FindNewPosition(mobjnum);
+			delay->caller = P_FindNewPosition_Hashtable(mobjnum);
 		}
 	}
 }
@@ -5061,70 +5083,70 @@ static void P_RelinkPointers(void)
 		{
 			temp = (UINT32)(size_t)mobj->tracer;
 			mobj->tracer = NULL;
-			if (!P_SetTarget(&mobj->tracer, P_FindNewPosition(temp)))
+			if (!P_SetTarget(&mobj->tracer, P_FindNewPosition_Hashtable(temp)))
 				CONS_Debug(DBG_GAMELOGIC, "tracer not found on %d\n", mobj->type);
 		}
 		if (mobj->target)
 		{
 			temp = (UINT32)(size_t)mobj->target;
 			mobj->target = NULL;
-			if (!P_SetTarget(&mobj->target, P_FindNewPosition(temp)))
+			if (!P_SetTarget(&mobj->target, P_FindNewPosition_Hashtable(temp)))
 				CONS_Debug(DBG_GAMELOGIC, "target not found on %d\n", mobj->type);
 		}
 		if (mobj->hnext)
 		{
 			temp = (UINT32)(size_t)mobj->hnext;
 			mobj->hnext = NULL;
-			if (!(mobj->hnext = P_FindNewPosition(temp)))
+			if (!(mobj->hnext = P_FindNewPosition_Hashtable(temp)))
 				CONS_Debug(DBG_GAMELOGIC, "hnext not found on %d\n", mobj->type);
 		}
 		if (mobj->hprev)
 		{
 			temp = (UINT32)(size_t)mobj->hprev;
 			mobj->hprev = NULL;
-			if (!(mobj->hprev = P_FindNewPosition(temp)))
+			if (!(mobj->hprev = P_FindNewPosition_Hashtable(temp)))
 				CONS_Debug(DBG_GAMELOGIC, "hprev not found on %d\n", mobj->type);
 		}
 		if (mobj->player && mobj->player->capsule)
 		{
 			temp = (UINT32)(size_t)mobj->player->capsule;
 			mobj->player->capsule = NULL;
-			if (!P_SetTarget(&mobj->player->capsule, P_FindNewPosition(temp)))
+			if (!P_SetTarget(&mobj->player->capsule, P_FindNewPosition_Hashtable(temp)))
 				CONS_Debug(DBG_GAMELOGIC, "capsule not found on %d\n", mobj->type);
 		}
 		if (mobj->player && mobj->player->axis1)
 		{
 			temp = (UINT32)(size_t)mobj->player->axis1;
 			mobj->player->axis1 = NULL;
-			if (!P_SetTarget(&mobj->player->axis1, P_FindNewPosition(temp)))
+			if (!P_SetTarget(&mobj->player->axis1, P_FindNewPosition_Hashtable(temp)))
 				CONS_Debug(DBG_GAMELOGIC, "axis1 not found on %d\n", mobj->type);
 		}
 		if (mobj->player && mobj->player->axis2)
 		{
 			temp = (UINT32)(size_t)mobj->player->axis2;
 			mobj->player->axis2 = NULL;
-			if (!P_SetTarget(&mobj->player->axis2, P_FindNewPosition(temp)))
+			if (!P_SetTarget(&mobj->player->axis2, P_FindNewPosition_Hashtable(temp)))
 				CONS_Debug(DBG_GAMELOGIC, "axis2 not found on %d\n", mobj->type);
 		}
 		if (mobj->player && mobj->player->awayviewmobj)
 		{
 			temp = (UINT32)(size_t)mobj->player->awayviewmobj;
 			mobj->player->awayviewmobj = NULL;
-			if (!P_SetTarget(&mobj->player->awayviewmobj, P_FindNewPosition(temp)))
+			if (!P_SetTarget(&mobj->player->awayviewmobj, P_FindNewPosition_Hashtable(temp)))
 				CONS_Debug(DBG_GAMELOGIC, "awayviewmobj not found on %d\n", mobj->type);
 		}
 		if (mobj->player && mobj->player->followmobj)
 		{
 			temp = (UINT32)(size_t)mobj->player->followmobj;
 			mobj->player->followmobj = NULL;
-			if (!P_SetTarget(&mobj->player->followmobj, P_FindNewPosition(temp)))
+			if (!P_SetTarget(&mobj->player->followmobj, P_FindNewPosition_Hashtable(temp)))
 				CONS_Debug(DBG_GAMELOGIC, "followmobj not found on %d\n", mobj->type);
 		}
 		if (mobj->player && mobj->player->drone)
 		{
 			temp = (UINT32)(size_t)mobj->player->drone;
 			mobj->player->drone = NULL;
-			if (!P_SetTarget(&mobj->player->drone, P_FindNewPosition(temp)))
+			if (!P_SetTarget(&mobj->player->drone, P_FindNewPosition_Hashtable(temp)))
 				CONS_Debug(DBG_GAMELOGIC, "drone not found on %d\n", mobj->type);
 		}
 	}
@@ -5877,6 +5899,9 @@ boolean P_LoadGameState(const savestate_t* savestate)
     INT16 savedGameMap;
 	precise_t currentTime;
 	loadStateBenchmark = I_GetPreciseTime();
+
+	mobjnum_ht_linkedList_Init();
+
 	if (savestate->buffer == NULL)
 	{
 		loadStateBenchmark = I_GetPreciseTime() - loadStateBenchmark;