/// <summary> /// Register the given entity prototype class. /// </summary> /// <param name="entityComponentType">Entity class prototype.</param> internal static void TryRegister(Type entityComponentType) { if (!entityComponentType.IsAbstract && !entityComponentType.IsValueType && entityComponentType.GetConstructor(Type.EmptyTypes) == null) { string errorMessage = string.Format("Unable to register component of type {0}!{2}" + "The component doesn't have a default parameterless constructor defined which is not supported on classes inheriting from {1}." + "The {1} will overwrite the values of the constructor with the serialized values of the component." + "Use {3} instead to initialize your component.", entityComponentType.FullName, nameof(EntityComponent), Environment.NewLine, nameof(EntityComponent.OnInitialize)); throw new NotSupportedException(errorMessage); } var typeInfo = new TypeInfo { type = entityComponentType }; _componentTypes.Add(typeInfo); var guidAttribute = (GuidAttribute)entityComponentType.GetCustomAttributes(typeof(GuidAttribute), false).FirstOrDefault(); if (guidAttribute != null) { var guid = new Guid(guidAttribute.Value); var guidArray = guid.ToByteArray(); typeInfo.guid.hipart = BitConverter.ToUInt64(guidArray, 0); typeInfo.guid.lopart = BitConverter.ToUInt64(guidArray, 8); } else { // Fall back to generating GUID based on type var guidString = Engine.TypeToHash(entityComponentType); var half = (int)(guidString.Length / 2.0f); if (!ulong.TryParse(guidString.Substring(0, half), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out typeInfo.guid.hipart)) { Log.Error("Failed to parse {0} to UInt64", guidString.Substring(0, half)); } if (!ulong.TryParse(guidString.Substring(half, guidString.Length - half), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out typeInfo.guid.lopart)) { Log.Error("Failed to parse {0} to UInt64", guidString.Substring(half, guidString.Length - half)); } } var componentAttribute = (EntityComponentAttribute)entityComponentType.GetCustomAttributes(typeof(EntityComponentAttribute), false).FirstOrDefault(); if (componentAttribute == null) { componentAttribute = new EntityComponentAttribute(); } if (componentAttribute.Name.Length == 0) { componentAttribute.Name = entityComponentType.Name; } if (entityComponentType.IsAbstract || entityComponentType.IsInterface) { // By passing an empty string as the name the component will still be registered, // but the Sandbox will not show it in the AddComponent menu. componentAttribute.Name = string.Empty; } NativeInternals.Entity.RegisterComponent(entityComponentType, typeInfo.guid.hipart, typeInfo.guid.lopart, componentAttribute.Name, componentAttribute.Category, componentAttribute.Description, componentAttribute.Icon); // Register all bases, note that the base has to have been registered before the component we're registering right now! var baseType = entityComponentType.BaseType; while (baseType != typeof(object)) { NativeInternals.Entity.AddComponentBase(entityComponentType, baseType); baseType = baseType.BaseType; } if (!entityComponentType.IsAbstract) { RegisterComponentProperties(entityComponentType); } }