Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Outsource features to Arch.Extended #121

Open
genaray opened this issue May 25, 2023 · 3 comments
Open

Outsource features to Arch.Extended #121

genaray opened this issue May 25, 2023 · 3 comments
Labels
Refactor This should see a refactor.

Comments

@genaray
Copy link
Owner

genaray commented May 25, 2023

The newly introduced Relationships-Feature can be provided outside the core e.g. in Arch.Extended. The same goes with the CommandBuffer and the new Buffers in total.

This would keep the core slim and more organized.

@genaray genaray added the Refactor This should see a refactor. label May 25, 2023
@skelitheprogrammer
Copy link
Contributor

skelitheprogrammer commented Feb 29, 2024

Is it really worth removing the Command Buffer?
Referring to the explanation of Command Buffer, there is a desire, on the contrary, to make it one of the root things of Arch. In other ecs libraries, Command Buffer analogues are a plug for the multithreading problem, which is not excluded with Arch. They look like they were made from the side.

Is World really supposed to have operations to interact with Entity (Add/Set/Remove Component. Maybe even Create?)? Maybe this should be the area of responsibility of Command Buffer, and World should have Extension functionality?

If you look at the same Wikipedia, then in the terminology there is nothing about World, Command Buffer. These are convenient tools for working with ECS pattern (World - entity hub/center/storage; Command Buffer - operation recorder). If you follow the path of lightweight and performance, then you can go very deep, which will create more problems (for example, create common World interface and put World in a separate package, like concrete implementation).

What if, the command buffer will work under the hood (not forgetting the ability to create your own instances), leaving the same extension methods

@jzapdot
Copy link

jzapdot commented Feb 29, 2024

It seems like CommandBuffers are currently the best way to make structural changes in entity queries in Arch. Nearly all of my systems execute query logic akin to this:

World.Query(_queryDescription, entity =>
{
    // Do work to stage adding/removing components or creating/destroying entities by using a command buffer.
});

// Effect all changes by playing back the command buffer
_commandBuffer.Playback();

Are there reasonable or better alternatives to this type of design pattern? If not, it seems like it would be hard to justify extracting them as an optional feature.

@skelitheprogrammer
Copy link
Contributor

skelitheprogrammer commented May 10, 2024

What if we re-imagine Command Buffer?

We will have an IBufferCommand interface whose instances will be stored inside CommandBuffer and executed continuously.

public interface IBufferCommand
{
    void Execute();
}

public sealed partial class CommandBuffer : IDisposable
{
    private readonly List<IBufferCommand> _commands = new(); //or FIFO structure

    public void AddCommand(IBufferCommand command)
    {
        _commands.Add(command);
    }

    public void Playback(bool reset = true)
    {
        lock (this)
        {
            foreach (var item in _commands)
            {
                item.Execute();
            }

            if (reset)
            {
                Reset();
            }
        }
    }

    public void Reset()
    {
        _commands.Clear();
    }

    public void Dispose()
    {
        throw new NotImplementedException();
    }
}

This way we can encapsulate commands (Create/Destroy/Set/Add) in their own implementations and so can create custom commands to integrate them into the flow.

We can create a CreateEntityDeferredCommand that will do the same logic as the current CommandBuffer.Create(types).

public readonly struct CreateEntityDeferredCommand: IBufferCommand
{
    private readonly World _world;

    private readonly ComponentType[] _types;

    private readonly object[] _components;

    public CreateEntityCommand(World world, ComponentType[] types, object[] components) : this()
    {
        _world = world;
        _types = types;
        _components = components;
    }


    public void Execute()
    {
        _world.Create(_types).SetRange(_components);
    }
}

and instead of buffer.AddCommand(new CreateEntityDeferredCommand(...)) we can make some builder struct that allows the user to specify components using a fluent api. With this we can create an extension method to wrap everything in a nice form.

public static partial class CommandBufferExtensions
{
  public static CreateEntityDeferredBuilder Create(this CommandBuffer buffer, World world, in ComponentType[] types)
  {
     CreateEntityDeferredBuilder builder = new(world, types);
     buffer.AddCommand(builder); //or some better place to add command
     return builder;
  }
}
buffer.Create(world, types).Set(C1).Set(C2);

buffer.Set(someActualEntity, C1);
buffer.Add(someActualEntity, C2);
//or maybe wrap up with some api to create chain of modifications on a particular entity at the time

buffer.Remove<C1>(someActualEntity);
buffer.Destroy(someActualEntity)

buffer.Playback();

The same is true for other commands.

Thus, we can move the CommandBuffer to the Extended repo because it no longer applies only to the core. The user can add their own behavior to synchronize with deferred execution.

UPD:
Made a branch where I will implement something similliar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Refactor This should see a refactor.
Projects
Status: In Progress
Development

No branches or pull requests

3 participants