Fix #14848: incorrect tile element limit calculation

This commit is contained in:
Duncan 2021-06-10 12:22:31 +01:00 committed by GitHub
parent f9ad9ad3a5
commit 6b1141b5a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 36 additions and 19 deletions

View File

@ -119,6 +119,8 @@ static TilePointerIndex<TileElement> _tileIndex;
static std::vector<TileElement> _tileElements; static std::vector<TileElement> _tileElements;
static TilePointerIndex<TileElement> _tileIndexStash; static TilePointerIndex<TileElement> _tileIndexStash;
static std::vector<TileElement> _tileElementsStash; static std::vector<TileElement> _tileElementsStash;
static size_t _tileElementsInUse;
static size_t _tileElementsInUseStash;
static int32_t _mapSizeUnitsStash; static int32_t _mapSizeUnitsStash;
static int32_t _mapSizeMinus2Stash; static int32_t _mapSizeMinus2Stash;
static int32_t _mapSizeStash; static int32_t _mapSizeStash;
@ -132,6 +134,7 @@ void StashMap()
_mapSizeMinus2Stash = gMapSizeMinus2; _mapSizeMinus2Stash = gMapSizeMinus2;
_mapSizeStash = gMapSize; _mapSizeStash = gMapSize;
_currentRotationStash = gCurrentRotation; _currentRotationStash = gCurrentRotation;
_tileElementsInUseStash = _tileElementsInUse;
} }
void UnstashMap() void UnstashMap()
@ -142,6 +145,7 @@ void UnstashMap()
gMapSizeMinus2 = _mapSizeMinus2Stash; gMapSizeMinus2 = _mapSizeMinus2Stash;
gMapSize = _mapSizeStash; gMapSize = _mapSizeStash;
gCurrentRotation = _currentRotationStash; gCurrentRotation = _currentRotationStash;
_tileElementsInUse = _tileElementsInUseStash;
} }
const std::vector<TileElement>& GetTileElements() const std::vector<TileElement>& GetTileElements()
@ -153,6 +157,7 @@ void SetTileElements(std::vector<TileElement>&& tileElements)
{ {
_tileElements = std::move(tileElements); _tileElements = std::move(tileElements);
_tileIndex = TilePointerIndex<TileElement>(MAXIMUM_MAP_SIZE_TECHNICAL, _tileElements.data()); _tileIndex = TilePointerIndex<TileElement>(MAXIMUM_MAP_SIZE_TECHNICAL, _tileElements.data());
_tileElementsInUse = _tileElements.size();
} }
static void ReorganiseTileElements(size_t capacity) static void ReorganiseTileElements(size_t capacity)
@ -199,27 +204,39 @@ void ReorganiseTileElements()
ReorganiseTileElements(_tileElements.size()); ReorganiseTileElements(_tileElements.size());
} }
static bool map_check_free_elements_and_reorganise(size_t numElements) static bool map_check_free_elements_and_reorganise(size_t numElementsOnTile, size_t numNewElements)
{ {
// Check hard cap on num in use tiles (this would be the size of _tileElements immediately after a reorg)
if (_tileElementsInUse + numNewElements > MAX_TILE_ELEMENTS)
{
gGameCommandErrorText = STR_ERR_LANDSCAPE_DATA_AREA_FULL;
return false;
}
auto totalElementsRequired = numElementsOnTile + numNewElements;
auto freeElements = _tileElements.capacity() - _tileElements.size(); auto freeElements = _tileElements.capacity() - _tileElements.size();
if (freeElements >= numElements) if (freeElements >= totalElementsRequired)
{ {
return true; return true;
} }
else else
{ {
auto newCapacity = std::min<size_t>(MAX_TILE_ELEMENTS, _tileElements.capacity() * 2); // if space issue is due to fragmentation then Reorg Tiles without increasing capacity
if (newCapacity - _tileElements.size() < numElements) if (_tileElements.size() > totalElementsRequired + _tileElementsInUse)
{ {
// Limit reached ReorganiseTileElements();
gGameCommandErrorText = STR_ERR_LANDSCAPE_DATA_AREA_FULL; // This check is not expected to fail
return false; freeElements = _tileElements.capacity() - _tileElements.size();
} if (freeElements >= totalElementsRequired)
else {
{ return true;
ReorganiseTileElements(newCapacity); }
return true;
} }
// Capacity must increase to handle the space (Note capacity can go above MAX_TILE_ELEMENTS)
auto newCapacity = _tileElements.capacity() * 2;
ReorganiseTileElements(newCapacity);
return true;
} }
} }
@ -228,7 +245,7 @@ static size_t CountElementsOnTile(const CoordsXY& loc);
bool MapCheckCapacityAndReorganise(const CoordsXY& loc, size_t numElements) bool MapCheckCapacityAndReorganise(const CoordsXY& loc, size_t numElements)
{ {
auto numElementsOnTile = CountElementsOnTile(loc); auto numElementsOnTile = CountElementsOnTile(loc);
return map_check_free_elements_and_reorganise(numElementsOnTile + numElements); return map_check_free_elements_and_reorganise(numElementsOnTile, numElements);
} }
static void clear_elements_at(const CoordsXY& loc); static void clear_elements_at(const CoordsXY& loc);
@ -1011,7 +1028,7 @@ void tile_element_remove(TileElement* tileElement)
// Mark the latest element with the last element flag. // Mark the latest element with the last element flag.
(tileElement - 1)->SetLastForTile(true); (tileElement - 1)->SetLastForTile(true);
tileElement->base_height = MAX_ELEMENT_HEIGHT; tileElement->base_height = MAX_ELEMENT_HEIGHT;
_tileElementsInUse--;
if (tileElement == &_tileElements.back()) if (tileElement == &_tileElements.back())
{ {
_tileElements.pop_back(); _tileElements.pop_back();
@ -1127,16 +1144,17 @@ static size_t CountElementsOnTile(const CoordsXY& loc)
return count; return count;
} }
static TileElement* AllocateTileElements(size_t count) static TileElement* AllocateTileElements(size_t numElementsOnTile, size_t numNewElements)
{ {
if (!map_check_free_elements_and_reorganise(count)) if (!map_check_free_elements_and_reorganise(numElementsOnTile, numNewElements))
{ {
log_error("Cannot insert new element"); log_error("Cannot insert new element");
return nullptr; return nullptr;
} }
auto oldSize = _tileElements.size(); auto oldSize = _tileElements.size();
_tileElements.resize(_tileElements.size() + count); _tileElements.resize(_tileElements.size() + numElementsOnTile + numNewElements);
_tileElementsInUse += numNewElements;
return &_tileElements[oldSize]; return &_tileElements[oldSize];
} }
@ -1149,8 +1167,7 @@ TileElement* tile_element_insert(const CoordsXYZ& loc, int32_t occupiedQuadrants
const auto& tileLoc = TileCoordsXYZ(loc); const auto& tileLoc = TileCoordsXYZ(loc);
auto numElementsOnTileOld = CountElementsOnTile(loc); auto numElementsOnTileOld = CountElementsOnTile(loc);
auto numElementsOnTileNew = numElementsOnTileOld + 1; auto* newTileElement = AllocateTileElements(numElementsOnTileOld, 1);
auto* newTileElement = AllocateTileElements(numElementsOnTileNew);
auto* originalTileElement = _tileIndex.GetFirstElementAt(tileLoc); auto* originalTileElement = _tileIndex.GetFirstElementAt(tileLoc);
if (newTileElement == nullptr) if (newTileElement == nullptr)
{ {