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

Custom Source Generator Attributes #85

Open
lgarczyn opened this issue Feb 12, 2025 · 1 comment
Open

Custom Source Generator Attributes #85

lgarczyn opened this issue Feb 12, 2025 · 1 comment
Labels
enhancement New feature or request

Comments

@lgarczyn
Copy link

lgarczyn commented Feb 12, 2025

For context, we most use a BoundQuery wrapper over QueryDescription, that adds a few features:

  • A World is bound to each query
  • The world is locked on unlocked for each query, to avoid structural change during the query
  • All query types are checked on construction to see if they belong to that world (some types are available on multiple worlds, but it has to be defined)
  • By default, None<Destroyed> is added to the query
  • Utility methods are mirrored from QueryDescription, such as Count, Destroy Add and Remove

This is extremely useful. It even works as a higher order definition. For example our UI widgets simply define the query that will spawn them automatically.

However, it also has issues. Since our query api uses lambdas for flexibility, avoiding allocations is difficult. We also lose on some performance compared to the source generator.

Here's an simple example:

    private static readonly BoundQuery ContractQuery = PersistentQuery
                                                      .Base
                                                      .WithAll<ECContract>();

    public static void FailUnfinishedContract()
    {
        ContractQuery.Run((ref ECContract contract) =>
        {
            if (contract.State == ECContract.EState.InProgress)
            {
                contract.State = ECContract.EState.FinishedFailed;
                contract.FinishedTime = Time.timeAsDouble;
            }
        });
    }

I've attempted to mirror these features using the source generator, it's however been difficult. Adding default flags is impossible, and we have to specify and lock/unlock the world every time.

  • attributes are read by reading constructor parameters, so it's impossible to create an attribute with a "default" or hardcoded query params

  • only the first attribute that matches the name pattern is used, so "NotDestroyedAttribute" could not be composited with "NotAttribute"

I also wanted to be able to bind a world to a query, or to just add a wrapper around the query to lock the world, but neither seemed possible.

We end up with boilerplate around each generated queries:

    [Query]
    [All(typeof(ECResourceContainer), typeof(ECLink<ECShip>))]
    [None(typeof(ECFlag_Destroyed))]
    private void HandleContainer([Data]List<ResourceJobKey> queryBuffer, Entity containerEntity)
    {
      //...
    }

    private void HandleContainerQuery(List<ResourceJobKey> queryBuffer)
    {
        using WorldLock _ = new (ECSWorlds.Game);
        HandleContainerQuery(ECSWorlds.Game, queryBuffer);
    }

Ideally, this would look more like

    [NotDestroyedQuery]
    [GameWorld(lock: true)]
    [All(typeof(ECResourceContainer), typeof(ECLink<ECShip>))]
    private void HandleContainer([Data]List<ResourceJobKey> queryBuffer, Entity containerEntity)
    {
      //...
    }

This could be done by:

  • adding a virtual query argument attribute that can post-process the query
  • adding a virtual world attribute that can inject any world
  • adding some types of source-generated events with the world param to lock it and unlock it
@genaray genaray added the enhancement New feature or request label Mar 27, 2025
@genaray
Copy link
Owner

genaray commented Mar 27, 2025

Looks like a good addition! Will work on this in the future, pull requests are also welcome! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants