Skip to content

Commit dd0070a

Browse files
rollerozxaSmallJokerappgurueusfan5
authored
Expose client version information in non-debug builds (#15708)
Co-authored-by: SmallJoker <[email protected]> Co-authored-by: Lars Mueller <[email protected]> Co-authored-by: sfan5 <[email protected]>
1 parent e6cf081 commit dd0070a

File tree

8 files changed

+95
-24
lines changed

8 files changed

+95
-24
lines changed

builtin/game/misc_s.lua

+22
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,25 @@ if core.set_push_moveresult1 then
112112
end)
113113
core.set_push_moveresult1 = nil
114114
end
115+
116+
-- Protocol version table
117+
-- see also src/network/networkprotocol.cpp
118+
core.protocol_versions = {
119+
["5.0.0"] = 37,
120+
["5.1.0"] = 38,
121+
["5.2.0"] = 39,
122+
["5.3.0"] = 39,
123+
["5.4.0"] = 39,
124+
["5.5.0"] = 40,
125+
["5.6.0"] = 41,
126+
["5.7.0"] = 42,
127+
["5.8.0"] = 43,
128+
["5.9.0"] = 44,
129+
["5.9.1"] = 45,
130+
["5.10.0"] = 46,
131+
["5.11.0"] = 47,
132+
}
133+
134+
setmetatable(core.protocol_versions, {__newindex = function()
135+
error("core.protocol_versions is read-only")
136+
end})

doc/lua_api.md

+26-2
Original file line numberDiff line numberDiff line change
@@ -5578,7 +5578,7 @@ Utilities
55785578
* It's possible that multiple Luanti instances are running at the same
55795579
time, which may lead to corruption if you are not careful.
55805580
* `core.is_singleplayer()`
5581-
* `core.features`: Table containing API feature flags
5581+
* `core.features`: Table containing *server-side* API feature flags
55825582

55835583
```lua
55845584
{
@@ -5693,6 +5693,7 @@ Utilities
56935693
```
56945694

56955695
* `core.has_feature(arg)`: returns `boolean, missing_features`
5696+
* checks for *server-side* feature availability
56965697
* `arg`: string or table in format `{foo=true, bar=true}`
56975698
* `missing_features`: `{foo=true, bar=true}`
56985699
* `core.get_player_information(player_name)`: Table containing information
@@ -5714,17 +5715,40 @@ Utilities
57145715
min_jitter = 0.01, -- minimum packet time jitter
57155716
max_jitter = 0.5, -- maximum packet time jitter
57165717
avg_jitter = 0.03, -- average packet time jitter
5718+
5719+
-- The version information is provided by the client and may be spoofed
5720+
-- or inconsistent in engine forks. You must not use this for checking
5721+
-- feature availability of clients. Instead, do use the fields
5722+
-- `protocol_version` and `formspec_version` where it matters.
5723+
-- Use `core.protocol_versions` to map Luanti versions to protocol versions.
5724+
-- This version string is only suitable for analysis purposes.
5725+
version_string = "0.4.9-git", -- full version string
5726+
57175727
-- the following information is available in a debug build only!!!
57185728
-- DO NOT USE IN MODS
57195729
--serialization_version = 26, -- serialization version used by client
57205730
--major = 0, -- major version number
57215731
--minor = 4, -- minor version number
57225732
--patch = 10, -- patch version number
5723-
--version_string = "0.4.9-git", -- full version string
57245733
--state = "Active" -- current client state
57255734
}
57265735
```
57275736

5737+
* `core.protocol_versions`:
5738+
* Table mapping Luanti versions to corresponding protocol versions for modder convenience.
5739+
* For example, to check whether a client has at least the feature set
5740+
of Luanti 5.8.0 or newer, you could do:
5741+
`core.get_player_information(player_name).protocol_version >= core.protocol_versions["5.8.0"]`
5742+
* (available since 5.11)
5743+
5744+
```lua
5745+
{
5746+
[version string] = protocol version at time of release
5747+
-- every major and minor version has an entry
5748+
-- patch versions only for the first release whose protocol version is not already present in the table
5749+
}
5750+
```
5751+
57285752
* `core.get_player_window_information(player_name)`:
57295753

57305754
```lua

games/devtest/.luacheckrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ read_globals = {
3131
"PcgRandom",
3232

3333
string = {fields = {"split", "trim"}},
34-
table = {fields = {"copy", "getn", "indexof", "insert_all"}},
34+
table = {fields = {"copy", "getn", "indexof", "insert_all", "key_value_swap"}},
3535
math = {fields = {"hypot", "round"}},
3636
}
3737

games/devtest/mods/unittests/get_version.lua

-16
This file was deleted.

games/devtest/mods/unittests/init.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ dofile(modpath .. "/crafting.lua")
192192
dofile(modpath .. "/itemdescription.lua")
193193
dofile(modpath .. "/async_env.lua")
194194
dofile(modpath .. "/entity.lua")
195-
dofile(modpath .. "/get_version.lua")
195+
dofile(modpath .. "/version.lua")
196196
dofile(modpath .. "/itemstack_equals.lua")
197197
dofile(modpath .. "/content_ids.lua")
198198
dofile(modpath .. "/metadata.lua")
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
unittests.register("test_get_version", function()
2+
local version = core.get_version()
3+
assert(type(version) == "table")
4+
assert(type(version.project) == "string")
5+
assert(type(version.string) == "string")
6+
assert(type(version.proto_min) == "number")
7+
assert(type(version.proto_max) == "number")
8+
assert(version.proto_max >= version.proto_min)
9+
assert(type(version.is_dev) == "boolean")
10+
if version.is_dev then
11+
assert(type(version.hash) == "string")
12+
else
13+
assert(version.hash == nil)
14+
end
15+
end)
16+
17+
unittests.register("test_protocol_version", function(player)
18+
local info = core.get_player_information(player:get_player_name())
19+
20+
local maxver = 0
21+
for _, v in pairs(core.protocol_versions) do
22+
maxver = math.max(maxver, v)
23+
end
24+
assert(maxver > 0) -- table must contain something valid
25+
26+
-- If the client is older than a known version then it's pointless.
27+
if info.protocol_version < maxver then
28+
core.log("warning", "test_protocol_version: client is outdated, skipping test!")
29+
return
30+
end
31+
local info_server = core.get_version()
32+
if info.version_string ~= (info_server.hash or info_server.string) then
33+
core.log("warning", "test_protocol_version: client is not the same version. False-positive possible.")
34+
end
35+
36+
-- The protocol version the client and server agreed on must exist in the table.
37+
local match = table.key_value_swap(core.protocol_versions)[info.protocol_version]
38+
assert(match ~= nil)
39+
print(string.format("client proto matched: %s sent: %s", match, info.version_string))
40+
end, {player = true})

src/network/networkprotocol.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
[scheduled bump for 5.11.0]
6565
*/
6666

67+
// Note: Also update core.protocol_versions in builtin when bumping
6768
const u16 LATEST_PROTOCOL_VERSION = 47;
6869

6970
// See also formspec [Version History] in doc/lua_api.md

src/script/lua_api/l_server.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,10 @@ int ModApiServer::l_get_player_information(lua_State *L)
239239
lua_pushstring(L, info.lang_code.c_str());
240240
lua_settable(L, table);
241241

242+
lua_pushstring(L, "version_string");
243+
lua_pushstring(L, info.vers_string.c_str());
244+
lua_settable(L, table);
245+
242246
#ifndef NDEBUG
243247
lua_pushstring(L,"serialization_version");
244248
lua_pushnumber(L, info.ser_vers);
@@ -256,10 +260,6 @@ int ModApiServer::l_get_player_information(lua_State *L)
256260
lua_pushnumber(L, info.patch);
257261
lua_settable(L, table);
258262

259-
lua_pushstring(L,"version_string");
260-
lua_pushstring(L, info.vers_string.c_str());
261-
lua_settable(L, table);
262-
263263
lua_pushstring(L,"state");
264264
lua_pushstring(L, ClientInterface::state2Name(info.state).c_str());
265265
lua_settable(L, table);

0 commit comments

Comments
 (0)