void DThinker::DestroyMostThinkersInList (FThinkerList &list, int stat) { if (stat != STAT_PLAYER) { DestroyThinkersInList (list); } else if (list.Sentinel != NULL) { // If it's a voodoo doll, destroy it. Otherwise, simply remove // it from the list. G_FinishTravel() will find it later from // a players[].mo link and destroy it then, after copying various // information to a new player. for (DThinker *probe = list.Sentinel->NextThinker, *next; probe != list.Sentinel; probe = next) { next = probe->NextThinker; if (!probe->IsKindOf(RUNTIME_CLASS(APlayerPawn)) || // <- should not happen static_cast<AActor *>(probe)->player == NULL || static_cast<AActor *>(probe)->player->mo != probe) { probe->Destroy(); } else { probe->Remove(); // Technically, this doesn't need to be in any list now, since // it's only going to be found later and destroyed before ever // needing to tick again, but by moving it to a separate list, // I can keep my debug assertions that all thinkers are either // euthanizing or in a list. Thinkers[MAX_STATNUM+1].AddTail(probe); } } } }
int DThinker::TickThinkers (FThinkerList *list, FThinkerList *dest) { int count = 0; DThinker *node = list->GetHead(); if (node == NULL) { return 0; } while (node != list->Sentinel) { ++count; NextToThink = node->NextThinker; if (node->ObjectFlags & OF_JustSpawned) { // Leave OF_JustSpawn set until after Tick() so the ticker can check it. if (dest != NULL) { // Move thinker from this list to the destination list node->Remove(); dest->AddTail(node); } node->PostBeginPlay(); } else if (dest != NULL) { // Move thinker from this list to the destination list I_Error("There is a thinker in the fresh list that has already ticked.\n"); } if (!(node->ObjectFlags & OF_EuthanizeMe)) { // Only tick thinkers not scheduled for destruction // [BC] Don't tick the consoleplayer's actor in client // mode, because that's done in the main prediction function if (( NETWORK_InClientMode() == false ) || ( node->IsKindOf( RUNTIME_CLASS( AActor )) == false ) || ( static_cast<AActor *>( node ) != players[consoleplayer].mo )) { node->Tick(); } node->ObjectFlags &= ~OF_JustSpawned; GC::CheckGC(); } node = NextToThink; } return count; }
int DThinker::TickThinkers (FThinkerList *list, FThinkerList *dest) { int count = 0; DThinker *node = list->GetHead(); if (node == NULL) { return 0; } while (node != list->Sentinel) { ++count; NextToThink = node->NextThinker; if (node->ObjectFlags & OF_JustSpawned) { // Leave OF_JustSpawn set until after Tick() so the ticker can check it. if (dest != NULL) { // Move thinker from this list to the destination list node->Remove(); dest->AddTail(node); } node->PostBeginPlay(); } else if (dest != NULL) { // Move thinker from this list to the destination list I_Error("There is a thinker in the fresh list that has already ticked.\n"); } if (!(node->ObjectFlags & OF_EuthanizeMe)) { // Only tick thinkers not scheduled for destruction node->Tick(); node->ObjectFlags &= ~OF_JustSpawned; GC::CheckGC(); } node = NextToThink; } return count; }
void DThinker::SerializeAll(FArchive &arc, bool hubLoad) { DThinker *thinker; BYTE stat; int statcount; int i; // Save lists of thinkers, but not by storing the first one and letting // the archiver catch the rest. (Which leads to buttloads of recursion // and makes the file larger.) Instead, we explicitly save each thinker // in sequence. When restoring an archive, we also have to maintain // the thinker lists here instead of relying on the archiver to do it // for us. if (arc.IsStoring()) { for (statcount = i = 0; i <= MAX_STATNUM; i++) { statcount += (!Thinkers[i].IsEmpty() || !FreshThinkers[i].IsEmpty()); } arc << statcount; for (i = 0; i <= MAX_STATNUM; i++) { if (!Thinkers[i].IsEmpty() || !FreshThinkers[i].IsEmpty()) { stat = i; arc << stat; SaveList(arc, Thinkers[i].GetHead()); SaveList(arc, FreshThinkers[i].GetHead()); thinker = NULL; arc << thinker; // Save a final NULL for this list } } } else { if (hubLoad) DestroyMostThinkers(); else DestroyAllThinkers(); // Prevent the constructor from inserting thinkers into a list. bSerialOverride = true; try { arc << statcount; while (statcount > 0) { arc << stat << thinker; while (thinker != NULL) { // This may be a player stored in their ancillary list. Remove // them first before inserting them into the new list. if (thinker->NextThinker != NULL) { thinker->Remove(); } // Thinkers with the OF_JustSpawned flag set go in the FreshThinkers // list. Anything else goes in the regular Thinkers list. if (thinker->ObjectFlags & OF_EuthanizeMe) { // This thinker was destroyed during the loading process. Do // not link it in to any list. } else if (thinker->ObjectFlags & OF_JustSpawned) { FreshThinkers[stat].AddTail(thinker); } else { Thinkers[stat].AddTail(thinker); } arc << thinker; } statcount--; } } catch (class CDoomError &err) { bSerialOverride = false; // DestroyAllThinkers cannot be called here. It will try to delete the corrupted // object table left behind by the serializer and crash. // Trying to continue is not an option here because the garbage collector will // crash the next time it runs. // Even making this a fatal error will crash but at least the message can be seen // before the crash - which is not the case with all other options. //DestroyAllThinkers(); I_FatalError("%s", err.GetMessage()); throw; } bSerialOverride = false; } }
void DThinker::SerializeAll(FArchive &arc, bool hubLoad) { DThinker *thinker; BYTE stat; int statcount; int i; // Save lists of thinkers, but not by storing the first one and letting // the archiver catch the rest. (Which leads to buttloads of recursion // and makes the file larger.) Instead, we explicitly save each thinker // in sequence. When restoring an archive, we also have to maintain // the thinker lists here instead of relying on the archiver to do it // for us. if (arc.IsStoring()) { for (statcount = i = 0; i <= MAX_STATNUM; i++) { statcount += (!Thinkers[i].IsEmpty() || !FreshThinkers[i].IsEmpty()); } arc << statcount; for (i = 0; i <= MAX_STATNUM; i++) { if (!Thinkers[i].IsEmpty() || !FreshThinkers[i].IsEmpty()) { stat = i; arc << stat; SaveList(arc, Thinkers[i].GetHead()); SaveList(arc, FreshThinkers[i].GetHead()); thinker = NULL; arc << thinker; // Save a final NULL for this list } } } else { if (hubLoad) DestroyMostThinkers(); else DestroyAllThinkers(); // Prevent the constructor from inserting thinkers into a list. bSerialOverride = true; try { arc << statcount; while (statcount > 0) { arc << stat << thinker; while (thinker != NULL) { // This may be a player stored in their ancillary list. Remove // them first before inserting them into the new list. if (thinker->NextThinker != NULL) { thinker->Remove(); } // Thinkers with the OF_JustSpawned flag set go in the FreshThinkers // list. Anything else goes in the regular Thinkers list. if (thinker->ObjectFlags & OF_EuthanizeMe) { // This thinker was destroyed during the loading process. Do // not link it in to any list. } else if (thinker->ObjectFlags & OF_JustSpawned) { FreshThinkers[stat].AddTail(thinker); } else { Thinkers[stat].AddTail(thinker); } arc << thinker; } statcount--; } } catch (class CDoomError &) { bSerialOverride = false; DestroyAllThinkers(); throw; } bSerialOverride = false; } }