/// <summary> /// Load all entity definitions /// </summary> private static void LoadEntities() { // Retrieve asset path and build entity database path var path = Path.Combine(Paths.DataDirectory, "json", "entities"); // Inspect every JSON document and save in cache foreach (var file in Directory.GetFiles(path, "*.json", SearchOption.AllDirectories)) { // File is a full path to the particular document try { // Parse JSON file using (var fileReader = File.OpenText(file)) using (var jsonReader = new JsonTextReader(fileReader)) { // Since we want to allow multiple entity definitions in a single file, we require // the top level structure to be an array. var dataSource = JArray.ReadFrom(jsonReader) as JArray; // Loop through all entity definitions. They have to be objects. foreach (var entry in dataSource) { if (entry.Type != JTokenType.Object) { continue; } // Found an object. var currentObject = entry as JObject; // Try to create entity type info object and store in cache var info = new EntityTypeInfo(currentObject); TypeInfos.Add(info.Name, info); // Store JSON object in cache for later use JsonObjectCache.Add(info.Name, currentObject); } } } catch (Exception e) { Logger.PostMessageTagged(SeverityLevel.Fatal, "EntityManager", String.Format("Failed to load entity definition file \"{0}\": {1}", Path.GetFileName(file), e.Message)); throw; } } Logger.PostMessageTagged(SeverityLevel.Debug, "EntityManager", $"Loaded {TypeInfos.Count} entity types"); }
/// <summary> /// Internal implementation of entity construction. Populates entity components with those /// associated with given entity type info object, and then continues recursively for any /// base types present in the type. /// </summary> /// <param name="e">Entity to populate</param> /// <param name="type">Entity type info object to retrieve component types from</param> /// <exception cref="EntityDependencyException">If a component of a given type already exists in the entity.</exception> /// <exception cref="Exception">If component construction fails</exception> private static void ConstructInternal(Entity e, EntityTypeInfo type) { // Retrieve entity type JSON node. This is safe since we checked that // the type is actually known to us beforehand. var obj = JsonObjectCache[type.Name]; // Initialize all components foreach (var currentComponent in type.Components) { // Retrieve component type object var componentType = ComponentManager.GetComponentType(currentComponent); // Check if an instance of that component type already exists in the currently constructed // entity. This could indicate a circular dependency if (e.HasComponent(componentType)) { Logger.PostMessageTagged(SeverityLevel.Fatal, "EntityManager", String.Format("Found duplicate component type while constructing entity of type \"{0}\": \"{1}\"", e.TypeName, currentComponent)); Logger.PostMessageTagged(SeverityLevel.Info, "EntityManager", "This could indicate a circular dependency in the entity definition"); throw new EntityDependencyException("Multiple components of same type detected"); } // Retrieve JSON subobject corresponding to current component var subObject = obj[currentComponent] as JObject; // Construct empty component instance var component = Activator.CreateInstance(componentType) as IComponent; // Check for possible failure if (component == null) { Logger.PostMessageTagged(SeverityLevel.Fatal, "EntityManager", String.Format("Could not create instance of component \"{0}\"", currentComponent)); throw new Exception(String.Format("Could not create instance of component \"{0}\"", currentComponent)); } // Parse JSON subobject component.Construct(subObject); // Add it to entity e.AddComponent(currentComponent, component); } // Do the same with all registered base entities/templates foreach (var baseType in type.Bases) { // We know that this type exists, since we called CheckDependencies earlier. ConstructInternal(e, TypeInfos[baseType]); } }