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 TilePointerIndex<TileElement> _tileIndexStash;
static std::vector<TileElement> _tileElementsStash;
static size_t _tileElementsInUse;
static size_t _tileElementsInUseStash;
static int32_t _mapSizeUnitsStash;
static int32_t _mapSizeMinus2Stash;
static int32_t _mapSizeStash;
@ -132,6 +134,7 @@ void StashMap()
_mapSizeMinus2Stash = gMapSizeMinus2;
_mapSizeStash = gMapSize;
_currentRotationStash = gCurrentRotation;
_tileElementsInUseStash = _tileElementsInUse;
}
void UnstashMap()
@ -142,6 +145,7 @@ void UnstashMap()
gMapSizeMinus2 = _mapSizeMinus2Stash;
gMapSize = _mapSizeStash;
gCurrentRotation = _currentRotationStash;
_tileElementsInUse = _tileElementsInUseStash;
}
const std::vector<TileElement>& GetTileElements()
@ -153,6 +157,7 @@ void SetTileElements(std::vector<TileElement>&& tileElements)
{
_tileElements = std::move(tileElements);
_tileIndex = TilePointerIndex<TileElement>(MAXIMUM_MAP_SIZE_TECHNICAL, _tileElements.data());
_tileElementsInUse = _tileElements.size();
}
static void ReorganiseTileElements(size_t capacity)
@ -199,27 +204,39 @@ void ReorganiseTileElements()
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();
if (freeElements >= numElements)
if (freeElements >= totalElementsRequired)
{
return true;
}
else
{
auto newCapacity = std::min<size_t>(MAX_TILE_ELEMENTS, _tileElements.capacity() * 2);
if (newCapacity - _tileElements.size() < numElements)
// if space issue is due to fragmentation then Reorg Tiles without increasing capacity
if (_tileElements.size() > totalElementsRequired + _tileElementsInUse)
{
// Limit reached
gGameCommandErrorText = STR_ERR_LANDSCAPE_DATA_AREA_FULL;
return false;
}
else
{
ReorganiseTileElements(newCapacity);
return true;
ReorganiseTileElements();
// This check is not expected to fail
freeElements = _tileElements.capacity() - _tileElements.size();
if (freeElements >= totalElementsRequired)
{
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)
{
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);
@ -1011,7 +1028,7 @@ void tile_element_remove(TileElement* tileElement)
// Mark the latest element with the last element flag.
(tileElement - 1)->SetLastForTile(true);
tileElement->base_height = MAX_ELEMENT_HEIGHT;
_tileElementsInUse--;
if (tileElement == &_tileElements.back())
{
_tileElements.pop_back();
@ -1127,16 +1144,17 @@ static size_t CountElementsOnTile(const CoordsXY& loc)
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");
return nullptr;
}
auto oldSize = _tileElements.size();
_tileElements.resize(_tileElements.size() + count);
_tileElements.resize(_tileElements.size() + numElementsOnTile + numNewElements);
_tileElementsInUse += numNewElements;
return &_tileElements[oldSize];
}
@ -1149,8 +1167,7 @@ TileElement* tile_element_insert(const CoordsXYZ& loc, int32_t occupiedQuadrants
const auto& tileLoc = TileCoordsXYZ(loc);
auto numElementsOnTileOld = CountElementsOnTile(loc);
auto numElementsOnTileNew = numElementsOnTileOld + 1;
auto* newTileElement = AllocateTileElements(numElementsOnTileNew);
auto* newTileElement = AllocateTileElements(numElementsOnTileOld, 1);
auto* originalTileElement = _tileIndex.GetFirstElementAt(tileLoc);
if (newTileElement == nullptr)
{