I think maybe an example would help me understand.
Ok, here's an example. May be a little hand-wavy, but hopefully will help.
In the initial generation module, we have:
Code:
@Produces(SurfaceHeightFacet.class)
public class PerlinSurfaceHeightProvider extends FacetProvider {
public process(GeneratingRegion region) {
SurfaceHeightFacet facet = new SurfaceHeightFacet(region.getRegion(), region.getBorderFor(SurfaceHeightFacet.class);
region.put(SurfaceHeightFacet.class, facet);
}
}
@Produces(SolidityFacet.class)
@Requires(@Facet(SurfaceHeightFacet.class))
public class SolidityProvider extends FacetProvider {
public process(GeneratingRegion region) {
SurfaceHeightFacet heightFacet = region.getFacet(SurfaceHeightFacet.class);
SolidityFacet facet = new SolidityFacet(region.getRegion(), region.getBorderFor(SolidityFacet.class));
region.put(SolidityFacet .class, facet);
}
}
So this is basically an initial surface height calculation, followed by converting surface height into a 3-dimensional region of what areas are solid (which would then allow caves and underground dungeons to be carved out). Solidity provider is ordered after PerlinSurfaceHeightProvider by its requirement for SurfaceHeightFacet. Ideally what we want is if anything modifies SurfaceHeightFacet, it is slotted before SolidityProvider, even if this involves new facets. So lets say we wanted to add stone circles, that check and smooth the ground where they are. This would be done like so:
Code:
@Produces(StoneCircleFacet.class)
@Updates(@Facet(SurfaceHeightFacet.class)
public class StoneCircleProvider extends FacetProvider {
public process(GeneratingRegion region) {
SurfaceHeightFacet heightFacet = region.getFacet(SurfaceHeightFacet.class);
StoneCircleFacet facet = new StoneCircleFacet(region.getRegion(), region.getBorderFor(StoneCircleFacet.class));
region.put(StoneCircleFacet.class, facet);
}
}
Updates means that the provider needs to be done after the initial production of a facet, but before it is Required by another provider. So that slots it in between the two. It can also be use the other way, to pull a new facet into process of creating a facet:
Code:
@Updates(@Facet(SurfaceHeightFacet.class)
@Requires(@Facet(SeaLevelTemperatureFacet.class))
public class ColdMountainProvider extends FacetProvider {
public process(GeneratingRegion region) {
SurfaceHeightFacet heightFacet = region.getFacet(SurfaceHeightFacet.class);
SeaLevelTermperatureFacet temperatureFacet = region.getFacet(SeaLevelTemperatureFacet.class);
}
}
Again, this would be run before SoldityProvider.
There is still some loose ends in this. Assuming StoneCircleProvider checks surface height as well as updates it, you would want it ordered after major surface height providers so that nothing comes along and vastly disrupts the surface height afterwards. This suggests either a simple priority system or explicit ordering instructions against specific facet providers, or both. There is also the potential that something may update two facets, where the producer of one facet required the other, and various other unresolvable edge cases (recursions).
It is also worth noting that implementation-wise providers are not really added into a linear list and run in order. When a facet is requested (by a world rasterizer or otherwise), all the facet providers needed to generate that facet are run - and only those providers. If another facet is requested, all the facet providers required to generate that new facet are run, if they haven't been already.
I'm afraid that you won't be able to sqeeze anything between two facets, an example:
1. Using your approach - I had a FacetProvider1 saying (produces A), and FacetProvider2 saying (requires A, produces B), I will not be able to squeeze anything inbetween Facet A and Facet B.
2. Using my approach - I have a FacetProvider1 (produces A) and FacetProvider2 (produces B) saying @AfterFacets(A), then I will be able to introduce a FacetProvider3 saying (produces C), @AfterFacets(A), @BeforeFacets(B).
It is unclear to me why you want to squeeze FacetProvider3/FacetC between FacetProvider1 and 2? FacetProvider2 doesn't use C, so it can't be important to have C available at that point. If you have some future FacetProvider4 that further contributes to Facet B and uses Facet C, then yes C needs to be generated before that step, but I cannot see any value in it being available explicitly before or after FacetProvider2 - only before FacetProvider4 is required. It isn't a matter of everything touching A runs before everything touching B, the ordering is looser than that. It is about having guarantees where necessary, and allowing things to be ordered more loosely where they are not.
Given this, with your approach I feel BeforeFacets is unnecessary. No facet provider needs to run explicitly before a facet is generated, because if it does it cannot be using it - in which case it doesn't matter whether it runs before or after that facet is generated. It is more the case facet providers need to run after a facet is generated, because they require that facet to do their work.
Cervator, the loss of flexibility is probably a bit of a choice between:
1. Minecraft model - where mods are very independent and player essentially could (though with the arrival of modpacks does not anymore) cherry pick the mods - in that case you need very abstract facet model.
2. Modpack model - where mods define only their logic and an "integration mod" gathers them together, configures and defines world generator - in that case you don't need an abstract facet model, since you will know all facets in advance when defining the world.
I'm all for the second model, and while it sacrifices the flexibility offered by first model as players can't just randomly pick a mod and add it to enrich their experience, it seems people less and less do that even in Minecraft, probably for a very good reason.
This would of course require the Terasology to change, and not allow player to choose mods to play, but instead introduce a meta-mod level artifact, that gathers these into playable piles of mods and configures them.
I guess I'm thinking something in between? In the future, I envision Terasology will have GameTypes and Mods, where mods apply to specific gametypes or gametypes that meet some general requirements (usage of specific modules). Both of these things build on top of modules. A GameType will likely either have a fixed world generator or impose some requirements on the world generator - so this will guarantee some Facets will be in use. But at the same time, I would like to see Mods able to new features on top of this, or replace how existing Facets are generated and rasterized. Players will not be able to cherry pick individual modules when this is set up.