nostalgia/src/keel/assetmanager.hpp

303 lines
6.3 KiB
C++

/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <ox/event/signal.hpp>
#include <ox/fs/fs.hpp>
#include <ox/model/typenamecatcher.hpp>
#include <ox/std/hashmap.hpp>
#include <ox/std/utility.hpp>
namespace keel {
class AssetManager;
template<typename T>
class AssetRef;
#ifndef OX_BARE_METAL
template<typename T>
class AssetContainer {
friend AssetManager;
friend AssetRef<T>;
protected:
ox::Signal<ox::Error()> updated;
private:
T m_obj;
mutable int m_references = 0;
public:
template<class... Args>
explicit constexpr AssetContainer(Args&&... args): m_obj(ox::forward<Args>(args)...) {
}
AssetContainer(AssetContainer&) = delete;
AssetContainer(AssetContainer&&) = delete;
AssetContainer& operator=(AssetContainer&) = delete;
AssetContainer& operator=(AssetContainer&&) = delete;
[[nodiscard]]
constexpr T *get() noexcept {
return &m_obj;
}
[[nodiscard]]
constexpr const T *get() const noexcept {
return &m_obj;
}
constexpr void set(T &&val) {
m_obj = std::move(val);
}
constexpr void set(const T &val) {
m_obj = val;
}
protected:
constexpr void incRefs() const noexcept {
++m_references;
}
constexpr void decRefs() const noexcept {
--m_references;
}
[[nodiscard]]
constexpr int references() const noexcept {
return m_references;
}
};
template<typename T>
class AssetRef: public ox::SignalHandler {
private:
const AssetContainer<T> *m_ctr = nullptr;
public:
ox::Signal<ox::Error()> updated;
explicit constexpr AssetRef(const AssetContainer<T> *c = nullptr) noexcept;
constexpr AssetRef(const AssetRef &h) noexcept;
constexpr AssetRef(AssetRef &&h) noexcept;
~AssetRef() noexcept override {
if (m_ctr) {
m_ctr->decRefs();
}
}
[[nodiscard]]
constexpr const T *get() const noexcept {
if (m_ctr) {
return m_ctr->get();
}
return nullptr;
}
constexpr const T &operator*() const & noexcept {
return *m_ctr->get();
}
constexpr const T &&operator*() const && noexcept {
return *m_ctr->get();
}
constexpr const T *operator->() const noexcept {
return m_ctr->get();
}
AssetRef &operator=(const AssetRef &h) noexcept {
if (this == &h) {
return *this;
}
if (m_ctr) {
m_ctr->decRefs();
oxIgnoreError(m_ctr->updated.disconnectObject(this));
}
m_ctr = h.m_ctr;
m_ctr->updated.connect(&updated, &ox::Signal<ox::Error()>::emitCheckError);
if (m_ctr) {
m_ctr->incRefs();
}
return *this;
}
AssetRef &operator=(AssetRef &&h) noexcept {
if (this == &h) {
return *this;
}
if (m_ctr) {
m_ctr->decRefs();
oxIgnoreError(m_ctr->updated.disconnectObject(this));
}
m_ctr = h.m_ctr;
m_ctr->updated.connect(this, &AssetRef::emitUpdated);
h.m_ctr = nullptr;
return *this;
}
explicit constexpr operator bool() const noexcept {
return m_ctr;
}
private:
constexpr ox::Error emitUpdated() const noexcept {
updated.emit();
return OxError(0);
}
};
template<typename T>
constexpr AssetRef<T>::AssetRef(const AssetContainer<T> *c) noexcept: m_ctr(c) {
if (c) {
c->updated.connect(this, &AssetRef::emitUpdated);
}
}
template<typename T>
constexpr AssetRef<T>::AssetRef(const AssetRef &h) noexcept {
m_ctr = h.m_ctr;
if (m_ctr) {
m_ctr->updated.connect(this, &AssetRef::emitUpdated);
m_ctr->incRefs();
}
}
template<typename T>
constexpr AssetRef<T>::AssetRef(AssetRef &&h) noexcept {
m_ctr = h.m_ctr;
m_ctr->updated.connect(this, &AssetRef::emitUpdated);
h.m_ctr = nullptr;
}
class AssetManager {
private:
class AssetTypeManagerBase {
public:
virtual ~AssetTypeManagerBase() = default;
virtual void gc() noexcept = 0;
};
template<typename T>
class AssetTypeManager: public AssetTypeManagerBase {
private:
ox::HashMap<ox::String, ox::UniquePtr<AssetContainer<T>>> m_cache;
public:
ox::Result<AssetRef<T>> getAsset(const ox::String &assetId) const noexcept {
auto out = m_cache.at(assetId);
oxReturnError(out);
return AssetRef<T>(out.value->get());
}
ox::Result<AssetRef<T>> setAsset(const ox::String &assetId, const T &obj) noexcept {
auto &p = m_cache[assetId];
if (!p) {
p = ox::make_unique<AssetContainer<T>>(obj);
} else {
p->set(obj);
p->updated.emit();
}
return AssetRef<T>(p.get());
}
ox::Result<AssetRef<T>> setAsset(const ox::String &assetId, T &&obj) noexcept {
auto &p = m_cache[assetId];
if (!p) {
p = ox::make_unique<AssetContainer<T>>(obj);
} else {
p->set(std::move(obj));
p->updated.emit();
}
return AssetRef<T>(p.get());
}
void gc() noexcept final {
for (const auto &ack : m_cache.keys()) {
auto &ac = m_cache[ack];
if (!ac->references()) {
m_cache.erase(ack);
}
}
}
};
ox::HashMap<ox::String, ox::UniquePtr<AssetTypeManagerBase>> m_assetTypeManagers;
template<typename T>
AssetTypeManager<T> *getTypeManager() noexcept {
constexpr auto typeName = ox::requireModelTypeName<T>();
static_assert(ox_strcmp(typeName, "") != 0, "Types must have TypeName to use AssetManager");
auto &am = m_assetTypeManagers[typeName];
if (!am) {
am = ox::make_unique<AssetTypeManager<T>>();
}
return dynamic_cast<AssetTypeManager<T>*>(am.get());
}
public:
template<typename T>
ox::Result<AssetRef<T>> getAsset(const ox::String &assetId) noexcept {
auto m = getTypeManager<T>();
return m->getAsset(assetId);
}
template<typename T>
ox::Result<AssetRef<T>> setAsset(const ox::String &assetId, const T &obj) noexcept {
auto m = getTypeManager<T>();
return m->setAsset(assetId, obj);
}
void gc() noexcept {
for (const auto &amk : m_assetTypeManagers.keys()) {
auto &am = m_assetTypeManagers[amk];
am->gc();
}
}
};
#else
template<typename T>
class AssetRef {
private:
const T *m_obj = nullptr;
public:
constexpr AssetRef() noexcept = default;
explicit constexpr AssetRef(const T *obj) noexcept: m_obj(obj) {
}
constexpr const T *get() const noexcept {
return m_obj;
}
constexpr const T &operator*() const & noexcept {
return *m_obj;
}
constexpr const T &&operator*() const && noexcept {
return *m_obj;
}
constexpr const T *operator->() const noexcept {
return m_obj;
}
explicit constexpr operator bool() const noexcept {
return m_obj;
}
};
#endif
}