Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 249 additions & 0 deletions include/Types.hh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <cstdint>
#include <span>
#include <type_traits>
#include <utility>

namespace Kinoko {

Expand Down Expand Up @@ -166,4 +167,252 @@ private:
size_t m_size; ///< The number of T elements that fit in the buffer
};

/// @brief Dynamically sized array that only allocates once.
/// @details It's possible that only an upper bound is known for a given vector.
/// However, in the event that there are more objects than expected, we want to error. Effectively,
/// this behaves identically to <tt>std::vector<T, @ref EGG::Allocator<T>></tt> except that it
/// cannot be later resized.
/// @tparam T The type of objects in the array.
template <typename T>
class fixed_vector {
public:
/// @brief Non-initializing constructor.
fixed_vector() : m_data(nullptr), m_size(0), m_capacity(0) {}

/// @brief Initializing constructor.
/// @param capacity The number of elements to initialize the vector with.
fixed_vector(size_t capacity) : fixed_vector() {
allocate(capacity);
}

/// @brief Copy constructor
/// @details Allocates a buffer of the same capacity and deep copies existing elements
fixed_vector(const fixed_vector &rhs) : fixed_vector() {
if (rhs.initialized()) {
allocate(rhs.m_capacity);
for (size_t i = 0; i < rhs.m_size; ++i) {
push_back(rhs[i]);
}
}
}

/// @brief Move constructor
/// @details Transfers ownership of the buffer and leaves rhs in an invalid state
fixed_vector(fixed_vector &&rhs)
: m_data(rhs.m_data), m_size(rhs.m_size), m_capacity(rhs.m_capacity) {
rhs.m_data = nullptr;
rhs.m_size = 0;
rhs.m_capacity = 0;
}

/// @brief Copy assignment operator
/// @details Destroys the existing buffer, then allocates a new one and deep copies
fixed_vector &operator=(const fixed_vector &rhs) {
if (this != &rhs) {
destroy();
if (rhs.initialized()) {
allocate(rhs.m_capacity);
for (size_t i = 0; i < rhs.m_size; ++i) {
push_back(rhs[i]);
}
}
}

return *this;
}

/// @brief Move assignment operator
/// @details Destroys the existing buffer, then transfers ownership from rhs
fixed_vector &operator=(fixed_vector &&rhs) {
if (this != &rhs) {
destroy();

m_data = rhs.m_data;
m_size = rhs.m_size;
m_capacity = rhs.m_capacity;

rhs.m_data = nullptr;
rhs.m_size = 0;
rhs.m_capacity = 0;
}

return *this;
}

/// @brief Destructor.
/// @details Destroys all existing elements in the array in-place from the end to the start.
~fixed_vector() {
destroy();
}

/// @brief Copies a new element into the array.
/// @param obj The object to copy.
/// @return A reference to the object.
T &push_back(const T &obj) {
ASSERT(initialized() && !full());
new (m_data + m_size++) T(obj);
return back();
}

/// @brief Moves a new element into the array.
/// @param obj The object to move.
/// @return A reference to the object.
T &push_back(T &&obj) {
ASSERT(initialized() && !full());
new (m_data + m_size++) T(std::move(obj));
return back();
}

/// @brief Creates a new element in-place in the array.
/// @tparam ...Args Variadic template for packing.
/// @param ...args Arguments to the constructor.
/// @return A reference to the object.
template <typename... Args>
T &emplace_back(Args &&...args) {
ASSERT(initialized() && !full());
new (m_data + m_size++) T(std::forward<Args>(args)...);
return back();
}

/// @brief Deletes the last existing element from the array.
void pop_back() {
ASSERT(initialized() && !empty());
m_data[--m_size].~T();
}

/// @brief Initializes the vector with the provided capacity.
/// @param capacity The number of elements to initialize the vector with.
void reserve(size_t capacity) {
ASSERT(!initialized());
allocate(capacity);
}

/// @brief Checks if there are no existing elements in the array.
/// @return True if the array is empty, otherwise false.
[[nodiscard]] bool empty() const {
return m_size == 0;
}

/// @brief Checks if all elements exist in the array.
/// @return True if the array is full, otherwise false.
[[nodiscard]] bool full() const {
return m_size == m_capacity;
}

/// @brief Checks if the array exists and if the capacity is non-zero.
/// @return True if the array is initialized, otherwise false.
[[nodiscard]] bool initialized() const {
return m_data && m_capacity != 0;
}

/// @brief Gets the number of existing elements in the array.
/// @return The number of existing elements in the array.
[[nodiscard]] size_t size() const {
return m_size;
}

/// @brief Gets the maximum number of elements that can exist in the array.
/// @return The maximum number of elements that can exist in the array.
[[nodiscard]] size_t capacity() const {
return m_capacity;
}

/// @brief Gets the first element in the array.
/// @return A reference to the first element in the array.
T &front() {
ASSERT(m_size > 0);
return *m_data;
}

/// @brief Gets the first element in the array.
/// @return A const reference to the first element in the array.
const T &front() const {
ASSERT(m_size > 0);
return *m_data;
}

/// @brief Gets the last existing element in the array.
/// @return A reference to the last existing element in the array.
T &back() {
ASSERT(m_size > 0);
return m_data[m_size - 1];
}

/// @brief Gets the last existing element in the array.
/// @return A const reference to the last existing element in the array.
const T &back() const {
ASSERT(m_size > 0);
return m_data[m_size - 1];
}

/// @brief Indexes the array. Validates that the object exists.
/// @param idx The index to the array.
/// @return A reference to the object at the corresponding index.
T &operator[](size_t idx) {
ASSERT(idx < m_size);
return m_data[idx];
}

/// @brief Indexes the array. Validates that the object exists.
/// @param idx The index to the array.
/// @return A const reference to the object at the corresponding index.
const T &operator[](size_t idx) const {
ASSERT(idx < m_size);
return m_data[idx];
}

/// @brief Iterator for the beginning of the existing array.
/// @return Iterator.
T *begin() noexcept {
ASSERT(initialized());
return m_data;
}

/// @brief Iterator for the beginning of the existing array.
/// @return Const iterator.
const T *begin() const noexcept {
ASSERT(initialized());
return m_data;
}

/// @brief Iterator for the end of the existing array.
/// @return Iterator.
T *end() noexcept {
ASSERT(initialized());
return m_data + m_size;
}

/// @brief Iterator for the end of the existing array.
/// @return Const iterator.
const T *end() const noexcept {
ASSERT(initialized());
return m_data + m_size;
}

private:
/// @brief Destroys existing elements and frees the buffer, resetting to an uninitialized state.
void destroy() {
while (m_size > 0) {
pop_back();
}

EGG::egg_free(m_data);
m_data = nullptr;
m_capacity = 0;
}

/// @brief Allocates the array.
/// @param capacity The number of elements to initialize the vector with.
void allocate(size_t capacity) {
ASSERT(!initialized());
m_data = static_cast<T *>(
EGG::egg_alloc(sizeof(T) * capacity, static_cast<s32>(alignof(T))));
m_capacity = capacity;
}

T *m_data; // The underlying array pointer.
size_t m_size; // The number of existing elements in the array.
size_t m_capacity; // The maximum number of elements that can exist in the array.
};

} // namespace Kinoko
11 changes: 3 additions & 8 deletions source/game/field/ObjectDirector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@ void ObjectDirector::DestroyInstance() {
/// @addr{0x8082A38C}
ObjectDirector::ObjectDirector()
: m_flowTable("ObjFlow.bin"), m_hitTableKart("GeoHitTableKart.bin"),
m_hitTableKartObject("GeoHitTableKartObj.bin"), m_psea(nullptr) {}
m_hitTableKartObject("GeoHitTableKartObj.bin"), m_objects(MAX_UNIT_COUNT),
m_calcObjects(MAX_UNIT_COUNT), m_collisionObjects(MAX_UNIT_COUNT), m_psea(nullptr),
m_managedObjects(MAX_MANAGED_OBJECTS) {}

/// @addr{0x8082A694}
ObjectDirector::~ObjectDirector() {
Expand All @@ -166,13 +168,6 @@ void ObjectDirector::createObjects() {
const auto *courseMap = System::CourseMap::Instance();
size_t objectCount = courseMap->getGeoObjCount();

// It's possible for the KMP to specify settings for objects that aren't tracked here
// MAX_UNIT_COUNT is the upper bound for tracked object count, so we reserve the minimum
size_t maxCount = std::min(objectCount, MAX_UNIT_COUNT);
m_objects.reserve(maxCount);
m_calcObjects.reserve(maxCount);
m_collisionObjects.reserve(maxCount);

auto *objDrivableDir = ObjectDrivableDirector::Instance();
auto course = System::RaceConfig::Instance()->raceScenario().course;
bool rGV2 = course == Course::SNES_Ghost_Valley_2;
Expand Down
18 changes: 8 additions & 10 deletions source/game/field/ObjectDirector.hh
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,11 @@ public:
return m_hitDepths[idx];
}

[[nodiscard]] std::vector<ObjectCollidable *, EGG::Allocator<ObjectCollidable *>> &
managedObjects() {
[[nodiscard]] fixed_vector<ObjectCollidable *> &managedObjects() {
return m_managedObjects;
}

[[nodiscard]] const std::vector<ObjectCollidable *, EGG::Allocator<ObjectCollidable *>> &
managedObjects() const {
[[nodiscard]] const fixed_vector<ObjectCollidable *> &managedObjects() const {
return m_managedObjects;
}

Expand Down Expand Up @@ -104,11 +102,9 @@ private:
ObjectHitTable m_hitTableKart;
ObjectHitTable m_hitTableKartObject;

std::vector<ObjectBase *, EGG::Allocator<ObjectBase *>> m_objects; ///< All objects live here
std::vector<ObjectBase *, EGG::Allocator<ObjectBase *>>
m_calcObjects; ///< Objects needing calc() live here too.
std::vector<ObjectBase *, EGG::Allocator<ObjectBase *>>
m_collisionObjects; ///< Objects having collision live here too
fixed_vector<ObjectBase *> m_objects; ///< All objects live here
fixed_vector<ObjectBase *> m_calcObjects; ///< Objects needing calc() live here too.
fixed_vector<ObjectBase *> m_collisionObjects; ///< Objects having collision live here too

static constexpr size_t MAX_UNIT_COUNT = 0x100;

Expand All @@ -117,10 +113,12 @@ private:
std::array<EGG::Vector3f, MAX_UNIT_COUNT> m_hitDepths;
std::array<Kart::Reaction, MAX_UNIT_COUNT> m_reactions;
ObjectPsea *m_psea;
std::vector<ObjectCollidable *, EGG::Allocator<ObjectCollidable *>> m_managedObjects;
fixed_vector<ObjectCollidable *> m_managedObjects;

static f32 s_wanwanMaxPitch; ///< @addr{0x808C70E8}

static constexpr size_t MAX_MANAGED_OBJECTS = 400; ///< Maximum number of managed objects

static ObjectDirector *s_instance;
};

Expand Down
3 changes: 2 additions & 1 deletion source/game/field/ObjectDrivableDirector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ void ObjectDrivableDirector::DestroyInstance() {
}

/// @addr{0x8081B324}
ObjectDrivableDirector::ObjectDrivableDirector() : m_obakeManager(nullptr) {}
ObjectDrivableDirector::ObjectDrivableDirector()
: m_objects(MAX_OBJECTS), m_calcObjects(MAX_OBJECTS), m_obakeManager(nullptr) {}

/// @addr{0x8081B380}
ObjectDrivableDirector::~ObjectDrivableDirector() {
Expand Down
10 changes: 5 additions & 5 deletions source/game/field/ObjectDrivableDirector.hh
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ private:
ObjectDrivableDirector();
~ObjectDrivableDirector() override;

std::vector<ObjectDrivable *, EGG::Allocator<ObjectDrivable *>>
m_objects; ///< All objects live here
std::vector<ObjectDrivable *, EGG::Allocator<ObjectDrivable *>>
m_calcObjects; ///< Objects needing calc() live here too.
ObjectObakeManager *m_obakeManager; ///< Manages rGV2 blocks and spatial indexing.
fixed_vector<ObjectDrivable *> m_objects; ///< All objects live here
fixed_vector<ObjectDrivable *> m_calcObjects; ///< Objects needing calc() live here too.
ObjectObakeManager *m_obakeManager; ///< Manages rGV2 blocks and spatial indexing.

static constexpr size_t MAX_OBJECTS = 400; ///< Maximum number of objects in the vectors

static ObjectDrivableDirector *s_instance;
};
Expand Down
2 changes: 1 addition & 1 deletion source/game/field/RailManager.hh
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private:

void createPaths();

std::vector<Rail *, EGG::Allocator<Rail *>> m_rails;
fixed_vector<Rail *> m_rails;
u16 m_totalRails;
u16 m_extraInterplatorCount;
u16 m_pointCount;
Expand Down
6 changes: 1 addition & 5 deletions source/game/field/obj/ObjectObakeManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,15 @@ namespace Kinoko::Field {

/// @addr{0x8080B0D8}
ObjectObakeManager::ObjectObakeManager(const System::MapdataGeoObj &params)
: ObjectDrivable(params), m_blockCache({}) {
: ObjectDrivable(params), m_blockCache({}), m_blocks(MAX_BLOCKS), m_calcBlocks(MAX_BLOCKS) {
static constexpr f32 BLOCK_WIDTH = 195.00002f;
static constexpr f32 BLOCK_HEIGHT = 130.0f;
static constexpr size_t MAX_FALLING_BLOCKS = 256;

m_colBox = EGG::egg_new<ObjectCollisionBox>(BLOCK_WIDTH, BLOCK_HEIGHT, BLOCK_WIDTH,
EGG::Vector3f::zero);
m_colSphere = EGG::egg_new<ObjectCollisionSphere>(1.0f, EGG::Vector3f::zero);

addBlock(params);

// Pre-allocate max size now to avoid re-allocation during race when heap is locked.
m_calcBlocks.reserve(MAX_FALLING_BLOCKS);
}

/// @addr{0x8080BEA4}
Expand Down
Loading
Loading