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