void CUnitHandler::Update() { { GML_STDMUTEX_LOCK(runit); // Update if (!unitsToBeRemoved.empty()) { GML_RECMUTEX_LOCK(obj); // Update while (!unitsToBeRemoved.empty()) { eventHandler.DeleteSyncedObjects(); // the unit destructor may invoke eventHandler, so we need to call these for every unit to clear invaild references from the batching systems GML_RECMUTEX_LOCK(unit); // Update eventHandler.DeleteSyncedUnits(); GML_RECMUTEX_LOCK(proj); // Update - projectile drawing may access owner() and lead to crash GML_RECMUTEX_LOCK(sel); // Update - unit is removed from selectedUnits in ~CObject, which is too late. GML_RECMUTEX_LOCK(quad); // Update - make sure unit does not get partially deleted before before being removed from the quadfield CUnit* delUnit = unitsToBeRemoved.back(); unitsToBeRemoved.pop_back(); DeleteUnitNow(delUnit); } } eventHandler.UpdateUnits(); } GML::UpdateTicks(); #define VECTOR_SANITY_CHECK(v) \ assert(!math::isnan(v.x) && !math::isinf(v.x)); \ assert(!math::isnan(v.y) && !math::isinf(v.y)); \ assert(!math::isnan(v.z) && !math::isinf(v.z)); #define MAPPOS_SANITY_CHECK(unit) \ if (unit->unitDef->IsGroundUnit()) { \ assert(unit->pos.x >= -(float3::maxxpos * 16.0f)); \ assert(unit->pos.x <= (float3::maxxpos * 16.0f)); \ assert(unit->pos.z >= -(float3::maxzpos * 16.0f)); \ assert(unit->pos.z <= (float3::maxzpos * 16.0f)); \ } #define UNIT_SANITY_CHECK(unit) \ VECTOR_SANITY_CHECK(unit->pos); \ VECTOR_SANITY_CHECK(unit->midPos); \ VECTOR_SANITY_CHECK(unit->relMidPos); \ VECTOR_SANITY_CHECK(unit->speed); \ VECTOR_SANITY_CHECK(unit->deathSpeed); \ VECTOR_SANITY_CHECK(unit->residualImpulse); \ VECTOR_SANITY_CHECK(unit->rightdir); \ VECTOR_SANITY_CHECK(unit->updir); \ VECTOR_SANITY_CHECK(unit->frontdir); \ MAPPOS_SANITY_CHECK(unit); { SCOPED_TIMER("Unit::MoveType::Update"); std::list<CUnit*>::iterator usi; for (usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) { CUnit* unit = *usi; AMoveType* moveType = unit->moveType; UNIT_SANITY_CHECK(unit); if (moveType->Update()) { eventHandler.UnitMoved(unit); } if (!unit->pos.IsInBounds() && (unit->speed.SqLength() > (MAX_UNIT_SPEED * MAX_UNIT_SPEED))) { // this unit is not coming back, kill it now without any death // sequence (so deathScriptFinished becomes true immediately) unit->KillUnit(NULL, false, true, false); } UNIT_SANITY_CHECK(unit); GML::GetTicks(unit->lastUnitUpdate); } } { SCOPED_TIMER("Unit::Update"); std::list<CUnit*>::iterator usi; for (usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) { CUnit* unit = *usi; UNIT_SANITY_CHECK(unit); if (unit->deathScriptFinished) { // there are many ways to fiddle with "deathScriptFinished", so a unit may // arrive here without having been properly killed (and isDead still false), // which can result in MT deadlocking -- FIXME verify this // (KU returns early if isDead) unit->KillUnit(NULL, false, true); DeleteUnit(unit); } else { unit->Update(); } UNIT_SANITY_CHECK(unit); } } { SCOPED_TIMER("Unit::SlowUpdate"); // reset the iterator every <UNIT_SLOWUPDATE_RATE> frames if ((gs->frameNum & (UNIT_SLOWUPDATE_RATE - 1)) == 0) { activeSlowUpdateUnit = activeUnits.begin(); } // stagger the SlowUpdate's int n = (activeUnits.size() / UNIT_SLOWUPDATE_RATE) + 1; for (; activeSlowUpdateUnit != activeUnits.end() && n != 0; ++activeSlowUpdateUnit) { CUnit* unit = *activeSlowUpdateUnit; UNIT_SANITY_CHECK(unit); unit->SlowUpdate(); UNIT_SANITY_CHECK(unit); n--; } } }
void CUnitHandler::Update() { { if (!unitsToBeRemoved.empty()) { while (!unitsToBeRemoved.empty()) { eventHandler.DeleteSyncedObjects(); // the unit destructor may invoke eventHandler, so we need to call these for every unit to clear invaild references from the batching systems eventHandler.DeleteSyncedUnits(); CUnit* delUnit = unitsToBeRemoved.back(); unitsToBeRemoved.pop_back(); DeleteUnitNow(delUnit); } } eventHandler.UpdateUnits(); } #define MAPPOS_SANITY_CHECK(unit) \ if (unit->unitDef->IsGroundUnit()) { \ assert(unit->pos.x >= -(float3::maxxpos * 16.0f)); \ assert(unit->pos.x <= (float3::maxxpos * 16.0f)); \ assert(unit->pos.z >= -(float3::maxzpos * 16.0f)); \ assert(unit->pos.z <= (float3::maxzpos * 16.0f)); \ } #define UNIT_SANITY_CHECK(unit) \ unit->pos.AssertNaNs(); \ unit->midPos.AssertNaNs(); \ unit->relMidPos.AssertNaNs(); \ unit->speed.AssertNaNs(); \ unit->deathSpeed.AssertNaNs(); \ unit->rightdir.AssertNaNs(); \ unit->updir.AssertNaNs(); \ unit->frontdir.AssertNaNs(); \ MAPPOS_SANITY_CHECK(unit); { SCOPED_TIMER("Unit::MoveType::Update"); for (auto usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) { CUnit* unit = *usi; AMoveType* moveType = unit->moveType; UNIT_SANITY_CHECK(unit); if (moveType->Update()) { eventHandler.UnitMoved(unit); } if (!unit->pos.IsInBounds() && (Square(unit->speed.w) > (MAX_UNIT_SPEED * MAX_UNIT_SPEED))) { // this unit is not coming back, kill it now without any death // sequence (so deathScriptFinished becomes true immediately) unit->KillUnit(NULL, false, true, false); } UNIT_SANITY_CHECK(unit); } } { // Delete dead units for (auto usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) { CUnit* unit = *usi; if (unit->deathScriptFinished) { // there are many ways to fiddle with "deathScriptFinished", so a unit may // arrive here without having been properly killed (and isDead still false), // which can result in MT deadlocking -- FIXME verify this // (KU returns early if isDead) unit->KillUnit(NULL, false, true); DeleteUnit(unit); } } } { SCOPED_TIMER("Unit::UpdatePieceMatrices"); for (auto usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) { // UnitScript only applies piece-space transforms so // we apply the forward kinematics update separately // (only if we have any dirty pieces) CUnit* unit = *usi; unit->localModel->UpdatePieceMatrices(); } } { SCOPED_TIMER("Unit::Update"); for (auto usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) { CUnit* unit = *usi; UNIT_SANITY_CHECK(unit); unit->Update(); UNIT_SANITY_CHECK(unit); } } { SCOPED_TIMER("Unit::SlowUpdate"); // reset the iterator every <UNIT_SLOWUPDATE_RATE> frames if ((gs->frameNum & (UNIT_SLOWUPDATE_RATE - 1)) == 0) { activeSlowUpdateUnit = activeUnits.begin(); } // stagger the SlowUpdate's unsigned int n = (activeUnits.size() / UNIT_SLOWUPDATE_RATE) + 1; for (; activeSlowUpdateUnit != activeUnits.end() && n != 0; ++activeSlowUpdateUnit) { CUnit* unit = *activeSlowUpdateUnit; UNIT_SANITY_CHECK(unit); unit->SlowUpdate(); UNIT_SANITY_CHECK(unit); n--; } } }