Remove next_in_quadrant (#13754)

* Use std::vector of quadrants

* Prevent ptr invalidation issues

* Remove next_in_quadrant

* Make review changes

* Rebuild next_in_quadrant for S6Export

* Fix formatting

* Constexpr where possible

* Increment network version and update replays
This commit is contained in:
Duncan 2021-01-23 07:36:46 +00:00 committed by GitHub
parent 3cc283adb2
commit eb52391b9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 139 additions and 99 deletions

View File

@ -44,8 +44,8 @@ set(TITLE_SEQUENCE_SHA1 "304d13a126c15bf2c86ff13b81a2f2cc1856ac8d")
set(OBJECTS_URL "https://github.com/OpenRCT2/objects/releases/download/v1.0.20/objects.zip")
set(OBJECTS_SHA1 "151424d24b1d49a167932b58319bedaa6ec368e9")
set(REPLAYS_URL "https://github.com/OpenRCT2/replays/releases/download/v0.0.29/replays.zip")
set(REPLAYS_SHA1 "B64135F28A79758AD9FCB611625ADDB5C89FB40B")
set(REPLAYS_URL "https://github.com/OpenRCT2/replays/releases/download/v0.0.30/replays.zip")
set(REPLAYS_SHA1 "FD0949B81A7A267EA3A8F96CB02C460C8C21B923")
option(FORCE32 "Force 32-bit build. It will add `-m32` to compiler flags.")
option(WITH_TESTS "Build tests")

View File

@ -48,8 +48,8 @@
<TitleSequencesSha1>304d13a126c15bf2c86ff13b81a2f2cc1856ac8d</TitleSequencesSha1>
<ObjectsUrl>https://github.com/OpenRCT2/objects/releases/download/v1.0.20/objects.zip</ObjectsUrl>
<ObjectsSha1>151424d24b1d49a167932b58319bedaa6ec368e9</ObjectsSha1>
<ReplaysUrl>https://github.com/OpenRCT2/replays/releases/download/v0.0.29/replays.zip</ReplaysUrl>
<ReplaysSha1>B64135F28A79758AD9FCB611625ADDB5C89FB40B</ReplaysSha1>
<ReplaysUrl>https://github.com/OpenRCT2/replays/releases/download/v0.0.30/replays.zip</ReplaysUrl>
<ReplaysSha1>FD0949B81A7A267EA3A8F96CB02C460C8C21B923</ReplaysSha1>
</PropertyGroup>
<ItemGroup>

View File

@ -200,7 +200,6 @@ struct GameStateSnapshots final : public IGameStateSnapshots
const SpriteBase& spriteBase, const SpriteBase& spriteCmp, GameStateSpriteChange_t& changeData) const
{
COMPARE_FIELD(SpriteBase, sprite_identifier);
COMPARE_FIELD(SpriteBase, next_in_quadrant);
COMPARE_FIELD(SpriteBase, linked_list_index);
COMPARE_FIELD(SpriteBase, sprite_index);
COMPARE_FIELD(SpriteBase, flags);

View File

@ -34,7 +34,7 @@
// This string specifies which version of network stream current build uses.
// It is used for making sure only compatible builds get connected, even within
// single OpenRCT2 version.
#define NETWORK_STREAM_VERSION "14"
#define NETWORK_STREAM_VERSION "15"
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
static Peep* _pickup_peep = nullptr;

View File

@ -981,6 +981,27 @@ void S6Exporter::RebuildEntityLinks()
}
}
}
// Rebuild next_in_quadrant linked list entity indexs
for (auto x = 0; x < 255; ++x)
{
for (auto y = 0; y < 255; ++y)
{
uint16_t previous = SPRITE_INDEX_NULL;
for (auto* entity : EntityTileList(TileCoordsXY{ x, y }.ToCoordsXY()))
{
if (previous != SPRITE_INDEX_NULL)
{
_s6.sprites[previous].unknown.next_in_quadrant = entity->sprite_index;
}
previous = entity->sprite_index;
}
if (previous != SPRITE_INDEX_NULL)
{
_s6.sprites[previous].unknown.next_in_quadrant = SPRITE_INDEX_NULL;
}
}
}
}
void S6Exporter::ExportSprite(RCT2Sprite* dst, const rct_sprite* src)
@ -1046,8 +1067,8 @@ constexpr RCT12EntityLinkListOffset GetRCT2LinkListOffset(const SpriteBase* src)
void S6Exporter::ExportSpriteCommonProperties(RCT12SpriteBase* dst, const SpriteBase* src)
{
dst->sprite_identifier = src->sprite_identifier;
dst->next_in_quadrant = src->next_in_quadrant;
dst->linked_list_type_offset = GetRCT2LinkListOffset(src);
dst->next_in_quadrant = SPRITE_INDEX_NULL;
dst->sprite_height_negative = src->sprite_height_negative;
dst->sprite_index = src->sprite_index;
dst->flags = src->flags;

View File

@ -1656,7 +1656,6 @@ public:
void ImportSpriteCommonProperties(SpriteBase* dst, const RCT12SpriteBase* src)
{
dst->sprite_identifier = src->sprite_identifier;
dst->next_in_quadrant = src->next_in_quadrant;
dst->linked_list_index = static_cast<EntityListId>(EnumValue(src->linked_list_type_offset) >> 1);
dst->sprite_height_negative = src->sprite_height_negative;
dst->sprite_index = src->sprite_index;

View File

@ -397,16 +397,20 @@ CoordsXY footpath_bridge_get_info_from_pos(const ScreenCoordsXY& screenCoords, i
*/
void footpath_remove_litter(const CoordsXYZ& footpathPos)
{
auto quad = EntityTileList<Litter>(footpathPos);
for (auto litter : quad)
std::vector<Litter*> removals;
for (auto litter : EntityTileList<Litter>(footpathPos))
{
int32_t distanceZ = abs(litter->z - footpathPos.z);
if (distanceZ <= 32)
{
litter->Invalidate();
sprite_remove(litter);
removals.push_back(litter);
}
}
for (auto* litter : removals)
{
litter->Invalidate();
sprite_remove(litter);
}
}
/**

View File

@ -24,13 +24,14 @@
#include <algorithm>
#include <cmath>
#include <iterator>
#include <vector>
static rct_sprite _spriteList[MAX_SPRITES];
static std::array<std::list<uint16_t>, EnumValue(EntityListId::Count)> gEntityLists;
static bool _spriteFlashingList[MAX_SPRITES];
uint16_t gSpriteSpatialIndex[SPATIAL_INDEX_SIZE];
static std::array<std::vector<uint16_t>, SPATIAL_INDEX_SIZE> gSpriteSpatialIndex;
const rct_string_id litterNames[12] = { STR_LITTER_VOMIT,
STR_LITTER_VOMIT,
@ -45,7 +46,25 @@ const rct_string_id litterNames[12] = { STR_LITTER_VOMIT,
STR_SHOP_ITEM_SINGULAR_EMPTY_JUICE_CUP,
STR_SHOP_ITEM_SINGULAR_EMPTY_BOWL_BLUE };
static size_t GetSpatialIndexOffset(int32_t x, int32_t y);
constexpr size_t GetSpatialIndexOffset(int32_t x, int32_t y)
{
size_t index = SPATIAL_INDEX_LOCATION_NULL;
if (x != LOCATION_NULL)
{
x = std::clamp(x, 0, 0xFFFF);
y = std::clamp(y, 0, 0xFFFF);
int16_t flooredX = floor2(x, 32);
uint8_t tileY = y >> 5;
index = (flooredX << 3) | tileY;
}
if (index >= sizeof(gSpriteSpatialIndex))
{
return SPATIAL_INDEX_LOCATION_NULL;
}
return index;
}
// Required for GetEntity to return a default
template<> bool SpriteBase::Is<SpriteBase>() const
@ -116,7 +135,7 @@ SpriteBase* get_sprite(size_t spriteIndex)
return try_get_sprite(spriteIndex);
}
uint16_t sprite_get_first_in_quadrant(const CoordsXY& spritePos)
const std::vector<uint16_t>& GetEntityTileList(const CoordsXY& spritePos)
{
return gSpriteSpatialIndex[GetSpatialIndexOffset(spritePos.x, spritePos.y)];
}
@ -242,6 +261,8 @@ void reset_sprite_list()
reset_sprite_spatial_index();
}
static void SpriteSpatialInsert(SpriteBase* sprite, const CoordsXY& newLoc);
/**
*
* rct2: 0x0069EBE4
@ -250,40 +271,20 @@ void reset_sprite_list()
*/
void reset_sprite_spatial_index()
{
std::fill_n(gSpriteSpatialIndex, std::size(gSpriteSpatialIndex), SPRITE_INDEX_NULL);
for (auto& vec : gSpriteSpatialIndex)
{
vec.clear();
}
for (size_t i = 0; i < MAX_SPRITES; i++)
{
auto* spr = GetEntity(i);
if (spr != nullptr && spr->sprite_identifier != SpriteIdentifier::Null)
{
size_t index = GetSpatialIndexOffset(spr->x, spr->y);
uint32_t nextSpriteId = gSpriteSpatialIndex[index];
gSpriteSpatialIndex[index] = spr->sprite_index;
spr->next_in_quadrant = nextSpriteId;
SpriteSpatialInsert(spr, { spr->x, spr->y });
}
}
}
static size_t GetSpatialIndexOffset(int32_t x, int32_t y)
{
size_t index = SPATIAL_INDEX_LOCATION_NULL;
if (x != LOCATION_NULL)
{
x = std::clamp(x, 0, 0xFFFF);
y = std::clamp(y, 0, 0xFFFF);
int16_t flooredX = floor2(x, 32);
uint8_t tileY = y >> 5;
index = (flooredX << 3) | tileY;
}
if (index >= sizeof(gSpriteSpatialIndex))
{
return SPATIAL_INDEX_LOCATION_NULL;
}
return index;
}
#ifndef DISABLE_NETWORK
rct_sprite_checksum sprite_checksum()
@ -318,15 +319,6 @@ rct_sprite_checksum sprite_checksum()
copy.misc.sprite_left = copy.misc.sprite_right = copy.misc.sprite_top = copy.misc.sprite_bottom = 0;
copy.misc.sprite_width = copy.misc.sprite_height_negative = copy.misc.sprite_height_positive = 0;
// Next in quadrant might be a misc sprite, set first non-misc sprite in quadrant.
while (auto* nextSprite = GetEntity(copy.misc.next_in_quadrant))
{
if (nextSprite->sprite_identifier == SpriteIdentifier::Misc)
copy.misc.next_in_quadrant = nextSprite->next_in_quadrant;
else
break;
}
if (copy.misc.Is<Peep>())
{
// Name is pointer and will not be the same across clients
@ -365,14 +357,12 @@ static void sprite_reset(SpriteBase* sprite)
{
// Need to retain how the sprite is linked in lists
auto llto = sprite->linked_list_index;
uint16_t next_in_quadrant = sprite->next_in_quadrant;
uint16_t sprite_index = sprite->sprite_index;
_spriteFlashingList[sprite_index] = false;
std::memset(sprite, 0, sizeof(rct_sprite));
sprite->linked_list_index = llto;
sprite->next_in_quadrant = next_in_quadrant;
sprite->sprite_index = sprite_index;
sprite->sprite_identifier = SpriteIdentifier::Null;
}
@ -388,19 +378,10 @@ void sprite_clear_all_unused()
sprite_reset(sprite);
sprite->linked_list_index = EntityListId::Free;
// sprite->next_in_quadrant will only end up as zero owing to corruption
// most likely due to previous builds not preserving it when resetting sprites
// We reset it to SPRITE_INDEX_NULL to prevent cycles in the sprite lists
if (sprite->next_in_quadrant == 0)
{
sprite->next_in_quadrant = SPRITE_INDEX_NULL;
}
_spriteFlashingList[sprite->sprite_index] = false;
}
}
static void SpriteSpatialInsert(SpriteBase* sprite, const CoordsXY& newLoc);
static constexpr uint16_t MAX_MISC_SPRITES = 300;
static void AddToEntityList(const EntityListId linkedListIndex, SpriteBase* entity)
{
@ -605,41 +586,25 @@ void sprite_misc_update_all()
static void SpriteSpatialInsert(SpriteBase* sprite, const CoordsXY& newLoc)
{
size_t newIndex = GetSpatialIndexOffset(newLoc.x, newLoc.y);
auto* next = &gSpriteSpatialIndex[newIndex];
while (sprite->sprite_index < *next && *next != SPRITE_INDEX_NULL)
{
auto sprite2 = GetEntity(*next);
next = &sprite2->next_in_quadrant;
}
sprite->next_in_quadrant = *next;
*next = sprite->sprite_index;
auto& spatialVector = gSpriteSpatialIndex[newIndex];
auto index = std::lower_bound(std::begin(spatialVector), std::end(spatialVector), sprite->sprite_index);
spatialVector.insert(index, sprite->sprite_index);
}
static void SpriteSpatialRemove(SpriteBase* sprite)
{
size_t currentIndex = GetSpatialIndexOffset(sprite->x, sprite->y);
auto* index = &gSpriteSpatialIndex[currentIndex];
// This indicates that the spatial index data is incorrect.
if (*index == SPRITE_INDEX_NULL)
auto& spatialVector = gSpriteSpatialIndex[currentIndex];
auto index = std::lower_bound(std::begin(spatialVector), std::end(spatialVector), sprite->sprite_index);
if (index != std::end(spatialVector) && *index == sprite->sprite_index)
{
spatialVector.erase(index, index + 1);
}
else
{
log_warning("Bad sprite spatial index. Rebuilding the spatial index...");
reset_sprite_spatial_index();
}
auto* sprite2 = GetEntity(*index);
while (sprite != sprite2)
{
index = &sprite2->next_in_quadrant;
if (*index == SPRITE_INDEX_NULL)
{
break;
}
sprite2 = GetEntity(*index);
}
*index = sprite->next_in_quadrant;
}
static void SpriteSpatialMove(SpriteBase* sprite, const CoordsXY& newLoc)
@ -807,17 +772,22 @@ void litter_create(const CoordsXYZD& litterPos, LitterType type)
*/
void litter_remove_at(const CoordsXYZ& litterPos)
{
std::vector<Litter*> removals;
for (auto litter : EntityTileList<Litter>(litterPos))
{
if (abs(litter->z - litterPos.z) <= 16)
{
if (abs(litter->x - litterPos.x) <= 8 && abs(litter->y - litterPos.y) <= 8)
{
litter->Invalidate();
sprite_remove(litter);
removals.push_back(litter);
}
}
}
for (auto* litter : removals)
{
litter->Invalidate();
sprite_remove(litter);
}
}
/**

View File

@ -231,7 +231,6 @@ uint16_t GetEntityListCount(EntityListId list);
constexpr const uint32_t SPATIAL_INDEX_SIZE = (MAXIMUM_MAP_SIZE_TECHNICAL * MAXIMUM_MAP_SIZE_TECHNICAL) + 1;
constexpr const uint32_t SPATIAL_INDEX_LOCATION_NULL = SPATIAL_INDEX_SIZE - 1;
extern uint16_t gSpriteSpatialIndex[SPATIAL_INDEX_SIZE];
extern const rct_string_id litterNames[12];
@ -249,7 +248,7 @@ void litter_remove_at(const CoordsXYZ& litterPos);
uint16_t remove_floating_sprites();
void sprite_misc_explosion_cloud_create(const CoordsXYZ& cloudPos);
void sprite_misc_explosion_flare_create(const CoordsXYZ& flarePos);
uint16_t sprite_get_first_in_quadrant(const CoordsXY& spritePos);
const std::vector<uint16_t>& GetEntityTileList(const CoordsXY& spritePos);
///////////////////////////////////////////////////////////////
// Balloon
@ -333,25 +332,75 @@ public:
using iterator_category = std::forward_iterator_tag;
};
template<typename T> class EntityTileIterator
{
private:
std::vector<uint16_t>::const_iterator iter;
std::vector<uint16_t>::const_iterator end;
T* Entity = nullptr;
public:
EntityTileIterator(std::vector<uint16_t>::const_iterator _iter, std::vector<uint16_t>::const_iterator _end)
: iter(_iter)
, end(_end)
{
++(*this);
}
EntityTileIterator& operator++()
{
Entity = nullptr;
while (iter != end && Entity == nullptr)
{
Entity = GetEntity<T>(*iter++);
}
return *this;
}
EntityTileIterator operator++(int)
{
EntityTileIterator retval = *this;
++(*this);
return *iter;
}
bool operator==(EntityTileIterator other) const
{
return Entity == other.Entity;
}
bool operator!=(EntityTileIterator other) const
{
return !(*this == other);
}
T* operator*()
{
return Entity;
}
// iterator traits
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = const T*;
using reference = const T&;
using iterator_category = std::forward_iterator_tag;
};
template<typename T = SpriteBase> class EntityTileList
{
private:
uint16_t FirstEntity = SPRITE_INDEX_NULL;
using EntityTileIterator = EntityIterator<T, &SpriteBase::next_in_quadrant>;
const std::vector<uint16_t>& vec;
public:
EntityTileList(const CoordsXY& loc)
: FirstEntity(sprite_get_first_in_quadrant(loc))
: vec(GetEntityTileList(loc))
{
}
EntityTileIterator begin()
EntityTileIterator<T> begin()
{
return EntityTileIterator(FirstEntity);
return EntityTileIterator<T>(std::begin(vec), std::end(vec));
}
EntityTileIterator end()
EntityTileIterator<T> end()
{
return EntityTileIterator(SPRITE_INDEX_NULL);
return EntityTileIterator<T>(std::end(vec), std::end(vec));
}
};

View File

@ -9,7 +9,6 @@ enum class SpriteIdentifier : uint8_t;
struct SpriteBase
{
SpriteIdentifier sprite_identifier;
uint16_t next_in_quadrant;
// Valid values are EntityListId::...
EntityListId linked_list_index;
// Height from centre of sprite to bottom

View File

@ -131,7 +131,6 @@ static void AdvanceGameTicks(uint32_t ticks, std::unique_ptr<IContext>& context)
static void CompareSpriteDataCommon(const SpriteBase& left, const SpriteBase& right)
{
COMPARE_FIELD(sprite_identifier);
COMPARE_FIELD(next_in_quadrant);
COMPARE_FIELD(linked_list_index);
COMPARE_FIELD(sprite_index);
COMPARE_FIELD(flags);