Passengers usually prefer fast paths to short paths.
Average travel times of links are updated in real-time for use in Dijkstra's algorithm,
and newer travel times weigh more, just like capacities.
IsSavegameVersionUntil() did a [0, N] check, not [0, N) as the
name suggests.
Until can be a confusing word, where people consider it to be
including the upperbound. Dictionary states it means "before",
excluding the upperbound. There are long debates about who is right.
So, simply remove away from this ambiguity, and call it "before"
and "before or at". This makes the world easier for everyone.
We no longer need them. If you want to remove a field .. just
remove it! Because of the headers in the savegame, on loading,
it will do the right thing and skip the field.
Do remember to bump the savegame version, as otherwise older
clients can still load the game, but will reset the field you
have removed .. that might be unintentially.
We won't be able to make it fully self-descriptive (looking at you
MAP-chunks), but anything else can. With this framework, we can
add headers for each chunk explaining how each chunk looks like
in detail.
They also will all be tables, making it a lot easier to read in
external tooling, and opening the way to consider a database
(like SQLite) to use as savegame format.
Lastly, with the headers in the savegame, you can freely add
fields without needing a savegame version bump; older versions
of OpenTTD will simply ignore the new field. This also means
we can remove all the SLE_CONDNULL, as they are irrelevant.
The next few commits will start using this framework.
This makes it easier to spot chunks that have a save_proc that
is a nullptr, but also prevents confusion, where it looks like
the CH_ type of a chunk has influence on how it is being read.
It is not, it is only used for saving.
This adds two byte extra to those chunks, and might feel a bit
silly at first. But in later changes we will prefix CH_ARRAY with
a table header, and then this change shines.
Without this, we could still add headers to these chunks, but any
external reader wouldn't know if the CH_RIFF has them or not. This
way is much more practical, as they are now more like any other
chunk.
This means that during loading we can validate that what is saved
is also that what is expected. Additionally, this makes all list
types similar to how they are stored on disk:
First a gamma to indicate length, followed by the data.
The size still depends on the type.
Using SL_ARR for this gives us a bit of trouble later on, where we
add a length-field to SL_ARR. This of course is not the intention
of SLE_CONDNULL. So better seperate it.
This wasn't consistently done, and often variables were used that
were read by an earlier blob. By moving it next to the struct
itself, the code becomes a bit more self-contained and easier to
read.
Additionally, this allows for external tooling to know how many
structs to expect, instead of having to know where to find the
length-field or a hard-coded value that can change at any moment.
With the new SLEG_STRUCT it is much easier to embed a struct
in a struct, where the sub-struct has limitations on when it is
being used.
This makes both the code easier to read (less magic) and avoids
the SaveLoad needing to know all these things about Stations
and Vehicles.
The commits following this will use this new functionality.
Currently, a few places do this manually. This has as drawback that
the Save() and Load() code need to be in sync, and that any change
can result in (old) savegames no longer loading. In general, it is
annoying code to maintain.
By putting everything in a description table, and use that for
both Save() and Load(), it becomes easier to see what is going on,
and hopefully less likely for people to make mistakes.
You can easily mistake SlList / SL_LST to be a list of SL_VAR, but
it is a list of SL_REF. With this rename, it hopefully saves a few
people from "wtf?" moments.
Prepare the full description and send it to SlObject. This does
require some code to be able to read to a SLE_VAR_NULL, like strings
etc, as there is no way to know their length beforehand.
It was rather confusing which one was for what, especially as some
SaveLoad flags were settings-only. Clean up this mess a bit by
having only Setting flags.
Basically, this changes "SaveLoad *" to either:
1) "SaveLoadTable" if a list of SaveLoads was meant
2) "SaveLoad &" if a single entry was meant
As added bonus, this removes SL_END / SLE_END / SLEG_END. This
also adds core/span.hpp, a "std::span"-lite.