1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "system_graph_loader.h"
17
18 #include <charconv>
19 #include <cinttypes>
20 #include <cstddef>
21 #include <cstdint>
22
23 #include <base/containers/array_view.h>
24 #include <base/containers/iterator.h>
25 #include <base/containers/string.h>
26 #include <base/containers/string_view.h>
27 #include <base/containers/type_traits.h>
28 #include <base/containers/unique_ptr.h>
29 #include <base/containers/vector.h>
30 #include <base/math/quaternion.h>
31 #include <base/math/vector.h>
32 #include <base/namespace.h>
33 #include <base/util/uid.h>
34 #include <core/ecs/entity.h>
35 #include <core/ecs/intf_ecs.h>
36 #include <core/ecs/intf_system.h>
37 #include <core/ecs/intf_system_graph_loader.h>
38 #include <core/io/intf_file.h>
39 #include <core/io/intf_file_manager.h>
40 #include <core/log.h>
41 #include <core/namespace.h>
42 #include <core/plugin/intf_plugin.h>
43 #include <core/plugin/intf_plugin_register.h>
44 #include <core/property/intf_property_api.h>
45 #include <core/property/intf_property_handle.h>
46 #include <core/property/property.h>
47 #include <core/property/property_types.h>
48
49 #define JSON_IMPL
50 #include <core/json/json.h>
51
52 #include "json_util.h"
53
54 CORE_BEGIN_NAMESPACE()
55 using BASE_NS::array_view;
56 using BASE_NS::string;
57 using BASE_NS::string_view;
58 using BASE_NS::Uid;
59 using BASE_NS::Math::Quat;
60 using BASE_NS::Math::Vec2;
61 using BASE_NS::Math::Vec3;
62 using BASE_NS::Math::Vec4;
63
64 namespace {
65 constexpr size_t VERSION_SIZE { 5u };
66 constexpr uint32_t VERSION_MAJOR { 22u };
67
68 template<class TYPEINFO>
FindTypeInfo(const string_view name,const array_view<const ITypeInfo * const> & container)69 const TYPEINFO* FindTypeInfo(const string_view name, const array_view<const ITypeInfo* const>& container)
70 {
71 for (const auto& info : container) {
72 if (static_cast<const TYPEINFO*>(info)->typeName == name) {
73 return static_cast<const TYPEINFO*>(info);
74 }
75 }
76
77 return nullptr;
78 }
79
80 template<class TYPEINFO>
FindTypeInfo(const Uid & uid,const array_view<const ITypeInfo * const> & container)81 const TYPEINFO* FindTypeInfo(const Uid& uid, const array_view<const ITypeInfo* const>& container)
82 {
83 for (const auto& info : container) {
84 if (static_cast<const TYPEINFO* const>(info)->uid == uid) {
85 return static_cast<const TYPEINFO*>(info);
86 }
87 }
88
89 return nullptr;
90 }
91
92 struct PropertyValue {
93 const IPropertyHandle* handle;
94 void* offset;
95 const Property* info;
96 template<typename Type>
Get__anon78a6930b0110::PropertyValue97 Type& Get() const
98 {
99 return *static_cast<Type*>(offset);
100 }
101 };
102
103 // For primitive types..
104 template<typename ElementType>
ReadArrayPropertyValue(const json::value & jsonData,PropertyValue & propertyData,string & error)105 void ReadArrayPropertyValue(const json::value& jsonData, PropertyValue& propertyData, string& error)
106 {
107 ElementType* output = &propertyData.Get<ElementType>();
108 if (propertyData.info->type.isArray) {
109 // expecting array data.
110 if (auto const array = jsonData.find(propertyData.info->name); array) {
111 auto outputView = array_view<ElementType>(output, propertyData.info->count);
112 from_json(*array, outputView);
113 }
114 } else {
115 // expecting single value
116 ElementType result;
117 if (SafeGetJsonValue(jsonData, propertyData.info->name, error, result)) {
118 *output = result;
119 }
120 }
121 }
122
123 // Specialization for char types (strings).. (null terminate the arrays)
124 template<>
125 void ReadArrayPropertyValue<char>(const json::value& jsonData, PropertyValue& propertyData, string& error)
126 {
127 char* output = &propertyData.Get<char>();
128 string_view result;
129 if (SafeGetJsonValue(jsonData, propertyData.info->name, error, result)) {
130 if (propertyData.info->type.isArray) {
131 const auto end = result.copy(output, propertyData.info->count - 1, 0);
132 output[end] = 0;
133 } else {
134 *output = result[0];
135 }
136 }
137 }
138
139 template<class HandleType>
ReadHandlePropertyValue(const json::value & jsonData,PropertyValue & propertyData,string & error)140 void ReadHandlePropertyValue(const json::value& jsonData, PropertyValue& propertyData, string& error)
141 {
142 decltype(HandleType::id) result;
143 if (SafeGetJsonValue(jsonData, propertyData.info->name, error, result)) {
144 HandleType& handle = propertyData.Get<HandleType>();
145 handle.id = result;
146 }
147 }
148
149 template<class VecType, size_t n>
ReadVecPropertyValue(const json::value & jsonData,PropertyValue & propertyData,string &)150 void ReadVecPropertyValue(const json::value& jsonData, PropertyValue& propertyData, string& /* error */)
151 {
152 if (auto const array = jsonData.find(propertyData.info->name); array) {
153 VecType& result = propertyData.Get<VecType>();
154 from_json(*array, result.data);
155 }
156 }
157
ResolveComponentDependencies(IEcs & ecs,array_view<const Uid> dependencies,const array_view<const ITypeInfo * const> componentMetadata)158 bool ResolveComponentDependencies(
159 IEcs& ecs, array_view<const Uid> dependencies, const array_view<const ITypeInfo* const> componentMetadata)
160 {
161 for (const Uid& dependencyUid : dependencies) {
162 // default constructed UID is used as a wildcard for dependecies not known at compile time.
163 if (dependencyUid != Uid {}) {
164 // See if this component type exists in ecs
165 const IComponentManager* componentManager = ecs.GetComponentManager(dependencyUid);
166 if (!componentManager) {
167 // Find component type and create such manager.
168 const auto* componentTypeInfo =
169 FindTypeInfo<ComponentManagerTypeInfo>(dependencyUid, componentMetadata);
170 if (componentTypeInfo) {
171 ecs.CreateComponentManager(*componentTypeInfo);
172 } else {
173 // Could not find this component manager, unable to continue.
174 return false;
175 }
176 }
177 }
178 }
179
180 return true;
181 }
182
GetProperty(IPropertyHandle * handle,size_t i,void * offset)183 PropertyValue GetProperty(IPropertyHandle* handle, size_t i, void* offset)
184 {
185 auto* api = handle->Owner();
186 if (i < api->PropertyCount()) {
187 auto prop = api->MetaData(i);
188 return { handle, static_cast<uint8_t*>(offset) + prop->offset, prop };
189 }
190 return { nullptr, nullptr, nullptr };
191 }
192
ParseProperties(const json::value & jsonData,ISystem & system,string & error)193 void ParseProperties(const json::value& jsonData, ISystem& system, string& error)
194 {
195 if (const json::value* propertiesIt = jsonData.find("properties"); propertiesIt) {
196 if (auto systemPropertyHandle = system.GetProperties(); systemPropertyHandle) {
197 const IPropertyApi* propertyApi = systemPropertyHandle->Owner();
198 if (auto offset = systemPropertyHandle->WLock(); offset) {
199 for (size_t i = 0; i < propertyApi->PropertyCount(); ++i) {
200 PropertyValue property = GetProperty(systemPropertyHandle, i, offset);
201 if (property.info) {
202 switch (property.info->type) {
203 case PropertyType::BOOL_T:
204 case PropertyType::BOOL_ARRAY_T:
205 ReadArrayPropertyValue<bool>(*propertiesIt, property, error);
206 break;
207
208 case PropertyType::CHAR_T:
209 case PropertyType::CHAR_ARRAY_T:
210 ReadArrayPropertyValue<char>(*propertiesIt, property, error);
211 break;
212
213 case PropertyType::INT8_T:
214 case PropertyType::INT8_ARRAY_T:
215 ReadArrayPropertyValue<int8_t>(*propertiesIt, property, error);
216 break;
217
218 case PropertyType::INT16_T:
219 case PropertyType::INT16_ARRAY_T:
220 ReadArrayPropertyValue<int16_t>(*propertiesIt, property, error);
221 break;
222
223 case PropertyType::INT32_T:
224 case PropertyType::INT32_ARRAY_T:
225 ReadArrayPropertyValue<int32_t>(*propertiesIt, property, error);
226 break;
227
228 case PropertyType::INT64_T:
229 case PropertyType::INT64_ARRAY_T:
230 ReadArrayPropertyValue<int64_t>(*propertiesIt, property, error);
231 break;
232
233 case PropertyType::UINT8_T:
234 case PropertyType::UINT8_ARRAY_T:
235 ReadArrayPropertyValue<uint8_t>(*propertiesIt, property, error);
236 break;
237
238 case PropertyType::UINT16_T:
239 case PropertyType::UINT16_ARRAY_T:
240 ReadArrayPropertyValue<uint16_t>(*propertiesIt, property, error);
241 break;
242
243 case PropertyType::UINT32_T:
244 case PropertyType::UINT32_ARRAY_T:
245 ReadArrayPropertyValue<uint32_t>(*propertiesIt, property, error);
246 break;
247
248 case PropertyType::UINT64_T:
249 case PropertyType::UINT64_ARRAY_T:
250 ReadArrayPropertyValue<uint64_t>(*propertiesIt, property, error);
251 break;
252
253 case PropertyType::DOUBLE_T:
254 case PropertyType::DOUBLE_ARRAY_T:
255 ReadArrayPropertyValue<double>(*propertiesIt, property, error);
256 break;
257
258 case PropertyType::ENTITY_T:
259 ReadHandlePropertyValue<Entity>(*propertiesIt, property, error);
260 break;
261
262 case PropertyType::VEC2_T:
263 ReadVecPropertyValue<Vec2, 2>(*propertiesIt, property, error); // 2: Vec2
264 break;
265 case PropertyType::VEC3_T:
266 ReadVecPropertyValue<Vec3, 3>(*propertiesIt, property, error); // 3: Vec3
267 break;
268 case PropertyType::VEC4_T:
269 ReadVecPropertyValue<Vec4, 4>(*propertiesIt, property, error); // 4: Vec4
270 break;
271 case PropertyType::QUAT_T:
272 ReadVecPropertyValue<Quat, 4>(*propertiesIt, property, error); // 4: Vec4
273 break;
274 case PropertyType::MAT4X4_T:
275 // NOTE: Implement.
276 break;
277
278 case PropertyType::FLOAT_T:
279 case PropertyType::FLOAT_ARRAY_T:
280 ReadArrayPropertyValue<float>(*propertiesIt, property, error);
281 break;
282
283 case PropertyType::STRING_T:
284 ReadArrayPropertyValue<string>(*propertiesIt, property, error);
285 break;
286
287 case PropertyType::ENTITY_ARRAY_T:
288 case PropertyType::VEC2_ARRAY_T:
289 case PropertyType::VEC3_ARRAY_T:
290 case PropertyType::VEC4_ARRAY_T:
291 case PropertyType::QUAT_ARRAY_T:
292 case PropertyType::MAT4X4_ARRAY_T:
293 case PropertyType::INVALID:
294 // NOTE: Implement.
295 break;
296 }
297 }
298 }
299 }
300 systemPropertyHandle->WUnlock();
301 system.SetProperties(*systemPropertyHandle); // notify that the properties HAVE changed.
302 }
303 }
304 }
305
ParseSystem(const json::value & jsonData,const array_view<const ITypeInfo * const> & componentMetadata,const array_view<const ITypeInfo * const> & systemMetadata,IEcs & ecs,string & error)306 bool ParseSystem(const json::value& jsonData, const array_view<const ITypeInfo* const>& componentMetadata,
307 const array_view<const ITypeInfo* const>& systemMetadata, IEcs& ecs, string& error)
308 {
309 string typeName;
310 SafeGetJsonValue(jsonData, "typeName", error, typeName);
311
312 bool optional = false;
313 SafeGetJsonValue(jsonData, "optional", error, optional);
314
315 // Look up this system type from metadata.
316 const auto* typeInfo = FindTypeInfo<SystemTypeInfo>(typeName, systemMetadata);
317 if (typeInfo) {
318 // Ensure we have all components required by this system.
319 if (!ResolveComponentDependencies(ecs, typeInfo->componentDependencies, componentMetadata)) {
320 error += "Failed to resolve component dependencies: (" + typeName + ".\n";
321 return false;
322 }
323 if (!ResolveComponentDependencies(ecs, typeInfo->readOnlyComponentDependencies, componentMetadata)) {
324 error += "Failed to resolve read-only component dependencies: (" + typeName + ".\n";
325 return false;
326 }
327
328 // Create system and read properties.
329 ISystem* system = ecs.CreateSystem(*typeInfo);
330 ParseProperties(jsonData, *system, error);
331
332 return true;
333 }
334 CORE_LOG_W("Cannot find system: %s (optional: %s)", typeName.c_str(), (optional ? "true" : "false"));
335 if (!optional) {
336 error += "Cannot find system: " + typeName + ".\n";
337 }
338
339 return optional;
340 }
341 } // namespace
342
Load(const string_view uri,IEcs & ecs)343 SystemGraphLoader::LoadResult SystemGraphLoader::Load(const string_view uri, IEcs& ecs)
344 {
345 IFile::Ptr file = fileManager_.OpenFile(uri);
346 if (!file) {
347 CORE_LOG_D("Error loading '%s'", string(uri).c_str());
348 return LoadResult("Failed to open file.");
349 }
350
351 const uint64_t byteLength = file->GetLength();
352
353 string raw(static_cast<size_t>(byteLength), string::value_type());
354 if (file->Read(raw.data(), byteLength) != byteLength) {
355 CORE_LOG_D("Error loading '%s'", string(uri).c_str());
356 return LoadResult("Failed to read file.");
357 }
358
359 return LoadString(raw, ecs);
360 }
361
LoadString(const string_view jsonString,IEcs & ecs)362 SystemGraphLoader::LoadResult SystemGraphLoader::LoadString(const string_view jsonString, IEcs& ecs)
363 {
364 auto const json = json::parse(jsonString.data());
365 if (json) {
366 LoadResult finalResult;
367 {
368 string ver;
369 string type;
370 uint32_t verMajor { ~0u };
371 uint32_t verMinor { ~0u };
372 if (const json::value* iter = json.find("compatibility_info"); iter) {
373 SafeGetJsonValue(*iter, "type", finalResult.error, type);
374 SafeGetJsonValue(*iter, "version", finalResult.error, ver);
375 if (ver.size() == VERSION_SIZE) {
376 if (const auto delim = ver.find('.'); delim != string::npos) {
377 std::from_chars(ver.data(), ver.data() + delim, verMajor);
378 std::from_chars(ver.data() + delim + 1, ver.data() + ver.size(), verMinor);
379 }
380 }
381 }
382 if ((type != "systemgraph") || (verMajor != VERSION_MAJOR)) {
383 // NOTE: we're still loading the system graph
384 CORE_LOG_W("DEPRECATED SYSTEM GRAPH: invalid system graph type (%s) and / or invalid version (%s).",
385 type.c_str(), ver.c_str());
386 }
387 }
388 auto& pluginRegister = GetPluginRegister();
389 auto componentMetadata = pluginRegister.GetTypeInfos(ComponentManagerTypeInfo::UID);
390 auto systemMetadata = pluginRegister.GetTypeInfos(SystemTypeInfo::UID);
391 const auto& systemsArrayIt = json.find("systems");
392 if (systemsArrayIt && systemsArrayIt->is_array()) {
393 for (const auto& systemJson : systemsArrayIt->array_) {
394 if (!ParseSystem(systemJson, componentMetadata, systemMetadata, ecs, finalResult.error)) {
395 break;
396 }
397 }
398 }
399
400 finalResult.success = finalResult.error.empty();
401
402 return finalResult;
403 }
404 return LoadResult("Invalid json file.");
405 }
406
SystemGraphLoader(IFileManager & fileManager)407 SystemGraphLoader::SystemGraphLoader(IFileManager& fileManager) : fileManager_ { fileManager } {}
408
Destroy()409 void SystemGraphLoader::Destroy()
410 {
411 delete this;
412 }
413
414 // SystemGraphLoader factory
Create(IFileManager & fileManager)415 ISystemGraphLoader::Ptr SystemGraphLoaderFactory::Create(IFileManager& fileManager)
416 {
417 return ISystemGraphLoader::Ptr { new SystemGraphLoader(fileManager) };
418 }
419
GetInterface(const Uid & uid) const420 const IInterface* SystemGraphLoaderFactory::GetInterface(const Uid& uid) const
421 {
422 if ((uid == ISystemGraphLoaderFactory::UID) || (uid == IInterface::UID)) {
423 return this;
424 }
425 return nullptr;
426 }
427
GetInterface(const Uid & uid)428 IInterface* SystemGraphLoaderFactory::GetInterface(const Uid& uid)
429 {
430 if ((uid == ISystemGraphLoaderFactory::UID) || (uid == IInterface::UID)) {
431 return this;
432 }
433 return nullptr;
434 }
435
Ref()436 void SystemGraphLoaderFactory::Ref() {}
Unref()437 void SystemGraphLoaderFactory::Unref() {}
438
439 CORE_END_NAMESPACE()
440