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 == ¤tEntry) +// 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;