Chira Engine
A customizable MIT-licensed game engine.
Resource.h
1 #pragma once
2 
3 #include <string>
4 #include <string_view>
5 #include <unordered_map>
6 #include <vector>
7 #include <nlohmann/json.hpp>
8 #include <core/Logger.h>
9 #include <math/Types.h>
10 #include <utility/SharedPointer.h>
11 #include <utility/Types.h>
12 #include "provider/IResourceProvider.h"
13 
14 namespace chira {
15 
16 constexpr std::string_view RESOURCE_ID_SEPARATOR = "://";
17 
19 class Resource {
20  // To view resource data
21  friend class ResourceUsageTrackerPanel;
22 public:
23  explicit Resource(std::string identifier_)
24  : identifier(std::move(identifier_)) {}
25 
26  virtual ~Resource();
27 
28  virtual void compile(const byte /*buffer*/[], std::size_t /*bufferLength*/) = 0;
29 
30  [[nodiscard]] std::string_view getIdentifier() const {
31  return this->identifier;
32  }
33 
34 protected:
35  std::string identifier;
36 
37 //
38 // Static caching functions
39 //
40 public:
41  static void addResourceProvider(IResourceProvider* provider);
42 
43  static IResourceProvider* getLatestResourceProvider(const std::string& provider);
44 
45  static IResourceProvider* getResourceProviderWithResource(const std::string& identifier);
46 
47  template<typename ResourceType, typename... Params>
48  static SharedPointer<ResourceType> getResource(const std::string& identifier, Params... params) {
50  auto id = Resource::splitResourceIdentifier(identifier);
51  const std::string& provider = id.first, name = id.second;
52  if (Resource::resources[provider].count(name) > 0) {
53  return Resource::resources[provider][name].template cast<ResourceType>();
54  }
55  return Resource::getUniqueResource<ResourceType>(identifier, std::forward<Params>(params)...);
56  }
57 
58  template<typename ResourceType, typename... Params>
59  static void precacheResource(const std::string& identifier, Params... params) {
61  auto id = Resource::splitResourceIdentifier(identifier);
62  const std::string& provider = id.first, name = id.second;
63  if (Resource::resources[provider].count(name) > 0) {
64  return; // Already in cache
65  }
66 
67  for (auto i = Resource::providers[provider].rbegin(); i != Resource::providers[provider].rend(); i++) {
68  if ((*i)->hasResource(name)) {
69  Resource::resources[provider][name] = SharedPointer<Resource>(new ResourceType{identifier, std::forward<Params>(params)...});
70  (*i)->compileResource(name, Resource::resources[provider][name].get());
71  return; // Precached!
72  }
73  }
74  Resource::logResourceError("error.resource.resource_not_found", identifier);
75  }
76 
77  template<typename ResourceType>
78  static SharedPointer<ResourceType> getCachedResource(const std::string& identifier) {
80  auto id = Resource::splitResourceIdentifier(identifier);
81  const std::string& provider = id.first, name = id.second;
82  if (Resource::resources[provider].count(name) > 0) {
83  return Resource::resources[provider][name].cast<ResourceType>();
84  }
85  Resource::logResourceError("error.resource.cached_resource_not_found", identifier);
86  if (Resource::hasDefaultResource<ResourceType>())
87  return Resource::getDefaultResource<ResourceType>();
89  }
90 
91  template<typename ResourceType, typename... Params>
92  static SharedPointer<ResourceType> getUniqueResource(const std::string& identifier, Params... params) {
93  auto id = Resource::splitResourceIdentifier(identifier);
94  const std::string& provider = id.first, name = id.second;
95  for (auto i = Resource::providers[provider].rbegin(); i != Resource::providers[provider].rend(); i++) {
96  if ((*i)->hasResource(name)) {
97  Resource::resources[provider][name] = SharedPointer<Resource>(new ResourceType{identifier, std::forward<Params>(params)...});
98  (*i)->compileResource(name, Resource::resources[provider][name].get());
99  return Resource::resources[provider][name].template cast<ResourceType>();
100  }
101  }
102  Resource::logResourceError("error.resource.resource_not_found", identifier);
103  if (Resource::hasDefaultResource<ResourceType>())
104  return Resource::getDefaultResource<ResourceType>();
106  }
107 
109  template<typename ResourceType, typename... Params>
110  static SharedPointer<ResourceType> getUniqueUncachedResource(const std::string& identifier, Params... params) {
111  auto id = Resource::splitResourceIdentifier(identifier);
112  const std::string& provider = id.first, name = id.second;
113  for (auto i = Resource::providers[provider].rbegin(); i != Resource::providers[provider].rend(); i++) {
114  if ((*i)->hasResource(name)) {
115  auto resource = SharedPointer<ResourceType>(new ResourceType{identifier, std::forward<Params>(params)...});
116  (*i)->compileResource(name, resource.get());
117  // We're not holding onto this
118  resource.setHolderAmountForDelete(0);
119  return resource;
120  }
121  }
122  Resource::logResourceError("error.resource.resource_not_found", identifier);
124  }
125 
128  template<typename ResourceType, typename... Params>
129  static std::unique_ptr<ResourceType> getUniqueUncachedPropertyResource(const std::string& identifier, const nlohmann::json& props, Params... params) {
130  auto resource = std::make_unique<ResourceType>(identifier, std::forward<Params>(params)...);
131  resource->compile(props);
132  return resource;
133  }
134 
135  static std::pair<std::string, std::string> splitResourceIdentifier(const std::string& identifier);
136 
137  static const std::vector<std::unique_ptr<IResourceProvider>>& getResourceProviders(const std::string& providerName);
138 
139  static bool hasResource(const std::string& identifier);
140 
142  static void removeResource(const std::string& identifier);
143 
145  static void cleanup();
146 
148  static void discardAll();
149 
150  template<typename ResourceType>
151  static bool registerDefaultResource(const std::string& identifier) {
152  Resource::getDefaultResourceConstructors()[typeHash<ResourceType>()] = [identifier] {
153  Resource::defaultResources[typeHash<ResourceType>()] = Resource::getUniqueResource<ResourceType>(identifier).template cast<Resource>();
154  };
155  return true;
156  }
157 
158  template<typename ResourceType>
159  static bool hasDefaultResource() {
160  return Resource::defaultResources.contains(typeHash<ResourceType>());
161  }
162 
163  template<typename ResourceType>
164  static SharedPointer<ResourceType> getDefaultResource() {
165  return Resource::defaultResources[typeHash<ResourceType>()].template cast<ResourceType>();
166  }
167 
168  static void createDefaultResources() {
169  for (const auto& [id, ctor] : Resource::getDefaultResourceConstructors()) {
170  ctor();
171  }
172  }
173 
174 protected:
175  static inline std::unordered_map<std::string, std::vector<std::unique_ptr<IResourceProvider>>> providers;
176  static inline std::unordered_map<std::string, std::unordered_map<std::string, SharedPointer<Resource>>> resources;
177  static inline std::unordered_map<std::type_index, SharedPointer<Resource>> defaultResources;
178  static inline std::vector<std::string> garbageResources;
179 
180  static auto getDefaultResourceConstructors() -> std::unordered_map<std::type_index, std::function<void()>>& {
181  static std::unordered_map<std::type_index, std::function<void()>> defaultResourceConstructors;
182  return defaultResourceConstructors;
183  }
184 
186  static void logResourceError(const std::string& identifier, const std::string& resourceName);
187 };
188 
189 } // namespace chira
190 
191 #define CHIRA_REGISTER_DEFAULT_RESOURCE(type, identifier) \
192  static inline const auto type##DefaultResourceRegistryHelper = \
193  chira::Resource::registerDefaultResource<type>(identifier)
A chunk of data, usually a file. Is typically cached and shared.
Definition: Resource.h:19
static void logResourceError(const std::string &identifier, const std::string &resourceName)
We do a few predeclaration workarounds.
Definition: Resource.cpp:99
static void removeResource(const std::string &identifier)
If resource is present in the cache and has a reference count less than or equal to 2,...
Definition: Resource.cpp:67
static std::unique_ptr< ResourceType > getUniqueUncachedPropertyResource(const std::string &identifier, const nlohmann::json &props, Params... params)
The only way to make a PropertiesResource without a provider is to make it unique,...
Definition: Resource.h:129
static void cleanup()
Delete all resources marked for removal.
Definition: Resource.cpp:76
static void discardAll()
Deletes ALL resources and providers. Should only ever be called once, when the program closes.
Definition: Resource.cpp:84
static SharedPointer< ResourceType > getUniqueUncachedResource(const std::string &identifier, Params... params)
You might want to use this sparingly as it defeats the entire point of a cached, shared resource syst...
Definition: Resource.h:110