mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Create Entities Without Prototypes (#815)
* Retrofitted ComponentManager_Test to use DependencyCollection. * Renamed ComponentManager_Test to ComponentManager_Tests to follow naming conventions. * Added component add/remove/delete events to IComponentManager. Removed IEntity dependency from ComponentManager. * Entities can now be spawned without a prototype. * Removed unused function. * CreateEntity now actually works with a null prototype ID. The other spawn functions should work as well. Updated doc comments for IEntityManager.SpawnEntity().
This commit is contained in:
committed by
Pieter-Jan Briers
parent
dc4cc71c72
commit
61aba8fc50
@@ -22,9 +22,15 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
[Dependency]
|
||||
private readonly IComponentFactory _componentFactory;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<ComponentEventArgs> ComponentAdded;
|
||||
|
||||
[Dependency]
|
||||
private readonly IEntityManager _entityManager;
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<ComponentEventArgs> ComponentRemoved;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<ComponentEventArgs> ComponentDeleted;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Clear()
|
||||
@@ -105,6 +111,8 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
// mark the component as dirty for networking
|
||||
component.Dirty();
|
||||
|
||||
ComponentAdded?.Invoke(this, new ComponentEventArgs(component));
|
||||
}
|
||||
|
||||
component.OnAdd();
|
||||
@@ -174,6 +182,7 @@ namespace Robust.Shared.GameObjects
|
||||
component.Shutdown();
|
||||
|
||||
component.OnRemove();
|
||||
ComponentRemoved?.Invoke(this, new ComponentEventArgs(component));
|
||||
}
|
||||
|
||||
private void RemoveComponentImmediate(Component component)
|
||||
@@ -188,6 +197,7 @@ namespace Robust.Shared.GameObjects
|
||||
component.Shutdown();
|
||||
|
||||
component.OnRemove();
|
||||
ComponentRemoved?.Invoke(this, new ComponentEventArgs(component));
|
||||
|
||||
DeleteComponent(component);
|
||||
}
|
||||
@@ -206,9 +216,7 @@ namespace Robust.Shared.GameObjects
|
||||
private void DeleteComponent(Component component)
|
||||
{
|
||||
var reg = _componentFactory.GetRegistration(component.GetType());
|
||||
|
||||
_entityManager.RemoveSubscribedEvents(component);
|
||||
|
||||
|
||||
var entityUid = component.Owner.Uid;
|
||||
|
||||
foreach (var refType in reg.References)
|
||||
@@ -225,6 +233,8 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
// mark the owning entity as dirty for networking
|
||||
component.Owner.Dirty();
|
||||
|
||||
ComponentDeleted?.Invoke(this, new ComponentEventArgs(component));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -387,4 +397,24 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class ComponentEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that this event relates to.
|
||||
/// </summary>
|
||||
public IComponent Component { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="ComponentEventArgs"/>.
|
||||
/// </summary>
|
||||
/// <param name="component"></param>
|
||||
public ComponentEventArgs(IComponent component)
|
||||
{
|
||||
Component = component;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,8 @@ namespace Robust.Shared.GameObjects
|
||||
public virtual void Initialize()
|
||||
{
|
||||
_network.RegisterNetMessage<MsgEntity>(MsgEntity.NAME, HandleEntityNetworkMessage);
|
||||
|
||||
_componentManager.ComponentRemoved += (sender, args) => RemoveSubscribedEvents(args.Component);
|
||||
}
|
||||
|
||||
public virtual void Startup()
|
||||
@@ -228,6 +230,22 @@ namespace Robust.Shared.GameObjects
|
||||
/// Allocates an entity and stores it but does not load components or do initialization.
|
||||
/// </summary>
|
||||
private protected Entity AllocEntity(string prototypeName, EntityUid? uid = null)
|
||||
{
|
||||
var entity = AllocEntity(uid);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(prototypeName))
|
||||
return entity;
|
||||
|
||||
var prototype = PrototypeManager.Index<EntityPrototype>(prototypeName);
|
||||
entity.Prototype = prototype;
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates an entity and stores it but does not load components or do initialization.
|
||||
/// </summary>
|
||||
private protected Entity AllocEntity(EntityUid? uid = null)
|
||||
{
|
||||
if (uid == null)
|
||||
{
|
||||
@@ -239,10 +257,17 @@ namespace Robust.Shared.GameObjects
|
||||
throw new InvalidOperationException($"UID already taken: {uid}");
|
||||
}
|
||||
|
||||
var prototype = PrototypeManager.Index<EntityPrototype>(prototypeName);
|
||||
var entity = prototype.AllocEntity(uid.Value, this);
|
||||
var entity = new Entity();
|
||||
|
||||
entity.SetManagers(this);
|
||||
entity.SetUid(uid.Value);
|
||||
|
||||
// allocate the required MetaDataComponent
|
||||
_componentManager.AddComponent<MetaDataComponent>(entity);
|
||||
|
||||
Entities[entity.Uid] = entity;
|
||||
_allEntities.Add(entity);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -251,6 +276,9 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
private protected Entity CreateEntity(string prototypeName, EntityUid? uid = null)
|
||||
{
|
||||
if (prototypeName == null)
|
||||
return AllocEntity(uid);
|
||||
|
||||
var entity = AllocEntity(prototypeName, uid);
|
||||
entity.Prototype.LoadEntity(entity, ComponentFactory, null);
|
||||
return entity;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Shared.Interfaces.GameObjects
|
||||
@@ -7,8 +8,25 @@ namespace Robust.Shared.Interfaces.GameObjects
|
||||
/// <summary>
|
||||
/// Holds a collection of ECS components that are attached to entities.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public interface IComponentManager
|
||||
{
|
||||
/// <summary>
|
||||
/// A component was added to the manager.
|
||||
/// </summary>
|
||||
event EventHandler<ComponentEventArgs> ComponentAdded;
|
||||
|
||||
/// <summary>
|
||||
/// A component was removed from the manager.
|
||||
/// </summary>
|
||||
event EventHandler<ComponentEventArgs> ComponentRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// A component was deleted. This is usually deferred until some time after it was removed.
|
||||
/// Usually you will want to subscribe to <see cref="ComponentRemoved"/>.
|
||||
/// </summary>
|
||||
event EventHandler<ComponentEventArgs> ComponentDeleted;
|
||||
|
||||
/// <summary>
|
||||
/// Instantly clears all components from the manager. This will NOT shut them down gracefully.
|
||||
/// Any entities relying on existing components will be broken.
|
||||
|
||||
@@ -30,10 +30,10 @@ namespace Robust.Shared.Interfaces.GameObjects
|
||||
#region Entity Management
|
||||
|
||||
/// <summary>
|
||||
/// Spawns an initialized entity at the default location.
|
||||
/// Spawns an initialized entity at the default location, using the given prototype.
|
||||
/// </summary>
|
||||
/// <param name="protoName"></param>
|
||||
/// <returns></returns>
|
||||
/// <param name="protoName">The prototype to clone. If this is null, the entity won't have a prototype.</param>
|
||||
/// <returns>Newly created entity.</returns>
|
||||
IEntity SpawnEntity(string protoName);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -407,21 +407,6 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
internal Entity AllocEntity(EntityUid uid, IEntityManager manager)
|
||||
{
|
||||
var entity = (Entity)Activator.CreateInstance(ClassType ?? typeof(Entity));
|
||||
|
||||
entity.SetManagers(manager);
|
||||
entity.SetUid(uid);
|
||||
|
||||
// allocate the required MetaDataComponent
|
||||
manager.ComponentManager.AddComponent<MetaDataComponent>(entity);
|
||||
|
||||
entity.Prototype = this;
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
internal void LoadEntity(Entity entity, IComponentFactory factory, IEntityLoadContext context)
|
||||
{
|
||||
YamlObjectSerializer.Context defaultContext = null;
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
<Compile Include="Server\GameObjects\Components\Container_Test.cs" />
|
||||
<Compile Include="Server\GameObjects\Components\Transform_Test.cs" />
|
||||
<Compile Include="Shared\ContentPack\ResourceManagerTest.cs" />
|
||||
<Compile Include="Shared\GameObjects\ComponentManager_Test.cs" />
|
||||
<Compile Include="Shared\GameObjects\ComponentManager_Tests.cs" />
|
||||
<Compile Include="Shared\GameObjects\EntityState_Tests.cs" />
|
||||
<Compile Include="Shared\IoC\IoCManager_Test.cs" />
|
||||
<Compile Include="Shared\Map\MapChunk_Tests.cs" />
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -10,9 +9,8 @@ using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(ComponentManager))]
|
||||
class ComponentManager_Test
|
||||
[TestFixture, Parallelizable ,TestOf(typeof(ComponentManager))]
|
||||
class ComponentManager_Tests
|
||||
{
|
||||
private const uint CompNetId = 3;
|
||||
|
||||
@@ -237,11 +235,10 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// mimics the IoC system.
|
||||
|
||||
private static IComponentManager ManagerFactory(out IEntityManager entityManager)
|
||||
{
|
||||
var manager = new ComponentManager();
|
||||
var dependencies = new DependencyCollection();
|
||||
|
||||
// set up the registration
|
||||
var mockRegistration = new Mock<IComponentRegistration>();
|
||||
@@ -252,33 +249,19 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
mockFactory.Setup(x => x.GetRegistration(It.IsAny<IComponent>())).Returns(mockRegistration.Object);
|
||||
mockFactory.Setup(x => x.GetRegistration(It.IsAny<Type>())).Returns(mockRegistration.Object);
|
||||
mockFactory.Setup(x => x.GetComponent<DummyComponent>()).Returns(new DummyComponent());
|
||||
dependencies.RegisterInstance<IComponentFactory>(mockFactory.Object);
|
||||
|
||||
// set up the entity manager
|
||||
var mockEntMan = new Mock<IEntityManager>();
|
||||
dependencies.RegisterInstance<IEntityManager>(mockEntMan.Object);
|
||||
|
||||
// Inject the dependency into manager
|
||||
foreach (var field in GetDepFields(typeof(ComponentManager)))
|
||||
{
|
||||
if (field.FieldType.IsAssignableFrom(typeof(IComponentFactory)))
|
||||
{
|
||||
field.SetValue(manager, mockFactory.Object);
|
||||
}
|
||||
else if (field.FieldType.IsAssignableFrom(typeof(IEntityManager)))
|
||||
{
|
||||
field.SetValue(manager, mockEntMan.Object);
|
||||
}
|
||||
}
|
||||
var manager = new ComponentManager();
|
||||
dependencies.InjectDependencies(manager);
|
||||
|
||||
entityManager = mockEntMan.Object;
|
||||
return manager;
|
||||
}
|
||||
|
||||
private static IEnumerable<FieldInfo> GetDepFields(Type targetType)
|
||||
{
|
||||
return targetType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.Where(p => Attribute.GetCustomAttribute(p, typeof(DependencyAttribute)) != null);
|
||||
}
|
||||
|
||||
private class DummyComponent : Component, ICompType1, ICompType2
|
||||
{
|
||||
public override string Name => null;
|
||||
Reference in New Issue
Block a user