Dealing with system update orders and the eventual third party DOTS packages

I’m making a DOTS project that I hope to sell on the asset store eventually, and I was wondering how to deal with setting up execution order rules between two packages that don’t know about each other’s existence, without modifying the source code of these packages.

Imagine this scenario: you have PackageA and PackageB that have no knowledge of eachother’s existence because they were made by different people on the asset store, and you need PackageBSystem to update after PackageASystem. And you don’t want to modify the sources of either package because you don’t want to lose package upgradability. And you need to preserve the execution order rules that are already there, but just add some extra rules.

How do you guys envision dealing with this kind of execution order issues with third parties?

Without knowing if this is actually possible now, I’d imagine that some kind of API like this would be ideal (pseudocode):

SystemsUtility.AddUpdateAfterRuleForSystem(typeof(SuperCharacterSystem), typeof(AwesomeInputSystem));
SystemsUtility.AddUpdateBeforeRuleForSystem(typeof(SuperCharacterSystem), typeof(ExportPhysicsWorld));
SystemsUtility.SetUpdateInGroupRuleForSystem(typeof(SuperCharacterSystem), typeof(SimulationSystemGroup));
SystemsUtility.ClearAllUpdateRulesForSystem(typeof(SuperCharacterSystem));
SystemsUtility.UpdateSystemsSorting();

I imagine this might become pretty common once DOTS adpotion rises, so it would be good to have an easy way to accomplish this. Without rewriting the entire world bootstrap

6 Likes

Use pre-processor directives on the ComponentSystemGroup to conditionally define the attribute. That’s what Rendering.Hybrid does with HDRP.

I’m building my framework such that a user could grab an auto-generated partial system hierarchy and then rearrange the order as they see fit, which I believe solves this problem much more elegantly.

Doesn’t that imply having to modify the sources of the package though?

Like, let’s say someone else makes a cool package that needs to update before my character system and sells it on the store. I don’t want end users to have to go in my character system source code or in the other package’s source code and add the [UpdateAfter(…)] in there

Basically, I think we need to be able to setup execution orders from an utility function (from outside of the code of the systems we want to change the order of)

Oh. You mean allowing the user to fix the order? Unfortunately ComponentSystemGroup’s system list setter is protected. So I suppose you could use some hacky reflection and hide it behind an API.

Yeah, basically you have PackageA and PackageB that have no knowledge of eachother’s existence because they were made by different people, and you need PackageBSystem to update after PackageASystem

I probably should update OP with this example instead

I get what you mean now. Like I said, you can use reflection to access the system order in a ComponentSystemGroup (the list to update is also the order) and tweak it. You could very easily write a little helper API for this.

Otherwise sometime after the next DOTS release I will be publishing my framework which you could borrow from if necessary. That’s assuming the next DOTS release comes out before my brain takes a vacation from weekend game dev and travels to the land of lore, adventure, and nostalgia in two weeks.

Ideally authors of ECS packages should aim for order independence and use WriteGroups for extensibility.

But yes I believe the current World initialization code should be improved to (among other things) accommodate more flexible system ordering .

That’s just not always possible if you want frame-perfectness. An input system must update before a character movement system, and character movement system must update before a physics system

If input and character systems are two independant packages that don’t know eachother, we need a way to tell them to update one before the other

2 Likes

Yes not always. But to get back to your original question:

I would consider either

  1. Keep things simple and just put everything in your own SystemGroup(s) and load it at a sensible location (i.e. before or after TransformGroup).
  2. Disable auto creation and ship your own initialization code that can provide more fine grained loading (detecting whether user is using the default world initialization, multi-world support, etc…).

+1 on that suggestion!

But for you specific case wouldn’t be the best alternative still be to use Version Defines in you packages assemblies (which @DreamingImLatios suggested) to provide that integration natively in your package?

You can also enable others authors to integrate their packages into yours by simply creating your own SystemGroups (which @Singtaa suggested).

I think if you systems are in well defined system groups there shouldn’t be a reason for other authors to go and change that order.
But if for some reason you want to change the system update order in runtime you can at least change the ComponentSystemGroup they update on.
I used to change that by removing system from its current update group then add it to another:

    public static void UpdateInSystemGroup<T>(this World world, Type systemType)
      where T : ComponentSystemGroup {

      var system = world.GetOrCreateSystem(systemType);
      var groupsAssignedToSystem = GetSystemGroups(world, system);
      foreach (var groupSys in groupsAssignedToSystem)
        groupSys.RemoveSystemFromUpdateList(system);

      var groupMgr = world.GetOrCreateSystem<T>();
      groupMgr.AddSystemToUpdateList(system);
    }

    private static ComponentSystemGroup[] GetSystemGroups(World world, ComponentSystemBase system) {
      var groups = world.Systems.Where(s => s is ComponentSystemGroup).Cast<ComponentSystemGroup>();
      return groups.Where(group => group.Systems.Any(s => s == system)).ToArray();
    }
2 Likes

That’s not really a viable solution in my eyes. I don’t want users to have to depend on package authors hard-coding integrations with every other possible package that’s out there. Users need to feel like they have a way to deal with this by themselves in case the package author doesn’t do integrations. And they need to be able to do this without sacrificing package upgradability by modifying its sources

If we take the example of InputSystem and CharacterControlSystem, they both need to happen before physics and before transforms, but they still have to be one after the other. I do put things in update groups, but in this case there would be no way to guarantee the right update order just with sensible update group planning

Is there an easy way to add UpdateBefore/After rules to an UpdateGroup without modifying the code of the UpdateGroup itself? (and also while preserving the rules that are already in place?)

I think changing the groups of systems would be risky, because then you’d probably lose the execution order rules that were in place for the original group they were in.

I think that might work… but it doesn’t sound quite easy and “accessible” enough to me. I’m guessing most users won’t want to have to write a custom bootstrap everytime they add a package to their game.


There should be a quick and easy way to say “I want SystemA to also update before SystemB, on top of the other rules already in place” in one line of code without having to write tons of boilerplate and without having to modify the sources of either system. This will become a very common operation once DOTS becomes widespread on the asset store, and it should be made as easy and non-destructive as possible

2 Likes

There is no easy way to to that because once you change the update list everything falls into the SortSystemUpdateList of ComponentSystemGroup.
Another thing that i tried but wasn’t able implement was to add c# attributes at runtime, this way we could add UpdateAfter/UpdateBefore and then force a Sort.

Yes it will break the original order but then again i think that original order shouldn’t be messed with.

The wall you hit with trying to make something that end users don’t need to modify is that there are almost boundless reasons why an end user might need to order things differently in relation to their own systems. Your data isn’t really your data per say if it’s components, end users could have pre/post systems that make use of it. Or you could have systems that do the opposite, make use of entity/component data that is created by the end user.

Unity’s own systems like hybrid rendering that specify a group make this problem worse. Making it impossible to interleave our own systems which use their own groups. Unity.Physics is better since it doesn’t specify a group. Note Unity isn’t even consistent here in how they handle this between their own feature systems.

I think features should document order of systems and have reasonable defaults. And then have the ability something like the OP’s sample api to override ordering as well as grouping.

Ordering is a global thing. ECS data isn’t encapsulated like OOP design. You can have systems from different third party packages accessing the same data. Third party authors can’t possibly reason correctly about this they don’t have the context to do so. The end user making the game is the only one with complete information. So an api that lets the end user override ordering seems to be the only correct solution.

I do agree with you, the end user should have the power to modify that without modifying the source, what I was talking about you specific use case was just what common asset authors do already (like providing integration for PlayMaker Actions and such).

Didn’t tried yet (will when I get some free time), but I think that for now we can just create an empty system with both [UpdateBefore(typeof(MyCharacterSystem)] and [UpdateAfter(typeof(MyInputSystem)], but this feels too hacky for me.

You can override SortSystemUpdateList. So a user could remove a pair of ComponentSystemGroup from say the SimulationSystemGroup and add it into their custom ComponentSystemGroup in which they override SortSystemUpdateList to make sure the pair is ordered correctly.

It’s kinda clunky. I personally prefer all systems to be explicitly created and ordered by their parent ComponentSystemGroup hierarchically, perhaps with the exception of built-in Unity systems. For anyone who wants that behavior, you just need a simple ICustomBootstrap that filters out all systems that aren’t in the Unity namespace nor top level ComponentSystemGroups (specified by either a subclass type or an attribute).

My humble opinion is that your library should work in a separate World and you just give a user the opportunity to create it in the right time for them so your library world will update after or before the world they need to update things in (Physics, Input).

Second thought is to write a custom bootstrap utillity that will detect any other packages (Unity.Physics, Hybrid Render) so a user can simply add / modify / drag systems relative to your package system presses Save and Bootstrap.cs is getting generated by T4 template or w/e.

Other than that, you are worrying about something that you should not be worried. Eventually, it might discourage you.

Cheers.

I don’t think it’s always possible to separate things in a different world, unless you ask users to go take the built-in systems in the regular world (physics, transform, etc…) and put them each in their separate world, so you can update your world after physics but before transform. Either way, it sounds like a hacky and unsatisfying solution to me. You can’t really expect the average beginner user to be comfortable with doing that. This will make asset store support really painful

Now we’re just throwing unity’s execution order system in the trash and replacing it with our own. It would be much better to have this functionality built-in and easily accessible to all users

It’s important to worry about these things early on, because it might mean doing a big refactor if I wait too long

1 Like

Both of these solutions mean you lose the original execution order rules of the systems you’re reordering. If I make a complex package with lots of systems that must update at very specific times in unity’s update loop (which is what i’m making), there is a lot of room for error when users will attempt to reconstruct this order

I really believe that being able to add execution rules to systems while preserving the original ones, and being able to do so without editing package sources is the only way dealing with third party packages won’t become a nightmare. Both for users and for publishers

I don’t think this is an unrealistic expectation because the functionality is already there. We just need another way to do it aside from putting attributes on top of classes

Users will ask me how to set things up so their camera doesn’t appear to lag behind character movement. I know this from experience. And when they ask me that, I don’t want to tell them that they need to create a codegen’d world bootstrap, or that they need to manually reconstruct all of the rules I’ve already established. I want to just be able to tell them “make camera system also update after character system”

4 Likes