NAP
resourcemanager.h
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4 
5 #pragma once
6 
7 // Local Includes
8 #include "numeric.h"
9 #include "signalslot.h"
10 #include "corefactory.h"
11 #include "directorywatcher.h"
12 
13 // External Includes
14 #include <rtti/rtti.h>
15 #include <rtti/objectptr.h>
16 #include <utility/dllexport.h>
17 #include <rtti/unresolvedpointer.h>
18 #include <rtti/factory.h>
19 #include <rtti/deserializeresult.h>
20 #include <map>
21 
22 namespace nap
23 {
24  class Core;
25  class Scene;
26  class Device;
27 
28  class RTTIObjectGraphItem;
29  template<typename ITEM> class ObjectGraph;
31 
49  class NAPAPI ResourceManager final
50  {
51  friend class Core;
52  RTTI_ENABLE()
53  public:
54  ResourceManager(nap::Core& core);
55 
56  ~ResourceManager();
57 
66  bool loadFile(const std::string& filename, utility::ErrorState& errorState);
67 
68  /*
69  * Loads a json file containing objects. When the objects are loaded, a comparison is performed against the objects that are already loaded. Only
70  * the new objects and the objects that are different from the existing objects are loaded into the manager. The set of objects that is new
71  * or changed then receives an init() call in the correct dependency order: if an object has a pointer to another object, the pointee is initted
72  * first. In case an already existing object that wasn't in the file points to something that is changed, that object is recreated by
73  * cloning it and then calling init() on it. That also happens in the correct dependency order.
74  *
75  * Because there may be other objects pointing to objects that were read from json (which is only allowed through the ObjectPtr class), the updating
76  * mechanism patches all those pointers before calling init().
77  *
78  * In case one of the init() calls fail, the previous state is completely restored by patching the pointers back and destroying objects that were read.
79  * The client does not need to worry about handling such cases.
80  * In case all init() calls succeed, any old objects are destructed (the cloned and the previously existing objects).
81  *
82  * Before objects are destructed, onDestroy is called. onDestroy is called in the reverse initialization order. This way, it is still safe to use any
83  * pointers to perform cleanup of internal data.
84  *
85  * The file should be located in the 'data' folder (current working directory) of your application.
86  *
87  * @param filename json file containing all objects.
88  * @param externalChangedFile externally changed file that caused load of this file (like texture, shader etc)
89  * @param errorState if the function returns false, contains error information.
90  * @return if the file loaded successfully.
91  */
92  bool loadFile(const std::string& filename, const std::string& externalChangedFile, utility::ErrorState& errorState);
93 
99  rtti::ObjectPtr<rtti::Object> findObject(const std::string& id) const;
100 
114  template<class T>
115  rtti::ObjectPtr<T> findObject(const std::string& id) const { return rtti::ObjectPtr<T>(findObject(id)); }
116 
121  template<class T>
122  std::vector<rtti::ObjectPtr<T>> getObjects() const;
123 
129  rtti::ObjectPtr<rtti::Object> createObject(const rtti::TypeInfo& type);
130 
135  template<typename T>
137 
142  void checkForFileChanges();
143 
147  rtti::Factory& getFactory();
148 
155  void watchDirectory(const std::string& directory);
156 
161 
166 
167  private:
168  using InstanceByIDMap = std::unordered_map<std::string, rtti::Object*>; // Map from object ID to object (non-owned)
169  using ObjectByIDMap = std::unordered_map<std::string, std::unique_ptr<rtti::Object>>; // Map from object ID to object (owned)
170  using FileLinkMap = std::unordered_map<std::string, std::vector<std::string>>; // Map from target file to multiple source files
171 
172  class OverlayLinkResolver;
173 
174  enum class EFileModified : uint8_t
175  {
176  Yes,
177  No,
178  Error
179  };
180 
181  void addObject(const std::string& id, std::unique_ptr<rtti::Object> object);
182  void removeObject(const std::string& id);
183  void addFileLink(const std::string& sourceFile, const std::string& targetFile);
184 
188  bool loadFileAndDeserialize(const std::string& filename, rtti::DeserializeResult& readResult, utility::ErrorState& errorState);
189 
190  void determineObjectsToInit(const RTTIObjectGraph& objectGraph, const ObjectByIDMap& objectsToUpdate, const std::string& externalChangedFile, std::vector<std::string>& objectsToInit);
191  void buildObjectGraph(const ObjectByIDMap& objectsToUpdate, RTTIObjectGraph& objectGraph);
192  EFileModified isFileModified(const std::string& modifiedFile);
193  void stopAndDestroyAllObjects();
194  void destroyObjects(const std::unordered_set<std::string>& objectIDsToDelete, const RTTIObjectGraph& object_graph);
195 
196  private:
201  struct RollbackHelper final
202  {
203  public:
204  RollbackHelper(ResourceManager& service);
205  ~RollbackHelper();
206 
207  void clear();
208  void addExistingDevice(Device& device);
209  void addNewDevice(Device& device);
210  void addInitializedObject(rtti::Object& object);
211 
212  ObjectByIDMap& getObjectsToUpdate() { return mObjectsToUpdate; }
213 
214  private:
215  void destroyObjects();
216 
217  private:
218  ResourceManager& mService;
219  ObjectByIDMap mObjectsToUpdate;
220  std::vector<Device*> mExistingDevices;
221  std::vector<Device*> mNewDevices;
222  std::vector<rtti::Object*> mInitializedObjects;
223  bool mRollbackObjects = true;
224  };
225 
226  using ModifiedTimeMap = std::unordered_map<std::string, uint64>;
227 
228  ObjectByIDMap mObjects; // Holds all objects
229  std::set<std::string> mFilesToWatch; // Files currently loaded, used for watching changes on the files
230  FileLinkMap mFileLinkMap; // Map containing links from target to source file, for updating source files if the file monitor sees changes
231  std::unique_ptr<DirectoryWatcher> mDirectoryWatcher = nullptr; // File monitor, detects changes on files
232  ModifiedTimeMap mFileModTimes; // Cache for file modification times to avoid responding to too many file events
233  std::unique_ptr<CoreFactory> mFactory = nullptr; // Responsible for creating objects when de-serializing
234  Core& mCore; // Core
235  };
236 
237 
238  template<class T>
239  std::vector<rtti::ObjectPtr<T>> ResourceManager::getObjects() const
240  {
241  std::vector<rtti::ObjectPtr<T>> result;
242  for (auto& kvp : mObjects)
243  {
244  T* object = rtti_cast<T>(kvp.second.get());
245  if (object != nullptr)
246  result.push_back(object);
247  }
248 
249  return result;
250  }
251 }
nap::rtti::ObjectPtr
Definition: objectptr.h:154
nap::rtti::Object
Definition: object.h:30
nap::EWebSocketLogLevel::Error
@ Error
Recoverable error. Solution might be closing the connection with an appropiate error code.
nap::utility::ErrorState
Definition: errorstate.h:19
nap::ObjectGraph
Definition: objectgraph.h:20
nap::ResourceManager::findObject
rtti::ObjectPtr< T > findObject(const std::string &id) const
Definition: resourcemanager.h:115
nap::ResourceManager::mPreResourcesLoadedSignal
nap::Signal mPreResourcesLoadedSignal
Definition: resourcemanager.h:160
nap::Signal
Definition: signalslot.h:28
nap::Core
Definition: core.h:82
nap::ResourceManager::createObject
rtti::ObjectPtr< T > createObject()
Definition: resourcemanager.h:136
nap::rtti::DeserializeResult
Definition: deserializeresult.h:40
nap::ResourceManager::mPostResourcesLoadedSignal
nap::Signal mPostResourcesLoadedSignal
Definition: resourcemanager.h:165
nap::rtti::Factory
Definition: factory.h:78
nap
Definition: templateapp.h:17
nap::ResourceManager::getObjects
std::vector< rtti::ObjectPtr< T > > getObjects() const
Definition: resourcemanager.h:239
nap::rtti::TypeInfo
rttr::type TypeInfo
Definition: typeinfo.h:140
nap::ResourceManager
Definition: resourcemanager.h:49
nap::Device
Definition: device.h:20