Skip to content

Document compatibility guarantee precisely #223

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions content/for-creators/compatibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
---
title: Compatibility guarantee (for creators)
---

# Compatibility guarantee (for creators)

Luanti tries to guarantee backwards compatibility to a reasonable extent
so that content usually continues to work as Luanti evolves.

## Deprecations

Deprecations are found along with the documentation of the deprecated features
in the various files in the `doc` folder, in particular,
[`lua_api.md`](https://github.com/luanti-org/luanti/blob/master/doc/lua_api.md).

For many features, there are *deprecation warnings*,
some of which give you a stack trace that tells you where deprecated API usage originated.

{{< notice tip >}}
- For you to see warnings at all in-game, you might want to set `chat_log_level` to `warning` or higher.
- Your `deprecated_lua_api_handling` setting should be set to at least `log`;
you should even consider `error` if you have a relatively clean game.
- Full stack traces for deprecation warnings are logged at the lower `info` log level,
so you will want to set `debug_log_level` to `info` to see them in `debug.txt`.
{{< /notice >}}

### Minor Breakages

Despite trying to abide by [semantic versioning](https://semver.org),
Luanti will occasionally have minor compatibility breakages in minor releases (but not in patch releases) as is deemed sensible.
These will usually follow after a deprecation warning has been in for a couple releases.

{{< notice tip >}}
Make sure to follow [the changelog](https://docs.luanti.org/about/changelog/) for compatibility notes
including deprecations and minor breakages.
Test your content on newer engine versions and fix deprecation warnings.
{{< /notice >}}

### Major Breakages

Planned major breakages are documented in [`breakages.md`](https://github.com/luanti-org/luanti/blob/master/doc/breakages.md).
They are to be done eventually in the 6.0 release of Luanti, which is still far off.

## Undocumented behavior

Generally, you can not assume that undocumented (but exposed or observable)
APIs or behaviors are subject to the backwards compatibility guarantee.
You should always try to write your mods such that you only rely on documented behavior.
In particular, try to avoid "cargo culting": When taking code from somewhere else,
make sure to understand *why* it works and how (that) it uses the engine correctly.

If you find that you rely on an undocumented feature,
please bring it to the attention of the Luanti community by
[filing a feature request](https://github.com/luanti-org/luanti/issues/new?labels=Feature+request&template=feature_request.yaml),
asking for it to be documented.
Otherwise it is possible that behavior is accidentally or deliberately changed,
simply because there is no reason to assume that anyone is relying on it.

## Regressions

There will unfortunately always be bugs that accidentally cause backwards compatibility to be broken.
These are called *regressions*: Something that used to work no longer does.

Regressions are not strictly limited to documented behavior:
The documentation is not a practically complete specification,
so exceptions are commonly made, where the engine developers work to preserve a previous behavior even if it was undocumented.
Depending on the impact, regressions concerning undocumented behavior
may also be considered important enough to be fixed.

{{< notice tip >}}
If you find out that behavior unexpectedly changed, please
[file a bug report](https://github.com/luanti-org/luanti/issues/new?labels=Unconfirmed%20bug&template=bug_report.yaml).
{{< /notice >}}

## Client and Server

You need to decide whether older clients not supporting newer features
is game-breaking and older clients have to be prevented from playing,
or whether you can implement sensible fallbacks.

When writing a game you can provide an appropriate default value for `protocol_version_min`
in `minetest.conf` to facilitate this.

{{< notice tip >}}
You can use `core.get_player_information` to get protocol versions and `core.protocol_versions`
to relate them to Luanti client versions. For example, to check whether a client
has at least the feature set of Luanti 5.8.0 or newer, you could do:
`core.get_player_information(player_name).protocol_version >= core.protocol_versions["5.8.0"]`
{{< /notice >}}

## Lua API

"Namespaces" (like `core`), "classes" (like `VoxelArea`) and "structs"
(like the return value of `player:get_player_control()`)
can and will be extended by adding new constants, functions or methods.
Generally, as a rule of thumb, "tables may have more fields in the future".

Maybe there is no `core.frobnicate` today, but there might be tomorrow.
This is why you should use your own namespaces and wrappers
and not try to "extend" engine namespaces or classes yourself -
there could be collisions in the future. [^maintenance]
Comment on lines +95 to +101
Copy link
Member

@SmallJoker SmallJoker Apr 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we compress this this?

Suggested change
can and will be extended by adding new constants, functions or methods.
Generally, as a rule of thumb, "tables may have more fields in the future".
Maybe there is no `core.frobnicate` today, but there might be tomorrow.
This is why you should use your own namespaces and wrappers
and not try to "extend" engine namespaces or classes yourself -
there could be collisions in the future. [^maintenance]
may include additional fields, return values or function arguments in the future.
To avoid potentially sabotaging the engine API in the future,
mods should only use and extend their own namespace where possible.

If you are forced to share a namespace with the engine (e.g. in entity definitions or item definitions),
the current convention is to prefix your fields with an underscore (`_`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant documentation. Please refer to lua_api.md -> Entity definition -> _custom_field.

if multiple mods share the same namespace, using the mod name for namespacing is recommended.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example might be good.
I.e. do we suggest _modname_foo or modname_foo (or modname.foo)? Some common mods use the latter (pipeworks, mesecons), but we might want to recommend the former.


[^maintenance]: And additionally, even if the risk of this is low,
you make it harder for maintainers to see where something comes from.
A maintainer seeing `core.foo(...)` will expect it to be a Luanti function,
documented in the Luanti documentation, not something coming from some (which?) mod.

Similarly, functions that return tables may return tables with *more* fields in the future.
Hence you **should not** iterate over fields and raise errors for fields you don't recognize:
Just get the fields you want and ignore extraneous fields.

Functions that take tables might support more fields in the future.
Hence you **should not** provide extraneous fields in your tables
(it's also bad style and likely confusing to a reader).

Functions that take multiple parameters might take more parameters in the future.
Be warned that not all engine functions are designed to be overridden ("hookable").

{{< notice tip >}}
If you're hooking a function,
you should use a vararg to perfectly forward the parameter list, like this:

```lua
local frobnicate = core.frobnicate
function core.frobnicate(...)
print("frobnication is starting! arguments are:", ...)
return frobnicate(...)
end
```

This also applies conversely to perfectly forwarding return values.
This might require storing them in a table:

```lua
local function pack(...)
return {n = select("#", ...), ...}
end

local frobnicate = core.frobnicate
function core.frobnicate(...)
local results = pack(frobnicate(...))
print("frobnication is done! results are:", unpack(results, 1, results.n))
return unpack(results, 1, results.n)
end
```
{{< /notice >}}

Functions that take values of certain types today may take values of different types tomorrow.
For example it often happens that a parameter list is replaced with a single table argument.

## See also

- [Keeping world compatibility (as a creator)](/for-creators/keeping-world-compatibility/)
- [Compatibility guarantee for players](/for-players/compatibility)
70 changes: 70 additions & 0 deletions content/for-players/compatibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
title: Compatibility guarantee (for players)
---

# Compatibility guarantee (for players)

Luanti tries to guarantee backwards compatibility so you can usually
safely upgrade your client and continue playing the games and joining the servers you like.

{{< notice tip >}}
Check out the compatibility notes in [the changelog](https://docs.luanti.org/about/changelog/)
before upgrading.
{{< /notice >}}

## Client and Server

Newer clients should be able to connect to and be able to play without issues on older servers,
"downgrading" largely to the feature set of an older client.
(Some features however can be implemented purely on the server or on the client,
making them automatically compatible with older client and server versions respectively.)

Older clients should be able to connect to and be able to play on newer servers,
Copy link
Contributor

@sfan5 sfan5 Apr 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: this whole file is written in a specification style ("should" over "is/are" or "will") which isn't very reassuring to readers

Copy link
Member

@SmallJoker SmallJoker Apr 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just shorten the entire section to this:

Within the same major Luanti version, the server and client (i.e. the instance that you
are using) agree to communicate on the lower feature set of the two. Whereas some features
can be purely client-side or server-side, others do depend on the counterpart. Hence, using
an equally new or more recent client version tends to result in a better experience.

so long as they do not use any newer features they do not support.

This means that, so long as the game remains the same,
it is possible to safely upgrade client and server independently.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curiously this sentence is assertive while in practice it's more of a "mostly"


## Games and Mods

Backwards compatibility applies to the continued functioning of old *games*
(or "mod soups" more broadly) -
sets of mods that were written against an older Luanti version.

This means that there may be new features which are, possibly necessarily,
incompatible with the assumptions older mods may make, if they are used at all.

Hence you may have a working configuration, add a new mod enabling/using one of these new features,
and by this cause issues with existing old mods.

For example Luanti 5.9 allowed omitting punchers.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"... in on_punch callbacks"?

Copy link
Contributor Author

@appgurueu appgurueu Apr 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generally, both in obj:punch(...) and consequently also in the callbacks including e.g. on punchplayer (which is what breaks these mods), but i don't want to get into details too much

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't need more details, but context. "Omit punchers" without any context just sounds confusing.

Mods that were written against older versions may expect there to always be a puncher.
If a new mod now omits the puncher (as it may), this may cause a crash in an older mod,
which is inevitable: The older mod needs to decide what to do in case of a missing puncher.
Comment on lines +40 to +43
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

examples are good but this may be too technical

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i just wonder if the point is clear without the example. if not, maybe we can find a better example?


Backwards compatibility is not absolute.
Sometimes old mods or games will break because a bug has been fixed,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably also be in the for-creators file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is here deliberately - it explains to players that sometimes the engine will break content, and the solution may be to upgrade that content. like with the bone override bugfix.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it should be here. But also in for-creators, because this info is part of the API backwards compatibility doc. Creators need to know that it is possible that bugs in (non-side) effects can be fixed.

or because a minor breakage has happened following a deprecation.
In these cases, you should try updating your mods and games,
and if the problem persists, report the issue to the respective maintainers.

## Worlds

Newer Luanti versions will be able to open old worlds.
This is taken extremely seriously and goes back more than a decade.

However, once you open a world with a newer Luanti version,
that world need no longer be compatible with older Luanti versions,
e.g. because compression has been upgraded or legacy serialization formats
have been upgraded to more modern ones.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably mention the --migrate* and --recompress CLI features.

Also, while serialization formats are usually not deprecated, db backends are.
(((And btw., for advanced db backends, like postgres, that you only get if you (as an advanced user) explicitly enable it, you (as an advanced user) can be more easily expected to migrate, imo.)))

If you want to be on the safe side, make regular backups of your worlds.

## Command-Line Interface

The backwards compatibility guarantee does currently not extend to the command-line interface,
which should not be considered stable (but nevertheless, for the most part, doesn't change very much).

## See also

- [Compatibility guarantee for creators](/for-creators/compatibility)
4 changes: 4 additions & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@
"crossview",
"currentline",
"customised",
"culting",
"dannydark",
"Daretmavi",
"darkrose",
Expand Down Expand Up @@ -246,6 +247,8 @@
"freemove",
"freetype",
"Froggo",
"frobnicate",
"frobnication",
"FSAA",
"FTBFS",
"FXAA",
Expand Down Expand Up @@ -313,6 +316,7 @@
"humidmap",
"hunterdelyx",
"hypot",
"hookable",
"ifdef",
"IFRFSX",
"Ilya",
Expand Down