Today I wish to brainstorm the "GodsEye Presets System" that LM4 could incorporate.
... since it's been a while we worked on LM4
- A
Processis identical to aRulein LM3.- We renamed it in LM4 to be more logical relative to the Functions System we created.
- Processes (again, 'rules'...) have been moved to
settings.yml.- There is no more
rules.yml. - Also remember
presets.yml,groups.yml, etc.
- There is no more
This presets system is special: instead of presets only being usable
in certain places, such as the root of a Process with
use-presets: [...], the GodsEye presets system allows users to declare
use-presets wherever they wish. This greatly expands the wealth
of customisation that users have at their fingertips, allowing us and
users to reduce boilerplate in settings.yml to the maximum extent.
The reason I named this presets system 'GodsEye' is because the Presets
Parser will be able to work in every nook and cranny in settings.yml,
as I explained above.
In the file-loading stage of LM's startup, Configurate will load the YAML files as per normal. However, for the settings file, it will load its standard "synchronised" root node object AND then store a 'clone' of that object. The cloned object is treated as the standard one to use throughout the plugin, and the synchronised one is used only when the file needs to be updated between file-versions.
The reason we are using a clone of the root node instead of the actual file-synchronised object (where we can make edits and save them to the file - file-synchronised) is because we want to allow the Presets System to modify the file in memory but not save these changes back to the file. This allows us to very easily parse the settings file alongside the presets declared within it so that the Functions System doesn't even need to know that parts of the file are using presets at all. This massively simplifies the logic for each and every condition and action class in the parsing stage.
Once the cloned object is made, and the Presets file is loaded,
then LM will parse the settings file. The first part in the parsing
stage, before it converts any functions to objects and so on,
is preset replacement. Anywhere it says use-presets, it will do this:
- grab the config node for the preset
- clone it
- merge the node where the use-presets was declared into the cloned preset one
- set the original use-presets declaration node to the cloned preset one
That might sound a little complex, though this visual represention may help you understand it further...
'preset':
something: true
another_thing:
a: false
b: ['Hey']
x:
a: false
z: "Cool!"some-place-in-settings-yml:
use-presets: ['preset']
something: false
another_thing:
b: ['Hey Hey']
x:
b: "Cool"
z: "Very cool"
another_another_thing: 1.3431Essentially the preset + declared sections, where the declared section overrides the contents of the preset section if both overlap.
some-place-in-settings-yml:
something: false # +base < declared
another_thing: # +base = declared
a: false # +base
b: ['Hey Hey'] # +base < declared
x: # +base = declared
a: false # +base
b: "Cool" # +base < declared
z: "Very cool" # +base < declared
another_another_thing: 1.3431 # +declaredRemember that this is what the parser sees, not what the user's file looks like. The user's file is unaffected. This is all done in memory under a cloned object as I described before.
| Line feature | Description |
|---|---|
+base < declared |
Line added from base then overriden by declared |
+base = declared |
Line added from base and declared but unchanged |
+base |
Line added from base but not declared |
+declared |
Line added from declared |
Hopefully this system will work properly with lists and so on.
- In the last presets system brainstorm, we forced users to state
whether they wanted their preset to "add" to a process or "override"
conditions/actions of the same ID.
- This was messy and convoluted, and forced some situations to use a lot of boilerplate.
- This problem is solved because now users can achieve the same functionality with far less boilerplate as they can specify any configuration node to use a preset rather than only the root node of each Process. They can override specific actions/conditions, or they can add actions/conditions to a Process, whatever they want!
How is the merge being accomplished?
Think of Section A and Section B as two individual maps. Let Section A be a preset and Section B be any section inside settings.yml. Remember, this does not need to be at the root of a Process, it can be anywhere inside settings.yml.
Clone Method
let
sac= clone of Section A (Section A Clone)for each node in Section B (including the root node, where
use-presetswas declared)sac(so it overrides anything as well as adding them in if they didn't exist)sacis effectively Section B with the preset used as a baseplate.)set Section B to
sacto finalise the process.No-Clone Method
This is an alternative, but less intuitive method.
It is easier to say in English but doesn't look as good in code due to nested control structures (for, if, etc).
Why clone?
The reason that we clone Section A is that we do not want to modify the preset itself, rather we are using it as a template and overwriting it with Section B's contents.