Skip to content

Commit 0c0f127

Browse files
committed
Bug #9084: Skip refs without custom data in cell rest/recharge/respawn
CellStore::rest, recharge and respawn iterate over a cell's references and call methods that go through ensureCustomData(), which allocates custom data on every ref they touch. Once a ref has custom data its state gets serialized into the save file, so resting, waiting, or fast traveling in cells with many untouched refs (typical after cell:getAll forces them to load) gradually bloats saves with default-initialized state. Skip refs that have no custom data: they sit at default dynamic stats, have no container to recharge, and were not killed or looted, so these methods would have nothing to do anyway. The container loop in recharge already had this guard; this makes it consistent across all loops and adds a small RefData::hasCustomData() helper to keep the predicate readable.
1 parent 19e8ce3 commit 0c0f127

3 files changed

Lines changed: 28 additions & 9 deletions

File tree

apps/openmw/mwworld/cellstore.cpp

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,18 +1160,20 @@ namespace MWWorld
11601160
{
11611161
if (mState == State_Loaded)
11621162
{
1163+
// Refs with no custom data are at default dynamic stats — there is nothing to restore,
1164+
// and touching them would force-allocate custom data that then bloats the save file.
11631165
for (MWWorld::LiveCellRef<ESM::Creature>& creature : get<ESM::Creature>().mList)
11641166
{
11651167
Ptr ptr = getCurrentPtr(&creature);
1166-
if (!ptr.isEmpty() && ptr.getCellRef().getCount() > 0)
1168+
if (!ptr.isEmpty() && ptr.getCellRef().getCount() > 0 && ptr.getRefData().hasCustomData())
11671169
{
11681170
MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, hours, true);
11691171
}
11701172
}
11711173
for (MWWorld::LiveCellRef<ESM::NPC>& npc : get<ESM::NPC>().mList)
11721174
{
11731175
Ptr ptr = getCurrentPtr(&npc);
1174-
if (!ptr.isEmpty() && ptr.getCellRef().getCount() > 0)
1176+
if (!ptr.isEmpty() && ptr.getCellRef().getCount() > 0 && ptr.getRefData().hasCustomData())
11751177
{
11761178
MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, hours, true);
11771179
}
@@ -1186,26 +1188,28 @@ namespace MWWorld
11861188

11871189
if (mState == State_Loaded)
11881190
{
1191+
// Skip refs that have no custom data — they have no container to recharge,
1192+
// and getContainerStore() would force-allocate custom data and bloat the save file.
11891193
for (MWWorld::LiveCellRef<ESM::Creature>& creature : get<ESM::Creature>().mList)
11901194
{
11911195
Ptr ptr = getCurrentPtr(&creature);
1192-
if (!ptr.isEmpty() && ptr.getCellRef().getCount() > 0)
1196+
if (!ptr.isEmpty() && ptr.getCellRef().getCount() > 0 && ptr.getRefData().hasCustomData())
11931197
{
11941198
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
11951199
}
11961200
}
11971201
for (MWWorld::LiveCellRef<ESM::NPC>& npc : get<ESM::NPC>().mList)
11981202
{
11991203
Ptr ptr = getCurrentPtr(&npc);
1200-
if (!ptr.isEmpty() && ptr.getCellRef().getCount() > 0)
1204+
if (!ptr.isEmpty() && ptr.getCellRef().getCount() > 0 && ptr.getRefData().hasCustomData())
12011205
{
12021206
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
12031207
}
12041208
}
12051209
for (MWWorld::LiveCellRef<ESM::Container>& container : get<ESM::Container>().mList)
12061210
{
12071211
Ptr ptr = getCurrentPtr(&container);
1208-
if (!ptr.isEmpty() && ptr.getRefData().getCustomData() != nullptr && ptr.getCellRef().getCount() > 0
1212+
if (!ptr.isEmpty() && ptr.getRefData().hasCustomData() && ptr.getCellRef().getCount() > 0
12091213
&& ptr.getClass().getContainerStore(ptr).isResolved())
12101214
{
12111215
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
@@ -1229,27 +1233,35 @@ namespace MWWorld
12291233
it != get<ESM::Container>().mList.end(); ++it)
12301234
{
12311235
Ptr ptr = getCurrentPtr(&*it);
1232-
ptr.getClass().respawn(ptr);
1236+
if (ptr.getRefData().hasCustomData())
1237+
ptr.getClass().respawn(ptr);
12331238
}
12341239
}
12351240

1236-
// Actors need to respawn here even if they've been moved to another cell
1241+
// Skip refs with no custom data: they cannot have been killed, looted, or had their inventory
1242+
// generated, so respawn/clearCorpse have no work to do and would only force-allocate custom
1243+
// data that then bloats the save file. Actors need to respawn here even if they've been moved
1244+
// to another cell.
12371245
for (LiveCellRefBase& base : get<ESM::Creature>().mList)
12381246
{
12391247
Ptr ptr = getCurrentPtr(&base);
1248+
if (!ptr.getRefData().hasCustomData())
1249+
continue;
12401250
clearCorpse(ptr, mStore);
12411251
ptr.getClass().respawn(ptr);
12421252
}
12431253
for (LiveCellRefBase& base : get<ESM::NPC>().mList)
12441254
{
12451255
Ptr ptr = getCurrentPtr(&base);
1256+
if (!ptr.getRefData().hasCustomData())
1257+
continue;
12461258
clearCorpse(ptr, mStore);
12471259
ptr.getClass().respawn(ptr);
12481260
}
12491261
for (LiveCellRefBase& base : get<ESM::CreatureLevList>().mList)
12501262
{
12511263
Ptr ptr = getCurrentPtr(&base);
1252-
if (!ptr.mRef->isDeleted())
1264+
if (!ptr.mRef->isDeleted() && ptr.getRefData().hasCustomData())
12531265
ptr.getClass().respawn(ptr);
12541266
}
12551267
for (const auto& [base, _] : mMovedHere)
@@ -1261,7 +1273,7 @@ namespace MWWorld
12611273
case ESM::CreatureLevList::sRecordId:
12621274
{
12631275
MWWorld::Ptr ptr(base, this);
1264-
if (ptr.mRef->isDeleted())
1276+
if (ptr.mRef->isDeleted() || !ptr.getRefData().hasCustomData())
12651277
continue;
12661278
// Remove actors that have been dead a while, but don't belong here and didn't get hit by the
12671279
// logic above

apps/openmw/mwworld/refdata.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,11 @@ namespace MWWorld
269269
return mCustomData.get();
270270
}
271271

272+
bool RefData::hasCustomData() const
273+
{
274+
return mCustomData != nullptr;
275+
}
276+
272277
bool RefData::hasChanged() const
273278
{
274279
return mChanged || !mAnimationState.empty();

apps/openmw/mwworld/refdata.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ namespace MWWorld
135135

136136
const CustomData* getCustomData() const;
137137

138+
bool hasCustomData() const;
139+
138140
bool activate();
139141

140142
bool onActivate();

0 commit comments

Comments
 (0)