This changeset is motivated by a simple concept - get rid of the MiniYaml.Clone and MiniYamlNode.Clone methods to avoid deep copying yaml trees during merging. MiniYaml becoming immutable allows the merge function to reuse existing yaml trees rather than cloning them, saving on memory and improving merge performance. On initial loading the YAML for all maps is processed, so this provides a small reduction in initial loading time.
The rest of the changeset is dealing with the change in the exposed API surface. Some With* helper methods are introduced to allow creating new YAML from existing YAML. Areas of code that generated small amounts of YAML are able to transition directly to the immutable model without too much ceremony. Some use cases are far less ergonomic even with these helper methods and so a MiniYamlBuilder is introduced to retain mutable creation functionality. This allows those areas to continue to use the old mutable structures. The main users are the update rules and linting capabilities.
Moving the key duplication check allows a redundant check on top-level nodes to be avoided. Add tests to ensure key checks are functioning as expected.
MockString:
CollectionOfStrings:
StringA: A
MockString:
CollectionOfStrings:
StringB: B
MockString:
-CollectionOfStrings:
MockString:
CollectionOfStrings:
StringC: C
MockString:
CollectionOfStrings:
StringD: D
MockString:
CollectionOfStrings:
StringA: A
StringB: B
StringC: C
StringD: D
-CollectionOfStrings:
MockString:
CollectionOfStrings:
StringA: A
StringB: B
-CollectionOfStrings:
CollectionOfStrings:
StringC: C
StringD: D
- Providing the comparer as a type argument that is a struct allows the calls to be devirtualised, leading to approx a 3x performance improvement.
- Use a single backing array, rather than a list of arrays.
Two different issues were causing a path search to not explore cells in order of the cheapest estimated route first. This meant the search could sometimes miss a cheaper route and return a suboptimal path.
- PriorityQueue had a bug which would cause it to not check some elements when restoring the heap property of its internal data structure. Failing to do this would invalidate the heap property, meaning it would not longer return the items in correct priority order. Additional tests ensure this is covered.
- When a path search encountered the same cell again with a lower cost, it would not update the priority queue with the new cost. This meant the cell was not explored early enough as it was in the queue with its original, higher cost. Exploring other paths might close off surrounding cells, preventing the cell with the lower cost from progressing. Instead we now add a duplicate with the lower cost to ensure it gets explored at the right time. We remove the duplicate with the higher cost in CanExpand by checking for already Closed cells.
Requires<T> means that trait of type T will be initialized first, and asserts that at least one exists. The new NotBefore<T> means that trait of type T will be initialized first, but allows no traits.
This allows traits to control initialization order for optional dependencies. They want to be initialized second so they can rely on the dependencies having been initialized. But if the dependencies are optional then to not throw if none are present.
We apply this to Locomotor which was previously using AddFrameEndTask to work around trait order initialization. This improves the user experience as the initialization is applied whilst the loading screen is still visible, rather than the game starting and creating jank by performing initialization on the first tick.
- Stream lines in as memory rather than needing to realise a string for each line, via a new method in StreamExts.
- Use span to avoid string allocations during parsing until we want to realise the node itself, in MiniYaml.FromLines.
- Change several callsites to use the streaming extension method rather than string method where possible.
The Makefile behaviour is recreated using the new and significantly
cleaner .NET Core csproj format.
fixheader.exe is promoted to OpenRA.PostProcess.exe and now runs
on all platforms.