Compare commits

...

746 Commits

Author SHA1 Message Date
206d9eebfa Update 2024-02-27 14:16:37 +01:00
dd460bfbb3 Update 2024-02-27 14:14:29 +01:00
96749525a3 Update 2024-02-27 14:12:36 +01:00
c990427d07 update 2024-02-27 13:48:48 +01:00
sfan5
fbec378869 Fix more type promotion mistakes
Someone of these are probably actual bugs and gcc totally doesn't care to warn about them, wtf?
This issue seems to be new with the IrrlichtMt update.
2024-02-26 22:07:40 +01:00
sfan5
9fcd7f2dc0 Fix clang-tidy type promotion errors 2024-02-26 20:47:47 +01:00
sfan5
229389b7f6 Use newer IrrlichtMt 2024-02-26 20:47:47 +01:00
sfan5
5d8a22066c
Change how max_lag is calculated and reported (#14378)
-Change how max_lag is calculated and reported

- Cap singleplayer step at 60Hz

- Clarify dedicated_server_step
2024-02-26 20:46:57 +01:00
sfence
63a9853811
Remove attached sounds when the active object is removed (#14341) 2024-02-25 22:10:39 +00:00
kotek900
39b1311a1b
Add padding to settings tab buttons (#14408) 2024-02-25 22:10:07 +00:00
rubenwardy
f4eba3bfba
Add support for ContentDB package translation (#14410) 2024-02-25 22:09:52 +00:00
sfan5
4caf0e4cb9
Update LTO exclusion list (#14407) 2024-02-25 20:58:42 +01:00
sfan5
762fca538c
Expose SHA256 algorithm to Lua (#14403)
Co-authored-by: chmodsayshello <chmodsayshello@hotmail.com>
2024-02-25 18:12:23 +01:00
SmallJoker
fa1d80b53b
MetaData: restore undocumented set_string behaviour (#14396) 2024-02-25 16:03:05 +01:00
rubenwardy
b4be483d3e
Add support for translating content titles and descriptions (#12208) 2024-02-24 19:13:07 +00:00
grorp
57de599a29
Restore pre-5.9.0-dev behavior of touch_use_crosshair=false shootline (#14389)
* Fix incorrect shootline after releasing pointer if touch_use_crosshair=false

This happened because Android reuses pointer IDs.
Also includes a refactor to merge "m_known_ids" and "m_pointer_pos".

* Restore pre-5.9.0-dev behavior of shootline when !m_has_move_id
2024-02-24 13:12:53 +01:00
sfan5
492aab20fe Fix compiler warnings 2024-02-24 12:39:12 +01:00
ShadowRoi
6952bab519
Mark jpeg-turbo as the default library for compiling in macOS 2024-02-23 21:31:24 +01:00
Muhammad Rifqi Priyo Susanto
87fa4de59c Use forward-slash (/) for path separator in translation files
The lstrip (left trim) call is to make sure that there are no slashes at the beginning of file paths.
2024-02-23 21:30:34 +01:00
lhofhansl
0d4b489545
Detect air-only blocks instead of day/night differences (#14264)
* Detect air-only blocks instead day/night differences

* Write !is_air into the former day-night-diff bit on disk, so that old server can still read maps written by new servers

* Only set is_air bit when reading from disk
2024-02-22 21:47:42 -08:00
numzero
0d30a3071a Add meshgen tests 2024-02-22 15:45:17 +00:00
numzero
753f03ff6a Add mesh comparison functions, for tests 2024-02-22 15:45:17 +00:00
numzero
bf2098c07f Decouple MeshMakeData from Client 2024-02-22 15:45:17 +00:00
numzero
2f35b121a4 Const correctness 2024-02-22 15:45:17 +00:00
David Heidelberg
34286d77c7
Allow toggling touchscreen mode at runtime (#14075)
Signed-off-by: David Heidelberg <david@ixit.cz>
Co-authored-by: Gregor Parzefall <gregor.parzefall@posteo.de>
2024-02-22 15:44:49 +00:00
sfan5
e3cc26cb7c
Irrlicht support changes (#14383) 2024-02-19 21:14:47 +01:00
Gregor Parzefall
84dd812da4 Fix hud_elem_type warning triggered by builtin minimap
Fixes a deprecation warning introduced by adaa4cc2f3c6e624b2c9ab7f40df4139b2a61c5a.
2024-02-19 19:04:43 +01:00
Lars Müller
4acbd59162
Support absent scene node names (#14330)
Contains a hack to support IrrlichtMT revision 14 for now (until we release revision 15)
2024-02-18 11:39:16 +01:00
DS
1e316a9704
Don't use a reference for RaycastState::m_pointabilities (#14376) 2024-02-17 18:36:20 +01:00
sfan5
6ca214fefc
Introduce std::string_view into wider use (#14368) 2024-02-17 15:35:33 +01:00
wsor4035
fa47af737f
Upgrade CI actions (#14377) 2024-02-17 15:34:40 +01:00
sfan5
0f2517070e Update Docker image base 2024-02-17 12:40:18 +01:00
sfan5
f483d10c95
Switch to LLVM-based MinGW toolchain (#14329) 2024-02-16 21:36:19 +01:00
sfan5
8c3a6a819e Adjust bug report template 2024-02-16 12:34:40 +01:00
sfan5
933432e62d Annotate Lua packer with more comments 2024-02-16 12:34:40 +01:00
sfan5
2b97fead9e Fix some potential iterator invalidation issues 2024-02-16 12:34:40 +01:00
cx384
9ac6d330b4
Fix minimap textures overwrite (#14349) 2024-02-15 21:52:41 +01:00
DS
4843890c56
Inline g/setPixel in imageCleanTransparent (#14323) 2024-02-15 19:38:23 +01:00
lhofhansl
c81e0b7433
Allow shaders with disabled post processing pipeline (#14338)
- Allow disabling of the post processing pipeline while leaving shaders enabled
- Also disable post processing on Android by default
2024-02-15 08:25:33 -08:00
sfan5
ce97210eb1 Refactor how script api reads current mod name
This is to prevent future mistakes and make it clearer whether
the mod name can be trusted depending on how it is retrieved.
2024-02-15 11:06:21 +01:00
sfan5
cb5fa56e17 Remove insecure environment from async and emerge environment 2024-02-15 11:06:21 +01:00
fuzun
6cbb9193ea
Fix undefined behaviors (#14365)
* Initialize member `floats` in ContentFeatures

* Do not assign big double to u32

* Do not assign negative floating point number to unsigned integer
2024-02-15 11:05:42 +01:00
sfan5
3cac17d23e
Lua on each mapgen thread (#13092) 2024-02-13 22:47:30 +01:00
sfan5
d4b107e2e8 Enable dynamic_add_media to take the file data instead of a path 2024-02-13 22:44:10 +01:00
sfan5
c90ebad46b Allow specifying name for dynamic media files 2024-02-13 22:44:10 +01:00
sfan5
af69d4f7a9 Allow dynamic_add_media at mod load time 2024-02-13 22:44:10 +01:00
kromka-chleba
6c8ae2b72a
Fix liquid falling if in "float" group (#13789)
* Make falling liquid source nodes replace flowing nodes

This makes falling liquid source nodes in group:float replace
flowing nodes on the ground instead of being placed above
the flowing node.

* Make flowing liquids "fall through" for source nodes

This makes liquids in float and falling_node groups fall through
flowing liquid nodes instead of being supported by them in the air.

---------

Co-authored-by: SmallJoker <SmallJoker@users.noreply.github.com>
Co-authored-by: Lars Mueller <appgurulars@gmx.de>
2024-02-12 23:24:54 +01:00
cx384
7901087466
Rename MINETEST_SUBGAME_PATH to MINETEST_GAME_PATH (#14351) 2024-02-12 23:21:19 +01:00
paradust7
e2ccd14c05
Allow using VBOs for meshes all the way down to 4 vertices (#14366)
This may improve performance substantially if there are many meshes with "few" vertices that would otherwise be retransmitted to the GPU every frame. In testing, this does not seem to decrease performance, even if as few as 4 vertices are used (e.g. particles).
2024-02-12 23:20:48 +01:00
Lars Mueller
a14320fc44 Improve deprecation error messages 2024-02-12 22:58:26 +01:00
David Heidelberg
eb52a149a0
Enable IPO/LTO by default except for debug builds (#14198)
Test case:

```
$ cmake . -DRUN_IN_PLACE=TRUE -DCMAKE_BUILD_TYPE=Release -DBUILD_SERVER=TRUE -DENABLE_TOUCH=FALSE

         minetest minetestserver
W/o LTO:      13M           7.3M
W/  LTO:      11M           5.9M
difference:   15%            19%
```

Also fixes various compiler warnings resulting from compilation using LTO.

---------

Signed-off-by: David Heidelberg <david@ixit.cz>
2024-02-09 00:01:12 +01:00
cx384
adaa4cc2f3
Move hard coded minimap to builtin (#14071) 2024-02-07 20:13:23 +01:00
grorp
f2b99332d9
Add Lua API function to resolve node/collision/selection boxes (#13964) 2024-02-06 20:45:16 +01:00
Lars Müller
4859cf44ce
Fix translation updater script: Handle nested modpacks, support games (#14340) 2024-02-05 20:57:30 +01:00
sfence
83f779c52d
Fix active object adding to not generated block (#14311) 2024-02-04 21:24:08 +01:00
sfan5
c9e10e1dd9 Drop valgrind from CI and instead enable ASan
The recently added ioctl use is reported as a false-positive by valgrind.
I tried moving it to different compilers/versions two times and only
hit further issues that were valgrind's fault.

Also includes a tiny fix.
2024-02-04 21:23:05 +01:00
sfan5
4259ac96ea Optimize fs::CopyFileContents on Linux and Windows 2024-02-04 21:23:05 +01:00
sfan5
714c9361ea Add unit tests for fs::CopyFileContents 2024-02-04 21:23:05 +01:00
sfan5
93381014a0 Bypass media transfer in single player 2024-02-04 21:23:05 +01:00
someone-aka-sum1
16aaef097a
Make the protocol dissector heuristic (#14335) 2024-02-04 21:21:23 +01:00
Lars Müller
1d9c9710d7
Fix short raycasts missing large objects (#14339)
Increases the tolerance from one node to five nodes.
Also optimizes the "sphere" used for pre-filtering entities
to start in the middle of the line segment rather than at the start.
2024-02-04 14:04:05 +01:00
SmallJoker
e7dbd325d2
RemotePlayer: make peer ID always reflect the validity of PlayerSAO (#14317)
Upon disconnect, RemotePlayer still had a peer ID assigned even though
the PlayerSAO object was maked as gone (for removal). This commit makes
that the following always holds true:

	(!sao || sao->isGone()) === (peer_id == PEER_ID_INEXISTENT)
2024-02-02 22:13:24 +01:00
Zemtzov7
893594d81a
Add help formspec for CSM commands (#13937) 2024-02-02 22:12:59 +01:00
techno-sam
176e674a51
Add wear bar color API (#13328)
---------

Co-authored-by: Muhammad Rifqi Priyo Susanto <muhammadrifqipriyosusanto@gmail.com>
Co-authored-by: Lars Müller <34514239+appgurueu@users.noreply.github.com>
Co-authored-by: grorp <gregor.parzefall@posteo.de>
2024-02-02 21:21:00 +01:00
sfan5
e10d8080ba
Add flag to control mgv6 temple generation (#14293) 2024-01-30 21:52:04 +01:00
sfan5
9da1354f3a
Fix missing limit check for block y pos (#14320) 2024-01-30 21:51:51 +01:00
sfan5
e1f6108789 Revert class forward declaration in {client,server}opcodes.h
closes #14324
2024-01-30 17:04:12 +01:00
Lars Mueller
40bf88ac74 Performance: Limit blitting work to overlapping area 2024-01-28 13:01:54 +01:00
sfan5
ffec698d3e Change how [combine parameters are checked
the old checks were too strict
2024-01-28 13:01:54 +01:00
Lars Mueller
b1ee137177 Minor documentation fixes 2024-01-28 13:01:54 +01:00
c8eae52a8a Update CMakeLists.txt 2024-01-28 00:17:14 +01:00
da452bfb4d add debug_force 2024-01-28 00:17:14 +01:00
5d0c9a4838 add_stuff 2024-01-28 00:17:14 +01:00
53f570e699 test 2024-01-28 00:17:14 +01:00
4b9f6fdb7c Merge pull request 'master' (#7) from Mirrorlandia_minetest/minetest:master into master
Reviewed-on: #7
2024-01-28 00:16:41 +01:00
grorp
fbec168e91
Only pause rendering if the Android activity is stopped (#14211) 2024-01-27 14:37:00 +01:00
sfan5
89f3502b56 Move Server ban check to different point 2024-01-27 10:33:32 +01:00
sfan5
5dbc1d4c08 Move some files to src/server/ 2024-01-27 10:33:32 +01:00
sfan5
c0f852e016 Change NetworkPacket to reserve instead of resize
also make the bool serialization clearer and move the constructor
to the header file
2024-01-27 10:33:32 +01:00
sfan5
397682a5b0 Clean up client and server command sending / tables 2024-01-27 10:33:32 +01:00
grorp
2b99dabdac
Touchscreen: Abort ongoing short taps if touch interaction mode changes (#14305) 2024-01-26 23:19:06 +01:00
Bradley Pierce
df9975f35d
Add markdown admonition extension (#14303) 2024-01-26 22:10:57 +01:00
sfan5
4158759265 Move mtevent.h to src/client/ 2024-01-26 22:10:36 +01:00
sfan5
8927e7caf6 Handle some edge cases in tile images 2024-01-26 22:10:36 +01:00
sfan5
a46fe79939 Reduce code duplication in tile.cpp 2024-01-26 22:10:36 +01:00
lhofhansl
2ef080a51b
Slight simplification of RemoteClient::getNextBlocks(...) (#14302) 2024-01-25 11:32:18 -08:00
Sokomine
4468813d47
Show more lines in chat scrollback buffer (#14290) 2024-01-24 19:41:29 +01:00
Lars Mueller
6a2eb4da07 Restore pointability backwards compatibility 2024-01-23 23:30:28 +01:00
sfan5
731b84d725 Reduce some instances of useless data shuffling 2024-01-23 22:34:36 +01:00
sfan5
362e4505e8 Minor improvements to media request / announce code
I had to throw away the code switching sendRequestedMedia to
a bin packing algorithm because it actually performed worse. :(
2024-01-23 22:34:36 +01:00
sfan5
13013d1b8b Fix client loading not aborting correctly in certain cases 2024-01-23 22:34:36 +01:00
sfan5
6df0de565f Check media requests on the server more carefully 2024-01-23 22:34:36 +01:00
sfan5
89eabb5803 Drop speed tests and some other unused code from clientlauncher.cpp 2024-01-23 22:34:36 +01:00
sfan5
6aa4f14a28 Drop some unneeded MOD_REASONs 2024-01-23 22:34:36 +01:00
sfan5
be7844192b Don't save blocks asap for only lighting updates 2024-01-23 22:34:36 +01:00
sfan5
9e3a11534f
Allow fog color to be overriden properly (#14296) 2024-01-23 22:33:33 +01:00
grorp
a29d3cf074
Save the settings in more cases to avoid losing setting changes (especially on Android) (#14266) 2024-01-23 21:33:27 +01:00
Lars Mueller
f6ecd931dc Minor documentation formatting fixes 2024-01-22 22:41:33 +01:00
Lars Mueller
f0180ad488 Fix revoke callbacks being run for false values passed to set_privileges 2024-01-22 22:24:32 +01:00
Lars Müller
afc48cf224
Address set_player_privs footgun (#14297)
---------

Co-authored-by: grorp <gregor.parzefall@posteo.de>
2024-01-22 20:06:03 +01:00
cx384
5958714309
Tool specific pointing and blocking pointable type (#13992) 2024-01-22 18:27:08 +01:00
Lars Mueller
fb461d21a5 Fix waypoint precision wraparound, add bounds check 2024-01-21 21:00:09 +01:00
grorp
404a063fdf
Touchscreen: Allow mods to swap the meaning of short and long taps (punch with single tap) (#14087)
This works through a new field "touch_interaction" in item definitions.
The two most important use cases are:
 - Punching players/entities with short tap instead of long tap (enabled by default)
 - Making items usable that require holding the place button (e.g. bows and shields in MC-like games)
2024-01-21 17:44:08 +01:00
sfan5
8cbd629010
Fix bugs in ModifySafeMap (#14276) 2024-01-20 15:37:30 +01:00
DS
e9233bc169
Fix typo in minimap bumpmapping shader (#14280) 2024-01-20 15:36:53 +01:00
Jaidyn Ann
bec080be8d
Add Esperanto builtin translation (#14215) 2024-01-20 15:36:42 +01:00
David Heidelberg
371b9a7fc2
Move check for strlcpy before config.h generation
Fixes: 225aa107f671 ("Define strlcpy only on platforms where it's not available")
2024-01-19 22:48:43 +01:00
sfan5
699d1bf27c Use newer IrrlichtMt
now with SDL2
2024-01-19 11:54:40 +01:00
David Heidelberg
225aa107f6 Define strlcpy only on platforms where it's not available
Linux musl-libc and recent glibc > 2.38 have it.

Signed-off-by: David Heidelberg <david@ixit.cz>
2024-01-19 11:53:08 +01:00
DS
e416c99419
Fix signed overflow UB in PseudoRandom::next() 2024-01-19 11:52:53 +01:00
Lars
7c9706fdcf Remove unused meshgen_block_cache_size setting 2024-01-19 11:52:39 +01:00
HybridDog
f08e4bb27d
Return to the main menu if a shader compilation fails (#14256)
Before this change, if the shaders are broken, only an error message is shown and the player enters the world nonetheless, where he/she sees broken graphics.
2024-01-19 11:51:46 +01:00
savilli
432988a4ad
Fix multiple password changes in one session 2024-01-19 11:50:55 +01:00
Lars Müller
a8cf10b0b5
Docs: Recommend against using PseudoRandom 2024-01-17 21:48:17 +01:00
sfan5
e985b7a0bf Initialize random with better seed 2024-01-17 20:06:26 +01:00
sfan5
6caa06eaed Remove mistaken exec mode from three files 2024-01-17 20:06:26 +01:00
sfan5
02fa33252a Ignore MSVC unit test failures for now
Spending time to investigate and fix all them was not part of my plan.
2024-01-17 20:06:26 +01:00
sfan5
2211f4f8f7 Run unit tests in MSVC CI job 2024-01-17 20:06:26 +01:00
sfan5
5ceb327e55 Replace SHA256 implementation with one from LibreSSL
They have cleaner code than OpenSSL :)

from here: https://github.com/libressl/openbsd/tree/master/src/lib/libcrypto
and https://github.com/libressl/portable
2024-01-17 20:06:26 +01:00
sfan5
b0f76d82c5 Remove references to SHA1 and SHA512 from SRP code 2024-01-17 20:06:26 +01:00
sfan5
2bcebc4e4e Update mini-gmp to 6.3.0 2024-01-17 20:06:26 +01:00
sfan5
cd55a533e8 Update catch2 copy to 2.13.10 2024-01-17 20:06:26 +01:00
sfan5
021eddac73 Update jsoncpp copy to 1.9.5 2024-01-17 20:06:26 +01:00
sfan5
5756d6262e Minor improvements and fixes in httpfetch.cpp 2024-01-17 20:06:11 +01:00
sfan5
56943bef48 Use modern libcurl poll/wait methods 2024-01-17 20:06:11 +01:00
sfan5
ee727eb65e Migrate UDPSocket to use poll() 2024-01-17 20:06:11 +01:00
sfan5
e8008c1b21 Sanitize lang_code and full_version received from client
fixes #14262
2024-01-17 20:05:57 +01:00
sfan5
bdc124ba41 Require client to consistently use peer ID 2024-01-17 20:05:57 +01:00
sfan5
f27f701251 Make server disconnect lingering clients 2024-01-17 20:05:57 +01:00
sfan5
b2f0a37b18 Rate-limit client connection attempts 2024-01-17 20:05:57 +01:00
sfan5
050152eb90 Do not allocate packet quota to half-open connections 2024-01-17 20:05:57 +01:00
sfan5
3987318f09 Time out when reliables can't be delivered
If one of the channels stalls for whatever reason we can't pretend the connection is fine.
2024-01-17 20:05:57 +01:00
sfan5
9f684eac92 Remove weird command procession limit
it was set to 1 too, wtf?!
2024-01-17 20:05:57 +01:00
sfan5
abf3142b26 Send initial dummy packet as empty
No functional change and no compatibility implicatons
but this better matches what is documented everywhere.
2024-01-17 20:05:57 +01:00
sfan5
eeb873b23c Minor code corrections 2024-01-17 20:05:57 +01:00
sfan5
84d4647329 Scale resend timeout exponentially 2024-01-17 20:05:57 +01:00
sfan5
7acb14f7a1 Use fixed, lower timeout for half-open connections 2024-01-17 20:05:57 +01:00
sfan5
2587302987 Assign peer IDs randomly 2024-01-17 20:05:57 +01:00
sfan5
db88d24ff8 Track connection half-open state 2024-01-17 20:05:57 +01:00
cx384
2ea8d9ca11
Fix out of range enum casts in deSerialize functions (#14090) 2024-01-17 20:05:46 +01:00
sfan5
0383c44f0d
Custom data structure for active objects to get performance *and* safety (#13880) 2024-01-17 20:04:56 +01:00
Wuzzy
08ee6d8d4b
Add rotation support for wallmounted nodes in 'ceiling' or 'floor' mode (#11073) 2024-01-17 17:47:06 +01:00
Jude Melton-Houghton
e7dd9737bd Reduce minetest.after time complexity and provide ordering guarantee
---------

Co-authored-by: Lars Mueller <appgurulars@gmx.de>
2024-01-16 23:46:43 +01:00
sfence
ceaa7e2fb0
Add API for restoring PseudoRandom and PcgRandom state (#14123) 2024-01-16 23:20:52 +01:00
Vitaliy
8093044f07
Support OpenGL 3 (#13321) 2024-01-16 21:09:18 +01:00
AFCMS
9cca12ff0b
Fix language setting description in settingtypes.txt (#14048)
Co-authored-by: grorp <gregor.parzefall@posteo.de>
2024-01-15 19:27:24 +01:00
sfan5
1b0d2a37bb Set low active_block_range in test_multiplayer.sh 2024-01-15 11:44:26 +01:00
cx384
92c55c27cf
Add function to get all HUD elements (#14042) 2024-01-14 17:46:29 +01:00
SmallJoker
ed7d4037b2 Client: fix possible division by zero in [crack modifier 2024-01-14 16:28:03 +01:00
sfan5
dd094d7606 Write down some developer documentation
I think it's better suited here than in the wiki.
2024-01-14 13:17:53 +01:00
sfan5
1ba26d67bd Remove excessive includes from porting.h 2024-01-14 13:17:53 +01:00
sfan5
e824e9023f Simplify LuaPseudoRandom::l_next and fix docs
Also extends the allowed range on the C++ side. This has no side-effects.
2024-01-14 13:17:53 +01:00
sfan5
d20f1182f2 Fix Lua PseudoRandom seeds being mangled
closes #14237
2024-01-14 13:17:53 +01:00
sfan5
e83530d40b Use explicit types on PseudoRandom implementation 2024-01-14 13:17:53 +01:00
sfan5
6f494a968d Move setenv compat code to porting.h 2024-01-14 13:17:53 +01:00
sfan5
133f706bf3 Make unittests less reliant on files in the source distribution 2024-01-14 13:17:53 +01:00
sfan5
863c9b55b4 Remove broken MINETEST_SUBGAME_PATH test
The path being tested for is in the default search path for games,
so it would still pass if the env var was not working.
2024-01-14 13:17:53 +01:00
sfan5
45561b89a4 Make sure unittests don't try to write to cwd 2024-01-14 13:17:53 +01:00
grorp
6b9250e4ef
Document settings API behavior regarding default values (#14247) 2024-01-13 20:01:50 +01:00
Muhammad Rifqi Priyo Susanto
5089e8342f
Android: Use the correct value for notification (#14209)
The notification channel creation is moved into MainActivity.
The notification channel ID string is stored into a static variable.
The name and description of the notification channel are stored into the strings resource file.

Co-authored-by: sfan5 <sfan5@live.de>
2024-01-13 20:01:35 +01:00
grorp
b12be0498e
Don't enable relative mouse mode if in touchscreen mode (#14118) 2024-01-13 20:01:10 +01:00
lhofhansl
59abf1bb42
Allow active blocks to be generated (#14185) 2024-01-13 10:27:41 -08:00
lhofhansl
518ecd7f4e
Slight optimizations in ClientMap (#14251) 2024-01-12 16:53:08 -08:00
Lars Mueller
025516a005 Remove redundant and/or outdated client API docs 2024-01-12 16:24:15 +01:00
HybridDog
345e93d19c Code style: Use non-static member initialisation instead of member initialiser lists for shader uniform names
Before this change,
the member type and member name are at one place,
and the member name and uniform name are at another place.
If the uniform name is written directly at the member declaration,
the member type, member name and uniform name are all at one place,
which leads to shorter code and may be easier to read.
2024-01-11 20:04:42 +01:00
rubenwardy
d98ea7fdb6 Remove GitLab CI and redirect GitLab pages to api.minetest.net 2024-01-10 20:52:08 +00:00
SmallJoker
a7eaee77ca
ContentCAO: Fix threshold of alpha channel textures (#14213)
With disabled shaders, the material EMT_TRANSPARENT_ALPHA_CHANNEL uses the
parameter as an alpha threshold to decide whether to draw the texture.
Thus lowering this limit fixes the issue of vanishing textures below alpha 128.
2024-01-10 19:34:52 +01:00
Muhammad Rifqi Priyo Susanto
0d41996562 MSVC: Fix locale workaround code 2024-01-10 19:33:04 +01:00
Lars Müller
7bae8ab838
Fix HUD image (waypoint) docs 2024-01-10 19:32:49 +01:00
lhofhansl
4bf95703a0
Allow access into MapSector::m_blocks (#14232)
* New API to allow access into MapSector::m_blocks
* Use this API on ClientMap::touchMapBlocks(), ClientMap::updateDrawList(), and ClientMap::updateDrawListShadow() to speed them up
2024-01-10 09:17:26 -08:00
5adbc138c7 Merge pull request 'master' (#6) from Mirrorlandia_minetest/minetest:master into master
Reviewed-on: #6
2024-01-09 17:53:50 +01:00
cx384
2766c70ad3
Fix dividing by zero crashes in texture modifiers 2024-01-07 21:49:26 +01:00
sfan5
2c390b5473 Rework client connecting and enable fallback address use 2024-01-07 21:49:05 +01:00
sfan5
20692d54de Some minor cleanups for UDPSocket class 2024-01-07 21:49:05 +01:00
sfan5
dc7fb26921 Extend capabilities of Address class 2024-01-07 21:49:05 +01:00
Muhammad Rifqi Priyo Susanto
171f911237
Android: Add selection dialog (drop down/combo box) (#13814)
- The handling of IGUIComboBox uses the new setAndSendSelected() method.
- getDialogState() is now getInputDialogState() and returns the state of the input dialog.
- getLastDialogType() is added and returns current/last shown dialog's type.
- getInputDialogState() now returns an enum instead of int.
- getAndroidUIInput() now returns void instead of bool.
- New data types (enum) are added:
  (1) GameActivity.DialogType (Java) and porting::AndroidDialogType (C++)
  (2) GameActivity.DialogState (Java) and porting::AndroidDialogState (C++)
- When showing a text input dialog, there is no custom accept button text any more.
- showDialog()/showDialogUI() for text input is now showTextInputDialog()/showTextInputDialogUI().
- showInputDialog()/showDialogUI() for text input is now showTextInputDialog()/showTextInputDialogUI().
- getDialogValue()/getInputDialogValue() is now getDialogMessage()/getInputDialogMessage().


Co-authored-by: Gregor Parzefall <82708541+grorp@users.noreply.github.com>
2024-01-07 19:00:04 +07:00
lhofhansl
bd42cc2c77
Ensure deterministic client occlusion culling and minor improvements (#14212)
* Ensure deterministic client occlusion culling
* Increase culling optimize distance slightly
* More accurate culling when sampling
2024-01-06 18:43:46 -08:00
sfan5
8db4ba9e58 Fix some console window behavior on Windows 2024-01-06 15:38:09 +01:00
sfan5
7c7ae79f9f Fix native thread handle usage on win32 2024-01-06 15:38:09 +01:00
sfan5
8674dc831d Avoid unused argument spam with MinGW-clang 2024-01-06 15:38:09 +01:00
sfan5
3fbe42c3a2 Add unittest to check thread_local destructor brokenness 2024-01-06 15:38:09 +01:00
sfan5
6550bc252f Fix logic in porting::attachOrCreateConsole()
No functional change but now the comment is actually correct.
2024-01-06 15:38:09 +01:00
Artem
e04f618979
Add "--needed" to Arch command to avoid reinstalling packages 2024-01-06 15:35:51 +01:00
Zughy
c2c8d4d410
Remove controls listed in the pause menu (no touchscreen) (#13282) 2024-01-05 20:10:07 +00:00
sfan5
15f73258fd
Don't run CDB update_detector more than once (#14214) 2024-01-05 00:40:11 +01:00
Maintainer_
34ce86a8f5
Fix GameUI text staying visible during shutdown. (#14197) 2024-01-05 00:39:56 +01:00
grorp
05a53cd330
Touchscreen: Recognize double-taps as double-clicks (#14187) 2024-01-05 00:39:40 +01:00
Muhammad Rifqi Priyo Susanto
e17455cb22
Remove server's address and port from pause menu (#14082) 2024-01-05 00:39:11 +01:00
grorp
995c192874
Don't apply gui_scaling & DPI twice to table[] / textlist[] scrollbar (#14206) 2024-01-03 21:58:58 +01:00
DS
c9cd0d20ef
Use AL_SOFT_direct_channels_remix extension for non-positional stereo sounds (#14195) 2024-01-03 21:57:00 +01:00
DS
3eab5e9002
Replace clientmap's MeshBufListList with a hashmap 2024-01-03 21:56:38 +01:00
fluxionary
a22b1700a4
Legible Lua profiler (#14142) 2024-01-03 21:56:07 +01:00
ROllerozxa
8e9d7611ae Apply saturation even if tonemapping is disabled 2024-01-03 21:55:53 +01:00
ROllerozxa
de4cc5c20a Fix tonemapping effect 2024-01-03 21:55:53 +01:00
sfan5
0b423dd061 Remove reference to defunct gitlab docker image
see #14164
2024-01-03 17:02:51 +01:00
Lars Müller
2c44620e5e
Comply with base64 license terms (#14199) 2024-01-01 22:49:12 +01:00
sfence
d0753dddb1
Method add_pos for object/player (#14126) 2024-01-01 22:48:56 +01:00
Alfred Wingate
c9ab61aa8c Add missing header for gcc-14
https://gcc.gnu.org/gcc-14/porting_to.html

Signed-off-by: Alfred Wingate <parona@protonmail.com>
2023-12-31 19:26:33 +01:00
sfan5
431444ba9f Extend sanity checks in ActiveBlockList::update
also fixes the space indentation
2023-12-30 00:31:03 +01:00
lhofhansl
c99196d363
Do not emerge blocks in the active_object_send_range_blocks range (#14152)
The active object range is about active objects (not blocks). Activate blocks (and hence any object "in" them) in the cone define by the active object range (and fov) when they are loaded (i.e. visible), otherwise ignore them.
2023-12-29 14:18:06 -08:00
lhofhansl
22a1653702
Perform server occlusion check before a block is loaded or generated (#14148) 2023-12-29 21:53:27 +01:00
sfan5
edd947b645 Enable some runtime hardening on win32 2023-12-29 21:52:08 +01:00
sfan5
b8dc349099 Clean up gettext initialization 2023-12-29 21:52:08 +01:00
sfan5
93c2aff2cf Clean up OS-specific initialization 2023-12-29 21:52:08 +01:00
Desour
ad5e9aa5e3 Fix AsyncRunStep() skipping steps when dtime < 1 ms 2023-12-29 21:51:19 +01:00
cx384
467d3a8c62
Rename hud_elem_type to type (#14065) 2023-12-29 21:51:02 +01:00
lhofhansl
bc336480e6
Avoid short overflow with large viewing ranges (#14175) 2023-12-28 09:10:11 -08:00
grorp
32e492837c
Support both mouse and touch input in GUIs in a single binary (#14146) 2023-12-27 22:37:36 +01:00
superfloh247
4f1dbb127a
Update CMakeLists.txt to fix MacOS build (#14160)
Co-authored-by: sfan5 <sfan5@live.de>
2023-12-27 22:19:56 +01:00
sfan5
93dfa8a6d8
Optimize and improve built-in PNG writer (#14020) 2023-12-27 11:56:48 +01:00
Simon Boehm
5054918efc
MacOS: Add codesigning instructions to docs (#14060) 2023-12-27 11:44:54 +01:00
Gregor Parzefall
335af393f0 Make the loading screen progress bar respect "gui_scaling" 2023-12-26 20:21:15 +01:00
Gregor Parzefall
524721ee27 Remove non-existent textures from texture_packs.md
These textures were removed 5 years ago by 326eeca306f7bfb53ae3685eef18978dd81e587e.
2023-12-26 12:01:46 +01:00
sfan5
5405a558fd Fix minor issue with log_deprecated() 2023-12-25 19:47:34 +01:00
sfan5
094c433e58 Update clang-tidy workflow 2023-12-25 10:07:28 +01:00
sfan5
961652c2e9 Address some clang-tidy warnings 2023-12-25 10:07:28 +01:00
Desour
322c4a5b2b Rework server stepping and dtime calculation 2023-12-25 10:07:03 +01:00
Desour
b6c7c5a7ab Link with -latomic 2023-12-25 10:07:03 +01:00
grorp
46c930cf70
Touchscreen: Make server-sent overrides of button textures work (#14145) 2023-12-23 14:39:42 +01:00
Lars
e0d4a9d575 Make volumetric light effect strength server controllable
- Make volumetric light effect strength server controllable
- Separate volumetric and bloom shader pipeline
- Require bloom to be enable, scale godrays with bloom
2023-12-21 16:21:01 -08:00
x2048
04f0d545da Initial implementation of 'Godrays' 2023-12-21 16:21:01 -08:00
Lars Müller
cad8e895f2
Fix set_bone_position regression (error on passing none) 2023-12-21 18:55:12 +01:00
sfan5
cb38b841af Split windows from linux CI workflows 2023-12-21 18:54:57 +01:00
Lars Mueller
d58cc7fb7a Fix on_(grant|revoke) not being run by mods 2023-12-21 18:54:50 +01:00
Warr1024
7e143cb33d
Manually configurable minimum protocol version (#14054)
Partially address #13483.  Server operators can set a minimum
protocol version to match the game requirements (or any other
restriction they may want), and it's applied as an additional
constraint on top of the baseline compatibility range, optional
strict_protocol_version_checking, and any kick-on-join used by
the game/mods.
2023-12-21 18:53:30 +01:00
Gregor Parzefall
04dc4a10f0 Fix TouchScreenGUI ignoring server-sent pitch changes 2023-12-20 21:24:10 +01:00
sfan5
47e557b96a Enable segment heap on Windows 2023-12-20 21:23:08 +01:00
Gregor Parzefall
3b346fd3c9 Fix touch input on Linux
The code relied on touch IDs being consecutive. This is true on Android, but not on Linux.
Therefore, touch input on Linux was broken since 53886dcdb52de80d862539e22950c84fbf88df88.
2023-12-20 21:22:15 +01:00
Lars Müller
0d61598d8a
Extend bone override capabilities (#12388) 2023-12-20 21:21:53 +01:00
sfan5
61d0f613df
Hand roll UTF-16 conversion in CGUITTFont (#14121) 2023-12-19 20:18:43 +01:00
grorp
00d9d96e48
Android: Pause rendering while the app is paused (#14058) 2023-12-19 20:18:28 +01:00
HybridDog
b1aec1b5c8
Add dithering (#9014) 2023-12-19 20:18:11 +01:00
sfan5
5d3e830176 MinGW toolchain refresh 2023-12-17 20:47:26 +01:00
grorp
91ba02449b
Add touch_controls boolean to get_player_window_information() (#14092) 2023-12-17 20:47:07 +01:00
JosiahWI
7162b536eb
Extract Game::drawScene from Game::updateFrame 2023-12-17 20:44:45 +01:00
lhofhansl
ca1a723890
Allow cheaper culling checks at a distance (#14073)
* Allow cheaper culling checks at a distance
* Pick a random ray, so that far missing block will eventually be shown
2023-12-16 15:04:21 -08:00
superfloh247
16c22477c2
Update porting.h to fix build errors on macOS 14 / Xcode 15 2023-12-16 12:52:07 +01:00
sfan5
128ed87dd8 Reorder members of MapBlock for performance
Before and after as obtained via `pahole -C MapBlock bin/minetest`:
/* size: 336, cachelines: 6, members: 23 */
/* sum members: 329, holes: 4, sum holes: 7 */
vs.
/* size: 336, cachelines: 6, members: 23 */
/* sum members: 329, holes: 2, sum holes: 7 */

There is not much to be gained by packing but I made sure
to move the most important data (mainly for the client) into
the first cache line.
2023-12-16 12:51:42 +01:00
sfan5
9408a1a025 Reduce size of some MapBlock members
Also adds assertions to catch refcounting errors (on a debug build).
2023-12-16 12:51:42 +01:00
sfan5
777dca7043 Elide MapBlock::contents_cached 2023-12-16 12:51:42 +01:00
sfan5
f5b35a074f Get rid of parent pointer in MapBlock 2023-12-16 12:51:42 +01:00
sfan5
c6cf90f67b Change MapBlock content cache to a vector 2023-12-16 12:51:42 +01:00
sfan5
cb6e3ac6e1 Allocate data seperately from MapBlock class again
This effectively reverts commit b3503e7853a52a8c16431f6b983e30c9d25951bc.
2023-12-16 12:51:42 +01:00
sfan5
2c2bc4a427 Try to benchmark common MapBlock usage 2023-12-16 12:51:42 +01:00
sfan5
e5a6048eec Allow running individual benchmarks
mirrors and reuses the option from 2f6a9d12f1db84322e0b69fd5ddc986f1f143606
2023-12-16 12:51:42 +01:00
Gregor Parzefall
3c60d359ed Remove usage of removed "PP" macro
This fixes a compilation error introduced by e7be135.
2023-12-15 10:28:07 +01:00
SmallJoker
94a54375e2
Inventory: prevent item loss when stacking oversized ItemStacks (#14072) 2023-12-15 10:24:04 +01:00
sfan5
c871b6dd4e Hash-check buildbot dependencies 2023-12-15 10:23:52 +01:00
sfan5
62c6667b0b Get rid of VERSION_EXTRA for buildbot
This is probably a leftover of when CMake didn't automatically
detect the revision from git.
2023-12-15 10:23:52 +01:00
sfan5
704b5d88b9 Upload artifacts in MinGW CI 2023-12-15 10:23:52 +01:00
sfan5
a292cc42aa Fix Windows architecture reporting in sysinfo 2023-12-15 10:23:52 +01:00
Gary Miguel
da832a295e
Delete clang-format files and comments (#14079) 2023-12-15 10:23:44 +01:00
Vitaliy
64b59184d1
Reduce test framework macrosity 2023-12-15 10:23:32 +01:00
sfan5
bd06466d3a Improve clock_gettime usage
- correctly use value of _POSIX_MONOTONIC_CLOCK
- drop special path for macOS: it supports clock_gettime since macOS 10.12
2023-12-15 10:23:19 +01:00
sfan5
d4123a387c Clean up porting.h a bit 2023-12-15 10:23:19 +01:00
mazes-80
e7be135b78
Warning: inform about entity name when bug detected about attachement (#13354) 2023-12-15 10:22:58 +01:00
5b5526958d Merge pull request 'master' (#5) from Mirrorlandia_minetest/minetest:master into master
Reviewed-on: #5
2023-12-13 19:58:54 +01:00
83c2d31af5 Update CMakeLists.txt 2023-12-13 19:57:36 +01:00
36a9a9a4a3 Merge pull request 'master' (#4) from Mirrorlandia_minetest/minetest:master into master
Reviewed-on: #4
2023-12-13 19:56:19 +01:00
sfan5
d1a55e9ca4
Remove use_texture_alpha compatibility code for nodeboxes & meshes (#13929) 2023-12-13 13:15:59 +01:00
Gary Miguel
6eb9269741
Try to fix safeWriteToFile producing empty files on Windows (#14085)
Use win32 APIs to write the temporary file before copying to the final
destination. Because we've observed the final file being empty, we
suspect that std::ostream::flush is not flushing.

Also add a test for it.
2023-12-13 13:15:37 +01:00
lhofhansl
a98200bb4c
Avoid movement jitter (#13093)
This allows the client and server to agree on the position of objects and attached players even when there is lag.
2023-12-10 19:12:37 +01:00
Muhammad Rifqi Priyo Susanto
55fafb7d25
Add sound volume when unfocused setting (#14083)
This adds a new setting to set sound volume multiplier when Minetest window is unfocused/inactive (sound_volume_unfocused, located in Settings > Graphics and Audio > Audio > Volume when unfocused).

If the window is not focused, the sound volume will be multiplied by sound_volume_unfocused setting. The sound volume will be set back to sound_volume again when the window is focused.
2023-12-10 19:11:39 +01:00
SmallJoker
321bcf5c44
GUIFormspecMenu: Fix race condition between quit event and cleanup in Game (#14010)
To not instantly free GUIFormSpec upon close/quit, Game periodically
cleans up the remaining instance on the next frame.

When a new formspec is received and processed after closing the previous formspec
but before the cleanup in Game, the formspec would be closed regardless.
This now re-creates the formspec when the old one is already pending for removal.
2023-12-10 19:09:51 +01:00
c29f6c3f9e add debug_force 2023-12-09 21:40:45 +01:00
d50faa6036 Merge pull request 'master' (#3) from Mirrorlandia_minetest/minetest:master into master
Reviewed-on: #3
2023-12-09 21:39:17 +01:00
grorp
689aaf50b3
Fix unittest failure for release versions (#14067) 2023-12-08 21:13:33 +01:00
ZenonSeth
2ec3325381
Check if liquid can flow into empty node before picking it as source (#14057) 2023-12-08 21:13:18 +01:00
Gary Miguel
634e49b961
Improve Irrlicht instructions (#14055)
Specify the version when cloning.
Move instructions from CMakeLists to docs/compiling/.
2023-12-08 21:11:54 +01:00
rubenwardy
55f40a7f8d Continue with 5.9.0-dev 2023-12-04 17:15:43 +00:00
rubenwardy
49ce5a2de6 Bump version to 5.8.0 2023-12-04 17:15:36 +00:00
updatepo.sh
30769589bf Remove junk translation file 2023-12-03 19:11:32 +01:00
updatepo.sh
6cf9b7472a Run mod_translation_updater.py 2023-12-03 19:03:45 +01:00
updatepo.sh
4be8b77598 Run updatepo.sh 2023-12-03 18:48:54 +01:00
updatepo.sh
bae9f65411 Update from builtin/settingtypes.txt 2023-12-03 18:47:50 +01:00
Krock
0a20d30f83 Various little translation fixups 2023-12-03 18:41:15 +01:00
Nisa Syazwani
7245bcc614 Translated using Weblate (Malay)
Currently translated at 100.0% (1310 of 1310 strings)
2023-12-03 18:41:15 +01:00
chocomint
51136780d6 Translated using Weblate (Spanish)
Currently translated at 89.6% (1174 of 1310 strings)
2023-12-03 18:41:15 +01:00
gallegonovato
ea6eb0dfc8 Translated using Weblate (Spanish)
Currently translated at 89.6% (1174 of 1310 strings)
2023-12-03 18:41:15 +01:00
nyommer
ab88fc6835 Translated using Weblate (Hungarian)
Currently translated at 96.7% (1268 of 1310 strings)
2023-12-03 18:41:15 +01:00
AlexTECPlayz
30b28280eb Translated using Weblate (Romanian)
Currently translated at 49.3% (647 of 1310 strings)
2023-12-03 18:41:15 +01:00
BreadW
e5672111d2 Translated using Weblate (Japanese)
Currently translated at 100.0% (1310 of 1310 strings)
2023-12-03 18:41:15 +01:00
Spurnita
ce0aca49c2 Translated using Weblate (Catalan)
Currently translated at 22.5% (296 of 1310 strings)
2023-12-03 18:41:15 +01:00
Muhammad Rifqi Priyo Susanto
0d3b71564f Translated using Weblate (Javanese)
Currently translated at 12.9% (170 of 1310 strings)
2023-12-03 18:41:15 +01:00
Ritwik
0a51fde971 Translated using Weblate (Hindi)
Currently translated at 29.8% (391 of 1310 strings)
2023-12-03 18:41:15 +01:00
milewood
a13a165e9b Translated using Weblate (Chinese (Simplified))
Currently translated at 84.0% (1101 of 1310 strings)
2023-12-03 18:41:15 +01:00
Wuzzy
8a7d3d07de Translated using Weblate (German)
Currently translated at 100.0% (1310 of 1310 strings)
2023-12-03 18:41:15 +01:00
Giov4
0977728ea0 Translated using Weblate (Italian)
Currently translated at 93.7% (1228 of 1310 strings)
2023-12-03 18:41:15 +01:00
Yaya - Nurul Azeera Hidayah @ Muhammad Nur Hidayat Yasuyoshi
92eb63c867 Translated using Weblate (Malay + Jawi) 2023-12-03 18:41:07 +01:00
YearOfFuture
0c4a15fa16 Translated using Weblate (Ukrainian)
Currently translated at 75.4% (989 of 1310 strings)
2023-12-03 18:39:34 +01:00
Nanashi Mumei
01ac9e15ef Translated using Weblate (Russian)
Currently translated at 100.0% (1310 of 1310 strings)
2023-12-03 18:39:34 +01:00
Muhammad Rifqi Priyo Susanto
dd3fc83777 Translated using Weblate (Indonesian)
Currently translated at 100.0% (1310 of 1310 strings)
2023-12-03 18:39:34 +01:00
waxtatect
6a5e480a58 Translated using Weblate (French)
Currently translated at 100.0% (1310 of 1310 strings)
2023-12-03 18:39:34 +01:00
Lars Müller
419d971891
Translated using Weblate (German)
Currently translated at 99.0% (1297 of 1310 strings)
2023-12-03 18:17:42 +01:00
sfan5
9e62cb5c04
Translated using Weblate (German)
Currently translated at 99.0% (1297 of 1310 strings)
2023-12-03 18:17:42 +01:00
Desour
bf53e7e1ca Fix anticheat false positives whith speed physics override 2023-12-03 12:23:17 +01:00
AFCMS
91134015e7
Document minetest.get_gametime() returning nil at init time (#14047)
Co-authored-by: sfan5 <sfan5@live.de>
2023-12-03 15:00:29 +07:00
Muhammad Rifqi Priyo Susanto
047520d91e
Inventory: Add remaining items into the source slot directly (#14021)
Remaining items are added into the source slot directly when left-dragging over the source slot.
2023-12-03 15:00:07 +07:00
DS
6106e4e72b
Fix sound and particlespawner id generation (#14059)
* Fix server sound ids being reused to early

* Fix particlespawner id generation

It always returned 0.
Also, now the ids always grow, to make a conflict with ids in lua unlikely.
2023-12-01 00:09:53 +01:00
SmallJoker
a7e5456099
Server: avoid re-use of recent ParticleSpawner and Sound IDs (#14045)
This improves the reliability when removing and re-adding handles quickly.
Looping through the entire ID range avoids collisions caused by any race condition.
2023-11-29 21:10:19 +01:00
sfan5
d6a8b546e4 Enable clean transparent filter in more cases
It was determined that this fixes scaling artifacts that can happen with bilinear,
trilinear or anisotropic filtering alone.
Since the previous commit did not bring back the relevant setting, we fix this
shortcoming by just enabling it in all cases where it is known to help.
2023-11-29 21:09:21 +01:00
sfan5
7f9326805c Return texture filter settings to previous state
This partially reverts commit 72ef90885d5030bf6f7f9dd60a475339bde9a929.

fixes #14007
2023-11-29 21:09:21 +01:00
cdb4a5d8ce Merge pull request 'master' (#2) from Mirrorlandia_minetest/minetest:master into master
Reviewed-on: #2
2023-11-29 10:49:41 +01:00
sfan5
36f4953502
Update credits for 5.8.0 release (#14017) 2023-11-28 23:11:29 +01:00
grorp
dc6452db1b
Don't copy user texture packs into Android bundle (#14053) 2023-11-28 21:02:56 +01:00
grorp
cfe1953c2d
Take aliases into account for automatic package installation (#14052) 2023-11-28 21:02:41 +01:00
Wuzzy
dfe00f88e1
Fix missing word in German builtin translation (#14051) 2023-11-28 21:02:19 +01:00
Muhammad Rifqi Priyo Susanto
53886dcdb5
Formspec: Pass the second-touch event as is (#13872)
The second-touch event is passed to the GUIFormSpecMenu::OnEvent() function as a touch event.
There are two types of event for inventory formspec: (1) mouse event and (2) touch event.
The touch event is just a modifier of the mouse event.


Co-authored-by: Gregor Parzefall <82708541+grorp@users.noreply.github.com>
2023-11-28 07:00:07 +07:00
grorp
771da80bbb
Make it possible again to see item tooltips on Android (#14029)
This change is a quick fix so that item tooltips show again on Android.
2023-11-25 17:07:07 +01:00
Zughy
0f3ac7c956
Update Italian builtin translation (#13997) 2023-11-25 17:05:07 +01:00
Muhammad Rifqi Priyo Susanto
71490a417e
Update Indonesian translation of builtin (#13996)
U+0020 (Space) is changed to U+00A0 (No-Break Space) to match the original string.
2023-11-25 17:04:44 +01:00
grorp
4255ac3022
Mainmenu: Avoid the header being displayed behind the formspec (#13924)
This change keeps the current header placement code, but adds additional code to make sure the header doesn't end up behind the formspec.
2023-11-25 17:04:33 +01:00
a3c1115277 add_stuff 2023-11-25 09:27:52 +01:00
ff473180db Merge pull request 'master' (#1) from Mirrorlandia_minetest/minetest:master into master
Reviewed-on: #1
2023-11-24 22:11:16 +01:00
ZenonSeth
6783734612
Wireshark dissector: Made sure var 'pos' was only assigned locally to function (#14027) 2023-11-23 18:26:00 +01:00
Wuzzy
61db32beee
Fix mod translation updater bugs (#13974) 2023-11-19 20:46:52 +01:00
MisterE123
31ee7af3ab
lua_api.md: Add tick marks to position HUD element 2023-11-19 20:46:40 +01:00
jordan4ibanez
72edfe3d04
Fix openSUSE build dependencies
They were incomplete.
2023-11-19 20:46:03 +01:00
Desour
7199ee4ff8 Devtest: Fix testnodes bouncy color calculation
Values were out of range.
2023-11-19 20:45:18 +01:00
Desour
585e6aa80b Clamp values in read_ARGB8 2023-11-19 20:45:18 +01:00
Desour
1bc74b0ba1 Fix undefined inf to s32 cast in GUIScrollBar::setPos 2023-11-19 20:45:18 +01:00
Desour
73e85b2ebb Fix cached wanted_range and camera_fov being written with out-of-range value 2023-11-19 20:45:18 +01:00
sfan5
0e4de28988 Try to fix VS build failures 2023-11-19 16:22:20 +01:00
Muhammad Rifqi Priyo Susanto
aa912e90a7
Make text containers wider in the Volume Change dialog (#13995)
These containers are widened to account for translations.
2023-11-15 07:00:03 +07:00
DS
8cf76e004f
Add new flags to minetest.features for 5.8.0 features (#13978) 2023-11-12 20:08:57 +01:00
superfloh247
7cb20dd6c2
Fix undefined behaviour in modulo360f (#13976)
Resolves a crash on macOS/arm64 by no longer depending on UB.
2023-11-12 20:08:33 +01:00
updatepo.sh
2bc0d76f63 Update translation files 2023-11-11 12:11:09 +01:00
updatepo.sh
8abb5796ed Update example conf and settings translations 2023-11-11 12:09:35 +01:00
YearOfFuture
7a658c1a6a Translated using Weblate (Ukrainian)
Currently translated at 76.3% (1023 of 1340 strings)
2023-11-11 12:06:10 +01:00
Maksim Gamarnik
1b0a34b9d1 Translated using Weblate (Ukrainian)
Currently translated at 72.4% (971 of 1340 strings)
2023-11-11 12:06:10 +01:00
YearOfFuture
8b5fc7f23a Translated using Weblate (Ukrainian)
Currently translated at 72.4% (971 of 1340 strings)
2023-11-11 12:06:10 +01:00
Maksim Gamarnik
ee35d7df58 Translated using Weblate (Ukrainian)
Currently translated at 69.8% (936 of 1340 strings)
2023-11-11 12:06:10 +01:00
dandelionsmellr
8d1f1b4704 Translated using Weblate (Ukrainian)
Currently translated at 67.3% (902 of 1340 strings)
2023-11-11 12:06:10 +01:00
Maksim Gamarnik
50f48ce9df Translated using Weblate (Ukrainian)
Currently translated at 67.2% (901 of 1340 strings)
2023-11-11 12:06:10 +01:00
Yaya - Nurul Azeera Hidayah @ Muhammad Nur Hidayat Yasuyoshi
e14f905299 Translated using Weblate (Malay (Jawi))
Currently translated at 53.9% (723 of 1340 strings)
2023-11-11 12:06:10 +01:00
Nanashi Mumei
6980f516d4 Translated using Weblate (Russian)
Currently translated at 100.0% (1340 of 1340 strings)
2023-11-11 12:06:10 +01:00
dandelionsmellr
52e66b6dfe Translated using Weblate (Ukrainian)
Currently translated at 59.8% (802 of 1340 strings)
2023-11-11 12:06:10 +01:00
Maksim Gamarnik
645e4abf52 Translated using Weblate (Ukrainian)
Currently translated at 59.8% (802 of 1340 strings)
2023-11-11 12:06:10 +01:00
dandelionsmellr
b0932ef458 Translated using Weblate (Ukrainian)
Currently translated at 59.8% (802 of 1340 strings)
2023-11-11 12:06:10 +01:00
Muhammad Rifqi Priyo Susanto
330aee974e Translated using Weblate (Javanese)
Currently translated at 0.0% (0 of 1340 strings)
2023-11-11 12:06:10 +01:00
Timur Seber
a1d7c25587 Translated using Weblate (Tatar)
Currently translated at 7.6% (103 of 1340 strings)
2023-11-11 12:06:10 +01:00
109247019824
6c4352eaf9 Translated using Weblate (Bulgarian)
Currently translated at 32.5% (436 of 1340 strings)
2023-11-11 12:06:10 +01:00
Spurnita
2f279d2403 Translated using Weblate (Catalan)
Currently translated at 18.6% (250 of 1340 strings)
2023-11-11 12:06:10 +01:00
chocomint
842b2bbd36 Translated using Weblate (Spanish)
Currently translated at 90.8% (1218 of 1340 strings)
2023-11-11 12:06:10 +01:00
BreadW
d056bb3ee7 Translated using Weblate (Japanese)
Currently translated at 94.4% (1265 of 1340 strings)
2023-11-11 12:06:10 +01:00
waxtatect
adf9a3953b Translated using Weblate (French)
Currently translated at 100.0% (1340 of 1340 strings)
2023-11-11 12:06:10 +01:00
Ярослав Рукавицын
c7dd8c18ed Translated using Weblate (Russian)
Currently translated at 94.1% (1262 of 1340 strings)
2023-11-11 12:06:10 +01:00
Lemente
d8c8bf1897 Translated using Weblate (French)
Currently translated at 96.9% (1299 of 1340 strings)
2023-11-11 12:06:10 +01:00
waxtatect
b0c92e885e Translated using Weblate (French)
Currently translated at 96.9% (1299 of 1340 strings)
2023-11-11 12:06:10 +01:00
ROllerozxa
80ae408eb9 Translated using Weblate (Swedish)
Currently translated at 65.5% (879 of 1340 strings)
2023-11-11 12:06:10 +01:00
Yaya - Nurul Azeera Hidayah @ Muhammad Nur Hidayat Yasuyoshi
9720eb50b3 Translated using Weblate (Malay)
Currently translated at 100.0% (1340 of 1340 strings)
2023-11-11 12:06:10 +01:00
Bas Huis
1efa3a165e Translated using Weblate (Dutch)
Currently translated at 83.9% (1125 of 1340 strings)
2023-11-11 12:06:10 +01:00
ROllerozxa
b730c0aa9a Translated using Weblate (Norwegian Bokmål)
Currently translated at 52.4% (703 of 1340 strings)
2023-11-11 12:06:10 +01:00
Muhammad Rifqi Priyo Susanto
bc26bdc2bf Translated using Weblate (Indonesian)
Currently translated at 100.0% (1340 of 1340 strings)
2023-11-11 12:06:09 +01:00
Translator
3187aca3c9 Translated using Weblate (French)
Currently translated at 94.4% (1265 of 1340 strings)
2023-11-11 12:06:09 +01:00
Janar Leas
c4b7876a1a Translated using Weblate (Estonian)
Currently translated at 44.7% (600 of 1340 strings)
2023-11-11 12:06:09 +01:00
Wuzzy
904dbe730d Translated using Weblate (German)
Currently translated at 100.0% (1340 of 1340 strings)
2023-11-11 12:06:09 +01:00
Gregor Parzefall
8bf2031310 Get rid of hidden settings in settings_translation_file.cpp 2023-11-11 11:59:38 +01:00
Wuzzy
af474d10a4
Fix bad translation function names in builtin (#13977) 2023-11-10 07:00:17 +07:00
MisterE123
fe8d04d0b3
Fix misrendered fall_damage_add_percent calculation formula (#13969)
Co-authored-by: rubenwardy <rw@rubenwardy.com>
2023-11-09 19:55:26 +01:00
Gregor Parzefall
394450758e
Fix auto_install_spec being used as a table (#13970)
(It's a string since #13906.)
2023-11-09 19:54:47 +01:00
JosiahWI
56902745c8
Extract updateClouds method from updateFrame (#13939)
Co-authored-by: Gregor Parzefall <82708541+grorp@users.noreply.github.com>
2023-11-08 07:00:59 +07:00
corpserot
9e952603b2
Lump MT_LOGCOLOR env together with other color env (#13887) 2023-11-08 07:00:36 +07:00
Thresher
80c4c260ae
Refactor and move world_format.txt to world_format.md (#13504)
Co-authored-by: Muhammad Rifqi Priyo Susanto <muhammadrifqipriyosusanto@gmail.com>
2023-11-08 07:00:04 +07:00
ROllerozxa
570fc90bf6
Debundle Minetest Game (#13818) 2023-11-07 22:18:26 +01:00
Muhammad Rifqi Priyo Susanto
7213ff7a00
Resolves some warnings for Android version (#13862) 2023-11-05 19:02:01 +01:00
Gregor Parzefall
adec16790b
Offer ContentDB updates for leftover bundled Minetest Game (#13906) 2023-11-05 19:01:19 +01:00
Zughy
726326924d
Clarify get_translated_string string argument (#13948)
Co-authored-by: sfan5 <sfan5@live.de>
Co-authored-by: Muhammad Rifqi Priyo Susanto <muhammadrifqipriyosusanto@gmail.com>
Co-authored-by: rubenwardy <rw@rubenwardy.com>
2023-11-05 19:00:32 +01:00
Muhammad Rifqi Priyo Susanto
4d2227cfa5
Rename mtt_update to mod_translation_updater (#13952)
Clarifies the purpose of the translation update script by giving it a more explanatory name.
2023-11-05 09:11:30 +01:00
a5268beb14 test 2023-11-04 14:14:07 +01:00
Nils Dagsson Moskopp
2025dcffbd Revert "Don't trigger a key event if a key with the same associated char was pressed (#13773)"
This partially reverts commit d57c936b080f404df0b544561242b0c45c57f04d.

The reverted commit prevented recognition of key combinations.
It correctly changed a test case to no longer use “KEY_NUMPAD_5”.

Several keyboard layouts use a key combination to input a “+” (e.g. Neo2);
therefore some users could no longer input “+” to increase the view range.

Co-authored-by: savilli <78875209+savilli@users.noreply.github.com>
2023-10-30 21:23:59 +01:00
Desour
ec7a1f02e7 Fix out-of-bounds access in NodeDefManager::nodeboxConnects 2023-10-30 21:23:47 +01:00
Desour
1d31533601 Reformat rot array in NodeDefManager::nodeboxConnects, to make it less magic 2023-10-30 21:23:47 +01:00
Desour
64104585c5 Devtest: Add more connected nodebox test nodes 2023-10-30 21:23:47 +01:00
Gregor Parzefall
96197025b9
Fix hypertext[] sometimes calculating incorrect scrollbar height (#13943) 2023-10-29 17:54:31 +01:00
Muhammad Rifqi Priyo Susanto
1363059416
Fix issues in Minetest's English texts (#13913)
Co-authored-by: Gregor Parzefall <82708541+grorp@users.noreply.github.com>
Co-authored-by: rubenwardy <rw@rubenwardy.com>
Co-authored-by: Desour <ds.desour@proton.me>
Co-authored-by: sfan5 <sfan5@live.de>
2023-10-29 12:24:39 +01:00
Desour
b3988d964a Sounds: Do not fade paused sounds 2023-10-29 00:40:07 +02:00
Desour
2ad17136dc Sounds: Fix dtime being in milliseconds 2023-10-29 00:40:07 +02:00
Desour
b2aa5d9261 Sounds: Don't pause new sounds when paused 2023-10-29 00:39:38 +02:00
rubenwardy
4ee32c5441
Add package update detection on Content tab (#13807) 2023-10-28 17:33:44 +01:00
sfan5
ddce858c34 Speed up macOS CI
- skip nonsense during package installation
- use actual number of available cores
2023-10-27 20:22:40 +02:00
sfan5
00be802c5c Use macos-latest for CI
Old versions lose support relatively quickly and then silently degrade
to e.g. compiling all dependencies from source which takes a long time.
2023-10-27 20:22:40 +02:00
sfan5
8d2e1289a4 Use newer IrrlichtMt 2023-10-27 19:00:04 +02:00
Desour
454eb3901d Inventory: Fix deleted inventory being used for regaining locked lists 2023-10-27 18:47:07 +02:00
Desour
a464b41d99 Inventory: Release resizes-locked lists on all on_-callbacks 2023-10-27 18:47:07 +02:00
ROllerozxa
1a562ca144 Prevent Windows Defender warnings in singleplayer (Bind singleplayer server to 127.0.0.1) 2023-10-27 12:03:43 +02:00
sfan5
03ba9370b9 Deprecate .bmp format 2023-10-27 11:05:27 +02:00
Nils Dagsson Moskopp
2f16227302
Set color of scrollbar/dropdown button symbols and checkmarks to white
Before this patch, the symbols were rendered black on dark background.

Previous images were edited like this:

1. The colors were inverted with GIMP's “linear inversion” method.
2. Image files were optimized using “optipng -o7 -zm1-9 -strip all”.

Co-authored-by: ROllerozxa <rollerozxa@voxelmanip.se>
Co-authored-by: rubenwardy <rw@rubenwardy.com>
2023-10-27 11:05:00 +02:00
sfan5
15c3fb7b7a Amend list of planned breakages 2023-10-24 18:17:18 +02:00
Gregor Parzefall
2ce14ce4eb
Use hypertext[] for credits so that long lines are wrapped (#13914) 2023-10-22 15:32:14 +02:00
SmallJoker
906417cc0d
GUI: Autofocus newly opened GUIModalMenu instances (#13911)
This in particular fixes incorrect event propagation to menus that
are no longer shown, such as the key change menu when opened within
the settings tab.
2023-10-22 15:31:42 +02:00
SmallJoker
2fbf5f4250
CSM: Fix duplicate player names (#13910) 2023-10-22 15:31:29 +02:00
Alexander Chibrikin
3491509b21
Add Russian translation of builtin (#13896)
Co-authored-by: Zemtzov7 <72821250+zmv7@users.noreply.github.com>
2023-10-22 15:31:11 +02:00
DS
7e8831a414
Inventory: Don't throw resize lock exception in destructor (#13894)
... of nodemeta inventories.
2023-10-22 15:30:11 +02:00
Cora de la Mouche
341e53f2e2
Remove deprecation mark on TGA texture format (#13877) 2023-10-22 15:29:28 +02:00
Muhammad Rifqi Priyo Susanto
c9655e54ce
Change some keys to be triggered once every key press (#13883)
Those keys are below:
- KeyType::CAMERA_MODE
- KeyType::SCREENSHOT
- KeyType::TOGGLE_BLOCK_BOUNDS
- KeyType::TOGGLE_HUD
- KeyType::MINIMAP
- KeyType::TOGGLE_CHAT
- KeyType::TOGGLE_FOG
- KeyType::TOGGLE_DEBUG
- KeyType::TOGGLE_PROFILER
- KeyType::RANGESELECT


Co-authored-by: Gregor Parzefall <82708541+grorp@users.noreply.github.com>
2023-10-22 02:00:08 +07:00
updatepo.sh
8a9855241c Update translation files 2023-10-20 23:14:25 +02:00
updatepo.sh
72fc564758 Update example conf and settings translations 2023-10-20 23:12:43 +02:00
81fee2207e Translated using Weblate (Slovak)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:05 +02:00
gallegonovato
425db09ede Translated using Weblate (Spanish)
Currently translated at 92.1% (1248 of 1355 strings)
2023-10-20 22:54:05 +02:00
Gábriel
0f2b196b32 Translated using Weblate (Hungarian)
Currently translated at 98.2% (1331 of 1355 strings)
2023-10-20 22:54:05 +02:00
watilin
bb7c0ceea0 Translated using Weblate (French)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:05 +02:00
Jorge Batista Ramos Junior
24c2ef2996 Translated using Weblate (Portuguese (Brazil))
Currently translated at 99.0% (1342 of 1355 strings)
2023-10-20 22:54:04 +02:00
Filippo Alfieri
f7775640d5 Translated using Weblate (Italian)
Currently translated at 99.5% (1349 of 1355 strings)
2023-10-20 22:54:04 +02:00
Christian Elbrianno
3127dd902a Added translation using Weblate (Javanese) 2023-10-20 22:54:04 +02:00
Wuzzy
6445fbaadc Translated using Weblate (German)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:04 +02:00
Tirifto
4a4861c26f Translated using Weblate (Esperanto)
Currently translated at 85.9% (1164 of 1355 strings)
2023-10-20 22:54:04 +02:00
Farooq Karimi Zadeh
332f1af325 Translated using Weblate (Persian)
Currently translated at 0.1% (1 of 1355 strings)
2023-10-20 22:54:04 +02:00
José Douglas
520cfaf13e Translated using Weblate (Portuguese (Brazil))
Currently translated at 97.9% (1327 of 1355 strings)
2023-10-20 22:54:04 +02:00
Farooq Karimi Zadeh
7b14b867f5 Added translation using Weblate (Persian) 2023-10-20 22:54:04 +02:00
Marco Ciampa
8ab517d242 Translated using Weblate (Esperanto)
Currently translated at 85.2% (1155 of 1355 strings)
2023-10-20 22:54:04 +02:00
Claybiokiller
c0f0770f65 Translated using Weblate (Chinese (Simplified))
Currently translated at 91.0% (1234 of 1355 strings)
2023-10-20 22:54:04 +02:00
Linerly
28e06f7d9c Translated using Weblate (Indonesian)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:04 +02:00
Nanashi Mumei
77f2c94395 Translated using Weblate (Russian)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:04 +02:00
Emmily
17e0ec27eb Translated using Weblate (Esperanto)
Currently translated at 84.7% (1149 of 1355 strings)
2023-10-20 22:54:04 +02:00
yue weikai
d62abbc938 Translated using Weblate (Chinese (Simplified))
Currently translated at 90.4% (1226 of 1355 strings)
2023-10-20 22:54:04 +02:00
Eoghan Murray
00b7208b5a Translated using Weblate (Irish)
Currently translated at 1.7% (24 of 1355 strings)
2023-10-20 22:54:04 +02:00
Eoghan Murray
92248e8018 Translated using Weblate (Gaelic)
Currently translated at 11.1% (151 of 1355 strings)
2023-10-20 22:54:04 +02:00
Yic95
78aad07be9 Translated using Weblate (Chinese (Traditional))
Currently translated at 70.9% (961 of 1355 strings)
2023-10-20 22:54:04 +02:00
Eoghan Murray
50cdf0e9bf Added translation using Weblate (Irish) 2023-10-20 22:54:04 +02:00
Muhammad Rifqi Priyo Susanto
a644e8c70a Translated using Weblate (Indonesian)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:04 +02:00
Hugo Carvalho
2e96f99e9c Translated using Weblate (Portuguese)
Currently translated at 98.8% (1339 of 1355 strings)
2023-10-20 22:54:04 +02:00
Milan Šalka
57cc054bb3 Translated using Weblate (Slovak)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:04 +02:00
Hugo Rosa
8df315378d Translated using Weblate (Portuguese (Brazil))
Currently translated at 97.9% (1327 of 1355 strings)
2023-10-20 22:54:04 +02:00
Jynweythek Vordhosbn
bcfd1fcdba Translated using Weblate (Spanish)
Currently translated at 92.1% (1248 of 1355 strings)
2023-10-20 22:54:04 +02:00
Joaquín Villalba
6f93853e65 Translated using Weblate (Spanish)
Currently translated at 92.1% (1248 of 1355 strings)
2023-10-20 22:54:04 +02:00
joserene-007
c8b98d1eeb Translated using Weblate (Spanish)
Currently translated at 92.1% (1248 of 1355 strings)
2023-10-20 22:54:04 +02:00
Skrripy
356ee9d2a9 Translated using Weblate (Ukrainian)
Currently translated at 55.2% (749 of 1355 strings)
2023-10-20 22:54:04 +02:00
Ács Zoltán
db5a15e14c Translated using Weblate (Hungarian)
Currently translated at 97.9% (1327 of 1355 strings)
2023-10-20 22:54:04 +02:00
Salif Mehmed
e2ab89d253 Translated using Weblate (Bulgarian)
Currently translated at 34.2% (464 of 1355 strings)
2023-10-20 22:54:04 +02:00
Jakub Z
85884c15e7 Translated using Weblate (Polish)
Currently translated at 87.3% (1183 of 1355 strings)
2023-10-20 22:54:04 +02:00
Dominik Gęgotek
fc5ff8d8c7 Translated using Weblate (Polish)
Currently translated at 87.3% (1183 of 1355 strings)
2023-10-20 22:54:04 +02:00
Janar Leas
34e566f726 Translated using Weblate (Estonian)
Currently translated at 46.8% (635 of 1355 strings)
2023-10-20 22:54:04 +02:00
MikeL
a18be49827 Translated using Weblate (Maori)
Currently translated at 0.8% (11 of 1355 strings)
2023-10-20 22:54:04 +02:00
MikeL
2871a9fee8 Added translation using Weblate (Maori) 2023-10-20 22:54:04 +02:00
ROllerozxa
2817b9d84b Translated using Weblate (Swedish)
Currently translated at 66.6% (903 of 1355 strings)
2023-10-20 22:54:04 +02:00
Timur Seber
4cc05906bd Translated using Weblate (Tatar)
Currently translated at 0.9% (13 of 1355 strings)
2023-10-20 22:54:04 +02:00
facilitas
d04c1b5d73 Added translation using Weblate (Interlingua) 2023-10-20 22:54:04 +02:00
Артём Котлубай
2a518d8661 Translated using Weblate (Russian)
Currently translated at 99.3% (1346 of 1355 strings)
2023-10-20 22:54:04 +02:00
Application-maker
9b71b2f5d9 Translated using Weblate (Russian)
Currently translated at 99.3% (1346 of 1355 strings)
2023-10-20 22:54:04 +02:00
Robinson
261bf52440 Translated using Weblate (Czech)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:04 +02:00
Pexauteau Santander
9b76946540 Translated using Weblate (Czech)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:04 +02:00
Martin Šimek
9c0b546942 Translated using Weblate (Czech)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:04 +02:00
Roland Meier
ac3bb40692 Translated using Weblate (German)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:04 +02:00
Vít Skalický
54a39e3c4e Translated using Weblate (Czech)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:04 +02:00
Martin Šimek
def9db9d16 Translated using Weblate (Czech)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:04 +02:00
Nicolae Crefelean
ed1c6b432c Translated using Weblate (Romanian)
Currently translated at 53.5% (725 of 1355 strings)
2023-10-20 22:54:04 +02:00
Yaya - Nurul Azeera Hidayah @ Muhammad Nur Hidayat Yasuyoshi
dc88e6f927 Translated using Weblate (Malay (Jawi))
Currently translated at 59.0% (800 of 1355 strings)
2023-10-20 22:54:04 +02:00
Yaya - Nurul Azeera Hidayah @ Muhammad Nur Hidayat Yasuyoshi
ce53230ab2 Translated using Weblate (Malay)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:04 +02:00
José Muñoz
cd17caab7e Translated using Weblate (Spanish)
Currently translated at 89.6% (1215 of 1355 strings)
2023-10-20 22:54:04 +02:00
nyommer
d5bf34271f Translated using Weblate (Hungarian)
Currently translated at 97.6% (1323 of 1355 strings)
2023-10-20 22:54:04 +02:00
Sharpik
941896ef28 Translated using Weblate (Czech)
Currently translated at 68.2% (925 of 1355 strings)
2023-10-20 22:54:04 +02:00
Furkan Baytekin
f362ed0880 Translated using Weblate (Turkish)
Currently translated at 89.0% (1207 of 1355 strings)
2023-10-20 22:54:04 +02:00
Sava Kujundžić
be1c441157 Translated using Weblate (Serbian (latin))
Currently translated at 7.4% (101 of 1355 strings)
2023-10-20 22:54:04 +02:00
Pexauteau Santander
8c3b8b7b4c Translated using Weblate (Slovak)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:04 +02:00
Mimi Kush
be1e781399 Translated using Weblate (Spanish)
Currently translated at 89.6% (1215 of 1355 strings)
2023-10-20 22:54:04 +02:00
Šimon Brandner
b10fe4ddd6 Translated using Weblate (Czech)
Currently translated at 68.1% (924 of 1355 strings)
2023-10-20 22:54:04 +02:00
José Muñoz
d54d4b4618 Translated using Weblate (Spanish)
Currently translated at 88.5% (1200 of 1355 strings)
2023-10-20 22:54:04 +02:00
jolesh
09104e17a0 Translated using Weblate (Esperanto)
Currently translated at 84.3% (1143 of 1355 strings)
2023-10-20 22:54:04 +02:00
Walter Bulbazor
7a5247cc33 Translated using Weblate (Occitan)
Currently translated at 24.3% (330 of 1355 strings)
2023-10-20 22:54:04 +02:00
Tor Egil Hoftun Kvæstad
475809ed40 Translated using Weblate (Norwegian Nynorsk)
Currently translated at 43.2% (586 of 1355 strings)
2023-10-20 22:54:04 +02:00
Артём Котлубай
2f9742c64f Translated using Weblate (Russian)
Currently translated at 99.1% (1344 of 1355 strings)
2023-10-20 22:54:03 +02:00
Tsaqib Fadhlurrahman Soka
85c9c27e42 Translated using Weblate (Indonesian)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:03 +02:00
Marian
8c9a4d9a05 Translated using Weblate (Slovak)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:03 +02:00
Ács Zoltán
1b2396ee9e Translated using Weblate (Hungarian)
Currently translated at 97.0% (1315 of 1355 strings)
2023-10-20 22:54:03 +02:00
Alexandros Koutroulis
dd587aa30d Translated using Weblate (Greek)
Currently translated at 27.8% (377 of 1355 strings)
2023-10-20 22:54:03 +02:00
waxtatect
9ccc0bd27e Translated using Weblate (French)
Currently translated at 100.0% (1355 of 1355 strings)
2023-10-20 22:54:03 +02:00
Gregor Parzefall
b1dec37adb
Clean up and improve mainmenu theme / game theme code (#13885) 2023-10-18 20:18:50 +02:00
JosiahWI
62eb6cfed0
Extract updatePauseState from Game::run (#13893) 2023-10-18 20:17:30 +02:00
DS
6026003508
Warn only once about positional stereo sounds (#13895) 2023-10-18 20:16:45 +02:00
Gregor Parzefall
6fdc7e0dad
Make hypertext[] respect font size settings (#13858) 2023-10-16 20:47:16 +02:00
DS
3c41195986
Inventory: Fix picking up items via drop and pickup doubleclick (#13891) 2023-10-16 20:46:57 +02:00
Desour
5e0f14266d Fix forgotten CLANG_MINIMUM_VERSION update 2023-10-14 13:23:17 +02:00
DS
12e98678f6
Particle cleanup (#13394) 2023-10-11 17:07:30 +02:00
Thresher
352a403bd0
Optimize PNG files (#13509) 2023-10-09 17:13:44 +02:00
Muhammad Rifqi Priyo Susanto
7e678b5686
Prevent early respawns caused by up/down button in the death screen (#13870) 2023-10-09 17:13:33 +02:00
sfan5
b270c2bd68 Don't print ASCII art when using ncurses 2023-10-09 17:13:18 +02:00
DS
11ec75c2ad
ActiveObjectMgr fixes (#13560) 2023-10-09 17:13:04 +02:00
Loïc Blot
929a13a9a0
build: Allow disabling documentation build + print more build flags (#13871)
* build: permit to disable documentation build

* build: add a message about some BUILD_* flags
2023-10-09 15:35:12 +02:00
Wuzzy
2c74797d34
Add script to update/generate mod translations (#13739) 2023-10-08 16:47:11 +01:00
Gregor Parzefall
d05da513be
Notify users to reinstall MTG if worlds exist (#13850) 2023-10-08 16:47:00 +01:00
Gregor Parzefall
26bb397852
Add advanced settings checkbox and hide advanced settings by default (#13861)
Co-authored-by: rubenwardy <rw@rubenwardy.com>
2023-10-07 21:34:59 +02:00
sfan5
01d26c0e0e Warn when ignoring bind_address 2023-10-05 17:29:49 +02:00
sfan5
e02bf9fb1a Log timeout when a httpfetch times out 2023-10-05 17:29:49 +02:00
sfan5
9ec40ce8e9 Enforce minimum for curl(_file_download)_timeout 2023-10-05 17:29:49 +02:00
rvenson
ac8a9f9502
Update range values of meta set functions in the documentation 2023-10-05 17:29:26 +02:00
Muhammad Rifqi Priyo Susanto
c60d971bc4
Move unsupported language list into a separate file (#13865) 2023-10-05 17:29:02 +02:00
DS
8db4381304
MapblockMeshGenerator: Use more verbose member names (#13244) 2023-10-04 00:28:43 +02:00
corpserot
5a5697273b
lua_api_deploy: fix code blocks parsing (#13847) 2023-10-03 20:34:24 +02:00
sfan5
de0036f4c1 Document air_equivalent as deprecated 2023-10-02 13:44:12 +02:00
Gregor Parzefall
33cc29bbda
Allow setting custom third person front view camera offset (#13686)
Co-authored-by: Muhammad Rifqi Priyo Susanto <muhammadrifqipriyosusanto@gmail.com>
Co-authored-by: SmallJoker <SmallJoker@users.noreply.github.com>
2023-10-02 13:44:03 +02:00
Caleb Butler
3a4bf14c20
Replace all core::unicode::ustring with std::u32string (#13775) 2023-10-02 13:43:38 +02:00
Gregor Parzefall
56965bc814
Android: Add field_enter_after_edit[] formspec element (#13836) 2023-10-01 11:20:50 +02:00
Gregor Parzefall
94eba15c34
Misc. mainmenu fixes (#13859)
* settingstypes.txt: Fix wrong default value for profiler.report_path

* Disable Irrlicht file picker on Android
  (It doesn't work.)

* Join Game tab: Fix server description textarea being misaligned with background

* Reduce distance between tab and gamebar on Android
  Allows using a higher gui_scaling value without the gamebar going off-screen.

Co-authored-by: ROllerozxa <rollerozxa@voxelmanip.se>
2023-10-01 11:19:52 +02:00
Desour
c90c545d33 Put the internal sound definitions into a new sound namespace 2023-09-30 18:54:26 +02:00
Desour
bbc64a2eb5 Split sound_openal_internal into serval files 2023-09-30 18:54:26 +02:00
Desour
606215fae9 Move sound_openal and sound_openal_internal into new src/client/sound directory 2023-09-30 18:54:26 +02:00
Desour
8fa2ea71ef Move soundmanager into its own thread
Fixes sound queues running empty on client step hiccups.
2023-09-26 22:10:57 +02:00
Desour
591e45657f Bump minimum clang version to 7.0.1
std::variant is broken in clang < 7.0.1 with libstdc++
see: https://github.com/llvm/llvm-project/issues/32569
2023-09-26 22:10:57 +02:00
sfan5
b0d5cedeb6 Fix missing initialization for m_game_focused 2023-09-24 16:46:18 +02:00
sfan5
d113636a43 Fix UB in NetworkPacket class 2023-09-24 16:46:18 +02:00
sfan5
5109fa7eda Fix crash when processing empty mesh buffers 2023-09-24 16:46:18 +02:00
Gregor Parzefall
ff87be6e5f
Remove unused "mNormal" uniform to fix crash on GLES2 with shaders 2023-09-24 16:46:05 +02:00
Gregor Parzefall
4cf900c779
Fix error when enabling texture packs (#13829) 2023-09-23 18:20:23 +02:00
ROllerozxa
c247761213
Escape package description in content tab 2023-09-22 21:25:58 +02:00
savilli
d57c936b08
Don't trigger a key event if a key with the same associated char was pressed (#13773) 2023-09-22 21:25:13 +02:00
David Leal
9f47e123d2
animaition -> animation (#13827)
Also changed `range` to `frame_range`,
2023-09-22 18:41:33 +02:00
sfan5
c3114132d3
Improve readability and infos in verbose log (#13828) 2023-09-22 18:41:10 +02:00
ROllerozxa
5949172735
Build MkDocs Lua API docs using GitHub CI, deploy to api.minetest.net (#13675)
* Build MkDocs Lua API documentation using GitHub CI and Pages instead

* Remove Lua highlight hack as codeblocks are correctly marked as Lua now

* fix line endings
2023-09-18 19:17:18 +03:00
x2048
e36b2226b9
Skip face culling in shadows for double-sided materials (e.g. plantlike) (#13500)
* Skip face culling in shadows for double-sided materials (e.g. plantlike)

* Keep previous face culling for transparent surfaces e.g. water
2023-09-17 21:42:14 +02:00
ROllerozxa
a88e61c2cf
Improve UX when no game exists and drop default_game (#13550) 2023-09-17 18:45:28 +01:00
Gregor Parzefall
5bfc5d44c0
Two ContentDB GUI fixes (#13806) 2023-09-16 18:36:28 +02:00
Gregor Parzefall
4f735fba05
Settings GUI: Noise parameter setting fixes (#13797) 2023-09-16 18:35:35 +02:00
Wuzzy
8ebaf753d3
New physics overrides (#11465) 2023-09-15 20:10:08 +02:00
sfan5
2479d51cc6 Fix double-free of minimap textures 2023-09-13 15:27:07 +02:00
sfan5
033128d8dc Show better description to users when std::bad_alloc happens 2023-09-13 13:58:59 +02:00
SmallJoker
4ef93fe25f
Allow place_param2 = 0 node placement predictions (#13787)
The placement prediction value 0 was accidentally ignored
and made the clients fall back to automatic rotation based
on the node paramtype2 value.

This now changes the internal representation to properly
indicate the disabled state (e.g. 'nil' in Lua).
2023-09-13 13:57:57 +02:00
Gregor Parzefall
833c324498
Make the crosshair DPI-aware (#13772) 2023-09-11 18:59:32 +02:00
rubenwardy
48ab1835da Replace settings tab with button 2023-09-09 18:49:33 +02:00
Zughy
798b9eae4a Add settings button icon 2023-09-09 18:49:33 +02:00
Desour
010d08f6a4 Fix -Wmissing-braces warnings in mapblock_mesh.cpp 2023-09-09 18:48:56 +02:00
Desour
7897450b27 Fix -Winconsistent-missing-override warnings 2023-09-09 18:48:56 +02:00
Desour
2ad4c9e0ce Fix -Wunused-but-set-variable warnings 2023-09-09 18:48:56 +02:00
Gregor Parzefall
95056f9783
Higher default graphics settings on Android (#13780) 2023-09-07 17:55:11 +02:00
Gregor Parzefall
1a568cc491
Fix that negative integer values for float settings don't get a ".0" suffix (#13779) 2023-09-05 15:36:05 +02:00
Gregor Parzefall
83b85ba16a
Rewrite the gamebar (#13768) 2023-09-02 23:02:02 +02:00
chmodsayshello
294ad98776
Send ever lasting particle spawners to all players (#13774) 2023-09-02 22:58:11 +02:00
sfan5
f080aa29b5 Remove usage of obsolete HighPrecisionFPU field 2023-09-01 12:46:36 +02:00
Gregor Parzefall
4252f9d4d5
Restore the appearance of the "Start Game" tab after #13761 (#13769) 2023-08-30 14:45:44 +02:00
rubenwardy
0cbf96cc83
Use formspec version 6 in the main menu (#13761) 2023-08-28 22:36:54 +01:00
Gregor Parzefall
7b56daa236
Small setting-related fixes (#13755) 2023-08-27 20:18:41 +02:00
savilli
852d6a7976
Fix potential freeze in core.check_for_falling 2023-08-27 20:12:53 +02:00
SmallJoker
bf9f831cb2 Inventory: skip redundant stack movement
The list of dragged stacks includes the source stack, which
however does not need to be moved onto itself.
This is an optimization.
2023-08-27 20:12:10 +02:00
SmallJoker
0ba899e239 Inventory: Fix assertion caused by a no-op stack movement 2023-08-27 20:12:10 +02:00
Rising Leaf
660151572f
Do not render objects that are invisble into the shadow map 2023-08-26 20:12:17 +02:00
1F616EMO~nya
54eacca287
Use issue templates for creating issues (#13222) 2023-08-25 22:00:05 +07:00
Desour
f47b00426a Revert "Get rid of guiroot"
This reverts commit 45e7a800575f6d96ea307d99f1945aeb6c22a4e1.
2023-08-24 22:14:44 +02:00
Desour
7e4dccb3b5 Revert "Get rid of global guienv variable"
This reverts commit 16da954bd70b326f21cec9547237f55de18d4253.
2023-08-24 22:14:44 +02:00
Desour
f98726c516 Revert "Use our GUIButton in touchscreengui"
This reverts commit f7f3aaf43c88179bafd255f3c67275f316cff91a.
Fixes #13743.
2023-08-24 22:14:44 +02:00
Gregor Parzefall
aea9242a96
Allow nodes to have their post_effect_color affected by lighting (#13637)
Co-authored-by: DS <ds.desour@proton.me>
2023-08-24 20:16:36 +02:00
Gregor Parzefall
92b6ff4721
TouchScreenGUI: Fix only 9 hotbar slots being usable (#13698)
Co-authored-by: Muhammad Rifqi Priyo Susanto <muhammadrifqipriyosusanto@gmail.com>
2023-08-24 17:45:51 +02:00
Gregor Parzefall
72ef90885d
Clean up texture filtering settings (#13683) 2023-08-24 10:50:47 +02:00
gamefreq0
d0ee63c766
Enable shift-click crafting (#13729) 2023-08-24 12:00:54 +07:00
fluxionary
587e2b2526
Set item description as infotext for item entities (#13728) 2023-08-24 12:00:18 +07:00
rubenwardy
a65cdbe66e
Settings GUI: Use language names rather than codes (#13752) 2023-08-24 00:33:23 +01:00
Montandalar
7b3ed32003
Persist text inputs in mainmenu local tab
Co-authored-by: archfan <33993466+archfan7411@users.noreply.github.com>
2023-08-14 18:17:53 +02:00
sfan5
2c987b66c1 Move implementations of some LuaVoxelManip functions to l_mapgen 2023-08-14 18:16:36 +02:00
sfan5
e48f15c135 Skip liquid updates in MapgenSinglenode if not applicable 2023-08-14 18:16:36 +02:00
sfan5
bf36a90579 Optimize Mapgen::updateLiquid()
-55% runtime in singlenode usage, which is the best case
2023-08-14 18:16:36 +02:00
sfan5
43c9c38a28 Fix itemdef defaults not being applied in async env 2023-08-14 18:16:36 +02:00
sfan5
f6bddc4e8d Fix registered_craftitems not populated in async env 2023-08-14 18:16:36 +02:00
sfan5
f9c881eb5a Add two missing classes to async environment 2023-08-14 18:16:36 +02:00
Desour
16da954bd7 Get rid of global guienv variable
(It can already be accessed via the renderingengine.)
2023-08-14 18:13:47 +02:00
Desour
45e7a80057 Get rid of guiroot
The guienvironment already provides a root gui element, we don't need to add another one.
(For CGUIEnvironment, the env itself is the root element.)
2023-08-14 18:13:47 +02:00
Desour
7e7aceb8c1 Replace all actual uses of irrlicht CGUIStaticText with our StaticText 2023-08-14 18:13:47 +02:00
Desour
d75c956dbc Remove ugly hack in static_text.h
Just use the root element, like GUIButton:add().
2023-08-14 18:13:47 +02:00
Desour
f7f3aaf43c Use our GUIButton in touchscreengui 2023-08-14 18:13:47 +02:00
Desour
91c0439922 Use our GUIButton in our GUIScrollBar
Note that GUIScrollBar needs an ISimpleTextureSource now due to button styling.
2023-08-14 18:13:47 +02:00
Desour
9d62abbe46 Replace any uses of CGUIScrollBar and IGUIScrollBar with GUIScrollBar 2023-08-14 18:13:47 +02:00
Desour
124d064015 GUIButton: Default BgColor to white, as opposed to unintialized
(Same as what CGUIButton uses (via colors=0).)
2023-08-14 18:13:47 +02:00
Desour
2903f692ba GUIButton: Use default member initializers 2023-08-14 18:13:47 +02:00
Desour
7f9de5db0b Make touchscreengui compile 2023-08-14 18:13:47 +02:00
jordan4ibanez
14441a289e
Document openSUSE Required Packages 2023-08-14 18:13:36 +02:00
rubenwardy
137e4ce866
Fix hypertext in the mainmenu (#13731) 2023-08-13 13:28:33 +01:00
Gregor Parzefall
526c5f2348
ContentDB GUI: Load package list asynchronously (#13551) 2023-08-13 13:28:24 +01:00
ROllerozxa
e4bedc7ea8
Make content tab use real coordinates and minor cleanups (#13719) 2023-08-13 13:28:16 +01:00
rubenwardy
c6a0ead72d
Add warning for initial properties directly inside definition (#9650) 2023-08-13 00:19:03 +01:00
Zughy
98f097dc2f
Warn about unsupported file extensions for media 2023-08-06 14:16:00 +02:00
ROllerozxa
c816aa5374
Settings GUI: Fix path settings on Windows 2023-08-06 14:15:49 +02:00
Zughy
4d9a67682d
DOCS: state that get_wielded_item returns a copy of the item 2023-08-06 14:15:34 +02:00
rubenwardy
d16d1a1341
Settings GUI: Add setting dependencies (#13704) 2023-08-05 17:55:27 +01:00
rubenwardy
752ce1a1b2
Settings GUI: Move shadow presets to Shaders, remove Most Used (#13713) 2023-08-05 17:33:18 +01:00
JosiahWI
28fce8aad5
Add dev stage to docker image (#13573) 2023-07-30 15:29:25 +01:00
Nikita K
e0948f42ab
Add Void Linux specifics to build documentation (#13693) 2023-07-30 14:55:06 +01:00
Joachim Stolberg
21ecdd5681
Fix textarea scrollbar inside border=false (#13678) 2023-07-30 14:54:52 +01:00
Wuzzy
20e9969313
Improve object documentation in lua_api.md (#13239)
Co-authored-by: DS <ds.desour@proton.me>
2023-07-30 14:54:01 +01:00
OgelGames
3f2a10bb4b
Fix decode_base64 returning nothing instead of nil (#13697) 2023-07-30 14:53:47 +01:00
doxygen-spammer
9f25378ddd
Add performance test nodes, using complex meshes. (#13161) 2023-07-30 14:53:08 +01:00
Gregor Parzefall
cc8280426f
Minor additions to the VoxelManip docs 2023-07-28 00:40:01 +02:00
Stvk imension
ba6de431a2
Android: Remove Migration Code (#13590)
Co-authored-by: Muhammad Rifqi Priyo Susanto <muhammadrifqipriyosusanto@gmail.com>
2023-07-27 14:09:17 +02:00
Gregor Parzefall
c14e4d1795
Increase the resolution of the logo shown on the about tab 2023-07-22 17:20:12 +02:00
Desour
e0192e256f Fix incorrect rounding in GUIInventoryList::getItemIndexAtPos 2023-07-22 17:19:57 +02:00
ndren
53c594abe0
Introduce and start using microsecond sleep on Linux (#13445) 2023-07-22 17:19:49 +02:00
sfan5
72ed8514c5 Use newer IrrlichtMt 2023-07-20 22:15:47 +02:00
Gregor Parzefall
6f0d36c41a Fixes and improvements 2023-07-20 22:15:47 +02:00
Gregor Parzefall
7473e4cafd Expose all OpenGL filtering modes, use OpenGL names for them
Because of a review comment on the Irrlicht PR by numberZero.
2023-07-20 22:15:47 +02:00
Gregor Parzefall
6bf63d4b41 Rename SMaterial::TextureLayer -> SMaterial::TextureLayers
It's not the "texture layer" of the material, but an array of texture layers.
2023-07-20 22:15:47 +02:00
Gregor Parzefall
05ebe2418b Rename E_MATERIAL_FLAG -> E_MATERIAL_PROP
The enum values don't reference material flags, but material properties.
2023-07-20 22:15:47 +02:00
Gregor Parzefall
9bef3c136a Split up texture filtering properties of SMaterialLayer into MinFilter and MagFilter
You can	now set	the filter used	when scaling textures down and the filter used when scaling textures up separately.
2023-07-20 22:15:47 +02:00
Gregor Parzefall
307e380f30 Refactor the way you set material properties
Instead of using SMaterial::setFlag, you now set them directly on SMaterial or SMaterialLayer.
2023-07-20 22:15:47 +02:00
SmallJoker
128d22e6ee
GUI: Automatic scaling of checkboxes and scrollbars (#13666)
Mainly helpful on high-DPI screens or when 'gui_scaling' is changed
2023-07-17 20:46:15 +02:00
Nekobit
50234b8e5c
Fix string conversion for FreeBSD (#13648) 2023-07-17 20:46:06 +02:00
Gregor Parzefall
3552537fc4
Fix that transparent text still draws a text shadow (#13649)
Makes fade out animations of text more pleasant to look at.
2023-07-17 20:45:56 +02:00
DS
f41e9e3e0f
Add Irrlicht device info to the mainmenu About tab (#13636) 2023-07-17 20:44:54 +02:00
archfan
9b310a6e6f
Decrease sneak margin to combat phasing through thin walls (#13607)
A 1/16th-node-thick wall is 0.625 meters thick, and the previous margin of 0.1 meters meant that these walls could be phased through by sneaking against them. A margin lower than 0.625 prevents this.
2023-07-17 20:44:33 +02:00
Muhammad Rifqi Priyo Susanto
2061984313 Simplifies code by using Irrlicht's operator overloads
New variables are added to replace in-place calculations.
2023-07-14 21:00:29 +07:00
Muhammad Rifqi Priyo Susanto
3a47559e86 Fix some memory leaks and code style issues
Maximum line length is 95 characters.
Some members' name are changed.
Struct initialisations use brace syntax; eliminating the usage of the memset function.
Iterations use for-each-loop instead of while-loop+iterator.
char * -> std::string
button_info * -> std::shared_ptr<button_info>
2023-07-14 21:00:29 +07:00
Desour
8e09077de8 Fix sound manager not being stepped by GUIEngine 2023-07-14 14:42:00 +02:00
Gregor Parzefall
1837a11c22
Improve messages when changing viewing range and exceeding server-set limit (#13647) 2023-07-14 14:41:45 +02:00
sfan5
bf987bf58a Handle blit_back_with_light with empty area
fixes #13306
2023-07-13 20:42:39 +02:00
sfan5
4a14a18799 Fix mapgen_v6 crashing
this variable was accidentally shadowed in 20b10b569
2023-07-09 20:59:57 +02:00
lhofhansl
136a93f628
Reverse eye-offset Z-coordinate in 3rd person front view (#13369) 2023-07-08 12:00:15 +07:00
Gregor Parzefall
0218963f1b
Fix max_formspec_size not taking gui_scaling into account (#13493) 2023-07-07 21:42:43 +02:00
SmallJoker
078bd95a49
Formspec: prevent infinite loop caused by negative background9[] size (#13624) 2023-07-07 21:42:10 +02:00
lhofhansl
869df17ddf
Server enforcement for fog_distance (#13448) to block cheating (#13643)
This enforces the fog_distance (if set) at the server, so that a hacked client could not cheat and retrieve blocks beyond the set distance.
2023-07-06 09:36:46 -07:00
Gregor Parzefall
26453df2f7
Don't crash if a Lua error occurs inside get_staticdata 2023-07-03 20:34:02 +02:00
ROllerozxa
d71872af23
Fix texture paths for system-installed builds
* window icon
* custom touchscreen checkbox sprites
2023-07-03 20:31:03 +02:00
Lars Müller
25ef8f3934
Fix local animations not resetting
Converts `LocalPlayerAnimation` to a scoped enum to prevent such bugs in the future
2023-07-02 12:47:18 +02:00
Muhammad Rifqi Priyo Susanto
ff498fc206
Android: Reliably showing an IME for text input dialog (#13521)
This commit is inspired by this blog post: https://developer.squareup.com/blog/showing-the-android-keyboard-reliably/
2023-07-01 14:00:30 +07:00
lhofhansl
0ade097e99
Allow the server to control fog_distance and fog_start via the sky-api (#13448) 2023-06-30 19:11:17 -07:00
numzero
dde8f0e20a Replace a non-aligned cuboid with a cylinder in client::ActiveObjectMgr::getActiveSelectableObjects 2023-06-29 18:58:10 +02:00
numzero
21035bf5d4 Add unit test on client::ActiveObjectMgr::getActiveSelectableObjects 2023-06-29 18:58:10 +02:00
numzero
d7291e0600 Update client::ActiveObjectMgr::getActiveSelectableObjects API 2023-06-29 18:58:10 +02:00
AFCMS
aaae9d5a77
Fix .clang-format file config values 2023-06-29 18:57:55 +02:00
x2048
c09a3a52ac
Add antialiasing filters (FXAA, SSAA) (#13253) 2023-06-28 05:30:08 +02:00
sfan5
442d5fc75c Add unit tests for isBlockInSight() 2023-06-26 22:51:49 +02:00
numzero
3b74cc4a41 Replace PP with direct printing 2023-06-26 22:51:32 +02:00
numzero
de77fe8ade Allow printing irr::core::vector[23]d directly to an std::ostream 2023-06-26 22:51:32 +02:00
Vitaliy
2f6a9d12f1
Allow running individual unit tests 2023-06-25 11:13:48 +02:00
Vitaliy
aada2403c9
Try all known video drivers if the requested one fails to initialize 2023-06-25 11:13:23 +02:00
LoneWolfHT
35ad3dabab
Fix MSVC github action 2023-06-24 20:38:31 +02:00
s20
4fb6754903
Adding gettext in the compilation dependency packages list 2023-06-24 20:38:11 +02:00
Gregor Parzefall
7e51e2dea6 Fix compiler error on MSVC with ENABLE_TOUCH=TRUE 2023-06-24 20:37:59 +02:00
sfan5
84fb663d6c Add VoxelArea::intersect() 2023-06-23 09:05:26 +02:00
sfan5
659828b142 Rename ModApiEnvMod and ModApiItemMod
The 'mod' could have meant module in the past but no other classes do this.
2023-06-23 09:05:26 +02:00
sfan5
610578e3e2 Use swapNode for set_node_level and add_node_level
While this is a behaviour change I don't think the old one made any sense.
It's possible that someone hit this before and wrote a workaround for it,
they won't be affected by this change.
It only makes things work that didn't before.
2023-06-23 09:05:26 +02:00
sfan5
62629939ff Genericize find_node_near and find_node_in implementations in C++ 2023-06-23 09:05:26 +02:00
sfan5
20b10b5691 Refactor EmergeParams owner 2023-06-23 09:05:26 +02:00
sfan5
32ff832108 Save Lua globals after mod loading
These are used for the async env currently and will be needed elsewhere soon.
2023-06-23 09:05:26 +02:00
sfan5
4fdd2dec59 Move core.run_callbacks and related to common folder 2023-06-23 09:05:26 +02:00
sfan5
524d446757 Minor script api fixes/cleanups 2023-06-23 09:05:26 +02:00
wsor4035
5b6bc8a12b
Remove unsupported media formats from client.cpp 2023-06-22 17:52:48 +02:00
LoneWolfHT
6a328197a5
MSVC CI job: Compile with gettext and LuaJIT 2023-06-22 17:52:35 +02:00
Gregor Parzefall
03ffc2618c
TouchScreenGUI: Add an exit / "ESC" button to the rare controls bar (#13574) 2023-06-22 17:50:36 +02:00
numzero
7c26cb1c35 Drop unused tile rotations 2023-06-22 17:50:20 +02:00
numzero
1102f92dac Use a enum for tile rotation 2023-06-22 17:50:20 +02:00
numzero
729671d6ae In getNodeTile, use a descriptive struct for the lookup table 2023-06-22 17:50:20 +02:00
numzero
d676520526 Optimize trigonometry out of MapblockMeshGenerator::drawCuboid 2023-06-22 17:50:20 +02:00
numzero
c29d897854 Optimize trigonometry out of MapblockMeshGenerator::drawLiquidTop 2023-06-22 17:50:09 +02:00
numzero
b8ddde0a96 Store liquid data as dimensionless fractions instead of BS multiplies 2023-06-22 17:50:09 +02:00
Vitaliy
43c9647fe5
Use absolute URL for the roadmap (#13617) 2023-06-21 16:00:04 +07:00
Desour
03dda13910 OpenALSoundManager: Fix a buffer overflow 2023-06-20 20:54:39 +02:00
AFCMS
531122ee86
Add .fleet folder to gitignore (#13611) 2023-06-20 22:00:15 +07:00
lhofhansl
8f25f487fe
Instrument touchMapBlocks and block loading/deserialization. (#13314) 2023-06-19 16:59:08 -07:00
Vitaliy
f1feeb319c
Cull liquid back face on liquid-glasslike interface (#13594) 2023-06-18 13:52:14 -07:00
DS
edcbfa31c9
Sound refactor and improvements (#12764) 2023-06-16 20:15:21 +02:00
Wuzzy
8e1af25738 DevTest: Add example nodes for disable_descend 2023-06-16 20:14:08 +02:00
Wuzzy
6b3deaa170 Add disable_descend to disable active node sinking 2023-06-16 20:14:08 +02:00
Muhammad Rifqi Priyo Susanto
a4e69d6843
TouchScreenGUI: Read coordinates directly for virtual joystick (#13567)
The movement's direction and speed are calculated directly from the button's relative screen coordinate. The previous method was to trigger the movement using a keyboard event.
The only virtual joystick status left is Aux1 button.

---------

Co-authored-by: Gregor Parzefall <gregor.parzefall@posteo.de>
2023-06-16 22:40:16 +07:00
Desour
c549e84abb Silence a -Wsign-compare warning for invlist indices 2023-06-15 13:55:20 +02:00
Desour
dade95e142 Fix curl deprecation warnings, and set minimum curl version to 7.56.0 2023-06-15 13:55:20 +02:00
Desour
f947e2afec Fix some gcc -Wself-move warnings 2023-06-15 13:55:20 +02:00
Desour
6a05d63993 Use [[noreturn]] 2023-06-15 10:38:44 +02:00
Desour
5e6d144567 Enable -Wimplicit-fallthrough and use [[fallthrough]] attribute 2023-06-15 10:38:44 +02:00
Desour
9c348d057e Replace the old STATIC_ASSERT macro with static_assert 2023-06-15 10:38:44 +02:00
Desour
8b108ed5f2 Use nicer syntax for nested namespace definitions 2023-06-15 10:38:44 +02:00
Desour
e700182f44 Replace Optional with std::optional 2023-06-15 10:38:44 +02:00
Desour
34ad551efc Use MutexAutoLock for Thread::m_start_finished_mutex 2023-06-15 10:38:44 +02:00
Desour
5d863d7e9c Bump C++ std to 17 2023-06-15 10:38:44 +02:00
Desour
28766d1879 Bump minimum gcc and clang versions 2023-06-15 10:38:44 +02:00
Pascal Abresch
ba80d1ce1f Implement check_offset for decorations 2023-06-15 10:36:46 +02:00
DS
c91182e1b3
Move the platform-dependent stuff in renderingengine.cpp to irrlicht (#13348) 2023-06-11 14:17:39 +02:00
DS
553dc02deb
Fix some memleaks from GUIButtonImage (#13564)
* `m_foreground_image` was grabbed, but not dropped in the destructor.
* `m_image` was created with new. It is grabbed by itself and by the env (not only by the env!, so it's an owning ptr). This owning ptr also was never dropped.
2023-06-06 19:01:32 +02:00
Desour
1b51ff333a Use unique_ptr for ServerInventoryManager::DetachedInventory::inventory 2023-06-05 20:43:33 +02:00
Desour
1780d1bbde Use unique_ptrs for MapSector::m_blocks 2023-06-05 20:43:33 +02:00
Desour
08ea467bfe Use unique_ptr for g_httpfetch_thread 2023-06-05 20:43:33 +02:00
Desour
cfb1b879e0 Use unique_ptrs for CurlFetchThread::m_all_ongoing 2023-06-05 20:43:33 +02:00
Desour
d0bcdff5ce Use unique_ptrs for leveldb db and iterators 2023-06-05 20:43:33 +02:00
Zughy
8445c5fe60
Extend roadmap approval time from one week to one month 2023-06-05 12:02:59 +02:00
Gregor Parzefall
a1463263b5
Auto-detect locale on Android (#13561) 2023-06-05 12:02:10 +02:00
Gregor Parzefall
a857c46e6e
Make the settings GUI more usable on Android (#13543) 2023-06-05 12:01:54 +02:00
Desour
d9f478cbfb Remove a misleading MutexAutoLock in l_to_table
The temporary is immediately destructed, so the mutex isn't locked
after the line.
Removed the lock, because the Settings member-functions used by
push_settings_table lock the mutex and are thread-safe, but would
cause a dead-lock.
2023-06-05 12:01:08 +02:00
OgelGames
252c79d53a
Inventory mouse shortcut improvements (#13146)
Co-authored-by: Muhammad Rifqi Priyo Susanto <muhammadrifqipriyosusanto@gmail.com>
2023-06-05 12:00:32 +02:00
mazes-80
23f7aab354
Item Entity: prevent moveresult assert when attached (#13353) 2023-06-05 12:00:11 +02:00
Gregor Parzefall
e5a5d5a672 Fix various cases of double-escaped error messages 2023-06-05 11:59:37 +02:00
Riley Adams
29b7aea38b
Cavegen y biome check (#13472) 2023-06-05 11:59:22 +02:00
sfan5
1ef9fc9d1f Bump used IrrlichtMt version 2023-06-04 20:36:46 +02:00
Muhammad Rifqi Priyo Susanto
7221de6ede Option to invert direction or disable mouse wheel for hotbar item selection
More changed callbacks for the settings are added in readSettings(). Those are also deregistered when the Game object is destroyed.
2023-05-31 12:40:00 +07:00
Treer
8cd1296049
Add additional texture modifiers (#10100)
* Adjust hue, saturation, and lightness
* Colorize using hue, saturation, and lightness
* Adjust contrast & brightness
* Hard light
* Overlay
* Screen
* Create texture of a given size and color
2023-05-29 20:17:39 +01:00
lhofhansl
a8ec6092e2
Load blocks and objects behind player when in third-persion front-view (#13431) 2023-05-29 10:26:42 -07:00
Gregor Parzefall
fc3d6c1dd9
Place nodes with single tap on Android (+ bugfix) (#13187)
Don't place nodes when closing button bars.
Update docs (also in-game).
Rename "Default controls" -> "Controls" in Android pause menu since players can't change them (normally), so calling them "default" doesn't make sense.
2023-05-29 12:30:30 +07:00
lhofhansl
6832bf044e
Avoid jittering when player is attached (#12439)
* Avoid very jittering when player is attached.

Co-authored-by: sfan5 <sfan5@live.de>
Co-authored-by: Vitaliy <numzer0@yandex.ru>
2023-05-28 11:36:34 -07:00
ROllerozxa
394dd9ffa5
Fix settings dialog not resetting filter when closed (#13513) 2023-05-27 15:35:01 +01:00
sfan5
8cccd75e81
Android build via CMake (#13528)
* the thing

* the thing 2
2023-05-26 15:21:23 +02:00
Thresher
00c647e4cc
Convert spaces to tabs (#13506)
* Convert spaces to tabs

* Desour reviews 1-3 fix

* Desour fixes

* Undo alignment changes
2023-05-26 15:13:57 +02:00
ROllerozxa
f4cb16cc2d
Disable desynchronize_mapblock_texture_animation by default (#13514) 2023-05-26 13:48:37 +02:00
Zughy
d6eb6ff973
Reset player lighting when passing no arguments (#13525)
Co-authored-by: Zughy <4279489-marco_a@users.noreply.gitlab.com>
2023-05-26 13:47:16 +02:00
Zughy
b60d38b7f9
Reset day/night ratio even when passing no arguments (#13524)
* reset day_night_ratio when passing zero fields

* Update lua_api.md

---------

Co-authored-by: Zughy <4279489-marco_a@users.noreply.gitlab.com>
2023-05-26 13:45:42 +02:00
Thresher
180ec92ef9
Remove trailing whitespace (#13505) 2023-05-18 20:34:18 +02:00
SmallJoker
95a9f4ab7c
Inventory: Allow InvRef:set_list with new_size >= old_size (#13497)
Fixes a regression introduced by enforced checks to work with
valid pointers within inventory actions.
2023-05-18 20:32:55 +02:00
Gregor Parzefall
f393214fef
Settings menu improvements regarding default values (#13489)
The reset button now removes the setting from minetest.conf instead of setting it to its default value.
The reset button is now shown whenever a value is present in minetest.conf
Float settings now get a .0 suffix if they have no decimal places.
2023-05-18 20:32:26 +02:00
savilli
5ba70cf5ef
Fix crash on handling wallmounted nodes with invalid param2 (#13487) 2023-05-18 20:31:04 +02:00
Zemtzov7
35112f2453
Disable vertical movement when both jump and sneak keys are pressed (#13426) 2023-05-18 20:30:21 +02:00
Gregor Parzefall
15fb4cab15
Fix Android segfault when game exits before TouchScreenGUI is initalized 2023-05-11 22:51:16 +02:00
sfan5
15445a0fbe Raise and clean up _WIN32_WINNT constant 2023-05-11 22:51:01 +02:00
Wuzzy
80574cdbe8
Fix rotation of 4dir in schematic placement (#13432) 2023-05-11 22:50:52 +02:00
Stvk imension
3de54039ae
Document Android controls (#13061) 2023-05-11 22:50:38 +02:00
Buckaroo Banzai
65692ad1b5
Add min/max protocol version to minetest.get_version() (#13482) 2023-05-06 16:16:21 +01:00
rubenwardy
bc4fc6d648
Fix shadows dropdown and clean up shader settings (#13481) 2023-05-03 22:28:02 +01:00
rubenwardy
ad37df7f2e Fix crash when multiple mods with the same name provide settings 2023-05-01 21:39:12 +01:00
rubenwardy
d35672e78e Redesign/unify mainmenu settings interface 2023-05-01 17:11:41 +01:00
Zughy
a421a1d764 Add setting icons 2023-05-01 17:11:41 +01:00
DS
bec9c68bf3
Release invlist resizelock while doing the recursive callback in move_somewhere mode (#13470)
Fixes a crash in popular creative inventory mods that set the list when you put
something into trash.
2023-04-30 18:20:48 +02:00
lhofhansl
b35aa10579
Guarantee ActiveObjectMgr::m_active_object is not modified while iterating (#13468)
Currently if a mod creates new active objects in on_deactivate the server could crash.
2023-04-28 11:17:48 -07:00
Desour
7f6b09dce8 Use json forward-declarations 2023-04-27 18:50:33 +02:00
Desour
8b73743baa Reduce number of recursively included headers
This should improve compilation speed.

Things changed:
* Prefer forward-declarations in headers.
* Move header-includes out of headers if possible.
* Move some functions definitions out of headers.
* Put some member variables into unique_ptrs (see Client).
2023-04-27 18:50:33 +02:00
Desour
e9e8eed360 GUIChatConsole: Use primary selection 2023-04-27 18:50:17 +02:00
Desour
062b4d036a GUIEditBox: Use primary selection 2023-04-27 18:50:17 +02:00
AFCMS
d197ff0f9d
Use Lua code blocks in lua_api.md 2023-04-26 20:09:08 +02:00
rubenwardy
9c90358912
Split compilation instructions from README.md (#13457) 2023-04-25 21:02:02 +01:00
SmallJoker
0fb6dbab36
InventoryManager: Disallow resizing or deleting inventory lists that are in use (#13360)
Naive solution to prevent InventoryList UAF and OOB ItemStack access caused by shrink/clear operations on InventoryLists within callbacks of an inventory action.

Co-authored-by: Desour <ds.desour@proton.me>
2023-04-22 17:42:36 +02:00
rubenwardy
4158b72971
Add ability to override item images using meta (#12614) 2023-04-17 19:44:41 +01:00
rubenwardy
8c2c7fadbf Add lua_api.txt to point to new file 2023-04-16 20:23:53 +01:00
rubenwardy
b1786e88ac Use .md extension for markdown files
Linking to line numbers is brittle, linking to sections/headings is better.

If you still want to link to a line number, you can append ?plain=1 to GitHub's URL
2023-04-16 20:23:53 +01:00
rubenwardy
5cd6a22dd3
Update settingtypes (#13428)
* Move settings to correct sections
* Improve titles and descriptions
* Make defaults in settingtypes.txt and defaultsettings.cpp match

Fixes #9002
2023-04-14 22:32:25 +01:00
rubenwardy
b89077187b Fix ContentDB page not being reset when changing type
Fixes #13362
2023-04-14 20:34:03 +01:00
rubenwardy
c5fb50298a
Prevent installing mods when there is no base game (#13429) 2023-04-14 20:23:25 +01:00
Desour
ccd696c49a Throw Hocroft-Karp onto shapeless recipes 2023-04-14 21:10:09 +02:00
Desour
50e91b882c Add some simple unittests for shapeless recipes
Note: devtest also has craft unittests (see devtest/mods/unittests/crafting.lua),
      but those aren't run at load-time.
2023-04-14 21:10:09 +02:00
DS
ae7271b725
Fix background[] pos-offset lower-right-corner being at least (1,1) (#13320)
IGUIElement has a MinSize for the RelativeRect, which is at least (1,1).
This means a pos offset of (0,0) will cause a seemingly off-by-1 error at the
lower right corner, and (0.1,0.1) for example will just not work on the lower
right corner.
Ergo, we can't use the AbsoluteRect for storing the pos offset.
2023-04-14 21:05:09 +02:00
DS
d49d80a4a0
Store whether window is maximized (#12861) 2023-04-14 21:04:03 +02:00
cat-master21
0b08e1b1d2
AppImageBuilder.yml: add script section and update to Jammy (#13078) 2023-04-14 13:00:20 +01:00
Lars
f9b1176fa9 Track server's max AsyncRunStep 2023-04-14 00:09:48 +01:00
Lars
3d232e2345 Add MAX profiler option 2023-04-14 00:09:48 +01:00
rubenwardy
9d1ae80e89
Add focused styling to buttons (#13414) 2023-04-14 00:09:29 +01:00
Gregor Parzefall
2a1bc82887
Fix black loading screen background if menu_clouds = false (#13322) 2023-04-13 18:12:48 +01:00
Wuzzy
d1e5dbefc7
Clarify documentation of punch key (#13238) 2023-04-13 18:12:26 +01:00
Desour
fc116ec950 Play object footstep sound at feet 2023-04-13 18:12:13 +01:00
Desour
7283d2495f Devtest: Add bigfoot for footstep sounds 2023-04-13 18:12:13 +01:00
Zardshard
e139749b5c
Simulate all keys being released when when game loses focus (#13336) 2023-04-13 18:06:21 +02:00
sfan5
fe75ec8d0d Update dependency libraries in buildbot 2023-04-13 18:05:39 +02:00
AFCMS
68f81ace97
Add vector.in_area() utility function (#13390) 2023-04-12 10:46:26 +01:00
rubenwardy
2fc7eb3ea2
Remove formspec_default_bg_color/opacity settings (#13419)
These settings are unnecessary. They only apply when formspecs don't have a background/bgcolor set. In practice, most games do theme their GUIs. Removing low value settings simplifies code and improves UX by decluttering the settings menu

Split out from #12140
2023-04-11 19:57:36 +01:00
Desour
1dd13da37d Get rid of global dummySoundManager
There is no need for this to be globally unique.
2023-04-11 20:06:15 +02:00
Desour
b201c03625 Use smart-ptrs in GUIEngine 2023-04-11 20:06:15 +02:00
Desour
ceec560779 Add make_irr 2023-04-11 20:06:15 +02:00
Stvk imension
d39a07efea
Android: Minor Code Improvements (#13342) 2023-04-10 23:05:01 +01:00
Riley Adams
73391013f7
Add node pos to node damage HP change reason (#13196) 2023-04-10 23:04:52 +01:00
David Leal
1d88d85f1c
Add progress_bar.png and progress_bar_bg.png to LICENSE file 2023-04-10 19:57:41 +02:00
Lars
4a742be73e Do not call updateDrawList, updateDrawListShadow, and touchMapBlocks in the same frame 2023-04-09 07:10:17 -10:00
Lars
8982998681 Add a default direction light for shadows 2023-04-09 07:10:17 -10:00
sfan5
9d736e8b8b Drop ENABLE_GLES option
ENABLE_GLES predates forking Irrlicht. Its primary use was to distinguish Irrlicht-ogles from upstream version as Minetest could be compiled with either.
That's not necessary anymore and gets in the way sometimes.
2023-04-08 20:19:35 +02:00
sfan5
c26e122485 Move video_driver default selection to runtime 2023-04-08 20:19:35 +02:00
Desour
67068cfaf4 Get rid of wgettext 2023-04-08 20:17:50 +02:00
Vitaliy
35929d27e3
Remove fast faces (#13216)
Co-authored-by: Lars <larsh@apache.org>
2023-04-08 20:17:15 +02:00
sfan5
c2a9ac24ac Continue with 5.8.0-dev 2023-04-08 18:05:03 +02:00
892 changed files with 151996 additions and 99361 deletions

@ -1,33 +0,0 @@
BasedOnStyle: LLVM
IndentWidth: 4
UseTab: Always
TabWidth: 4
BreakBeforeBraces: Custom
Standard: Cpp11
BraceWrapping:
AfterClass: true
AfterControlStatement: false
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
AfterUnion: true
BeforeCatch: false
BeforeElse: false
FixNamespaceComments: false
AllowShortIfStatementsOnASingleLine: false
IndentCaseLabels: false
AccessModifierOffset: -4
ColumnLimit: 90
AllowShortFunctionsOnASingleLine: InlineOnly
SortIncludes: false
IncludeCategories:
- Regex: '^".*'
Priority: 2
- Regex: '^<.*'
Priority: 1
AlignAfterOpenBracket: DontAlign
ContinuationIndentWidth: 8
ConstructorInitializerIndentWidth: 8
BreakConstructorInitializers: AfterColon
AlwaysBreakTemplateDeclarations: Yes

2
.editorconfig Executable file → Normal file

@ -2,7 +2,7 @@
end_of_line = lf
[*.{cpp,h,lua,txt,glsl,md,c,cmake,java,gradle}]
charset = utf8
charset = utf-8
indent_size = 4
indent_style = tab
insert_final_newline = true

@ -19,7 +19,7 @@ Contributions are welcome! Here's how you can help:
developers.
Any Pull Request that isn't a bug fix and isn't covered by
[the roadmap](../doc/direction.md) will be closed within a week unless it
[the roadmap](../doc/direction.md) will be closed within a month unless it
receives a concept approval from a Core Developer. For this reason, it is
recommended that you open an issue for any such pull requests before doing
the work, to avoid disappointment.
@ -30,7 +30,7 @@ Contributions are welcome! Here's how you can help:
3. Start coding!
- Refer to the
[Lua API](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt),
[Lua API](https://github.com/minetest/minetest/blob/master/doc/lua_api.md),
[Developer Wiki](http://dev.minetest.net/Main_Page) and other
[documentation](https://github.com/minetest/minetest/tree/master/doc).
- Follow the [C/C++](http://dev.minetest.net/Code_style_guidelines) and
@ -67,20 +67,6 @@ Contributions are welcome! Here's how you can help:
might need more work in the future.
5. It uses protocols and formats which include the required compatibility.
### Important note about automated GitHub checks
When you submit a pull request, GitHub automatically runs checks on the Minetest
Engine combined with your changes. One of these checks is called 'cpp lint /
clang format', which checks code formatting. Because formatting for readability
requires human judgement this check often fails and often makes unsuitable
formatting requests which make code readability worse.
If this check fails, look at the details to check for any clear mistakes and
correct those. However, you should not apply everything ClangFormat requests.
Ignore requests that make code readability worse and any other clearly
unsuitable requests. Discuss in the pull request with a core developer about how
to progress.
## Issues
If you experience an issue, we would like to know the details - especially when

@ -1,32 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: Unconfirmed bug
assignees: ''
---
##### Minetest version
<!--
Paste Minetest version between quotes below.
If you are on a devel version, please add git commit hash.
You can use `minetest --version` to find it.
-->
```
```
##### OS / Hardware
<!-- General information about your hardware and operating system -->
Operating system:
CPU:
<!-- For graphical issues only -->
GPU model:
OpenGL version:
##### Summary
<!-- Describe your problem here -->
##### Steps to reproduce
<!-- Explain how the problem has happened, providing a minimal test (i.e. a code snippet reduced to the bone) where possible -->

84
.github/ISSUE_TEMPLATE/bug_report.yaml vendored Normal file

@ -0,0 +1,84 @@
name: Bug report
description: Create a report to help us improve
labels: ["Unconfirmed bug"]
body:
- type: markdown
attributes:
value: |
Please note the following:
1. **Please update your Minetest Engine to the latest stable or dev version** before submitting bug reports. Make sure the bug is still reproducible on the latest version.
2. This page is for reporting the bugs of **the engine itself**. For bugs in a particular game, please [search for the game in the ContentDB](https://content.minetest.net/packages/?type=game) and submit a bug report in their issue trackers.
* For example, you can submit issues about the Minetest Game (the official game of Minetest) [in its own repository](https://github.com/minetest/minetest_game/issues).
3. Please provide as many details as possible for us to spot the problem quicker.
- type: textarea
attributes:
label: Minetest version
description: |
Paste the Minetest version below.
If you are on a dev version, please also indicate the git commit hash.
Refer to the "About" tab of the menu or run `minetest --version` on the command line.
placeholder: |
Example:
Minetest 5.7.0-dev-ca13c51 (Linux)
Using Irrlicht 1.9.0mt9
Using LuaJIT 2.1.0-beta3
BUILD_TYPE=Release
RUN_IN_PLACE=1
USE_CURL=1
USE_GETTEXT=1
USE_SOUND=1
STATIC_SHAREDIR="."
STATIC_LOCALEDIR="locale"
render: "true"
validations:
required: true
- type: input
attributes:
label: Irrlicht device
description:
placeholder: "Example: X11"
validations:
required: false
- type: input
attributes:
label: Operating system and version
description: It is recommended to upgrade your operating system to see if the problem persists.
placeholder: "Example: Ubuntu 22.04"
validations:
required: true
- type: input
attributes:
label: CPU model
description: Usually found in OS/system settings.
placeholder: "Example: Intel Core i5-2410M"
validations:
required: false
- type: markdown
attributes:
value: The GPU model and renderer can be omitted if the bug is not a graphical issue.
- type: input
attributes:
label: GPU model
description: Usually found in OS/system settings.
placeholder: "Example: NVIDIA GeForce GTX 1660"
validations:
required: false
- type: input
attributes:
label: Active renderer
description: You can find this in the "About" tab in the main menu.
placeholder: "Example: OpenGL 4.6.0"
validations:
required: false
- type: textarea
attributes:
label: Summary
description: Describe your problem here.
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: Explain how the problem has happened, providing a minimal test (e.g. a minimized code snippet) where possible.
validations:
required: true

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file

@ -0,0 +1,8 @@
blank_issues_enabled: true
contact_links:
- name: Submit issues about Minetest Game
url: https://github.com/minetest/minetest_game/issues/new/choose
about: Only submit issues of the engine in this repository's issue tracker. Submit those of Minetest Game in its own issue tracker.
- name: Search for issue trackers of third-party games
url: https://content.minetest.net/packages/?type=game
about: For issues of third-party games, search for the game in the ContentDB and then submit an issue in their issue tracker.

@ -1,25 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: Feature request
assignees: ''
---
## Problem
A clear and concise description of what the problem is.
ie: Why is this needed?
Ex. I'm always frustrated when [...]
## Solutions
A clear and concise description of what you want to happen.
## Alternatives
A clear and concise description of any alternative solutions or features you've considered.
## Additional context
Add any other context or screenshots about the feature request here.

@ -0,0 +1,39 @@
name: Feature request
description: Suggest an idea for this project
labels: ["Feature request"]
body:
- type: markdown
attributes:
value: |
Please note the following:
1. Only submit a feature request if the feature does not exist on the latest dev version.
2. This page is for suggesting changes to **the engine itself**. To suggest changes to games, please [search for the game in the ContentDB](https://content.minetest.net/packages/?type=game) and submit a feature request in their issue trackers.
- type: textarea
attributes:
label: Problem
description: |
A clear and concise description of the problem, i.e. "Why is this needed?"
Example: I'm always frustrated when [...]
validations:
required: true
- type: textarea
attributes:
label: Solutions
description: |
A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Alternatives
description: |
A clear and concise description of any alternative solutions or features you've considered.
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: |
Add any other context or screenshots about the feature request here.
validations:
required: false

@ -3,7 +3,7 @@ Add compact, short information about your PR for easier understanding:
- Goal of the PR
- How does the PR work?
- Does it resolve any reported issue?
- Does this relate to a goal in [the roadmap](../doc/direction.md)?
- Does this relate to a goal in [the roadmap](https://github.com/minetest/minetest/blob/master/doc/direction.md)?
- If not a bug fix, why is this PR needed? What usecases does it solve?
## To do

@ -8,6 +8,8 @@ on:
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- 'android/**'
- '.github/workflows/android.yml'
pull_request:
@ -16,6 +18,8 @@ on:
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- 'android/**'
- '.github/workflows/android.yml'
@ -23,7 +27,7 @@ jobs:
build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install deps
run: |
sudo apt-get update
@ -31,22 +35,22 @@ jobs:
- name: Build with Gradle
run: cd android; ./gradlew assemblerelease
- name: Save armeabi artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Minetest-armeabi-v7a.apk
path: android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk
- name: Save arm64 artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Minetest-arm64-v8a.apk
path: android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
- name: Save x86 artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Minetest-x86.apk
path: android/app/build/outputs/apk/release/app-x86-release-unsigned.apk
- name: Save x86_64 artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Minetest-x86_64.apk
path: android/app/build/outputs/apk/release/app-x86_64-release-unsigned.apk

@ -1,276 +0,0 @@
name: build
# build on c/cpp changes or workflow changes
on:
push:
paths:
- 'lib/**.[ch]'
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- 'util/buildbot/**'
- 'util/ci/**'
- '.github/workflows/**.yml'
- 'Dockerfile'
- '.dockerignore'
pull_request:
paths:
- 'lib/**.[ch]'
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- 'util/buildbot/**'
- 'util/ci/**'
- '.github/workflows/**.yml'
- 'Dockerfile'
- '.dockerignore'
env:
MINETEST_POSTGRESQL_CONNECT_STRING: 'host=localhost user=minetest password=minetest dbname=minetest'
jobs:
# Older gcc version (should be close to our minimum supported version)
gcc_5:
runs-on: ubuntu-18.04
if: "false" # FIXME
steps:
- uses: actions/checkout@v3
- name: Install deps
run: |
source ./util/ci/common.sh
install_linux_deps g++-5
- name: Build
run: |
./util/ci/build.sh
env:
CC: gcc-5
CXX: g++-5
- name: Test
run: |
./bin/minetest --run-unittests
# Current gcc version
gcc_12:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Install deps
run: |
source ./util/ci/common.sh
install_linux_deps g++-12 libluajit-5.1-dev
- name: Build
run: |
./util/ci/build.sh
env:
CC: gcc-12
CXX: g++-12
- name: Test
run: |
./bin/minetest --run-unittests
# Older clang version (should be close to our minimum supported version)
clang_3_9:
runs-on: ubuntu-18.04
if: "false" # FIXME
steps:
- uses: actions/checkout@v3
- name: Install deps
run: |
source ./util/ci/common.sh
install_linux_deps clang-3.9 valgrind
- name: Build
run: |
./util/ci/build.sh
env:
CC: clang-3.9
CXX: clang++-3.9
- name: Unittest
run: |
./bin/minetest --run-unittests
- name: Valgrind
run: |
valgrind --leak-check=full --leak-check-heuristics=all --undef-value-errors=no --error-exitcode=9 ./bin/minetest --run-unittests
# Current clang version
clang_14:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Install deps
run: |
source ./util/ci/common.sh
install_linux_deps clang-14 gdb
- name: Build
run: |
./util/ci/build.sh
env:
CC: clang-14
CXX: clang++-14
- name: Test
run: |
./bin/minetest --run-unittests
- name: Integration test + devtest
run: |
./util/test_multiplayer.sh
# Build with prometheus-cpp (server-only)
clang_9_prometheus:
name: "clang_9 (PROMETHEUS=1)"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Install deps
run: |
source ./util/ci/common.sh
install_linux_deps clang-9
- name: Build prometheus-cpp
run: |
./util/ci/build_prometheus_cpp.sh
- name: Build
run: |
./util/ci/build.sh
env:
CC: clang-9
CXX: clang++-9
CMAKE_FLAGS: "-DENABLE_PROMETHEUS=1 -DBUILD_CLIENT=0"
- name: Test
run: |
./bin/minetestserver --run-unittests
docker:
name: "Docker image"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Build docker image
run: |
docker build . -t minetest:latest
docker run --rm minetest:latest /usr/local/bin/minetestserver --version
win32:
name: "MinGW cross-compiler (32-bit)"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Install compiler
run: |
sudo apt-get update && sudo apt-get install -y gettext
wget http://minetest.kitsunemimi.pw/mingw-w64-i686_11.2.0_ubuntu20.04.tar.xz -O mingw.tar.xz
sudo tar -xaf mingw.tar.xz -C /usr
- name: Build
run: |
EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh winbuild
env:
NO_MINETEST_GAME: 1
NO_PACKAGE: 1
win64:
name: "MinGW cross-compiler (64-bit)"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Install compiler
run: |
sudo apt-get update && sudo apt-get install -y gettext
wget http://minetest.kitsunemimi.pw/mingw-w64-x86_64_11.2.0_ubuntu20.04.tar.xz -O mingw.tar.xz
sudo tar -xaf mingw.tar.xz -C /usr
- name: Build
run: |
EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh winbuild
env:
NO_MINETEST_GAME: 1
NO_PACKAGE: 1
msvc:
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
runs-on: windows-2019
env:
VCPKG_VERSION: 5cf60186a241e84e8232641ee973395d4fde90e1
# 2022.02
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry
strategy:
fail-fast: false
matrix:
config:
- {
arch: x86,
generator: "-G'Visual Studio 16 2019' -A Win32",
vcpkg_triplet: x86-windows
}
- {
arch: x64,
generator: "-G'Visual Studio 16 2019' -A x64",
vcpkg_triplet: x64-windows
}
type: [portable]
# type: [portable, installer]
# The installer type is working, but disabled, to save runner jobs.
# Enable it, when working on the installer.
steps:
- uses: actions/checkout@v3
- name: Checkout IrrlichtMt
run: |
$ref = @(Get-Content misc\irrlichtmt_tag.txt)
git clone https://github.com/minetest/irrlicht lib\irrlichtmt --depth 1 -b $ref[0]
- name: Restore from cache and run vcpkg
uses: lukka/run-vcpkg@v7
with:
vcpkgArguments: ${{env.vcpkg_packages}}
vcpkgDirectory: '${{ github.workspace }}\vcpkg'
appendedCacheKey: ${{ matrix.config.vcpkg_triplet }}
vcpkgGitCommitId: ${{ env.VCPKG_VERSION }}
vcpkgTriplet: ${{ matrix.config.vcpkg_triplet }}
- name: Minetest CMake
run: |
cmake ${{matrix.config.generator}} `
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
-DCMAKE_BUILD_TYPE=Release `
-DENABLE_POSTGRESQL=OFF `
-DRUN_IN_PLACE=${{ contains(matrix.type, 'portable') }} .
- name: Build Minetest
run: cmake --build . --config Release
- name: CPack
run: |
If ($env:TYPE -eq "installer")
{
cpack -G WIX -B "$env:GITHUB_WORKSPACE\Package"
}
ElseIf($env:TYPE -eq "portable")
{
cpack -G ZIP -B "$env:GITHUB_WORKSPACE\Package"
}
env:
TYPE: ${{matrix.type}}
- name: Package Clean
run: rm -r $env:GITHUB_WORKSPACE\Package\_CPack_Packages
- uses: actions/upload-artifact@v3
with:
name: msvc-${{ matrix.config.arch }}-${{ matrix.type }}
path: .\Package\

@ -23,32 +23,18 @@ on:
- 'util/ci/**'
- '.github/workflows/**.yml'
env:
CLANG_TIDY: clang-tidy-15
jobs:
# clang_format:
# runs-on: ubuntu-20.04
# steps:
# - uses: actions/checkout@v3
# - name: Install clang-format
# run: |
# sudo apt-get update
# sudo apt-get install -y clang-format-9
#
# - name: Run clang-format
# run: |
# source ./util/ci/clang-format.sh
# check_format
# env:
# CLANG_FORMAT: clang-format-9
clang_tidy:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install deps
run: |
source ./util/ci/common.sh
install_linux_deps clang-tidy-9
install_linux_deps $CLANG_TIDY
- name: Run clang-tidy
run: |

163
.github/workflows/linux.yml vendored Normal file

@ -0,0 +1,163 @@
name: linux
# build on c/cpp changes or workflow changes
on:
push:
paths:
- 'lib/**.[ch]'
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- 'util/ci/**'
- 'misc/irrlichtmt_tag.txt'
- 'Dockerfile'
- '.dockerignore'
- '.github/workflows/linux.yml'
pull_request:
paths:
- 'lib/**.[ch]'
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- 'util/ci/**'
- 'misc/irrlichtmt_tag.txt'
- 'Dockerfile'
- '.dockerignore'
- '.github/workflows/linux.yml'
env:
MINETEST_POSTGRESQL_CONNECT_STRING: 'host=localhost user=minetest password=minetest dbname=minetest'
jobs:
# Older gcc version (should be close to our minimum supported version)
gcc_7:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- name: Install deps
run: |
source ./util/ci/common.sh
install_linux_deps g++-7
- name: Build
run: |
./util/ci/build.sh
env:
CC: gcc-7
CXX: g++-7
- name: Test
run: |
./bin/minetest --run-unittests
# Current gcc version
gcc_12:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install deps
run: |
source ./util/ci/common.sh
install_linux_deps g++-12 libluajit-5.1-dev
- name: Build
run: |
./util/ci/build.sh
env:
CC: gcc-12
CXX: g++-12
- name: Test
run: |
mkdir nowrite
chmod a-w nowrite
cd nowrite
../bin/minetest --run-unittests
# Older clang version (should be close to our minimum supported version)
clang_7:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- name: Install deps
run: |
source ./util/ci/common.sh
install_linux_deps clang-7 llvm
- name: Build
run: |
./util/ci/build.sh
env:
CC: clang-7
CXX: clang++-7
CMAKE_FLAGS: '-DCMAKE_C_FLAGS="-fsanitize=address" -DCMAKE_CXX_FLAGS="-fsanitize=address"'
- name: Unittest
run: |
./bin/minetest --run-unittests
# Current clang version
clang_14:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install deps
run: |
source ./util/ci/common.sh
install_linux_deps clang-14 gdb
- name: Build
run: |
./util/ci/build.sh
env:
CC: clang-14
CXX: clang++-14
- name: Test
run: |
./bin/minetest --run-unittests
- name: Integration test + devtest
run: |
./util/test_multiplayer.sh
# Build with prometheus-cpp (server-only)
clang_9_prometheus:
name: "clang_9 (PROMETHEUS=1)"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- name: Install deps
run: |
source ./util/ci/common.sh
install_linux_deps clang-9
- name: Build prometheus-cpp
run: |
./util/ci/build_prometheus_cpp.sh
- name: Build
run: |
./util/ci/build.sh
env:
CC: clang-9
CXX: clang++-9
CMAKE_FLAGS: "-DENABLE_PROMETHEUS=1 -DBUILD_CLIENT=0"
- name: Test
run: |
./bin/minetestserver --run-unittests
docker:
name: "Docker image"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- name: Build docker image
run: |
docker build . -t minetest:latest
docker run --rm minetest:latest /usr/local/bin/minetestserver --version

@ -19,7 +19,7 @@ jobs:
name: "Compile and run multiplayer tests"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install deps
run: |
source ./util/ci/common.sh
@ -43,11 +43,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: leafo/gh-actions-lua@v9
- uses: actions/checkout@v4
- uses: leafo/gh-actions-lua@v10
with:
luaVersion: "5.1.5"
- uses: leafo/gh-actions-luarocks@v4
- uses: leafo/gh-actions-luarocks@v4.3.0
- name: Install LuaJIT
run: |

48
.github/workflows/lua_api_deploy.yml vendored Normal file

@ -0,0 +1,48 @@
name: lua_api_deploy
permissions:
contents: read
pages: write
id-token: write
on:
push:
paths:
- '.github/workflows/lua_api_deploy.yml'
- 'doc/lua_api.md'
- 'doc/mkdocs/'
branches:
- master
jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.11
- name: Install mkdocs
run: |
pip install -U -r doc/mkdocs/requirements.txt
- name: Build documentation
run: |
cd doc/mkdocs/
./build.sh
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: 'public/'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

@ -21,16 +21,11 @@ on:
- 'cmake/Modules/**'
- '.github/workflows/macos.yml'
env:
MINETEST_GAME_REPO: https://github.com/minetest/minetest_game.git
MINETEST_GAME_BRANCH: master
MINETEST_GAME_NAME: minetest_game
jobs:
build:
runs-on: macos-11
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install deps
run: |
source ./util/ci/common.sh
@ -38,7 +33,6 @@ jobs:
- name: Build
run: |
git clone -b $MINETEST_GAME_BRANCH $MINETEST_GAME_REPO games/$MINETEST_GAME_NAME
git clone https://github.com/minetest/irrlicht lib/irrlichtmt --depth 1 -b $(cat misc/irrlichtmt_tag.txt)
mkdir build
cd build
@ -48,7 +42,7 @@ jobs:
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
-DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE \
-DINSTALL_DEVTEST=TRUE
make -j2
cmake --build . -j$(sysctl -n hw.logicalcpu)
make install
- name: Test
@ -62,7 +56,7 @@ jobs:
cd build
cpack -G ZIP -B macos
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: minetest-macos
path: ./build/macos/*.zip

152
.github/workflows/windows.yml vendored Normal file

@ -0,0 +1,152 @@
name: windows
# build on c/cpp changes or workflow changes
on:
push:
paths:
- 'lib/**.[ch]'
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- 'util/buildbot/**'
- 'misc/irrlichtmt_tag.txt'
- 'misc/*.manifest'
- '.github/workflows/windows.yml'
pull_request:
paths:
- 'lib/**.[ch]'
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- 'util/buildbot/**'
- 'misc/irrlichtmt_tag.txt'
- 'misc/*.manifest'
- '.github/workflows/windows.yml'
jobs:
mingw32:
name: "MinGW cross-compiler (32-bit)"
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install compiler
run: |
sudo apt-get update && sudo apt-get install -y gettext
sudo ./util/buildbot/download_toolchain.sh /usr
- name: Build
run: |
EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh B
- uses: actions/upload-artifact@v4
with:
name: mingw32
path: B/build/*.zip
if-no-files-found: error
mingw64:
name: "MinGW cross-compiler (64-bit)"
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install compiler
run: |
sudo apt-get update && sudo apt-get install -y gettext
sudo ./util/buildbot/download_toolchain.sh /usr
- name: Build
run: |
EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh B
- uses: actions/upload-artifact@v4
with:
name: mingw64
path: B/build/*.zip
if-no-files-found: error
msvc:
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
runs-on: windows-2019
env:
VCPKG_VERSION: 8eb57355a4ffb410a2e94c07b4dca2dffbee8e50
# 2023.10.19
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp sdl2
strategy:
fail-fast: false
matrix:
config:
- {
arch: x86,
generator: "-G'Visual Studio 16 2019' -A Win32",
vcpkg_triplet: x86-windows
}
- {
arch: x64,
generator: "-G'Visual Studio 16 2019' -A x64",
vcpkg_triplet: x64-windows
}
type: [portable]
# type: [portable, installer]
# The installer type is working, but disabled, to save runner jobs.
# Enable it, when working on the installer.
steps:
- uses: actions/checkout@v4
- name: Checkout IrrlichtMt
run: |
$ref = @(Get-Content misc\irrlichtmt_tag.txt)
git clone https://github.com/minetest/irrlicht lib\irrlichtmt --depth 1 -b $ref[0]
- name: Restore from cache and run vcpkg
uses: lukka/run-vcpkg@v7
with:
vcpkgArguments: ${{env.vcpkg_packages}}
vcpkgDirectory: '${{ github.workspace }}\vcpkg'
appendedCacheKey: ${{ matrix.config.vcpkg_triplet }}
vcpkgGitCommitId: ${{ env.VCPKG_VERSION }}
vcpkgTriplet: ${{ matrix.config.vcpkg_triplet }}
- name: Minetest CMake
run: |
cmake ${{matrix.config.generator}} `
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
-DCMAKE_BUILD_TYPE=Release `
-DENABLE_POSTGRESQL=OFF `
-DENABLE_LUAJIT=TRUE `
-DREQUIRE_LUAJIT=TRUE `
-DRUN_IN_PLACE=${{ contains(matrix.type, 'portable') }} .
- name: Build Minetest
run: cmake --build . --config Release
- name: Unittests
# need this workaround for stdout to work
run: |
$proc = start .\bin\Release\minetest.exe --run-unittests -NoNewWindow -Wait -PassThru
exit $proc.ExitCode
continue-on-error: true # FIXME!!
- name: CPack
run: |
If ($env:TYPE -eq "installer")
{
cpack -G WIX -B "$env:GITHUB_WORKSPACE\Package"
}
ElseIf($env:TYPE -eq "portable")
{
cpack -G ZIP -B "$env:GITHUB_WORKSPACE\Package"
}
rm -r $env:GITHUB_WORKSPACE\Package\_CPack_Packages
env:
TYPE: ${{matrix.type}}
- uses: actions/upload-artifact@v4
with:
name: msvc-${{ matrix.config.arch }}-${{ matrix.type }}
path: .\Package\
if-no-files-found: error

13
.gitignore vendored

@ -28,10 +28,17 @@ gtags.files
# Visual Studio Code & plugins
.vscode/
build/.cmake/
# Fleet
.fleet
# Gradle
.gradle
# Clang
.cache
# AppImage
*.AppImage
*.zsync
appimage-build
AppDir
## Files related to Minetest development cycle
/*.patch
@ -85,11 +92,8 @@ cmake_install.cmake
CMakeCache.txt
CPackConfig.cmake
CPackSourceConfig.cmake
src/test_config.h
src/cmake_config.h
src/cmake_config_githash.h
src/unittest/test_world/world.mt
games/devtest/mods/testnodes/textures/testnodes_generated_*.png
/locale/
.directory
*.cbp
@ -114,8 +118,5 @@ compile_commands.json
*.sln
.vs/
# Optional user provided library folder
lib/irrlichtmt
# Generated mod storage database
client/mod_storage.sqlite

@ -3,131 +3,14 @@
# https://gitlab.com/minetest/minetest
# Pipelines URL: https://gitlab.com/minetest/minetest/pipelines
stages:
- build
- package
- deploy
variables:
MINETEST_GAME_REPO: "https://github.com/minetest/minetest_game.git"
CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH
.build_template:
stage: build
before_script:
- apt-get update
- DEBIAN_FRONTEND=noninteractive apt-get -y install build-essential gettext git cmake libpng-dev libjpeg-dev libxi-dev libgl1-mesa-dev libsqlite3-dev libleveldb-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev libzstd-dev libluajit-5.1-dev
script:
- git clone https://github.com/minetest/irrlicht lib/irrlichtmt --depth 1 -b $(cat misc/irrlichtmt_tag.txt)
- mkdir build && cd build
- cmake -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE ..
- make -j $(($(nproc) + 1))
- make install
artifacts:
when: on_success
expire_in: 1h
paths:
- artifact/*
##
## Ubuntu (prerequisite for AppImage build)
##
build:ubuntu-20.04:
extends: .build_template
image: ubuntu:focal
##
## MinGW for Windows
##
.generic_win_template:
image: ubuntu:focal
before_script:
- apt-get update
- DEBIAN_FRONTEND=noninteractive apt-get install -y wget xz-utils unzip git cmake gettext
- wget -nv http://minetest.kitsunemimi.pw/mingw-w64-${WIN_ARCH}_11.2.0_ubuntu20.04.tar.xz -O mingw.tar.xz
- tar -xaf mingw.tar.xz -C /usr
.build_win_template:
extends: .generic_win_template
stage: build
artifacts:
expire_in: 90 day
paths:
- minetest-*-win*/*
build:win32:
extends: .build_win_template
script:
- EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh build
- unzip -q build/build/*.zip
variables:
WIN_ARCH: "i686"
build:win64:
extends: .build_win_template
script:
- EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh build
- unzip -q build/build/*.zip
variables:
WIN_ARCH: "x86_64"
##
## Docker
##
package:docker:
stage: package
image: docker:stable
services:
- docker:dind
before_script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com
script:
- ./util/ci/docker.sh
##
## Gitlab Pages (Lua API documentation)
##
pages:
stage: deploy
image: python:3.8
before_script:
- pip install -U -r doc/mkdocs/requirements.txt
script:
- cd doc/mkdocs && ./build.sh
- ./misc/make_redirects.sh
artifacts:
paths:
- public
only:
- master
##
## AppImage
##
package:appimage-client:
stage: package
image: appimagecrafters/appimage-builder
needs:
- build:ubuntu-20.04
before_script:
- apt-get update
- apt-get install -y git
# Collect files
- mkdir AppDir
- cp -a artifact/minetest/usr/ AppDir/usr/
- cp -a clientmods AppDir/usr/share/minetest
- git clone $MINETEST_GAME_REPO AppDir/usr/share/minetest/games/minetest_game
- rm -rf AppDir/usr/share/minetest/games/minetest_game/.git
# Remove PrefersNonDefaultGPU property due to validation errors
- sed -i '/PrefersNonDefaultGPU/d' AppDir/usr/share/applications/net.minetest.minetest.desktop
script:
- export VERSION=$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA
- appimage-builder --skip-test --recipe misc/AppImageBuilder.yml
artifacts:
expire_in: 90 day
paths:
- ./*.AppImage

3
.gitmodules vendored Normal file

@ -0,0 +1,3 @@
[submodule "lib/irrlichtmt"]
path = lib/irrlichtmt
url = git@brn.systems:BRNSystems/irrlicht.git

@ -17,6 +17,7 @@ read_globals = {
"VoxelArea",
"profiler",
"Settings",
"PerlinNoise", "PerlinNoiseMap",
string = {fields = {"split", "trim"}},
table = {fields = {"copy", "getn", "indexof", "insert_all"}},

@ -1,24 +1,17 @@
cmake_minimum_required(VERSION 3.5)
# Set policies up to 3.9 since we want to enable the IPO option
if(${CMAKE_VERSION} VERSION_LESS 3.9)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.9)
endif()
cmake_minimum_required(VERSION 3.12)
# This can be read from ${PROJECT_NAME} after project() is called
project(minetest)
set(PROJECT_NAME_CAPITALIZED "Minetest")
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(GCC_MINIMUM_VERSION "5.1")
set(CLANG_MINIMUM_VERSION "3.5")
set(GCC_MINIMUM_VERSION "7.5")
set(CLANG_MINIMUM_VERSION "7.0.1")
# You should not need to edit these manually, use util/bump_version.sh
set(VERSION_MAJOR 5)
set(VERSION_MINOR 7)
set(VERSION_MINOR 9)
set(VERSION_PATCH 0)
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
@ -32,15 +25,32 @@ elseif(DEVELOPMENT_BUILD)
set(VERSION_STRING "${VERSION_STRING}-dev")
endif()
if (CMAKE_BUILD_TYPE STREQUAL Debug)
# Append "-debug" to version string
set(VERSION_STRING "${VERSION_STRING}-debug")
endif()
message(STATUS "*** Will build version ${VERSION_STRING} ***")
# Configuration options
set(BUILD_CLIENT TRUE CACHE BOOL "Build client")
set(BUILD_SERVER FALSE CACHE BOOL "Build server")
set(BUILD_UNITTESTS TRUE CACHE BOOL "Build unittests")
set(BUILD_BENCHMARKS FALSE CACHE BOOL "Build benchmarks")
set(BUILD_DOCUMENTATION TRUE CACHE BOOL "Build documentation")
set(DEFAULT_ENABLE_LTO TRUE)
# by default don't enable on Debug builds to get faster builds
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(DEFAULT_ENABLE_LTO FALSE)
endif()
#### LTO testing list ####
# - Linux: seems to work always
# - win32/msvc: works
# - win32/gcc: fails to link
# - win32/clang: works
# - macOS on x86: seems to be fine
# - macOS on ARM: crashes, see <https://github.com/minetest/minetest/issues/14397>
# Note: since CMake has no easy architecture detection disabling for Mac entirely
#### ####
if((WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR APPLE)
set(DEFAULT_ENABLE_LTO FALSE)
endif()
set(ENABLE_LTO ${DEFAULT_ENABLE_LTO} CACHE BOOL "Use Link Time Optimization")
set(DEFAULT_RUN_IN_PLACE FALSE)
if(WIN32)
set(DEFAULT_RUN_IN_PLACE TRUE)
@ -48,11 +58,13 @@ endif()
set(RUN_IN_PLACE ${DEFAULT_RUN_IN_PLACE} CACHE BOOL
"Run directly in source directory structure")
set(BUILD_CLIENT TRUE CACHE BOOL "Build client")
set(BUILD_SERVER FALSE CACHE BOOL "Build server")
set(BUILD_UNITTESTS TRUE CACHE BOOL "Build unittests")
set(BUILD_BENCHMARKS FALSE CACHE BOOL "Build benchmarks")
message(STATUS "*** Will build version ${VERSION_STRING} ***")
message(STATUS "BUILD_CLIENT: " ${BUILD_CLIENT})
message(STATUS "BUILD_SERVER: " ${BUILD_SERVER})
message(STATUS "BUILD_UNITTESTS: " ${BUILD_UNITTESTS})
message(STATUS "BUILD_BENCHMARKS: " ${BUILD_BENCHMARKS})
message(STATUS "BUILD_DOCUMENTATION: " ${BUILD_DOCUMENTATION})
message(STATUS "RUN_IN_PLACE: " ${RUN_IN_PLACE})
set(WARN_ALL TRUE CACHE BOOL "Enable -Wall for Release build")
@ -67,9 +79,17 @@ set(ENABLE_UPDATE_CHECKER (NOT ${DEVELOPMENT_BUILD}) CACHE BOOL
# Included stuff
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
# Load default options for Android
if(ANDROID)
cmake_minimum_required(VERSION 3.20)
include(MinetestAndroidLibs)
endif()
set(IRRLICHTMT_BUILD_DIR "" CACHE PATH "Path to IrrlichtMt build directory.")
if(NOT "${IRRLICHTMT_BUILD_DIR}" STREQUAL "")
if(ANDROID)
# currently manually provided
elseif(NOT "${IRRLICHTMT_BUILD_DIR}" STREQUAL "")
find_package(IrrlichtMt QUIET
PATHS "${IRRLICHTMT_BUILD_DIR}"
NO_DEFAULT_PATH
@ -101,9 +121,7 @@ else()
find_package(IrrlichtMt QUIET)
if(NOT TARGET IrrlichtMt::IrrlichtMt)
string(CONCAT explanation_msg
"The Minetest team has forked Irrlicht to make their own customizations. "
"It can be found here: https://github.com/minetest/irrlicht\n"
"For example use: git clone --depth=1 https://github.com/minetest/irrlicht lib/irrlichtmt\n")
"You must install IrrlichMt as described in docs/compiling/\n")
if(BUILD_CLIENT)
message(FATAL_ERROR "IrrlichtMt is required to build the client, but it was not found.\n${explanation_msg}")
endif()
@ -120,14 +138,16 @@ else()
endif()
endif()
if(BUILD_CLIENT AND TARGET IrrlichtMt::IrrlichtMt)
if(ANDROID)
# skipped for now
elseif(BUILD_CLIENT AND TARGET IrrlichtMt::IrrlichtMt)
# retrieve version somehow
if(NOT IrrlichtMt_VERSION)
get_target_property(IrrlichtMt_VERSION IrrlichtMt VERSION)
endif()
message(STATUS "Found IrrlichtMt ${IrrlichtMt_VERSION}")
set(TARGET_VER_S 1.9.0mt10)
set(TARGET_VER_S 1.9.0mt15)
string(REPLACE "mt" "." TARGET_VER ${TARGET_VER_S})
if(IrrlichtMt_VERSION VERSION_LESS ${TARGET_VER})
message(FATAL_ERROR "At least IrrlichtMt ${TARGET_VER_S} is required to build")
@ -136,6 +156,20 @@ if(BUILD_CLIENT AND TARGET IrrlichtMt::IrrlichtMt)
endif()
endif()
if (ENABLE_LTO OR CMAKE_INTERPROCEDURAL_OPTIMIZATION)
include(CheckIPOSupported)
check_ipo_supported(RESULT lto_supported OUTPUT lto_output)
if(lto_supported)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
message(STATUS "LTO/IPO is enabled")
else()
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE)
message(STATUS "LTO/IPO was requested but is not supported by the compiler: ${lto_output}")
endif()
else()
message(STATUS "LTO/IPO is not enabled")
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "${GCC_MINIMUM_VERSION}")
message(FATAL_ERROR "Insufficient gcc version, found ${CMAKE_CXX_COMPILER_VERSION}. "
@ -247,9 +281,6 @@ if(RUN_IN_PLACE)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/textures/texture_packs_here.txt" DESTINATION "${SHAREDIR}/textures")
endif()
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/minetest_game" DESTINATION "${SHAREDIR}/games/"
COMPONENT "SUBGAME_MINETEST_GAME" OPTIONAL PATTERN ".git*" EXCLUDE )
set(INSTALL_DEVTEST FALSE CACHE BOOL "Install Development Test")
if(INSTALL_DEVTEST)
@ -267,11 +298,11 @@ if(BUILD_CLIENT)
endif()
install(FILES "README.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "doc/lua_api.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "doc/client_lua_api.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "doc/menu_lua_api.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "doc/texture_packs.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "doc/world_format.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "doc/lua_api.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "doc/client_lua_api.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "doc/menu_lua_api.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "doc/texture_packs.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "doc/world_format.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "minetest.conf.example" DESTINATION "${EXAMPLE_CONF_DIR}")
if(UNIX AND NOT APPLE)
@ -321,16 +352,6 @@ cpack_add_component(Docs
DESCRIPTION "Documentation about Minetest and Minetest modding"
)
cpack_add_component(SUBGAME_MINETEST_GAME
DISPLAY_NAME "Minetest Game"
DESCRIPTION "The default game bundled in the Minetest engine. Mainly used as a modding base."
GROUP "Games"
)
cpack_add_component_group(Subgames
DESCRIPTION "Games for the Minetest engine."
)
if(WIN32)
# Include all dynamically linked runtime libraries such as MSVCRxxx.dll
include(InstallRequiredSystemLibraries)
@ -384,13 +405,15 @@ include(CPack)
# Add a target to generate API documentation with Doxygen
find_package(Doxygen)
if(DOXYGEN_FOUND)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile.in
${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile @ONLY)
add_custom_target(doc
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc
COMMENT "Generating API documentation with Doxygen" VERBATIM
)
if(BUILD_DOCUMENTATION)
find_package(Doxygen)
if(DOXYGEN_FOUND)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile.in
${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile @ONLY)
add_custom_target(doc
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc
COMMENT "Generating API documentation with Doxygen" VERBATIM
)
endif()
endif()

1
CNAME Normal file

@ -0,0 +1 @@
api.minetest.net

@ -1,9 +1,8 @@
ARG DOCKER_IMAGE=alpine:3.16
FROM $DOCKER_IMAGE AS builder
ARG DOCKER_IMAGE=alpine:3.19
FROM $DOCKER_IMAGE AS dev
ENV MINETEST_GAME_VERSION master
ENV IRRLICHT_VERSION master
ENV SPATIALINDEX_VERSION 1.9.3
ENV SPATIALINDEX_VERSION master
ENV LUAJIT_VERSION v2.1
RUN apk add --no-cache git build-base cmake curl-dev zlib-dev zstd-dev \
@ -11,7 +10,7 @@ RUN apk add --no-cache git build-base cmake curl-dev zlib-dev zstd-dev \
gmp-dev jsoncpp-dev ninja ca-certificates
WORKDIR /usr/src/
RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp/ && \
RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp && \
cd prometheus-cpp && \
cmake -B build \
-DCMAKE_INSTALL_PREFIX=/usr/local \
@ -30,11 +29,13 @@ RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp/ && \
cd /usr/src/ && \
git clone --recursive https://luajit.org/git/luajit.git -b ${LUAJIT_VERSION} && \
cd luajit && \
make && make install && \
make amalg && make install && \
cd /usr/src/ && \
git clone --depth=1 https://github.com/minetest/irrlicht/ -b ${IRRLICHT_VERSION} && \
git clone --depth=1 https://github.com/minetest/irrlicht -b ${IRRLICHT_VERSION} && \
cp -r irrlicht/include /usr/include/irrlichtmt
FROM dev as builder
COPY .git /usr/src/minetest/.git
COPY CMakeLists.txt /usr/src/minetest/CMakeLists.txt
COPY README.md /usr/src/minetest/README.md
@ -50,20 +51,17 @@ COPY src /usr/src/minetest/src
COPY textures /usr/src/minetest/textures
WORKDIR /usr/src/minetest
RUN git clone --depth=1 -b ${MINETEST_GAME_VERSION} https://github.com/minetest/minetest_game.git ./games/minetest_game && \
rm -fr ./games/minetest_game/.git && \
cmake -B build \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SERVER=TRUE \
-DENABLE_PROMETHEUS=TRUE \
-DBUILD_UNITTESTS=FALSE \
-DBUILD_CLIENT=FALSE \
-GNinja && \
cmake --build build && \
cmake --install build
RUN cmake -B build \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SERVER=TRUE \
-DENABLE_PROMETHEUS=TRUE \
-DBUILD_UNITTESTS=FALSE -DBUILD_BENCHMARKS=FALSE \
-DBUILD_CLIENT=FALSE \
-GNinja && \
cmake --build build && \
cmake --install build
ARG DOCKER_IMAGE=alpine:3.16
FROM $DOCKER_IMAGE AS runtime
RUN apk add --no-cache curl gmp libstdc++ libgcc libpq jsoncpp zstd-libs \

@ -61,7 +61,11 @@ Zughy:
textures/base/pack/cdb_downloading.png
textures/base/pack/cdb_queued.png
textures/base/pack/cdb_update.png
textures/base/pack/cdb_update_cropped.png
textures/base/pack/cdb_viewonline.png
textures/base/pack/settings_btn.png
textures/base/pack/settings_info.png
textures/base/pack/settings_reset.png
appgurueu:
textures/base/pack/server_incompatible.png
@ -71,15 +75,23 @@ erlehmann, Warr1024, rollerozxa:
kilbith:
textures/base/pack/server_favorite.png
textures/base/pack/progress_bar.png
textures/base/pack/progress_bar_bg.png
SmallJoker:
textures/base/pack/cdb_clear.png
textures/base/pack/server_favorite_delete.png (based on server_favorite.png)
DS:
games/devtest/mods/soundstuff/textures/soundstuff_bigfoot.png
games/devtest/mods/soundstuff/textures/soundstuff_jukebox.png
games/devtest/mods/soundstuff/textures/soundstuff_racecar.png
games/devtest/mods/soundstuff/sounds/soundstuff_sinus.ogg
games/devtest/mods/testtools/textures/testtools_branding_iron.png
grorp:
textures/base/pack/exit_btn.png
License of Minetest source code
-------------------------------

357
README.md

@ -10,12 +10,6 @@ Minetest is a free open-source voxel game engine with easy modding and game crea
Copyright (C) 2010-2022 Perttu Ahola <celeron55@gmail.com>
and contributors (see source file comments and the version control log)
In case you downloaded the source code
--------------------------------------
If you downloaded the Minetest Engine source code in which this file is
contained, you probably want to download the [Minetest Game](https://github.com/minetest/minetest_game/)
project too. See its README.txt for more information.
Table of Contents
------------------
@ -31,11 +25,11 @@ Table of Contents
Further documentation
----------------------
- Website: https://minetest.net/
- Website: https://www.minetest.net/
- Wiki: https://wiki.minetest.net/
- Developer wiki: https://dev.minetest.net/
- Forum: https://forum.minetest.net/
- GitHub: https://github.com/minetest/minetest/
- [Developer documentation](doc/developing/)
- [doc/](doc/) directory of source distribution
Default controls
@ -51,7 +45,7 @@ Some can be changed in the key config dialog in the settings tab.
| Shift | Sneak/move down |
| Q | Drop itemstack |
| Shift + Q | Drop single item |
| Left mouse button | Dig/punch/take item |
| Left mouse button | Dig/punch/use |
| Right mouse button | Place/use |
| Shift + right mouse button | Build (without using) |
| I | Inventory menu |
@ -124,352 +118,17 @@ Command-line options
Compiling
---------
### Compiling on GNU/Linux
#### Dependencies
| Dependency | Version | Commentary |
|------------|---------|------------|
| GCC | 5.1+ | or Clang 3.5+ |
| CMake | 3.5+ | |
| IrrlichtMt | - | Custom version of Irrlicht, see https://github.com/minetest/irrlicht |
| Freetype | 2.0+ | |
| SQLite3 | 3+ | |
| Zstd | 1.0+ | |
| LuaJIT | 2.0+ | Bundled Lua 5.1 is used if not present |
| GMP | 5.0.0+ | Bundled mini-GMP is used if not present |
| JsonCPP | 1.0.0+ | Bundled JsonCPP is used if not present |
For Debian/Ubuntu users:
sudo apt install g++ make libc6-dev cmake libpng-dev libjpeg-dev libxi-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev libzstd-dev libluajit-5.1-dev
For Fedora users:
sudo dnf install make automake gcc gcc-c++ kernel-devel cmake libcurl-devel openal-soft-devel libpng-devel libjpeg-devel libvorbis-devel libXi-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel spatialindex-devel libzstd-devel
For Arch users:
sudo pacman -S base-devel libcurl-gnutls cmake libxi libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses zstd
For Alpine users:
sudo apk add build-base cmake libpng-dev jpeg-dev libxi-dev mesa-dev sqlite-dev libogg-dev libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev gmp-dev jsoncpp-dev luajit-dev zstd-dev
#### Download
You can install Git for easily keeping your copy up to date.
If you dont want Git, read below on how to get the source without Git.
This is an example for installing Git on Debian/Ubuntu:
sudo apt install git
For Fedora users:
sudo dnf install git
For Arch users:
sudo pacman -S git
For Alpine users:
sudo apk add git
Download source (this is the URL to the latest of source repository, which might not work at all times) using Git:
git clone --depth 1 https://github.com/minetest/minetest.git
cd minetest
Download Minetest Game (otherwise only the "Development Test" game is available) using Git:
git clone --depth 1 https://github.com/minetest/minetest_game.git games/minetest_game
Download IrrlichtMt to `lib/irrlichtmt`, it will be used to satisfy the IrrlichtMt dependency that way:
git clone --depth 1 https://github.com/minetest/irrlicht.git lib/irrlichtmt
Download source, without using Git:
wget https://github.com/minetest/minetest/archive/master.tar.gz
tar xf master.tar.gz
cd minetest-master
Download Minetest Game, without using Git:
cd games/
wget https://github.com/minetest/minetest_game/archive/master.tar.gz
tar xf master.tar.gz
mv minetest_game-master minetest_game
cd ..
Download IrrlichtMt, without using Git:
cd lib/
wget https://github.com/minetest/irrlicht/archive/master.tar.gz
tar xf master.tar.gz
mv irrlicht-master irrlichtmt
cd ..
#### Build
Build a version that runs directly from the source directory:
cmake . -DRUN_IN_PLACE=TRUE
make -j$(nproc)
Run it:
./bin/minetest
- Use `cmake . -LH` to see all CMake options and their current state.
- If you want to install it system-wide (or are making a distribution package),
you will want to use `-DRUN_IN_PLACE=FALSE`.
- You can build a bare server by specifying `-DBUILD_SERVER=TRUE`.
- You can disable the client build by specifying `-DBUILD_CLIENT=FALSE`.
- You can select between Release and Debug build by `-DCMAKE_BUILD_TYPE=<Debug or Release>`.
- Debug build is slower, but gives much more useful output in a debugger.
- If you build a bare server you don't need to compile IrrlichtMt, just the headers suffice.
- In that case use `-DIRRLICHT_INCLUDE_DIR=/some/where/irrlichtmt/include`.
- Minetest will use the IrrlichtMt package that is found first, given by the following order:
1. Specified `IRRLICHTMT_BUILD_DIR` CMake variable
2. `${PROJECT_SOURCE_DIR}/lib/irrlichtmt` (if existent)
3. Installation of IrrlichtMt in the system-specific library paths
4. For server builds with disabled `BUILD_CLIENT` variable, the headers from `IRRLICHT_INCLUDE_DIR` will be used.
- NOTE: Changing the IrrlichtMt build directory (includes system installs) requires regenerating the CMake cache (`rm CMakeCache.txt`)
### CMake options
General options and their default values:
BUILD_CLIENT=TRUE - Build Minetest client
BUILD_SERVER=FALSE - Build Minetest server
BUILD_UNITTESTS=TRUE - Build unittest sources
BUILD_BENCHMARKS=FALSE - Build benchmark sources
CMAKE_BUILD_TYPE=Release - Type of build (Release vs. Debug)
Release - Release build
Debug - Debug build
SemiDebug - Partially optimized debug build
RelWithDebInfo - Release build with debug information
MinSizeRel - Release build with -Os passed to compiler to make executable as small as possible
ENABLE_CURL=ON - Build with cURL; Enables use of online mod repo, public serverlist and remote media fetching via http
ENABLE_CURSES=ON - Build with (n)curses; Enables a server side terminal (command line option: --terminal)
ENABLE_GETTEXT=ON - Build with Gettext; Allows using translations
ENABLE_GLES=OFF - Enable extra support code for OpenGL ES (requires support by IrrlichtMt)
ENABLE_LEVELDB=ON - Build with LevelDB; Enables use of LevelDB map backend
ENABLE_POSTGRESQL=ON - Build with libpq; Enables use of PostgreSQL map backend (PostgreSQL 9.5 or greater recommended)
ENABLE_REDIS=ON - Build with libhiredis; Enables use of Redis map backend
ENABLE_SPATIAL=ON - Build with LibSpatial; Speeds up AreaStores
ENABLE_SOUND=ON - Build with OpenAL, libogg & libvorbis; in-game sounds
ENABLE_LUAJIT=ON - Build with LuaJIT (much faster than non-JIT Lua)
ENABLE_PROMETHEUS=OFF - Build with Prometheus metrics exporter (listens on tcp/30000 by default)
ENABLE_SYSTEM_GMP=ON - Use GMP from system (much faster than bundled mini-gmp)
ENABLE_SYSTEM_JSONCPP=ON - Use JsonCPP from system
RUN_IN_PLACE=FALSE - Create a portable install (worlds, settings etc. in current directory)
ENABLE_UPDATE_CHECKER=TRUE - Whether to enable update checks by default
INSTALL_DEVTEST=FALSE - Whether the Development Test game should be installed alongside Minetest
USE_GPROF=FALSE - Enable profiling using GProf
VERSION_EXTRA= - Text to append to version (e.g. VERSION_EXTRA=foobar -> Minetest 0.4.9-foobar)
ENABLE_TOUCH=FALSE - Enable Touchscreen support (requires support by IrrlichtMt)
Library specific options:
CURL_DLL - Only if building with cURL on Windows; path to libcurl.dll
CURL_INCLUDE_DIR - Only if building with cURL; directory where curl.h is located
CURL_LIBRARY - Only if building with cURL; path to libcurl.a/libcurl.so/libcurl.lib
EGL_INCLUDE_DIR - Only if building with GLES; directory that contains egl.h
EGL_LIBRARY - Only if building with GLES; path to libEGL.a/libEGL.so
EXTRA_DLL - Only on Windows; optional paths to additional DLLs that should be packaged
FREETYPE_INCLUDE_DIR_freetype2 - Directory that contains files such as ftimage.h
FREETYPE_INCLUDE_DIR_ft2build - Directory that contains ft2build.h
FREETYPE_LIBRARY - Path to libfreetype.a/libfreetype.so/freetype.lib
FREETYPE_DLL - Only on Windows; path to libfreetype-6.dll
GETTEXT_DLL - Only when building with gettext on Windows; paths to libintl + libiconv DLLs
GETTEXT_INCLUDE_DIR - Only when building with gettext; directory that contains libintl.h
GETTEXT_LIBRARY - Optional/platform-dependent with gettext; path to libintl.so/libintl.dll.a
GETTEXT_MSGFMT - Only when building with gettext; path to msgfmt/msgfmt.exe
ICONV_LIBRARY - Optional/platform-dependent; path to libiconv.so/libiconv.dylib
IRRLICHT_DLL - Only on Windows; path to IrrlichtMt.dll
IRRLICHT_INCLUDE_DIR - Directory that contains IrrCompileConfig.h (usable for server build only)
LEVELDB_INCLUDE_DIR - Only when building with LevelDB; directory that contains db.h
LEVELDB_LIBRARY - Only when building with LevelDB; path to libleveldb.a/libleveldb.so/libleveldb.dll.a
LEVELDB_DLL - Only when building with LevelDB on Windows; path to libleveldb.dll
PostgreSQL_INCLUDE_DIR - Only when building with PostgreSQL; directory that contains libpq-fe.h
PostgreSQL_LIBRARY - Only when building with PostgreSQL; path to libpq.a/libpq.so/libpq.lib
REDIS_INCLUDE_DIR - Only when building with Redis; directory that contains hiredis.h
REDIS_LIBRARY - Only when building with Redis; path to libhiredis.a/libhiredis.so
SPATIAL_INCLUDE_DIR - Only when building with LibSpatial; directory that contains spatialindex/SpatialIndex.h
SPATIAL_LIBRARY - Only when building with LibSpatial; path to libspatialindex.so/spatialindex-32.lib
LUA_INCLUDE_DIR - Only if you want to use LuaJIT; directory where luajit.h is located
LUA_LIBRARY - Only if you want to use LuaJIT; path to libluajit.a/libluajit.so
OGG_DLL - Only if building with sound on Windows; path to libogg.dll
OGG_INCLUDE_DIR - Only if building with sound; directory that contains an ogg directory which contains ogg.h
OGG_LIBRARY - Only if building with sound; path to libogg.a/libogg.so/libogg.dll.a
OPENAL_DLL - Only if building with sound on Windows; path to OpenAL32.dll
OPENAL_INCLUDE_DIR - Only if building with sound; directory where al.h is located
OPENAL_LIBRARY - Only if building with sound; path to libopenal.a/libopenal.so/OpenAL32.lib
SQLITE3_INCLUDE_DIR - Directory that contains sqlite3.h
SQLITE3_LIBRARY - Path to libsqlite3.a/libsqlite3.so/sqlite3.lib
VORBISFILE_LIBRARY - Only if building with sound; path to libvorbisfile.a/libvorbisfile.so/libvorbisfile.dll.a
VORBIS_DLL - Only if building with sound on Windows; paths to vorbis DLLs
VORBIS_INCLUDE_DIR - Only if building with sound; directory that contains a directory vorbis with vorbisenc.h inside
VORBIS_LIBRARY - Only if building with sound; path to libvorbis.a/libvorbis.so/libvorbis.dll.a
ZLIB_DLL - Only on Windows; path to zlib1.dll
ZLIB_INCLUDE_DIR - Directory that contains zlib.h
ZLIB_LIBRARY - Path to libz.a/libz.so/zlib.lib
ZSTD_DLL - Only on Windows; path to libzstd.dll
ZSTD_INCLUDE_DIR - Directory that contains zstd.h
ZSTD_LIBRARY - Path to libzstd.a/libzstd.so/ztd.lib
### Compiling on Windows using MSVC
### Requirements
- [Visual Studio 2015 or newer](https://visualstudio.microsoft.com)
- [CMake](https://cmake.org/download/)
- [vcpkg](https://github.com/Microsoft/vcpkg)
- [Git](https://git-scm.com/downloads)
### Compiling and installing the dependencies
It is highly recommended to use vcpkg as package manager.
After you successfully built vcpkg you can easily install the required libraries:
```powershell
vcpkg install zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry --triplet x64-windows
```
- **Don't forget about IrrlichtMt.** The easiest way is to clone it to `lib/irrlichtmt` as described in the Linux section.
- `curl` is optional, but required to read the serverlist, `curl[winssl]` is required to use the content store.
- `openal-soft`, `libvorbis` and `libogg` are optional, but required to use sound.
- `luajit` is optional, it replaces the integrated Lua interpreter with a faster just-in-time interpreter.
- `gmp` and `jsoncpp` are optional, otherwise the bundled versions will be compiled
There are other optional libraries, but they are not tested if they can build and link correctly.
Use `--triplet` to specify the target triplet, e.g. `x64-windows` or `x86-windows`.
### Compile Minetest
#### a) Using the vcpkg toolchain and CMake GUI
1. Start up the CMake GUI
2. Select **Browse Source...** and select DIR/minetest
3. Select **Browse Build...** and select DIR/minetest-build
4. Select **Configure**
5. Choose the right visual Studio version and target platform. It has to match the version of the installed dependencies
6. Choose **Specify toolchain file for cross-compiling**
7. Click **Next**
8. Select the vcpkg toolchain file e.g. `D:/vcpkg/scripts/buildsystems/vcpkg.cmake`
9. Click Finish
10. Wait until cmake have generated the cash file
11. If there are any errors, solve them and hit **Configure**
12. Click **Generate**
13. Click **Open Project**
14. Compile Minetest inside Visual studio.
#### b) Using the vcpkg toolchain and the commandline
Run the following script in PowerShell:
```powershell
cmake . -G"Visual Studio 15 2017 Win64" -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_GETTEXT=OFF -DENABLE_CURSES=OFF
cmake --build . --config Release
```
Make sure that the right compiler is selected and the path to the vcpkg toolchain is correct.
### Windows Installer using WiX Toolset
Requirements:
* [Visual Studio 2017](https://visualstudio.microsoft.com/)
* [WiX Toolset](https://wixtoolset.org/)
In the Visual Studio 2017 Installer select **Optional Features -> WiX Toolset**.
Build the binaries as described above, but make sure you unselect `RUN_IN_PLACE`.
Open the generated project file with Visual Studio. Right-click **Package** and choose **Generate**.
It may take some minutes to generate the installer.
### Compiling on MacOS
#### Requirements
- [Homebrew](https://brew.sh/)
- [Git](https://git-scm.com/downloads)
Install dependencies with homebrew:
```
brew install cmake freetype gettext gmp hiredis jpeg jsoncpp leveldb libogg libpng libvorbis luajit zstd
```
#### Download
Download source (this is the URL to the latest of source repository, which might not work at all times) using Git:
```bash
git clone --depth 1 https://github.com/minetest/minetest.git
cd minetest
```
Download Minetest Game (otherwise only the "Development Test" game is available) using Git:
```
git clone --depth 1 https://github.com/minetest/minetest_game.git games/minetest_game
```
Download Minetest's fork of Irrlicht:
```
git clone --depth 1 https://github.com/minetest/irrlicht.git lib/irrlichtmt
```
#### Build
```bash
mkdir build
cd build
cmake .. \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
-DCMAKE_FIND_FRAMEWORK=LAST \
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
-DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE
make -j$(sysctl -n hw.logicalcpu)
make install
```
#### Run
```
open ./build/macos/minetest.app
```
- [Compiling on GNU/Linux](doc/compiling/linux.md)
- [Compiling on Windows](doc/compiling/windows.md)
- [Compiling on MacOS](doc/compiling/macos.md)
Docker
------
We provide Minetest server Docker images using the GitLab mirror registry.
Images are built on each commit and available using the following tag scheme:
- [Developing minetestserver with Docker](doc/developing/docker.md)
* `registry.gitlab.com/minetest/minetest/server:latest` (latest build)
* `registry.gitlab.com/minetest/minetest/server:<branch/tag>` (current branch or current tag)
* `registry.gitlab.com/minetest/minetest/server:<commit-id>` (current commit id)
If you want to test it on a Docker server you can easily run:
sudo docker run registry.gitlab.com/minetest/minetest/server:<docker tag>
If you want to use it in a production environment you should use volumes bound to the Docker host
to persist data and modify the configuration:
sudo docker create -v /home/minetest/data/:/var/lib/minetest/ -v /home/minetest/conf/:/etc/minetest/ registry.gitlab.com/minetest/minetest/server:master
Data will be written to `/home/minetest/data` on the host, and configuration will be read from `/home/minetest/conf/minetest.conf`.
**Note:** If you don't understand the previous commands please read the official Docker documentation before use.
You can also host your Minetest server inside a Kubernetes cluster. See our example implementation in [`misc/kubernetes.yml`](misc/kubernetes.yml).
We provide a Dockerfile that can be used to build the server.
Version scheme

@ -54,7 +54,9 @@ android {
task prepareAssets() {
def assetsFolder = "build/assets"
def projRoot = rootDir.parent
def gameToCopy = "minetest_game"
// See issue #4638
def unsupportedLanguages = new File("${projRoot}/src/unsupported_language_list.txt").text.readLines()
doFirst {
logger.lifecycle('Preparing assets at {}', assetsFolder)
@ -79,14 +81,13 @@ task prepareAssets() {
from "${projRoot}/fonts" include "*.ttf" into "${assetsFolder}/fonts"
}
copy {
from "${projRoot}/games/${gameToCopy}" into "${assetsFolder}/games/${gameToCopy}"
}
copy {
from "${projRoot}/textures" into "${assetsFolder}/textures"
from "${projRoot}/textures/base/pack" into "${assetsFolder}/textures/base/pack"
}
// compile translations
fileTree("${projRoot}/po").include("**/*.po").forEach { poFile ->
fileTree("${projRoot}/po").include("**/*.po").grep {
it.parentFile.name !in unsupportedLanguages
}.forEach { poFile ->
def moPath = "${assetsFolder}/locale/${poFile.parentFile.name}/LC_MESSAGES/"
file(moPath).mkdirs()
exec {

@ -5,20 +5,13 @@
android:installLocation="auto">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-feature android:glEsVersion="0x00020000" />
<!--
`android:requestLegacyExternalStorage="true"` is workaround for using `/sdcard`
instead of the `getFilesDir()` patch for assets. Check link below for more information:
https://developer.android.com/training/data-storage/compatibility
-->
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/label"
android:requestLegacyExternalStorage="true"
android:resizeableActivity="false"
tools:ignore="UnusedAttribute">
@ -53,7 +46,7 @@
</intent-filter>
<meta-data
android:name="android.app.lib_name"
android:value="Minetest" />
android:value="minetest" />
</activity>
<service

@ -2,6 +2,8 @@
Minetest
Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
Copyright (C) 2023 srifqi, Muhammad Rifqi Priyo Susanto
<muhammadrifqipriyosusanto@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@ -29,17 +31,52 @@ import androidx.appcompat.widget.AppCompatEditText;
import java.util.Objects;
public class CustomEditText extends AppCompatEditText {
private int editType = 2; // single line text input as default
private boolean wantsToShowKeyboard = false;
public CustomEditText(Context context) {
super(context);
}
public CustomEditText(Context context, int _editType) {
super(context);
editType = _editType;
}
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
// For multi-line, do not close the dialog after pressing back button
if (editType != 1 && keyCode == KeyEvent.KEYCODE_BACK) {
InputMethodManager mgr = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
Objects.requireNonNull(mgr).hideSoftInputFromWindow(this.getWindowToken(), 0);
}
return false;
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
tryShowKeyboard();
}
public void requestFocusTryShow() {
requestFocus();
wantsToShowKeyboard = true;
tryShowKeyboard();
}
private void tryShowKeyboard() {
if (hasWindowFocus() && wantsToShowKeyboard) {
if (isFocused()) {
CustomEditText that = this;
post(() -> {
final InputMethodManager imm = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(that, 0);
});
}
wantsToShowKeyboard = false;
}
}
}

@ -23,7 +23,6 @@ package net.minetest.minetest;
import android.app.NativeActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
@ -32,7 +31,6 @@ import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import androidx.annotation.Keep;
@ -40,6 +38,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.core.content.FileProvider;
import java.io.File;
import java.util.Locale;
import java.util.Objects;
// Native code finds these methods by name (see porting_android.cpp).
@ -49,13 +48,16 @@ import java.util.Objects;
public class GameActivity extends NativeActivity {
static {
System.loadLibrary("c++_shared");
System.loadLibrary("Minetest");
System.loadLibrary("minetest");
}
private int messageReturnCode = -1;
private String messageReturnValue = "";
enum DialogType { TEXT_INPUT, SELECTION_INPUT }
enum DialogState { DIALOG_SHOWN, DIALOG_INPUTTED, DIALOG_CANCELED }
public static native void putMessageBoxResult(String text);
private DialogType lastDialogType = DialogType.TEXT_INPUT;
private DialogState inputDialogState = DialogState.DIALOG_CANCELED;
private String messageReturnValue = "";
private int selectionReturnValue = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -64,11 +66,10 @@ public class GameActivity extends NativeActivity {
}
private void makeFullScreen() {
if (Build.VERSION.SDK_INT >= 19)
this.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
this.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
@Override
@ -84,36 +85,43 @@ public class GameActivity extends NativeActivity {
makeFullScreen();
}
private native void saveSettings();
@Override
protected void onStop() {
super.onStop();
// Avoid losing setting changes in case the app is onDestroy()ed later.
// Saving stuff in onStop() is recommended in the Android activity
// lifecycle documentation.
saveSettings();
}
@Override
public void onBackPressed() {
// Ignore the back press so Minetest can handle it
}
public void showDialog(String acceptButton, String hint, String current, int editType) {
runOnUiThread(() -> showDialogUI(hint, current, editType));
public void showTextInputDialog(String hint, String current, int editType) {
runOnUiThread(() -> showTextInputDialogUI(hint, current, editType));
}
private void showDialogUI(String hint, String current, int editType) {
public void showSelectionInputDialog(String[] optionList, int selectedIdx) {
runOnUiThread(() -> showSelectionInputDialogUI(optionList, selectedIdx));
}
private void showTextInputDialogUI(String hint, String current, int editType) {
lastDialogType = DialogType.TEXT_INPUT;
inputDialogState = DialogState.DIALOG_SHOWN;
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
LinearLayout container = new LinearLayout(this);
container.setOrientation(LinearLayout.VERTICAL);
builder.setView(container);
AlertDialog alertDialog = builder.create();
EditText editText;
// For multi-line, do not close the dialog after pressing back button
if (editType == 1) {
editText = new EditText(this);
} else {
editText = new CustomEditText(this);
}
CustomEditText editText = new CustomEditText(this, editType);
container.addView(editText);
editText.setMaxLines(8);
editText.requestFocus();
editText.setHint(hint);
editText.setText(current);
final InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
Objects.requireNonNull(imm).toggleSoftInput(InputMethodManager.SHOW_FORCED,
InputMethodManager.HIDE_IMPLICIT_ONLY);
if (editType == 1)
editText.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_FLAG_MULTI_LINE);
@ -122,12 +130,13 @@ public class GameActivity extends NativeActivity {
InputType.TYPE_TEXT_VARIATION_PASSWORD);
else
editText.setInputType(InputType.TYPE_CLASS_TEXT);
editText.setSelection(editText.getText().length());
editText.setSelection(Objects.requireNonNull(editText.getText()).length());
final InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
editText.setOnKeyListener((view, keyCode, event) -> {
// For multi-line, do not submit the text after pressing Enter key
if (keyCode == KeyEvent.KEYCODE_ENTER && editType != 1) {
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
messageReturnCode = 0;
inputDialogState = DialogState.DIALOG_INPUTTED;
messageReturnValue = editText.getText().toString();
alertDialog.dismiss();
return true;
@ -141,28 +150,55 @@ public class GameActivity extends NativeActivity {
doneButton.setText(R.string.ime_dialog_done);
doneButton.setOnClickListener((view -> {
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
messageReturnCode = 0;
inputDialogState = DialogState.DIALOG_INPUTTED;
messageReturnValue = editText.getText().toString();
alertDialog.dismiss();
}));
}
alertDialog.show();
alertDialog.setOnCancelListener(dialog -> {
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
inputDialogState = DialogState.DIALOG_CANCELED;
messageReturnValue = current;
messageReturnCode = -1;
});
alertDialog.show();
editText.requestFocusTryShow();
}
public int getDialogState() {
return messageReturnCode;
public void showSelectionInputDialogUI(String[] optionList, int selectedIdx) {
lastDialogType = DialogType.SELECTION_INPUT;
inputDialogState = DialogState.DIALOG_SHOWN;
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setSingleChoiceItems(optionList, selectedIdx, (dialog, selection) -> {
inputDialogState = DialogState.DIALOG_INPUTTED;
selectionReturnValue = selection;
dialog.dismiss();
});
builder.setOnCancelListener(dialog -> {
inputDialogState = DialogState.DIALOG_CANCELED;
selectionReturnValue = selectedIdx;
});
AlertDialog alertDialog = builder.create();
alertDialog.show();
}
public String getDialogValue() {
messageReturnCode = -1;
public int getLastDialogType() {
return lastDialogType.ordinal();
}
public int getInputDialogState() {
return inputDialogState.ordinal();
}
public String getDialogMessage() {
inputDialogState = DialogState.DIALOG_CANCELED;
return messageReturnValue;
}
public int getDialogSelection() {
inputDialogState = DialogState.DIALOG_CANCELED;
return selectionReturnValue;
}
public float getDensity() {
return getResources().getDisplayMetrics().density;
}
@ -205,4 +241,28 @@ public class GameActivity extends NativeActivity {
Intent shareIntent = Intent.createChooser(intent, null);
startActivity(shareIntent);
}
public String getLanguage() {
String langCode = Locale.getDefault().getLanguage();
// getLanguage() still uses old language codes to preserve compatibility.
// List of code changes in ISO 639-2:
// https://www.loc.gov/standards/iso639-2/php/code_changes.php
switch (langCode) {
case "in":
langCode = "id"; // Indonesian
break;
case "iw":
langCode = "he"; // Hebrew
break;
case "ji":
langCode = "yi"; // Yiddish
break;
case "jw":
langCode = "jv"; // Javanese
break;
}
return langCode;
}
}

@ -20,38 +20,30 @@ with this program; if not, write to the Free Software Foundation, Inc.,
package net.minetest.minetest;
import android.Manifest;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static net.minetest.minetest.UnzipService.*;
public class MainActivity extends AppCompatActivity {
public static final String NOTIFICATION_CHANNEL_ID = "Minetest channel";
private final static int versionCode = BuildConfig.VERSION_CODE;
private final static int PERMISSIONS = 1;
private static final String[] REQUIRED_SDK_PERMISSIONS =
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
private static final String SETTINGS = "MinetestSettings";
private static final String TAG_VERSION_CODE = "versionCode";
@ -95,63 +87,21 @@ public class MainActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter filter = new IntentFilter(ACTION_UPDATE);
registerReceiver(myReceiver, filter);
mProgressBar = findViewById(R.id.progressBar);
mTextView = findViewById(R.id.textView);
sharedPreferences = getSharedPreferences(SETTINGS, Context.MODE_PRIVATE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
checkPermission();
else
checkAppVersion();
}
checkAppVersion();
private void checkPermission() {
final List<String> missingPermissions = new ArrayList<>();
for (final String permission : REQUIRED_SDK_PERMISSIONS) {
final int result = ContextCompat.checkSelfPermission(this, permission);
if (result != PackageManager.PERMISSION_GRANTED)
missingPermissions.add(permission);
}
if (!missingPermissions.isEmpty()) {
final String[] permissions = missingPermissions
.toArray(new String[0]);
ActivityCompat.requestPermissions(this, permissions, PERMISSIONS);
} else {
final int[] grantResults = new int[REQUIRED_SDK_PERMISSIONS.length];
Arrays.fill(grantResults, PackageManager.PERMISSION_GRANTED);
onRequestPermissionsResult(PERMISSIONS, REQUIRED_SDK_PERMISSIONS, grantResults);
}
}
@Override
public void onRequestPermissionsResult(
int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSIONS) {
for (int grantResult : grantResults) {
if (grantResult != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, R.string.not_granted, Toast.LENGTH_LONG).show();
finish();
return;
}
}
checkAppVersion();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
createNotificationChannel();
}
private void checkAppVersion() {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
Toast.makeText(this, R.string.no_external_storage, Toast.LENGTH_LONG).show();
finish();
return;
}
if (UnzipService.getIsRunning()) {
mProgressBar.setVisibility(View.VISIBLE);
mProgressBar.setIndeterminate(true);
@ -176,6 +126,28 @@ public class MainActivity extends AppCompatActivity {
startActivity(intent);
}
@RequiresApi(Build.VERSION_CODES.O)
private void createNotificationChannel() {
NotificationManager notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (notifyManager == null)
return;
NotificationChannel notifyChannel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID,
getString(R.string.notification_channel_name),
NotificationManager.IMPORTANCE_LOW
);
notifyChannel.setDescription(getString(R.string.notification_channel_description));
// Configure the notification channel without sound set
notifyChannel.setSound(null, null);
notifyChannel.enableLights(false);
notifyChannel.enableVibration(false);
// It is fine to always create the notification channel because creating a channel
// with the same ID is the same as overriding it (only its name and description).
notifyManager.createNotificationChannel(notifyChannel);
}
@Override
public void onBackPressed() {
// Prevent abrupt interruption when copy game files from assets

@ -22,13 +22,11 @@ package net.minetest.minetest;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import androidx.annotation.NonNull;
@ -59,9 +57,11 @@ public class UnzipService extends IntentService {
private String failureMessage;
private static boolean isRunning = false;
public static synchronized boolean getIsRunning() {
return isRunning;
}
private static synchronized void setIsRunning(boolean v) {
isRunning = v;
}
@ -88,7 +88,6 @@ public class UnzipService extends IntentService {
}
}
migrate(notificationBuilder, userDataDirectory);
unzip(notificationBuilder, zipFile, userDataDirectory);
} catch (IOException e) {
isSuccess = false;
@ -101,28 +100,13 @@ public class UnzipService extends IntentService {
}
}
@NonNull
private Notification.Builder createNotification() {
String name = "net.minetest.minetest";
String channelId = "Minetest channel";
String description = "notifications from Minetest";
Notification.Builder builder;
if (mNotifyManager == null)
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel mChannel = null;
if (mNotifyManager != null)
mChannel = mNotifyManager.getNotificationChannel(channelId);
if (mChannel == null) {
mChannel = new NotificationChannel(channelId, name, importance);
mChannel.setDescription(description);
// Configure the notification channel, NO SOUND
mChannel.setSound(null, null);
mChannel.enableLights(false);
mChannel.enableVibration(false);
mNotifyManager.createNotificationChannel(mChannel);
}
builder = new Notification.Builder(this, channelId);
builder = new Notification.Builder(this, MainActivity.NOTIFICATION_CHANNEL_ID);
} else {
builder = new Notification.Builder(this);
}
@ -137,9 +121,9 @@ public class UnzipService extends IntentService {
PendingIntent intent = PendingIntent.getActivity(this, 0,
notificationIntent, pendingIntentFlag);
builder.setContentTitle(getString(R.string.notification_title))
builder.setContentTitle(getString(R.string.unzip_notification_title))
.setSmallIcon(R.mipmap.ic_launcher)
.setContentText(getString(R.string.notification_description))
.setContentText(getString(R.string.unzip_notification_description))
.setContentIntent(intent)
.setOngoing(true)
.setProgress(0, 0, true);
@ -182,9 +166,9 @@ public class UnzipService extends IntentService {
try {
Process p = new ProcessBuilder("/system/bin/mv",
src.getAbsolutePath(), dst.getAbsolutePath()).start();
int exitcode = p.waitFor();
if (exitcode != 0)
throw new IOException("Move failed with exit code " + exitcode);
int exitCode = p.waitFor();
if (exitCode != 0)
throw new IOException("Move failed with exit code " + exitCode);
} catch (InterruptedException e) {
throw new IOException("Move operation interrupted");
}
@ -200,45 +184,7 @@ public class UnzipService extends IntentService {
}
}
/**
* Migrates user data from deprecated external storage to app scoped storage
*/
private void migrate(Notification.Builder notificationBuilder, File newLocation) throws IOException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return;
}
File oldLocation = new File(Environment.getExternalStorageDirectory(), "Minetest");
if (!oldLocation.isDirectory())
return;
publishProgress(notificationBuilder, R.string.migrating, 0);
if (!newLocation.mkdir()) {
Log.e("UnzipService", "New installation folder cannot be made");
}
String[] dirs = new String[] { "worlds", "games", "mods", "textures", "client" };
for (int i = 0; i < dirs.length; i++) {
publishProgress(notificationBuilder, R.string.migrating, 100 * i / dirs.length);
File dir = new File(oldLocation, dirs[i]), dir2 = new File(newLocation, dirs[i]);
if (dir.isDirectory() && !dir2.isDirectory()) {
moveFileOrDir(dir, dir2);
}
}
for (String filename : new String[] { "minetest.conf" }) {
File file = new File(oldLocation, filename), file2 = new File(newLocation, filename);
if (file.isFile() && !file2.isFile()) {
moveFileOrDir(file, file2);
}
}
if (!recursivelyDeleteDirectory(oldLocation)) {
Log.w("UnzipService", "Old installation files cannot be deleted successfully");
}
}
private void publishProgress(@Nullable Notification.Builder notificationBuilder, @StringRes int message, int progress) {
private void publishProgress(@Nullable Notification.Builder notificationBuilder, @StringRes int message, int progress) {
Intent intentUpdate = new Intent(ACTION_UPDATE);
intentUpdate.putExtra(ACTION_PROGRESS, progress);
intentUpdate.putExtra(ACTION_PROGRESS_MESSAGE, message);

@ -38,7 +38,6 @@ public class Utils {
public static boolean isInstallValid(@NonNull Context context) {
File userDataDirectory = getUserDataDirectory(context);
return userDataDirectory.isDirectory() &&
new File(userDataDirectory, "games").isDirectory() &&
new File(userDataDirectory, "builtin").isDirectory() &&
new File(userDataDirectory, "client").isDirectory() &&
new File(userDataDirectory, "textures").isDirectory();

@ -1,13 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="label">Minetest</string>
<string name="loading">Loading&#8230;</string>
<string name="migrating">Migrating save data from old install&#8230; (this may take a while)</string>
<string name="not_granted">Required permission wasn\'t granted, Minetest can\'t run without it</string>
<string name="notification_title">Loading Minetest</string>
<string name="notification_description">Less than 1 minute&#8230;</string>
<string name="notification_channel_name">General notification</string>
<string name="notification_channel_description">Notifications from Minetest</string>
<string name="unzip_notification_title">Loading Minetest</string>
<string name="unzip_notification_description">Less than 1 minute&#8230;</string>
<string name="ime_dialog_done">Done</string>
<string name="no_external_storage">External storage isn\'t available. If you use an SDCard, please reinsert it. Otherwise, try restarting your phone or contacting the Minetest developers</string>
</resources>

@ -1,11 +1,10 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
project.ext.set("versionMajor", 5) // Version Major
project.ext.set("versionMinor", 7) // Version Minor
project.ext.set("versionMinor", 9) // Version Minor
project.ext.set("versionPatch", 0) // Version Patch
project.ext.set("versionExtra", "") // Version Extra
project.ext.set("versionCode", 44) // Android Version Code
project.ext.set("developmentBuild", 0) // Whether it is a development build, or a release
// ^ keep in sync with cmake
project.ext.set("versionCode", 46) // Android Version Code
// NOTE: +2 after each release!
// +1 for ARM and +1 for ARM64 APK's, because
// each APK must have a larger `versionCode` than the previous

@ -7,5 +7,5 @@ org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.parallel.threads=8
org.gradle.configureondemand=true
android.enableJetifier=true
android.enableJetifier=false
android.useAndroidX=true

111
android/icons/exit_btn.svg Normal file

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 135.46666 135.46667"
version="1.1"
id="svg8"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
sodipodi:docname="exit_btn.svg"
inkscape:export-filename="../../textures/base/pack/exit_btn.png"
inkscape:export-xdpi="24.000002"
inkscape:export-ydpi="24.000002"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs10" />
<sodipodi:namedview
id="base"
pagecolor="#404040"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="0.84958349"
inkscape:cx="-94.752312"
inkscape:cy="291.31922"
inkscape:document-units="px"
inkscape:current-layer="layer2"
showgrid="true"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:pagecheckerboard="false"
inkscape:snap-grids="true"
inkscape:snap-page="true"
showguides="false"
inkscape:showpageshadow="2"
inkscape:deskcolor="#404040">
<inkscape:grid
type="xygrid"
id="grid16"
spacingx="0.26458333"
spacingy="0.26458333"
empspacing="4"
color="#40ff40"
opacity="0.1254902"
empcolor="#40ff40"
empopacity="0.25098039" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Layer 2"
style="display:inline">
<path
id="rect5028"
style="display:inline;fill:none;stroke:#ffffff;stroke-width:5.99996;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
d="m 78.052082,90.48746 v 17.4625 l -50.535415,4e-5 V 27.516667 l 50.535415,3.7e-5 v 17.462423"
sodipodi:nodetypes="cccccc" />
<path
style="display:inline;fill:none;stroke:#ffffff;stroke-width:5.99996;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 101.49853,55.033202 12.69966,12.700052 -12.69966,12.699942"
id="path4737"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="display:inline;fill:none;stroke:#ffffff;stroke-width:6;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 113.36416,67.733332 H 59.484405"
id="path4729"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

@ -9,20 +9,18 @@ android {
minSdkVersion 21
targetSdkVersion 33
externalNativeBuild {
ndkBuild {
arguments '-j' + Runtime.getRuntime().availableProcessors(),
"versionMajor=${versionMajor}",
"versionMinor=${versionMinor}",
"versionPatch=${versionPatch}",
"versionExtra=${versionExtra}",
"developmentBuild=${developmentBuild}"
cmake {
arguments "-DANDROID_STL=c++_shared",
"-DENABLE_CURL=1", "-DENABLE_SOUND=1",
"-DENABLE_TOUCH=1", "-DENABLE_GETTEXT=1",
"-DBUILD_UNITTESTS=0", "-DENABLE_UPDATE_CHECKER=0"
}
}
}
externalNativeBuild {
ndkBuild {
path file('jni/Android.mk')
cmake {
path file("../../CMakeLists.txt")
}
}
@ -37,12 +35,6 @@ android {
buildTypes {
release {
externalNativeBuild {
ndkBuild {
arguments 'NDEBUG=1'
}
}
ndk {
debugSymbolLevel 'SYMBOL_TABLE'
}

@ -1,302 +0,0 @@
LOCAL_PATH := $(call my-dir)/..
#LOCAL_ADDRESS_SANITIZER:=true
#USE_BUILTIN_LUA:=true
include $(CLEAR_VARS)
LOCAL_MODULE := Curl
LOCAL_SRC_FILES := deps/$(APP_ABI)/Curl/libcurl.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libmbedcrypto
LOCAL_SRC_FILES := deps/$(APP_ABI)/Curl/libmbedcrypto.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libmbedtls
LOCAL_SRC_FILES := deps/$(APP_ABI)/Curl/libmbedtls.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libmbedx509
LOCAL_SRC_FILES := deps/$(APP_ABI)/Curl/libmbedx509.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Freetype
LOCAL_SRC_FILES := deps/$(APP_ABI)/Freetype/libfreetype.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Iconv
LOCAL_SRC_FILES := deps/$(APP_ABI)/Iconv/libiconv.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libcharset
LOCAL_SRC_FILES := deps/$(APP_ABI)/Iconv/libcharset.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Irrlicht
LOCAL_SRC_FILES := deps/$(APP_ABI)/Irrlicht/libIrrlichtMt.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Irrlicht-libpng
LOCAL_SRC_FILES := deps/$(APP_ABI)/Irrlicht/libpng.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Irrlicht-libjpeg
LOCAL_SRC_FILES := deps/$(APP_ABI)/Irrlicht/libjpeg.a
include $(PREBUILT_STATIC_LIBRARY)
ifndef USE_BUILTIN_LUA
include $(CLEAR_VARS)
LOCAL_MODULE := LuaJIT
LOCAL_SRC_FILES := deps/$(APP_ABI)/LuaJIT/libluajit.a
include $(PREBUILT_STATIC_LIBRARY)
endif
include $(CLEAR_VARS)
LOCAL_MODULE := OpenAL
LOCAL_SRC_FILES := deps/$(APP_ABI)/OpenAL-Soft/libopenal.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Gettext
LOCAL_SRC_FILES := deps/$(APP_ABI)/Gettext/libintl.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := SQLite3
LOCAL_SRC_FILES := deps/$(APP_ABI)/SQLite/libsqlite3.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Vorbis
LOCAL_SRC_FILES := deps/$(APP_ABI)/Vorbis/libvorbis.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libvorbisfile
LOCAL_SRC_FILES := deps/$(APP_ABI)/Vorbis/libvorbisfile.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libogg
LOCAL_SRC_FILES := deps/$(APP_ABI)/Vorbis/libogg.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Zstd
LOCAL_SRC_FILES := deps/$(APP_ABI)/Zstd/libzstd.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Minetest
LOCAL_CFLAGS += \
-DJSONCPP_NO_LOCALE_SUPPORT \
-DHAVE_TOUCHSCREENGUI \
-DENABLE_GLES=1 \
-DUSE_CURL=1 \
-DUSE_SOUND=1 \
-DUSE_LEVELDB=0 \
-DUSE_GETTEXT=1 \
-DVERSION_MAJOR=${versionMajor} \
-DVERSION_MINOR=${versionMinor} \
-DVERSION_PATCH=${versionPatch} \
-DVERSION_EXTRA=${versionExtra} \
-DDEVELOPMENT_BUILD=${developmentBuild} \
$(GPROF_DEF)
ifdef USE_BUILTIN_LUA
LOCAL_CFLAGS += -DUSE_LUAJIT=0
else
LOCAL_CFLAGS += -DUSE_LUAJIT=1
endif
ifdef NDEBUG
LOCAL_CFLAGS += -DNDEBUG=1
endif
ifdef GPROF
GPROF_DEF := -DGPROF
PROFILER_LIBS := android-ndk-profiler
LOCAL_CFLAGS += -pg
endif
LOCAL_C_INCLUDES := \
../../src \
../../src/script \
../../lib/gmp \
../../lib/jsoncpp \
deps/$(APP_ABI)/Curl/include \
deps/$(APP_ABI)/Freetype/include/freetype2 \
deps/$(APP_ABI)/Irrlicht/include \
deps/$(APP_ABI)/Gettext/include \
deps/$(APP_ABI)/Iconv/include \
deps/$(APP_ABI)/OpenAL-Soft/include \
deps/$(APP_ABI)/SQLite/include \
deps/$(APP_ABI)/Vorbis/include \
deps/$(APP_ABI)/Zstd/include
ifdef USE_BUILTIN_LUA
LOCAL_C_INCLUDES += \
../../lib/lua/src \
../../lib/bitop
else
LOCAL_C_INCLUDES += deps/$(APP_ABI)/LuaJIT/include
endif
LOCAL_SRC_FILES := \
$(wildcard ../../src/client/*.cpp) \
$(wildcard ../../src/client/*/*.cpp) \
$(wildcard ../../src/content/*.cpp) \
../../src/database/database.cpp \
../../src/database/database-dummy.cpp \
../../src/database/database-files.cpp \
../../src/database/database-sqlite3.cpp \
$(wildcard ../../src/gui/*.cpp) \
$(wildcard ../../src/irrlicht_changes/*.cpp) \
$(wildcard ../../src/mapgen/*.cpp) \
$(wildcard ../../src/network/*.cpp) \
$(wildcard ../../src/script/*.cpp) \
$(wildcard ../../src/script/*/*.cpp) \
$(wildcard ../../src/server/*.cpp) \
$(wildcard ../../src/threading/*.cpp) \
$(wildcard ../../src/util/*.c) \
$(wildcard ../../src/util/*.cpp) \
../../src/ban.cpp \
../../src/chat.cpp \
../../src/clientiface.cpp \
../../src/collision.cpp \
../../src/content_mapnode.cpp \
../../src/content_nodemeta.cpp \
../../src/convert_json.cpp \
../../src/craftdef.cpp \
../../src/debug.cpp \
../../src/defaultsettings.cpp \
../../src/emerge.cpp \
../../src/environment.cpp \
../../src/face_position_cache.cpp \
../../src/filesys.cpp \
../../src/gettext.cpp \
../../src/httpfetch.cpp \
../../src/hud.cpp \
../../src/inventory.cpp \
../../src/inventorymanager.cpp \
../../src/itemdef.cpp \
../../src/itemstackmetadata.cpp \
../../src/light.cpp \
../../src/lighting.cpp \
../../src/log.cpp \
../../src/main.cpp \
../../src/map.cpp \
../../src/map_settings_manager.cpp \
../../src/mapblock.cpp \
../../src/mapnode.cpp \
../../src/mapsector.cpp \
../../src/metadata.cpp \
../../src/modchannels.cpp \
../../src/nameidmapping.cpp \
../../src/nodedef.cpp \
../../src/nodemetadata.cpp \
../../src/nodetimer.cpp \
../../src/noise.cpp \
../../src/objdef.cpp \
../../src/object_properties.cpp \
../../src/particles.cpp \
../../src/pathfinder.cpp \
../../src/player.cpp \
../../src/porting.cpp \
../../src/porting_android.cpp \
../../src/profiler.cpp \
../../src/raycast.cpp \
../../src/reflowscan.cpp \
../../src/remoteplayer.cpp \
../../src/rollback.cpp \
../../src/rollback_interface.cpp \
../../src/serialization.cpp \
../../src/server.cpp \
../../src/serverenvironment.cpp \
../../src/serverlist.cpp \
../../src/settings.cpp \
../../src/staticobject.cpp \
../../src/texture_override.cpp \
../../src/tileanimation.cpp \
../../src/tool.cpp \
../../src/translation.cpp \
../../src/version.cpp \
../../src/voxel.cpp \
../../src/voxelalgorithms.cpp
# Built-in Lua
ifdef USE_BUILTIN_LUA
LOCAL_SRC_FILES += \
../../lib/lua/src/lapi.c \
../../lib/lua/src/lauxlib.c \
../../lib/lua/src/lbaselib.c \
../../lib/lua/src/lcode.c \
../../lib/lua/src/ldblib.c \
../../lib/lua/src/ldebug.c \
../../lib/lua/src/ldo.c \
../../lib/lua/src/ldump.c \
../../lib/lua/src/lfunc.c \
../../lib/lua/src/lgc.c \
../../lib/lua/src/linit.c \
../../lib/lua/src/liolib.c \
../../lib/lua/src/llex.c \
../../lib/lua/src/lmathlib.c \
../../lib/lua/src/lmem.c \
../../lib/lua/src/loadlib.c \
../../lib/lua/src/lobject.c \
../../lib/lua/src/lopcodes.c \
../../lib/lua/src/loslib.c \
../../lib/lua/src/lparser.c \
../../lib/lua/src/lstate.c \
../../lib/lua/src/lstring.c \
../../lib/lua/src/lstrlib.c \
../../lib/lua/src/ltable.c \
../../lib/lua/src/ltablib.c \
../../lib/lua/src/ltm.c \
../../lib/lua/src/lundump.c \
../../lib/lua/src/lvm.c \
../../lib/lua/src/lzio.c \
../../lib/bitop/bit.c
endif
# GMP
LOCAL_SRC_FILES += ../../lib/gmp/mini-gmp.c
# JSONCPP
LOCAL_SRC_FILES += ../../lib/jsoncpp/jsoncpp.cpp
LOCAL_STATIC_LIBRARIES += \
Curl libmbedcrypto libmbedtls libmbedx509 \
Freetype \
Iconv libcharset \
Irrlicht Irrlicht-libpng Irrlicht-libjpeg \
OpenAL \
Gettext \
SQLite3 \
Vorbis libvorbisfile libogg \
Zstd
ifndef USE_BUILTIN_LUA
LOCAL_STATIC_LIBRARIES += LuaJIT
endif
LOCAL_STATIC_LIBRARIES += android_native_app_glue $(PROFILER_LIBS)
LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES -lz
include $(BUILD_SHARED_LIBRARY)
ifdef GPROF
$(call import-module,android-ndk-profiler)
endif
$(call import-module,android/native_app_glue)

@ -1,32 +0,0 @@
APP_PLATFORM := ${APP_PLATFORM}
APP_ABI := ${TARGET_ABI}
APP_STL := c++_shared
NDK_TOOLCHAIN_VERSION := clang
APP_SHORT_COMMANDS := true
APP_MODULES := Minetest
APP_CPPFLAGS := -O2 -fvisibility=hidden
ifeq ($(APP_ABI),armeabi-v7a)
APP_CPPFLAGS += -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb
endif
ifeq ($(APP_ABI),x86)
APP_CPPFLAGS += -mssse3 -mfpmath=sse -funroll-loops
endif
ifndef NDEBUG
APP_CPPFLAGS := -g -Og -fno-omit-frame-pointer
endif
APP_CFLAGS := $(APP_CPPFLAGS) -Wno-inconsistent-missing-override -Wno-parentheses-equality
APP_CXXFLAGS := $(APP_CPPFLAGS) -fexceptions -frtti -std=gnu++14
APP_LDFLAGS := -Wl,--no-warn-mismatch,--gc-sections,--icf=safe
ifeq ($(APP_ABI),arm64-v8a)
APP_LDFLAGS := -Wl,--no-warn-mismatch,--gc-sections
endif
ifndef NDEBUG
APP_LDFLAGS :=
endif

@ -36,11 +36,16 @@ do
setmetatable(v, {__newindex = {}})
-- Reassemble the other tables
if v.type == "node" then
getmetatable(v).__index = all.nodedef_default
all.registered_nodes[k] = v
elseif v.type == "craftitem" then
elseif v.type == "craft" then
getmetatable(v).__index = all.craftitemdef_default
all.registered_craftitems[k] = v
elseif v.type == "tool" then
getmetatable(v).__index = all.tooldef_default
all.registered_tools[k] = v
else
getmetatable(v).__index = all.noneitemdef_default
end
end

@ -7,6 +7,7 @@ dofile(clientpath .. "register.lua")
dofile(commonpath .. "after.lua")
dofile(commonpath .. "mod_storage.lua")
dofile(commonpath .. "chatcommands.lua")
dofile(commonpath .. "information_formspecs.lua")
dofile(clientpath .. "chatcommands.lua")
dofile(clientpath .. "death_formspec.lua")
dofile(clientpath .. "misc.lua")

@ -5,3 +5,14 @@ function core.setting_get_pos(name)
end
return core.string_to_pos(value)
end
-- old non-method sound functions
function core.sound_stop(handle, ...)
return handle:stop(...)
end
function core.sound_fade(handle, ...)
return handle:fade(...)
end

@ -1,4 +1,116 @@
local jobs = {}
-- This is an implementation of a job sheduling mechanism. It guarantees that
-- coexisting jobs will execute primarily in order of least expiry, and
-- secondarily in order of first registration.
-- These functions implement an intrusive singly linked list of one or more
-- elements where the first element has a pointer to the last. The next pointer
-- is stored with key list_next. The pointer to the last is with key list_end.
local function list_init(first)
first.list_end = first
end
local function list_append(first, append)
first.list_end.list_next = append
first.list_end = append
end
local function list_append_list(first, first_append)
first.list_end.list_next = first_append
first.list_end = first_append.list_end
end
-- The jobs are stored in a map from expiration times to linked lists of jobs
-- as above. The expiration times are also stored in an array representing a
-- binary min heap, which is a particular arrangement of binary tree. A parent
-- at index i has children at indices i*2 and i*2+1. Out-of-bounds indices
-- represent nonexistent children. A parent is never greater than its children.
-- This structure means that, if there is at least one job, the next expiration
-- time is the first item in the array.
-- Push element on a binary min-heap,
-- "bubbling up" the element by swapping with larger parents.
local function heap_push(heap, element)
local index = #heap + 1
while index > 1 do
local parent_index = math.floor(index / 2)
local parent = heap[parent_index]
if element < parent then
heap[index] = parent
index = parent_index
else
break
end
end
heap[index] = element
end
-- Pop smallest element from the heap,
-- "sinking down" the last leaf on the last layer of the heap
-- by swapping with the smaller child.
local function heap_pop(heap)
local removed_element = heap[1]
local length = #heap
local element = heap[length]
heap[length] = nil
length = length - 1
if length > 0 then
local index = 1
while true do
local old_index = index
local smaller_element = element
local left_index = index * 2
local right_index = index * 2 + 1
if left_index <= length then
local left_element = heap[left_index]
if left_element < smaller_element then
index = left_index
smaller_element = left_element
end
end
if right_index <= length then
if heap[right_index] < smaller_element then
index = right_index
end
end
if old_index ~= index then
heap[old_index] = heap[index]
else
break
end
end
heap[index] = element
end
return removed_element
end
local job_map = {}
local expiries = {}
-- Adds an individual job with the given expiry.
-- The worst-case complexity is O(log n), where n is the number of distinct
-- expiration times.
local function add_job(expiry, job)
local list = job_map[expiry]
if list then
list_append(list, job)
else
list_init(job)
job_map[expiry] = job
heap_push(expiries, expiry)
end
end
-- Removes the next expiring jobs and returns the linked list of them.
-- The worst-case complexity is O(log n), where n is the number of distinct
-- expiration times.
local function remove_first_jobs()
local removed_expiry = heap_pop(expiries)
local removed = job_map[removed_expiry]
job_map[removed_expiry] = nil
return removed
end
local time = 0.0
local time_next = math.huge
@ -9,42 +121,54 @@ core.register_globalstep(function(dtime)
return
end
time_next = math.huge
-- Remove the expired jobs.
local expired = remove_first_jobs()
-- Iterate backwards so that we miss any new timers added by
-- a timer callback.
for i = #jobs, 1, -1 do
local job = jobs[i]
if time >= job.expire then
core.set_last_run_mod(job.mod_origin)
job.func(unpack(job.arg))
local jobs_l = #jobs
jobs[i] = jobs[jobs_l]
jobs[jobs_l] = nil
elseif job.expire < time_next then
time_next = job.expire
-- Remove other expired jobs and append them to the list.
while true do
time_next = expiries[1] or math.huge
if time_next > time then
break
end
list_append_list(expired, remove_first_jobs())
end
-- Run the callbacks afterward to prevent infinite loops with core.after(0, ...).
local last_expired = expired.list_end
while true do
core.set_last_run_mod(expired.mod_origin)
expired.func(unpack(expired.args, 1, expired.args.n))
if expired == last_expired then
break
end
expired = expired.list_next
end
end)
function core.after(after, func, ...)
assert(tonumber(after) and type(func) == "function",
"Invalid minetest.after invocation")
local expire = time + after
local new_job = {
func = func,
expire = expire,
arg = {...},
mod_origin = core.get_last_run_mod(),
}
local job_metatable = {__index = {}}
jobs[#jobs + 1] = new_job
time_next = math.min(time_next, expire)
return {
cancel = function()
new_job.func = function() end
new_job.args = {}
end
}
local function dummy_func() end
function job_metatable.__index:cancel()
self.func = dummy_func
self.args = {n = 0}
end
function core.after(after, func, ...)
assert(tonumber(after) and not core.is_nan(after) and type(func) == "function",
"Invalid minetest.after invocation")
local new_job = {
mod_origin = core.get_last_run_mod(),
func = func,
args = {
n = select("#", ...),
...
},
}
local expiry = time + after
add_job(expiry, new_job)
time_next = math.min(time_next, expiry)
return setmetatable(new_job, job_metatable)
end

@ -89,7 +89,7 @@ local function do_help_cmd(name, param)
if #args > 1 then
return false, S("Too many arguments, try using just /help <command>")
end
local use_gui = INIT ~= "client" and core.get_player_by_name(name)
local use_gui = INIT == "client" or core.get_player_by_name(name)
use_gui = use_gui and not opts:find("t")
if #args == 0 and not use_gui then
@ -163,8 +163,8 @@ end
if INIT == "client" then
core.register_chatcommand("help", {
params = core.gettext("[all | <cmd>]"),
description = core.gettext("Get help for commands"),
params = core.gettext("[all | <cmd>] [-t]"),
description = core.gettext("Get help for commands (-t: output in chat)"),
func = function(param)
return do_help_cmd(nil, param)
end,

@ -61,15 +61,20 @@ local function build_chatcommands_formspec(name, sel, copy)
for i, data in ipairs(mod_cmds) do
rows[#rows + 1] = COLOR_BLUE .. ",0," .. F(data[1]) .. ","
for j, cmds in ipairs(data[2]) do
local has_priv = check_player_privs(name, cmds[2].privs)
local has_priv = INIT == "client" or check_player_privs(name, cmds[2].privs)
rows[#rows + 1] = ("%s,1,%s,%s"):format(
has_priv and COLOR_GREEN or COLOR_GRAY,
cmds[1], F(cmds[2].params))
if sel == #rows then
description = cmds[2].description
if copy then
core.chat_send_player(name, S("Command: @1 @2",
core.colorize("#0FF", "/" .. cmds[1]), cmds[2].params))
local msg = S("Command: @1 @2",
core.colorize("#0FF", "/" .. cmds[1]), cmds[2].params)
if INIT == "client" then
core.display_chat_message(msg)
else
core.chat_send_player(name, msg)
end
end
end
end
@ -111,26 +116,46 @@ end
-- DETAILED CHAT COMMAND INFORMATION
if INIT == "client" then
core.register_on_formspec_input(function(formname, fields)
if formname ~= "__builtin:help_cmds" or fields.quit then
return
end
core.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= "__builtin:help_cmds" or fields.quit then
return
end
local event = core.explode_table_event(fields.list)
if event.type ~= "INV" then
core.show_formspec("__builtin:help_cmds",
build_chatcommands_formspec(nil, event.row, event.type == "DCL"))
end
end)
else
core.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= "__builtin:help_cmds" or fields.quit then
return
end
local event = core.explode_table_event(fields.list)
if event.type ~= "INV" then
local name = player:get_player_name()
core.show_formspec(name, "__builtin:help_cmds",
build_chatcommands_formspec(name, event.row, event.type == "DCL"))
end
end)
local event = core.explode_table_event(fields.list)
if event.type ~= "INV" then
local name = player:get_player_name()
core.show_formspec(name, "__builtin:help_cmds",
build_chatcommands_formspec(name, event.row, event.type == "DCL"))
end
end)
end
function core.show_general_help_formspec(name)
core.show_formspec(name, "__builtin:help_cmds",
build_chatcommands_formspec(name))
if INIT == "client" then
core.show_formspec("__builtin:help_cmds",
build_chatcommands_formspec(name))
else
core.show_formspec(name, "__builtin:help_cmds",
build_chatcommands_formspec(name))
end
end
function core.show_privs_help_formspec(name)
core.show_formspec(name, "__builtin:help_privs",
build_privs_formspec(name))
if INIT ~= "client" then
function core.show_privs_help_formspec(name)
core.show_formspec(name, "__builtin:help_privs",
build_privs_formspec(name))
end
end

@ -144,6 +144,8 @@ local wallmounted_to_dir = {
vector.new(-1, 0, 0),
vector.new( 0, 0, 1),
vector.new( 0, 0, -1),
vector.new( 0, 1, 0),
vector.new( 0, -1, 0),
}
function core.wallmounted_to_dir(wallmounted)
return wallmounted_to_dir[wallmounted % 8]

@ -0,0 +1,74 @@
local builtin_shared = ...
do
local default = {mod = "??", name = "??"}
core.callback_origins = setmetatable({}, {
__index = function()
return default
end
})
end
function core.run_callbacks(callbacks, mode, ...)
assert(type(callbacks) == "table")
local cb_len = #callbacks
if cb_len == 0 then
if mode == 2 or mode == 3 then
return true
elseif mode == 4 or mode == 5 then
return false
end
end
local ret = nil
for i = 1, cb_len do
local origin = core.callback_origins[callbacks[i]]
core.set_last_run_mod(origin.mod)
local cb_ret = callbacks[i](...)
if mode == 0 and i == 1 then
ret = cb_ret
elseif mode == 1 and i == cb_len then
ret = cb_ret
elseif mode == 2 then
if not cb_ret or i == 1 then
ret = cb_ret
end
elseif mode == 3 then
if cb_ret then
return cb_ret
end
ret = cb_ret
elseif mode == 4 then
if (cb_ret and not ret) or i == 1 then
ret = cb_ret
end
elseif mode == 5 and cb_ret then
return cb_ret
end
end
return ret
end
function builtin_shared.make_registration()
local t = {}
local registerfunc = function(func)
t[#t + 1] = func
core.callback_origins[func] = {
mod = core.get_current_modname() or "??",
name = debug.getinfo(1, "n").name or "??"
}
end
return t, registerfunc
end
function builtin_shared.make_registration_reverse()
local t = {}
local registerfunc = function(func)
table.insert(t, 1, func)
core.callback_origins[func] = {
mod = core.get_current_modname() or "??",
name = debug.getinfo(1, "n").name or "??"
}
end
return t, registerfunc
end

@ -0,0 +1,113 @@
_G.core = {}
_G.vector = {metatable = {}}
dofile("builtin/common/vector.lua")
dofile("builtin/common/misc_helpers.lua")
function core.get_last_run_mod() return "*test*" end
function core.set_last_run_mod() end
local do_step
function core.register_globalstep(func)
do_step = func
end
dofile("builtin/common/after.lua")
describe("after", function()
it("executes callbacks when expected", function()
local result = ""
core.after(0, function()
result = result .. "a"
end)
core.after(1, function()
result = result .. "b"
end)
core.after(1, function()
result = result .. "c"
end)
core.after(2, function()
result = result .. "d"
end)
local cancel = core.after(2, function()
result = result .. "e"
end)
do_step(0)
assert.same("a", result)
do_step(1)
assert.same("abc", result)
core.after(2, function()
result = result .. "f"
end)
core.after(1, function()
result = result .. "g"
end)
core.after(-1, function()
result = result .. "h"
end)
cancel:cancel()
do_step(1)
assert.same("abchdg", result)
do_step(1)
assert.same("abchdgf", result)
end)
it("defers jobs with delay 0", function()
local result = ""
core.after(0, function()
core.after(0, function()
result = result .. "b"
end)
result = result .. "a"
end)
do_step(1)
assert.same("a", result)
do_step(1)
assert.same("ab", result)
end)
it("passes arguments", function()
core.after(0, function(...)
assert.same(0, select("#", ...))
end)
core.after(0, function(...)
assert.same(4, select("#", ...))
assert.same(1, (select(1, ...)))
assert.same(nil, (select(2, ...)))
assert.same("a", (select(3, ...)))
assert.same(nil, (select(4, ...)))
end, 1, nil, "a", nil)
do_step(0)
end)
it("rejects invalid arguments", function()
assert.has.errors(function() core.after() end)
assert.has.errors(function() core.after(nil, nil) end)
assert.has.errors(function() core.after(0) end)
assert.has.errors(function() core.after(0, nil) end)
assert.has.errors(function() core.after(nil, function() end) end)
assert.has.errors(function() core.after(0 / 0, function() end) end)
end)
-- Make sure that the underlying heap is working correctly
it("can be abused as a heapsort", function()
local t = {}
for i = 1, 1000 do
t[i] = math.random(100)
end
local sorted = table.copy(t)
table.sort(sorted)
local i = 0
for _, v in ipairs(t) do
core.after(v, function()
i = i + 1
assert.equal(v, sorted[i])
end)
end
do_step(math.max(unpack(t)))
assert.equal(#t, i)
end)
end)

@ -462,4 +462,11 @@ describe("vector", function()
end
end)
it("in_area()", function()
assert.True(vector.in_area(vector.zero(), vector.new(-10, -10, -10), vector.new(10, 10, 10)))
assert.True(vector.in_area(vector.new(-2, 5, -8), vector.new(-10, -10, -10), vector.new(10, 10, 10)))
assert.True(vector.in_area(vector.new(-10, -10, -10), vector.new(-10, -10, -10), vector.new(10, 10, 10)))
assert.False(vector.in_area(vector.new(-10, -10, -10), vector.new(10, 10, 10), vector.new(-11, -10, -10)))
end)
end)

@ -369,6 +369,12 @@ function vector.dir_to_rotation(forward, up)
return rot
end
function vector.in_area(pos, min, max)
return (pos.x >= min.x) and (pos.x <= max.x) and
(pos.y >= min.y) and (pos.y <= max.y) and
(pos.z >= min.z) and (pos.z <= max.z)
end
if rawget(_G, "core") and core.set_read_vector and core.set_push_vector then
local function read_vector(v)
return v.x, v.y, v.z

61
builtin/emerge/env.lua Normal file

@ -0,0 +1,61 @@
-- Reimplementations of some environment function on vmanips, since this is
-- what the emerge environment operates on
-- core.vmanip = <VoxelManip> -- set by C++
function core.set_node(pos, node)
return core.vmanip:set_node_at(pos, node)
end
function core.bulk_set_node(pos_list, node)
local vm = core.vmanip
local set_node_at = vm.set_node_at
for _, pos in ipairs(pos_list) do
if not set_node_at(vm, pos, node) then
return false
end
end
return true
end
core.add_node = core.set_node
-- we don't deal with metadata currently
core.swap_node = core.set_node
function core.remove_node(pos)
return core.vmanip:set_node_at(pos, {name="air"})
end
function core.get_node(pos)
return core.vmanip:get_node_at(pos)
end
function core.get_node_or_nil(pos)
local node = core.vmanip:get_node_at(pos)
return node.name ~= "ignore" and node
end
function core.get_perlin(seed, octaves, persist, spread)
local params
if type(seed) == "table" then
params = table.copy(seed)
else
assert(type(seed) == "number")
params = {
seed = seed,
octaves = octaves,
persist = persist,
spread = {x=spread, y=spread, z=spread},
}
end
params.seed = core.get_seed(params.seed) -- add mapgen seed
return PerlinNoise(params)
end
function core.get_perlin_map(params, size)
local params2 = table.copy(params)
params2.seed = core.get_seed(params.seed) -- add mapgen seed
return PerlinNoiseMap(params2, size)
end

21
builtin/emerge/init.lua Normal file

@ -0,0 +1,21 @@
local gamepath = core.get_builtin_path() .. "game" .. DIR_DELIM
local commonpath = core.get_builtin_path() .. "common" .. DIR_DELIM
local epath = core.get_builtin_path() .. "emerge" .. DIR_DELIM
local builtin_shared = {}
-- Import parts shared with "game" environment
dofile(gamepath .. "constants.lua")
assert(loadfile(commonpath .. "item_s.lua"))(builtin_shared)
dofile(gamepath .. "misc_s.lua")
dofile(gamepath .. "features.lua")
dofile(gamepath .. "voxelarea.lua")
-- Now for our own stuff
assert(loadfile(commonpath .. "register.lua"))(builtin_shared)
assert(loadfile(epath .. "register.lua"))(builtin_shared)
dofile(epath .. "env.lua")
builtin_shared.cache_content_ids()
core.log("info", "Initialized emerge Lua environment")

@ -0,0 +1,54 @@
local builtin_shared = ...
-- Copy all the registration tables over
do
local all = assert(core.transferred_globals)
core.transferred_globals = nil
all.registered_nodes = {}
all.registered_craftitems = {}
all.registered_tools = {}
for k, v in pairs(all.registered_items) do
-- Disable further modification
setmetatable(v, {__newindex = {}})
-- Reassemble the other tables
if v.type == "node" then
getmetatable(v).__index = all.nodedef_default
all.registered_nodes[k] = v
elseif v.type == "craft" then
getmetatable(v).__index = all.craftitemdef_default
all.registered_craftitems[k] = v
elseif v.type == "tool" then
getmetatable(v).__index = all.tooldef_default
all.registered_tools[k] = v
else
getmetatable(v).__index = all.noneitemdef_default
end
end
for k, v in pairs(all) do
core[k] = v
end
end
-- For tables that are indexed by item name:
-- If table[X] does not exist, default to table[core.registered_aliases[X]]
local alias_metatable = {
__index = function(t, name)
return rawget(t, core.registered_aliases[name])
end
}
setmetatable(core.registered_items, alias_metatable)
setmetatable(core.registered_nodes, alias_metatable)
setmetatable(core.registered_craftitems, alias_metatable)
setmetatable(core.registered_tools, alias_metatable)
--
-- Callbacks
--
local make_registration = builtin_shared.make_registration
core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration()
core.registered_on_generateds, core.register_on_generated = make_registration()
core.registered_on_shutdown, core.register_on_shutdown = make_registration()

@ -1,5 +1,6 @@
--Minetest
--Copyright (C) 2014 sapier
--Copyright (C) 2023 Gregor Parzefall
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
@ -16,115 +17,103 @@
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
local function buttonbar_formspec(self)
local BASE_SPACING = 0.1
local function get_scroll_btn_width()
return core.settings:get_bool("enable_touch") and 0.8 or 0.5
end
local function buttonbar_formspec(self)
if self.hidden then
return ""
end
local formspec = string.format("box[%f,%f;%f,%f;%s]",
self.pos.x,self.pos.y ,self.size.x,self.size.y,self.bgcolor)
local formspec = {
"style_type[box;noclip=true]",
string.format("box[%f,%f;%f,%f;%s]", self.pos.x, self.pos.y, self.size.x,
self.size.y, self.bgcolor),
"style_type[box;noclip=false]",
}
for i=self.startbutton,#self.buttons,1 do
local btn_name = self.buttons[i].name
local btn_pos = {}
local btn_size = self.size.y - 2*BASE_SPACING
if self.orientation == "horizontal" then
btn_pos.x = self.pos.x + --base pos
(i - self.startbutton) * self.btn_size + --button offset
self.btn_initial_offset
else
btn_pos.x = self.pos.x + (self.btn_size * 0.05)
end
-- Spacing works like CSS Flexbox with `justify-content: space-evenly;`.
-- `BASE_SPACING` is used as the minimum spacing, like `gap` in CSS Flexbox.
if self.orientation == "vertical" then
btn_pos.y = self.pos.y + --base pos
(i - self.startbutton) * self.btn_size + --button offset
self.btn_initial_offset
else
btn_pos.y = self.pos.y + (self.btn_size * 0.05)
end
-- The number of buttons per page is always calculated as if the scroll
-- buttons were visible.
local avail_space = self.size.x - 2*BASE_SPACING - 2*get_scroll_btn_width()
local btns_per_page = math.floor((avail_space - BASE_SPACING) / (btn_size + BASE_SPACING))
if (self.orientation == "vertical" and
(btn_pos.y + self.btn_size <= self.pos.y + self.size.y)) or
(self.orientation == "horizontal" and
(btn_pos.x + self.btn_size <= self.pos.x + self.size.x)) then
self.num_pages = math.ceil(#self.buttons / btns_per_page)
self.cur_page = math.min(self.cur_page, self.num_pages)
local first_btn = (self.cur_page - 1) * btns_per_page + 1
local borders="true"
local show_scroll_btns = self.num_pages > 1
if self.buttons[i].image ~= nil then
borders="false"
end
-- In contrast, the button spacing calculation takes hidden scroll buttons
-- into account.
local real_avail_space = show_scroll_btns and avail_space or self.size.x
local btn_spacing = (real_avail_space - btns_per_page * btn_size) / (btns_per_page + 1)
formspec = formspec ..
string.format("image_button[%f,%f;%f,%f;%s;%s;%s;true;%s]tooltip[%s;%s]",
btn_pos.x, btn_pos.y, self.btn_size, self.btn_size,
self.buttons[i].image, btn_name, self.buttons[i].caption,
borders, btn_name, self.buttons[i].tooltip)
else
--print("end of displayable buttons: orientation: " .. self.orientation)
--print( "button_end: " .. (btn_pos.y + self.btn_size - (self.btn_size * 0.05)))
--print( "bar_end: " .. (self.pos.x + self.size.x))
local btn_start_x = self.pos.x + btn_spacing
if show_scroll_btns then
btn_start_x = btn_start_x + BASE_SPACING + get_scroll_btn_width()
end
for i = first_btn, first_btn + btns_per_page - 1 do
local btn = self.buttons[i]
if btn == nil then
break
end
local btn_pos = {
x = btn_start_x + (i - first_btn) * (btn_size + btn_spacing),
y = self.pos.y + BASE_SPACING,
}
table.insert(formspec, string.format("image_button[%f,%f;%f,%f;%s;%s;%s;true;false]tooltip[%s;%s]",
btn_pos.x, btn_pos.y, btn_size, btn_size, btn.image, btn.name,
btn.caption, btn.name, btn.tooltip))
end
if (self.have_move_buttons) then
local btn_dec_pos = {}
btn_dec_pos.x = self.pos.x + (self.btn_size * 0.05)
btn_dec_pos.y = self.pos.y + (self.btn_size * 0.05)
local btn_inc_pos = {}
local btn_size = {}
if show_scroll_btns then
local btn_prev_pos = {
x = self.pos.x + BASE_SPACING,
y = self.pos.y + BASE_SPACING,
}
local btn_next_pos = {
x = self.pos.x + self.size.x - BASE_SPACING - get_scroll_btn_width(),
y = self.pos.y + BASE_SPACING,
}
if self.orientation == "horizontal" then
btn_size.x = 0.5
btn_size.y = self.btn_size
btn_inc_pos.x = self.pos.x + self.size.x - 0.5
btn_inc_pos.y = self.pos.y + (self.btn_size * 0.05)
else
btn_size.x = self.btn_size
btn_size.y = 0.5
btn_inc_pos.x = self.pos.x + (self.btn_size * 0.05)
btn_inc_pos.y = self.pos.y + self.size.y - 0.5
end
table.insert(formspec, string.format("style[%s,%s;noclip=true]",
self.btn_prev_name, self.btn_next_name))
local text_dec = "<"
local text_inc = ">"
if self.orientation == "vertical" then
text_dec = "^"
text_inc = "v"
end
table.insert(formspec, string.format("button[%f,%f;%f,%f;%s;<]",
btn_prev_pos.x, btn_prev_pos.y, get_scroll_btn_width(), btn_size,
self.btn_prev_name))
formspec = formspec ..
string.format("image_button[%f,%f;%f,%f;;btnbar_dec_%s;%s;true;true]",
btn_dec_pos.x, btn_dec_pos.y, btn_size.x, btn_size.y,
self.name, text_dec)
formspec = formspec ..
string.format("image_button[%f,%f;%f,%f;;btnbar_inc_%s;%s;true;true]",
btn_inc_pos.x, btn_inc_pos.y, btn_size.x, btn_size.y,
self.name, text_inc)
table.insert(formspec, string.format("button[%f,%f;%f,%f;%s;>]",
btn_next_pos.x, btn_next_pos.y, get_scroll_btn_width(), btn_size,
self.btn_next_name))
end
return formspec
return table.concat(formspec)
end
local function buttonbar_buttonhandler(self, fields)
if fields["btnbar_inc_" .. self.name] ~= nil and
self.startbutton < #self.buttons then
self.startbutton = self.startbutton + 1
if fields[self.btn_prev_name] and self.cur_page > 1 then
self.cur_page = self.cur_page - 1
return true
end
if fields["btnbar_dec_" .. self.name] ~= nil and self.startbutton > 1 then
self.startbutton = self.startbutton - 1
if fields[self.btn_next_name] and self.cur_page < self.num_pages then
self.cur_page = self.cur_page + 1
return true
end
for i=1,#self.buttons,1 do
if fields[self.buttons[i].name] ~= nil then
for _, btn in ipairs(self.buttons) do
if fields[btn.name] then
return self.userbuttonhandler(fields)
end
end
@ -141,74 +130,45 @@ local buttonbar_metatable = {
delete = function(self) ui.delete(self) end,
add_button = function(self, name, caption, image, tooltip)
if caption == nil then caption = "" end
if image == nil then image = "" end
if tooltip == nil then tooltip = "" end
if caption == nil then caption = "" end
if image == nil then image = "" end
if tooltip == nil then tooltip = "" end
self.buttons[#self.buttons + 1] = {
name = name,
caption = caption,
image = image,
tooltip = tooltip
}
if self.orientation == "horizontal" then
if ( (self.btn_size * #self.buttons) + (self.btn_size * 0.05 *2)
> self.size.x ) then
self.btn_initial_offset = self.btn_size * 0.05 + 0.5
self.have_move_buttons = true
end
else
if ((self.btn_size * #self.buttons) + (self.btn_size * 0.05 *2)
> self.size.y ) then
self.btn_initial_offset = self.btn_size * 0.05 + 0.5
self.have_move_buttons = true
end
end
end,
set_bgparams = function(self, bgcolor)
if (type(bgcolor) == "string") then
self.bgcolor = bgcolor
end
end,
table.insert(self.buttons, {
name = name,
caption = caption,
image = image,
tooltip = tooltip,
})
end,
}
buttonbar_metatable.__index = buttonbar_metatable
function buttonbar_create(name, cbf_buttonhandler, pos, orientation, size)
assert(name ~= nil)
assert(cbf_buttonhandler ~= nil)
assert(orientation == "vertical" or orientation == "horizontal")
assert(pos ~= nil and type(pos) == "table")
assert(size ~= nil and type(size) == "table")
function buttonbar_create(name, pos, size, bgcolor, cbf_buttonhandler)
assert(type(name) == "string" )
assert(type(pos) == "table" )
assert(type(size) == "table" )
assert(type(bgcolor) == "string" )
assert(type(cbf_buttonhandler) == "function")
local self = {}
self.name = name
self.type = "addon"
self.bgcolor = "#000000"
self.name = name
self.pos = pos
self.size = size
self.orientation = orientation
self.startbutton = 1
self.have_move_buttons = false
self.hidden = false
if self.orientation == "horizontal" then
self.btn_size = self.size.y
else
self.btn_size = self.size.x
end
if (self.btn_initial_offset == nil) then
self.btn_initial_offset = self.btn_size * 0.05
end
self.bgcolor = bgcolor
self.userbuttonhandler = cbf_buttonhandler
self.buttons = {}
setmetatable(self,buttonbar_metatable)
self.hidden = false
self.buttons = {}
self.num_pages = 1
self.cur_page = 1
self.btn_prev_name = "btnbar_prev_" .. self.name
self.btn_next_name = "btnbar_next_" .. self.name
setmetatable(self, buttonbar_metatable)
ui.add(self)
return self

@ -38,8 +38,18 @@ local dialog_metatable = {
handle_events = function(self,event)
if not self.hidden then return self.eventhandler(self,event) end
end,
hide = function(self) self.hidden = true end,
show = function(self) self.hidden = false end,
hide = function(self)
if not self.hidden then
self.hidden = true
self.eventhandler(self, "DialogHide")
end
end,
show = function(self)
if self.hidden then
self.hidden = false
self.eventhandler(self, "DialogShow")
end
end,
delete = function(self)
if self.parent ~= nil then
self.parent:show()

@ -42,6 +42,7 @@ local function add_tab(self,tab)
event_handler = tab.cbf_events,
get_formspec = tab.cbf_formspec,
tabsize = tab.tabsize,
formspec_version = tab.formspec_version or 6,
on_change = tab.on_change,
tabdata = {},
}
@ -65,13 +66,38 @@ local function get_formspec(self)
local content, prepend = tab.get_formspec(self, tab.name, tab.tabdata, tab.tabsize)
local tsize = tab.tabsize or { width = self.width, height = self.height }
if self.parent == nil and not prepend then
local tsize = tab.tabsize or {width=self.width, height=self.height}
prepend = string.format("size[%f,%f,%s]", tsize.width, tsize.height,
dump(self.fixed_size))
if tab.formspec_version then
prepend = ("formspec_version[%d]"):format(tab.formspec_version) .. prepend
end
end
local end_button_size = 0.75
local tab_header_size = { width = tsize.width, height = 0.85 }
if self.end_button then
tab_header_size.width = tab_header_size.width - end_button_size - 0.1
end
local formspec = (prepend or "") .. self:tab_header(tab_header_size) .. content
if self.end_button then
formspec = formspec ..
("style[%s;noclip=true;border=false]"):format(self.end_button.name) ..
("tooltip[%s;%s]"):format(self.end_button.name, self.end_button.label) ..
("image_button[%f,%f;%f,%f;%s;%s;]"):format(
self.width - end_button_size,
(-tab_header_size.height - end_button_size) / 2,
end_button_size,
end_button_size,
core.formspec_escape(self.end_button.icon),
self.end_button.name)
end
local formspec = (prepend or "") .. self:tab_header() .. content
return formspec
end
@ -86,8 +112,12 @@ local function handle_buttons(self,fields)
return true
end
if self.end_button and fields[self.end_button.name] then
return self.end_button.on_click(self)
end
if self.glb_btn_handler ~= nil and
self.glb_btn_handler(self,fields) then
self.glb_btn_handler(self, fields) then
return true
end
@ -121,20 +151,23 @@ end
--------------------------------------------------------------------------------
local function tab_header(self)
local function tab_header(self, size)
local toadd = ""
for i=1,#self.tablist,1 do
for i = 1, #self.tablist do
if toadd ~= "" then
toadd = toadd .. ","
end
toadd = toadd .. self.tablist[i].caption
local caption = self.tablist[i].caption
if type(caption) == "function" then
caption = caption(self)
end
toadd = toadd .. caption
end
return string.format("tabheader[%f,%f;%s;%s;%i;true;false]",
self.header_x, self.header_y, self.name, toadd, self.last_tab_index);
return string.format("tabheader[%f,%f;%f,%f;%s;%s;%i;true;false]",
self.header_x, self.header_y, size.width, size.height, self.name, toadd, self.last_tab_index)
end
--------------------------------------------------------------------------------
@ -225,6 +258,8 @@ local tabview_metatable = {
function(self,handler) self.glb_evt_handler = handler end,
set_fixed_size =
function(self,state) self.fixed_size = state end,
set_end_button =
function(self, v) self.end_button = v end,
tab_header = tab_header,
handle_tab_buttons = handle_tab_buttons
}

@ -62,8 +62,8 @@ function ui.update()
-- handle errors
if gamedata ~= nil and gamedata.reconnect_requested then
local error_message = core.formspec_escape(
gamedata.errormessage or fgettext("<none available>"))
local error_message = core.formspec_escape(gamedata.errormessage)
or fgettext("<none available>")
formspec = {
"size[14,8]",
"real_coordinates[true]",

@ -87,20 +87,29 @@ core.builtin_auth_handler = {
core.settings:get("default_password")))
end
local prev_privs = auth_entry.privileges
auth_entry.privileges = privileges
core_auth.save(auth_entry)
-- Run grant callbacks
for priv, _ in pairs(privileges) do
if not auth_entry.privileges[priv] then
for priv, value in pairs(privileges) do
-- Warnings for improper API usage
if value == false then
core.log('deprecated', "`false` value given to `minetest.set_player_privs`, "..
"this is almost certainly a bug, "..
"granting a privilege rather than revoking it")
elseif value ~= true then
core.log('deprecated', "non-`true` value given to `minetest.set_player_privs`")
end
-- Run grant callbacks
if prev_privs[priv] == nil then
core.run_priv_callbacks(name, priv, nil, "grant")
end
end
-- Run revoke callbacks
for priv, _ in pairs(auth_entry.privileges) do
if not privileges[priv] then
for priv, _ in pairs(prev_privs) do
if privileges[priv] == nil then
core.run_priv_callbacks(name, priv, nil, "revoke")
end
end
@ -179,6 +188,20 @@ core.set_player_privs = auth_pass("set_privileges")
core.remove_player_auth = auth_pass("delete_auth")
core.auth_reload = auth_pass("reload")
function core.change_player_privs(name, changes)
local privs = core.get_player_privs(name)
for priv, change in pairs(changes) do
if change == true then
privs[priv] = true
elseif change == false then
privs[priv] = nil
else
error("non-bool value given to `minetest.change_player_privs`")
end
end
core.set_player_privs(name, privs)
end
local record_login = auth_pass("record_login")
core.register_on_joinplayer(function(player)
record_login(player:get_player_name())

@ -79,6 +79,9 @@ core.register_entity(":__builtin:falling_node", {
-- Cache whether we're supposed to float on water
self.floats = core.get_item_group(node.name, "float") ~= 0
-- Save liquidtype for falling water
self.liquidtype = def.liquidtype
-- Set entity visuals
if def.drawtype == "torchlike" or def.drawtype == "signlike" then
local textures
@ -150,7 +153,12 @@ core.register_entity(":__builtin:falling_node", {
-- Rotate entity
if def.drawtype == "torchlike" then
self.object:set_yaw(math.pi*0.25)
if (def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted")
and node.param2 % 8 == 7 then
self.object:set_yaw(-math.pi*0.25)
else
self.object:set_yaw(math.pi*0.25)
end
elseif ((node.param2 ~= 0 or def.drawtype == "nodebox" or def.drawtype == "mesh")
and (def.wield_image == "" or def.wield_image == nil))
or def.drawtype == "signlike"
@ -190,6 +198,10 @@ core.register_entity(":__builtin:falling_node", {
pitch, yaw = 0, -math.pi/2
elseif rot == 4 then
pitch, yaw = 0, math.pi
elseif rot == 6 then
pitch, yaw = math.pi/2, 0
elseif rot == 7 then
pitch, yaw = -math.pi/2, math.pi
end
else
if rot == 1 then
@ -202,6 +214,10 @@ core.register_entity(":__builtin:falling_node", {
pitch, yaw = math.pi/2, math.pi
elseif rot == 5 then
pitch, yaw = math.pi/2, 0
elseif rot == 6 then
pitch, yaw = math.pi, -math.pi/2
elseif rot == 7 then
pitch, yaw = 0, -math.pi/2
end
end
if def.drawtype == "signlike" then
@ -210,10 +226,20 @@ core.register_entity(":__builtin:falling_node", {
yaw = yaw + math.pi/2
elseif rot == 1 then
yaw = yaw - math.pi/2
elseif rot == 6 then
yaw = yaw - math.pi/2
pitch = pitch + math.pi
elseif rot == 7 then
yaw = yaw + math.pi/2
pitch = pitch + math.pi
end
elseif def.drawtype == "mesh" or def.drawtype == "normal" or def.drawtype == "nodebox" then
if rot >= 0 and rot <= 1 then
if rot == 0 or rot == 1 then
roll = roll + math.pi
elseif rot == 6 or rot == 7 then
if def.drawtype ~= "normal" then
roll = roll - math.pi/2
end
else
yaw = yaw + math.pi
end
@ -271,9 +297,17 @@ core.register_entity(":__builtin:falling_node", {
end
-- Decide if we're replacing the node or placing on top
-- This condition is very similar to the check in core.check_single_for_falling(p)
local np = vector.copy(bcp)
if bcd and bcd.buildable_to and
(not self.floats or bcd.liquidtype == "none") then
if bcd and bcd.buildable_to
and -- Take "float" group into consideration:
(
-- Fall through non-liquids
not self.floats or bcd.liquidtype == "none" or
-- Only let sources fall through flowing liquids
(self.floats and self.liquidtype ~= "none" and bcd.liquidtype ~= "source")
) then
core.remove_node(bcp)
else
np.y = np.y + 1
@ -284,7 +318,7 @@ core.register_entity(":__builtin:falling_node", {
local nd = core.registered_nodes[n2.name]
-- If it's not air or liquid, remove node and replace it with
-- it's drops
if n2.name ~= "air" and (not nd or nd.liquidtype == "none") then
if n2.name ~= "air" and (not nd or nd.liquidtype ~= "source") then
if nd and nd.buildable_to == false then
nd.on_dig(np, n2, nil)
-- If it's still there, it might be protected
@ -529,16 +563,24 @@ function core.check_single_for_falling(p)
if same and d_bottom.paramtype2 == "leveled" and
core.get_node_level(p_bottom) <
core.get_node_max_level(p_bottom) then
convert_to_falling_node(p, n)
return true
local success, _ = convert_to_falling_node(p, n)
return success
end
local d_falling = core.registered_nodes[n.name]
local do_float = core.get_item_group(n.name, "float") > 0
-- Otherwise only if the bottom node is considered "fall through"
if not same and
(not d_bottom.walkable or d_bottom.buildable_to) and
(core.get_item_group(n.name, "float") == 0 or
d_bottom.liquidtype == "none") then
convert_to_falling_node(p, n)
return true
(not d_bottom.walkable or d_bottom.buildable_to)
and -- Take "float" group into consideration:
(
-- Fall through non-liquids
not do_float or d_bottom.liquidtype == "none" or
-- Only let sources fall through flowing liquids
(do_float and d_falling.liquidtype == "source" and d_bottom.liquidtype ~= "source")
) then
local success, _ = convert_to_falling_node(p, n)
return success
end
end
end

@ -27,6 +27,16 @@ core.features = {
get_light_data_buffer = true,
mod_storage_on_disk = true,
compress_zstd = true,
sound_params_start_time = true,
physics_overrides_v2 = true,
hud_def_type_field = true,
random_state_restore = true,
after_order_expiry_registration = true,
wallmounted_rotate = true,
item_specific_pointabilities = true,
blocking_pointability_type = true,
dynamic_add_media_startup = true,
dynamic_add_media_filepath = true,
}
function core.has_feature(arg)

@ -10,7 +10,8 @@ local builtin_shared = {}
dofile(gamepath .. "constants.lua")
assert(loadfile(commonpath .. "item_s.lua"))(builtin_shared)
assert(loadfile(gamepath .. "item.lua"))(builtin_shared)
dofile(gamepath .. "register.lua")
assert(loadfile(commonpath .. "register.lua"))(builtin_shared)
assert(loadfile(gamepath .. "register.lua"))(builtin_shared)
if core.settings:get_bool("profiler.load") then
profiler = dofile(scriptpath .. "profiler" .. DIR_DELIM .. "init.lua")

@ -202,7 +202,40 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2,
elseif (def.paramtype2 == "wallmounted" or
def.paramtype2 == "colorwallmounted") and not param2 then
local dir = vector.subtract(under, above)
-- If you change this code, also change src/client/game.cpp
newnode.param2 = core.dir_to_wallmounted(dir)
if def.wallmounted_rotate_vertical and
(newnode.param2 == 0 or newnode.param2 == 1) then
local placer_pos = placer and placer:get_pos()
if placer_pos then
local pdir = {
x = above.x - placer_pos.x,
y = dir.y,
z = above.z - placer_pos.z
}
local rotate = false
if def.drawtype == "torchlike" then
if not ((pdir.x < 0 and pdir.z > 0) or
(pdir.x > 0 and pdir.z < 0)) then
rotate = true
end
if pdir.y > 0 then
rotate = not rotate
end
elseif def.drawtype == "signlike" then
if math.abs(pdir.x) < math.abs(pdir.z) then
rotate = true
end
else
if math.abs(pdir.x) > math.abs(pdir.z) then
rotate = true
end
end
if rotate then
newnode.param2 = newnode.param2 + 6
end
end
end
-- Calculate the direction for furnaces and chests and stuff
elseif (def.paramtype2 == "facedir" or
def.paramtype2 == "colorfacedir" or

@ -69,6 +69,7 @@ core.register_entity(":__builtin:item", {
automatic_rotate = math.pi * 0.5 * 0.2 / size,
wield_item = self.itemstring,
glow = glow,
infotext = stack:get_description(),
})
-- cache for usage in on_step
@ -178,6 +179,11 @@ core.register_entity(":__builtin:item", {
return
end
-- Prevent assert when item_entity is attached
if moveresult == nil and self.object:get_attach() then
return
end
if self.force_out then
-- This code runs after the entity got a push from the is_stuck code.
-- It makes sure the entity is entirely outside the solid node

@ -237,8 +237,8 @@ end
core.dynamic_media_callbacks = {}
-- Transfer of certain globals into async environment
-- see builtin/async/game.lua for the other side
-- Transfer of certain globals into seconday Lua environments
-- see builtin/async/game.lua or builtin/emerge/register.lua for the unpacking
local function copy_filtering(t, seen)
if type(t) == "userdata" or type(t) == "function" then
@ -261,6 +261,14 @@ function core.get_globals_to_transfer()
local all = {
registered_items = copy_filtering(core.registered_items),
registered_aliases = core.registered_aliases,
registered_biomes = core.registered_biomes,
registered_ores = core.registered_ores,
registered_decorations = core.registered_decorations,
nodedef_default = copy_filtering(core.nodedef_default),
craftitemdef_default = copy_filtering(core.craftitemdef_default),
tooldef_default = copy_filtering(core.tooldef_default),
noneitemdef_default = copy_filtering(core.noneitemdef_default),
}
return all
end

@ -64,6 +64,13 @@ function core.encode_png(width, height, data, compression)
error("Incorrect type for 'height', expected number, got " .. type(height))
end
if width < 1 then
error("Incorrect value for 'width', must be at least 1")
end
if height < 1 then
error("Incorrect value for 'height', must be at least 1")
end
local expected_byte_count = width * height * 4
if type(data) ~= "table" and type(data) ~= "string" then

@ -1,5 +1,6 @@
-- Minetest: builtin/register.lua
local builtin_shared = ...
local S = core.get_translator("__builtin")
--
@ -420,55 +421,6 @@ function core.override_item(name, redefinition)
register_item_raw(item)
end
do
local default = {mod = "??", name = "??"}
core.callback_origins = setmetatable({}, {
__index = function()
return default
end
})
end
function core.run_callbacks(callbacks, mode, ...)
assert(type(callbacks) == "table")
local cb_len = #callbacks
if cb_len == 0 then
if mode == 2 or mode == 3 then
return true
elseif mode == 4 or mode == 5 then
return false
end
end
local ret = nil
for i = 1, cb_len do
local origin = core.callback_origins[callbacks[i]]
core.set_last_run_mod(origin.mod)
local cb_ret = callbacks[i](...)
if mode == 0 and i == 1 then
ret = cb_ret
elseif mode == 1 and i == cb_len then
ret = cb_ret
elseif mode == 2 then
if not cb_ret or i == 1 then
ret = cb_ret
end
elseif mode == 3 then
if cb_ret then
return cb_ret
end
ret = cb_ret
elseif mode == 4 then
if (cb_ret and not ret) or i == 1 then
ret = cb_ret
end
elseif mode == 5 and cb_ret then
return cb_ret
end
end
return ret
end
function core.run_priv_callbacks(name, priv, caller, method)
local def = core.registered_privileges[priv]
if not def or not def["on_" .. method] or
@ -485,34 +437,6 @@ end
-- Callback registration
--
local function make_registration()
local t = {}
local registerfunc = function(func)
t[#t + 1] = func
core.callback_origins[func] = {
mod = core.get_current_modname() or "??",
name = debug.getinfo(1, "n").name or "??"
}
--local origin = core.callback_origins[func]
--print(origin.name .. ": " .. origin.mod .. " registering cbk " .. tostring(func))
end
return t, registerfunc
end
local function make_registration_reverse()
local t = {}
local registerfunc = function(func)
table.insert(t, 1, func)
core.callback_origins[func] = {
mod = core.get_current_modname() or "??",
name = debug.getinfo(1, "n").name or "??"
}
--local origin = core.callback_origins[func]
--print(origin.name .. ": " .. origin.mod .. " registering cbk " .. tostring(func))
end
return t, registerfunc
end
local function make_registration_wrap(reg_fn_name, clear_fn_name)
local list = {}
@ -600,6 +524,9 @@ core.registered_decorations = make_registration_wrap("register_decoration", "cle
core.unregister_biome = make_wrap_deregistration(core.register_biome,
core.clear_registered_biomes, core.registered_biomes)
local make_registration = builtin_shared.make_registration
local make_registration_reverse = builtin_shared.make_registration_reverse
core.registered_on_chat_messages, core.register_on_chat_message = make_registration()
core.registered_on_chatcommands, core.register_on_chatcommand = make_registration()
core.registered_globalsteps, core.register_globalstep = make_registration()

@ -3,7 +3,7 @@ local enable_damage = core.settings:get_bool("enable_damage")
local bar_definitions = {
hp = {
hud_elem_type = "statbar",
type = "statbar",
position = {x = 0.5, y = 1},
text = "heart.png",
text2 = "heart_gone.png",
@ -14,7 +14,7 @@ local bar_definitions = {
offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 16)},
},
breath = {
hud_elem_type = "statbar",
type = "statbar",
position = {x = 0.5, y = 1},
text = "bubble.png",
text2 = "bubble_gone.png",
@ -24,6 +24,13 @@ local bar_definitions = {
size = {x = 24, y = 24},
offset = {x = 25, y= -(48 + 24 + 16)},
},
minimap = {
type = "minimap",
position = {x = 1, y = 0},
alignment = {x = -1, y = 1},
offset = {x = -10, y = 10},
size = {x = 256 , y = 256},
},
}
local hud_ids = {}
@ -92,6 +99,16 @@ local function update_builtin_statbars(player)
end, name, hud.id_breathbar)
hud.id_breathbar = nil
end
-- Don't add a minimap for clients which already have it hardcoded in C++.
local show_minimap = flags.minimap and
minetest.get_player_information(name).protocol_version >= 44
if show_minimap and not hud.id_minimap then
hud.id_minimap = player:hud_add(bar_definitions.minimap)
elseif not show_minimap and hud.id_minimap then
player:hud_remove(hud.id_minimap)
hud.id_minimap = nil
end
end
local function cleanup_builtin_statbars(player)
@ -138,8 +155,7 @@ local function player_event_handler(player,eventname)
end
function core.hud_replace_builtin(hud_name, definition)
if type(definition) ~= "table" or
definition.hud_elem_type ~= "statbar" then
if type(definition) ~= "table" then
return false
end
@ -175,6 +191,20 @@ function core.hud_replace_builtin(hud_name, definition)
return true
end
if hud_name == "minimap" then
bar_definitions.minimap = definition
for name, ids in pairs(hud_ids) do
local player = core.get_player_by_name(name)
if player and ids.id_minimap then
player:hud_remove(ids.id_minimap)
ids.id_minimap = nil
update_builtin_statbars(player)
end
end
return true
end
return false
end

@ -31,8 +31,6 @@ minetest = core
-- Load other files
local scriptdir = core.get_builtin_path()
local gamepath = scriptdir .. "game" .. DIR_DELIM
local clientpath = scriptdir .. "client" .. DIR_DELIM
local commonpath = scriptdir .. "common" .. DIR_DELIM
local asyncpath = scriptdir .. "async" .. DIR_DELIM
@ -42,7 +40,7 @@ dofile(commonpath .. "serialize.lua")
dofile(commonpath .. "misc_helpers.lua")
if INIT == "game" then
dofile(gamepath .. "init.lua")
dofile(scriptdir .. "game" .. DIR_DELIM .. "init.lua")
assert(not core.get_http_api)
elseif INIT == "mainmenu" then
local mm_script = core.settings:get("main_menu_script")
@ -67,7 +65,9 @@ elseif INIT == "async" then
elseif INIT == "async_game" then
dofile(asyncpath .. "game.lua")
elseif INIT == "client" then
dofile(clientpath .. "init.lua")
dofile(scriptdir .. "client" .. DIR_DELIM .. "init.lua")
elseif INIT == "emerge" then
dofile(scriptdir .. "emerge" .. DIR_DELIM .. "init.lua")
else
error(("Unrecognized builtin initialization type %s!"):format(tostring(INIT)))
end

@ -1,4 +1,22 @@
# textdomain: __builtin
Invalid parameters (see /help @1).=Ungültige Parameter (siehe „/help @1“).
Too many arguments, try using just /help <command>=Zu viele Argumente. Probieren Sie es mit „/help <Befehl>“
Available commands: @1=Verfügbare Befehle: @1
Use '/help <cmd>' to get more information, or '/help all' to list everything.=„/help <Befehl>“ benutzen, um mehr Informationen zu erhalten, oder „/help all“, um alles aufzulisten.
Available commands:=Verfügbare Befehle:
Command not available: @1=Befehl nicht verfügbar: @1
[all | privs | <cmd>] [-t]=[all | privs | <Befehl>] [-t]
Get help for commands or list privileges (-t: output in chat)=Hilfe für Befehle erhalten oder Privilegien auflisten (-t: Ausgabe im Chat)
Available privileges:=Verfügbare Privilegien:
Command=Befehl
Parameters=Parameter
For more information, click on any entry in the list.=Für mehr Informationen klicken Sie auf einen beliebigen Eintrag in der Liste.
Double-click to copy the entry to the chat history.=Doppelklicken, um den Eintrag in die Chathistorie einzufügen.
Command: @1 @2=Befehl: @1 @2
Available commands: (see also: /help <cmd>)=Verfügbare Befehle: (siehe auch: /help <Befehl>)
Close=Schließen
Privilege=Privileg
Description=Beschreibung
Empty command.=Leerer Befehl.
Invalid command: @1=Ungültiger Befehl: @1
Invalid command usage.=Ungültige Befehlsverwendung.
@ -189,30 +207,6 @@ You are already dead.=Sie sind schon tot.
@1 is already dead.=@1 ist bereits tot.
@1 has been killed.=@1 wurde getötet.
Kill player or yourself=Einen Spieler oder Sie selbst töten
Invalid parameters (see /help @1).=Ungültige Parameter (siehe „/help @1“).
Too many arguments, try using just /help <command>=Zu viele Argumente. Probieren Sie es mit „/help <Befehl>“
Available commands: @1=Verfügbare Befehle: @1
Use '/help <cmd>' to get more information, or '/help all' to list everything.=„/help <Befehl>“ benutzen, um mehr Informationen zu erhalten, oder „/help all“, um alles aufzulisten.
Available commands:=Verfügbare Befehle:
Command not available: @1=Befehl nicht verfügbar: @1
[all | privs | <cmd>] [-t]=[all | privs | <Befehl>] [-t]
Get help for commands or list privileges (-t: output in chat)=Hilfe für Befehle erhalten oder Privilegien auflisten (-t: Ausgabe im Chat)
Available privileges:=Verfügbare Privilegien:
Command=Befehl
Parameters=Parameter
For more information, click on any entry in the list.=Für mehr Informationen klicken Sie auf einen beliebigen Eintrag in der Liste.
Double-click to copy the entry to the chat history.=Doppelklicken, um den Eintrag in die Chathistorie einzufügen.
Command: @1 @2=Befehl: @1 @2
Available commands: (see also: /help <cmd>)=Verfügbare Befehle: (siehe auch: /help <Befehl>)
Close=Schließen
Privilege=Privileg
Description=Beschreibung
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<Filter>] | dump [<Filter>] | save [<Format> [<Filter>]]
Handle the profiler and profiling data=Den Profiler und Profilingdaten verwalten
Statistics written to action log.=Statistiken zum Aktionsprotokoll geschrieben.
Statistics were reset.=Statistiken wurden zurückgesetzt.
Usage: @1=Verwendung: @1
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=Format kann entweder „txt“, „csv“, „lua“, „json“ oder „json_pretty“ sein (die Struktur kann sich in Zukunft ändern).
@1 joined the game.=@1 ist dem Spiel beigetreten.
@1 left the game.=@1 hat das Spiel verlassen.
@1 left the game (timed out).=@1 hat das Spiel verlassen (Netzwerkzeitüberschreitung).
@ -239,6 +233,12 @@ Unknown Item=Unbekannter Gegenstand
Air=Luft
Ignore=Ignorieren
You can't place 'ignore' nodes!=Sie können keine „ignore“-Blöcke platzieren!
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<Filter>] | dump [<Filter>] | save [<Format> [<Filter>]] | reset
Handle the profiler and profiling data=Den Profiler und Profilingdaten verwalten
Statistics written to action log.=Statistiken zum Aktionsprotokoll geschrieben.
Statistics were reset.=Statistiken wurden zurückgesetzt.
Usage: @1=Verwendung: @1
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=Format kann entweder „txt“, „csv“, „lua“, „json“ oder „json_pretty“ sein (die Struktur kann sich in Zukunft ändern).
Values below show absolute/relative times spend per server step by the instrumented function.=Die unten angegebenen Werte zeigen absolute/relative Zeitspannen, die je Server-Step von der instrumentierten Funktion in Anspruch genommen wurden.
A total of @1 sample(s) were taken.=Es wurden insgesamt @1 Datenpunkt(e) aufgezeichnet.
The output is limited to '@1'.=Die Ausgabe ist beschränkt auf „@1“.

@ -0,0 +1,246 @@
# textdomain: __builtin
Invalid parameters (see /help @1).=Nevalidaj parametroj (kontrolu /help @1)
Too many arguments, try using just /help <command>=Tro da parametroj, eble provu /help <ordono>
Available commands: @1=Nunaj ordonoj: @1
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Uzu «/help <ordono>» por specifa informo, aŭ «/help all» por listigi ĉion.
Available commands:=Nunaj ordonoj:
Command not available: @1=Ordono neuzebla: @1
[all | privs | <cmd>] [-t]=[all | privs | <ordono>]
Get help for commands or list privileges (-t: output in chat)=Listigi helpon pri ordonoj («all» aŭ <ordono>) aŭ rajtoj («privs») (kun -t: presu en babilejo)
Available privileges:=Nunaj rajtoj:
Command=Ordono
Parameters=Parametroj
For more information, click on any entry in the list.=Por pliaj informoj, klaku ajnan listeron.
Double-click to copy the entry to the chat history.=Dufoje-klaku por kopii listeron al la babilejo.
Command: @1 @2=Ordono: @1 @2
Available commands: (see also: /help <cmd>)=Nunaj ordonoj: (vidu ankaŭ: /help <ordono>)
Close=Fermi
Privilege=Rajto
Description=Priskribo
Empty command.=Malplena ordono.
Invalid command: @1=Nevalida ordono: @1
Invalid command usage.=Nevalida ordonouzo.
(@1 s)= (@1 s)
Command execution took @1 s=Ruliĝo de ordono postulis @1 s
You don't have permission to run this command (missing privileges: @1).=Vi ne rajtas uzi tiun ĉi ordonon (mankataj rajtoj: @1)
Unable to get position of player @1.=Ne povis akiri pozicion de ludanto @1.
Incorrect area format. Expected: (x1,y1,z1) (x2,y2,z2)=Nevalida loko-formo. Atendis: (x1,y1,z1) (x2,y2,z2)
<action>=<ago>
Show chat action (e.g., '/me orders a pizza' displays '<player name> orders a pizza')=Roli agon en la babilejo (ekz., «/me mendas picon» montras «<ludanto> mendas picon»)
Show the name of the server owner=Montri nomon de la serviladministranto
The administrator of this server is @1.=La adminstranto de tiu ĉi servilo estas @1.
There's no administrator named in the config file.=Estas neniu agordita administranto en la agordodosiero.
@1 does not have any privileges.=@1 havas neniun rajton.
Privileges of @1: @2=Rajtoj de @1: @2
[<name>]=[<nomo>]
Show privileges of yourself or another player=Montri rajtojn de vi aŭ alia ludanto
Player @1 does not exist.=Ludanto @1 ne ekzistas.
<privilege>=<rajto>
Return list of all online players with privilege=Listi ĉeretan ludanton kun specifa rajto
Invalid parameters (see /help haspriv).=Nevalidaj parametroj (vidu /help haspriv).
Unknown privilege!=Nekonata rajto!
No online player has the "@1" privilege.=Neniu ĉeretulo havas la rajton «@1».
Players online with the "@1" privilege: @2=Ĉeretuloj kun la rajto «@1»: @2
Your privileges are insufficient.=Viaj rajtoj ne sufiĉas.
Your privileges are insufficient. '@1' only allows you to grant: @2=Viaj rajtoj ne sufiĉas. «@1» sole lasas vin doni: @2
Unknown privilege: @1=Nekonata rajto: @1
@1 granted you privileges: @2=@1 donis al vi rajtojn: @2
<name> (<privilege> [, <privilege2> [<...>]] | all)=<nomo> (<rajto> [, <rajto2> [<...>]] | all)
Give privileges to player=Doni rajtojn al ludanto, aŭ aparte, aŭ ĉiun kune («all»)
Invalid parameters (see /help grant).=Nevalidaj parametroj (vidu /help grant).
<privilege> [, <privilege2> [<...>]] | all=<rajto> [, <rajto2> [<...>]] | all
Grant privileges to yourself=Doni rajtojn al vi mem, aŭ aparte, aŭ ĉiun kune («all»)
Invalid parameters (see /help grantme).=Nevalidaj parametroj (vidu /help grantme)
Your privileges are insufficient. '@1' only allows you to revoke: @2=Viaj rajtoj ne sufiĉas. «@1» sole lasas vin repreni: @2
Note: Cannot revoke in singleplayer: @1=Rimarko: Neeblas repreni rajton sole: @1
Note: Cannot revoke from admin: @1=Rimarko: Neeblas repreni rajnton de administranto: @1
No privileges were revoked.=Neniu rajto reprenita.
@1 revoked privileges from you: @2=@1 reprenis rajtojn de vi: @2
Remove privileges from player=Repreni rajtojn de ludanto, aŭ aparte, aŭ ĉiun kune («all»)
Invalid parameters (see /help revoke).=Nevalidaj parametroj (vidu /help revoke)
Revoke privileges from yourself=Repreni rajtojn de vi mem, aŭ aparte, aŭ ĉiun kune («all»)
Invalid parameters (see /help revokeme).=Nevalidaj parametroj (vidu /help revokeme).
<name> <password>=<nomo> <pasvorto>
Set player's password (sent unencrypted, thus insecure)=Agordi pasvorton de ludanto (sendute senĉifre, kaj tiel malsekure)
Name field required.=Nomo bezonata.
Your password was cleared by @1.=Via pasvorto forviŝiĝis de @1.
Password of player "@1" cleared.=Pasvorto de ludanto «@1» forviŝita.
Your password was set by @1.=Via pasvorto agordiĝis de @1.
Password of player "@1" set.=Pasvorto de ludanto «@1» agordita.
<name>=<nomo>
Set empty password for a player=Forviŝi pasvorton de ludanto
Reload authentication data=Reenlegi aŭtentigajn datumojn
Done.=Finite.
Failed.=Malsukcese..
Remove a player's data=Forviŝi datumojn de ludanto
Player "@1" removed.=Ludanto «@1» forigita.
No such player "@1" to remove.=Neniu ekzistanta ludanto «@1» forigebla.
Player "@1" is connected, cannot remove.=Ludanto «@1» ĉeretas, do ne forigeblas.
Unhandled remove_player return code @1.=Netraktata remove_player redonkodo @1.
Cannot teleport out of map bounds!=Neeblas teleporti ekstermonden!
Cannot get player with name @1.=Neeblas trovi ludanton nomitan «@1».
Cannot teleport, @1 is attached to an object!=Ne povas teleporti @1, ĝi estas ligita al objekto!
Teleporting @1 to @2.=Teleportante @1 al @2.
One does not teleport to oneself.=Oni kutimas ne teleportu al si mem.
Cannot get teleportee with name @1.=Ne trovis alteleportaton nomitan @1.
Cannot get target player with name @1.=Ne trovis celatan ludanton nomitan @1.
Teleporting @1 to @2 at @3.=Teleportante @1 al @2 ĉe @3.
<X>,<Y>,<Z> | <to_name> | <name> <X>,<Y>,<Z> | <name> <to_name>=<X>,<Y>,<Z> | <al_nomo> | <nomo> <X>,<Y>,<Z> | <nomo> <al_nomo>
Teleport to position or player=Teleportiĝi al pozicio aŭ ludanto
You don't have permission to teleport other players (missing privilege: @1).=Vi ne rajtas teleporti aliajn ludantojn (mankas rajto: @1).
([-n] <name> <value>) | <name>=([-n] <nomo> <valoro>) | <nomo>
Set or read server configuration setting=Agordi aŭ vidi servilan agordon
Failed. Cannot modify secure settings. Edit the settings file manually.=Malsukcese. Neeblas redakti sekurajn agordojn. Redaktu la agordodosieron permane.
Failed. Use '/set -n <name> <value>' to create a new setting.=Malsukcese. Uzu «/set -n <nomo> <valoro>» por krei novan agordon.
@1 @= @2=@1 @= @2
<not set>=<ne agordita>
Invalid parameters (see /help set).=Nevalidaj parametroj (vidu /help set).
Finished emerging @1 blocks in @2ms.=Finenlegis @1 monderojn dum @2ms.
emergeblocks update: @1/@2 blocks emerged (@3%)=emergeblocks ĝisdatigo: @1/@2 monderoj enlegitaj (@3%)
(here [<radius>]) | (<pos1> <pos2>)=(here [<radius>]) | (<pos1> <pos2>)
Load (or, if nonexistent, generate) map blocks contained in area pos1 to pos2 (<pos1> and <pos2> must be in parentheses)=Enlegi (aŭ, laŭnecese, krei) mondopecojn inter la pozicioj poz1 kaj pos2 (<poz1> kaj <poz2> devas esti inter krampoj)
Started emerge of area ranging from @1 to @2.=Ekenlegis mondpecojn inter @1 kaj @2.
Delete map blocks contained in area pos1 to pos2 (<pos1> and <pos2> must be in parentheses)=Forigi mondpecojn inter la pozicioj poz1 kaj poz2 (<poz1> kaj <poz2> devas esti inter krampoj)
Successfully cleared area ranging from @1 to @2.=Sukcese forigis mondpecojn inter @1 kaj @2.
Failed to clear one or more blocks in area.=Malsukcesis forigi unu aŭ pli da mondpecoj.
Resets lighting in the area between pos1 and pos2 (<pos1> and <pos2> must be in parentheses)=Reŝarĝas lumojn inter la pozicioj poz1 kaj poz2 (<poz1> kaj <poz2> devas esti inter krampoj)
Successfully reset light in the area ranging from @1 to @2.=Sukcesis reŝarĝi lumojn inter @1 kaj @2.
Failed to load one or more blocks in area.=Malsukcesis enlegante unu aŭ pli da monderoj.
List mods installed on the server=Listigi modifaĵojn instalitajn de la servilo.
No mods installed.=Neniu modifaĵ instalita.
Cannot give an empty item.=Neeblas doni neniun portaĵon.
Cannot give an unknown item.=Neeblas doni nekonatan portaĵon.
Giving 'ignore' is not allowed.=Doni «ignore» estas ne permesita.
@1 is not a known player.=@1 ne estas konata ludanto.
@1 partially added to inventory.=@1 parte enmetiĝis al portaĵujon.
@1 could not be added to inventory.=@1 ne enmetiĝis al portaĵujon.
@1 added to inventory.=@1 enmetiĝis al portaĵujon.
@1 partially added to inventory of @2.=@1 parte enmetiĝis al portaĵujon de @2.
@1 could not be added to inventory of @2.=@1 ne enmetiĝis al portaĵujon de @2.
@1 added to inventory of @2.=@1 enmetiĝis al portaĵujon de @2.
<name> <ItemString> [<count> [<wear>]]=<nomo> <PortaĵNomo> [<kvanto> <portu>]]
Give item to player=Doni portaĵon al ludanto
Name and ItemString required.=Nomo kaj PortaĵNomo postualtaj.
<ItemString> [<count> [<wear>]]=<PortaĵNomo> [<kvanto> [<portu>]]
Give item to yourself=Doni portaĵon al vi mem.
ItemString required.=PortaĵNomo postulata.
<EntityName> [<X>,<Y>,<Z>]=<EstaĵoNomo> [<X>, <Y>, <Z>]
Spawn entity at given (or your) position=Estigi estaĵon ĉe la donita (aŭ la via) pozicio
EntityName required.=EstaĵNomo postulata.
Unable to spawn entity, player is nil.=Ne povis estigi estaĵon, ĉar ludanto estas nil.
Cannot spawn an unknown entity.=Neeblas estigi nekonatan estaĵon.
Invalid parameters (@1).=Nevalidaj parametroj (@1).
@1 spawned.=@1 naskiĝis.
@1 failed to spawn.=@1 ne naskiĝis.
Destroy item in hand=Detrui portaĵon enmanan
Unable to pulverize, no player.=Ne povis detrui, neniu ludanto.
Unable to pulverize, no item in hand.=Ne povis detrui, ĉar nenio estas tenata.
An item was pulverized.=Portaĵo detruiĝis.
[<range>] [<seconds>] [<limit>]=[<intertempo>] [<sekundoj>] [<limo>]
Check who last touched a node or a node near it within the time specified by <seconds>. Default: range @= 0, seconds @= 86400 @= 24h, limit @= 5. Set <seconds> to inf for no time limit=Kontroli kiu lastafoje tuŝis monderon aŭ monderon proksiman al ĝi dum
Rollback functions are disabled.=Malfaraj funkcioj estas malŝaltitaj.
That limit is too high!=Tiu limo troaltas!
Checking @1 ...=Kontrolas @1…
Nobody has touched the specified location in @1 seconds.=Neniu tuŝis tiun lokon dum @1 sekundoj.
@1 @2 @3 -> @4 @5 seconds ago.=@1 @2 @3 -> @4 @5 sekundoj antaŭe.
Punch a node (range@=@1, seconds@=@2, limit@=@3).=Frapi monderon (intertempo@=@1, sekundoj@=@2, limo@=@3).
(<name> [<seconds>]) | (:<actor> [<seconds>])=(<nomo> [<sekundoj>]) | (:<aganto> [<sekundoj>])
Revert actions of a player. Default for <seconds> is 60. Set <seconds> to inf for no time limit=Malfari agojn de ludanto. Implicita valoro de <sekundoj> estas 60. Metu <sekundoj> kiel «inf» por neniu tempolimo
Invalid parameters. See /help rollback and /help rollback_check.=Nevalidaj parametroj. Vidu /help rollback kaj /help rollback_check.
Reverting actions of player '@1' since @2 seconds.=Malfaras agojn de ludanto «@1» ekde @2 sekundoj antaŭ nun.
Reverting actions of @1 since @2 seconds.=Malfaras agojn de @1 ekde @2 sekundoj antaŭ nun.
(log is too long to show)=(protokolo trolongas por montri)
Reverting actions succeeded.=Sukcesis malfari agojn.
Reverting actions FAILED.=MALsukcesis malfari agojn.
Show server status=Montri staton de servilon.
This command was disabled by a mod or game.=Tiun ordonon malŝaltis modifaĵo aŭ ludo.
[<0..23>:<0..59> | <0..24000>]=[<0..23>:<0..59> | <0..24000>]
Show or set time of day=Montri aŭ ŝanĝi la horon
Current time is @1:@2.=Nun estas @1:@2.
You don't have permission to run this command (missing privilege: @1).=Vi ne rajtas rulu tiun orodnon (mankata rajto: @1).
Invalid time (must be between 0 and 24000).=Nevalida tempo (estu inter 0 kaj 24000)
Time of day changed.=Horo ŝanĝita.
Invalid hour (must be between 0 and 23 inclusive).=Nevalida horo (estu inter 0 kaj 23, inkluzive).
Invalid minute (must be between 0 and 59 inclusive).=Nevalida minuto (estu inter 0 kaj 59, inkluzive).
Show day count since world creation=Montri pasitajn tagojn ekde mondokreo
Current day is @1.=Nuna tago estas @1.
[<delay_in_seconds> | -1] [-r] [<message>]=[<prokrasto_sekunde> | -1] [-r] [<mesaĝo>]
Shutdown server (-1 cancels a delayed shutdown, -r allows players to reconnect)=Malŝalti servilon (-1 nuligas planitan malŝalton, -r lasas ludantojn rekonektiĝi)
Server shutting down (operator request).=Servilo malŝaltiĝas (laŭ prizorganta peto)
Ban the IP of a player or show the ban list=Forbari la IP-adreson de ludanto, aŭ listigi forbaritojn
The ban list is empty.=La forbaritolisto malplenas.
Ban list: @1=Forbaritoj: @1
You cannot ban players in singleplayer!=Vi ne povas forbari ludantojn en unuludanta reĝimo!
Player is not online.=Ludanto ne ĉeretas.
Failed to ban player.=Malsukcesis forbari ludanton.
Banned @1.=Forbaris @1.
<name> | <IP_address>=<nomo> | <IP_adreso>
Remove IP ban belonging to a player/IP=Nuligi IP-forbaron de ludanto/IP
Failed to unban player/IP.=Malsukcesis nuligi forbaron de ludanto/IP-adreso
Unbanned @1.=Malforbaris @1.
<name> [<reason>]=<nomo> [<kialo>]
Kick a player=Elĵeti ludanton
Failed to kick player @1.=Malsukcesis elĵeti ludanton @1.
Kicked @1.=Elĵetis @1.
[full | quick]=[full | quick]
Clear all objects in world=Forigu ĉiujn lasitajn portaĵojn en la mondo
Invalid usage, see /help clearobjects.=Nevalida uzo, vidu /help clearobjects.
Clearing all objects. This may take a long time. You may experience a timeout. (by @1)=Forigante ĉiun lasitan portaĵon. Tio ĉi eble postulos longan tempon. Vi eble malkonektiĝos pro tempo-elĉerpo. (de @1)
Cleared all objects.=Forigis ĉiun lasitan portaĵon.
<name> <message>=<nomo> <mesaĝo>
Send a direct message to a player=Sendu rekte privatan mesaĝon al ludanto
Invalid usage, see /help msg.=Nevalida uzo, vidu /help msg.
The player @1 is not online.=La ludanto @1 ne ĉeretas.
DM from @1: @2=Privata mesaĝo de @1: @2
Message sent.=Mesaĝo sendita.
Get the last login time of a player or yourself=Vidi la lastan salutotempon de ludanto, aŭ vi mem
@1's last login time was @2.=Lasta salutotempo de @1 estas @2.
@1's last login time is unknown.=Lasta salutotempo de @1 estas nesciata.
Clear the inventory of yourself or another player=Malplenigi la portaĵujon de vi aŭ alia ludanto.
You don't have permission to clear another player's inventory (missing privilege: @1).=Vi ne rajtas malplenigi portaĵujon de alia ludanto (mankata rajto: @1).
@1 cleared your inventory.=@1 malplenigis vian portaĵujon.
Cleared @1's inventory.=Malplenigis portaĵujon de @1.
Player must be online to clear inventory!=Por malplenigi onian portaĵujon, tiu devas ĉereti!
Players can't be killed, damage has been disabled.=Nemortigeblas ludantoj, ĉar vundado estas malŝaltita.
Player @1 is not online.=Ludanto @1 ne ĉeretas.
You are already dead.=Vi jam estas mortinta.
@1 is already dead.=@1 estas mortinta.
@1 has been killed.=@1 estas murdita.
Kill player or yourself=Mortigi ludanton aŭ vin mem
@1 joined the game.=@1 aliĝis la ludon.
@1 left the game.=@1 foriris de la ludo.
@1 left the game (timed out).=@1 foriris de la ludo (tempo-elĉerpo)
(no description)=(neniu priskribo)
Can interact with things and modify the world=Povas interfaci kaj redakti la mondon
Can speak in chat=Povas paroli babileje
Can modify basic privileges (@1)=Povas redakti bazajn rajtojn (@1)
Can modify privileges=Povas redakti rajtojn
Can teleport self=Povas teleporti sin
Can teleport other players=Povas teleporti aliajn ludantojn
Can set the time of day using /time=Povas ŝanĝi la tempon per /time
Can do server maintenance stuff=Povas fari servilestrajn aferojn
Can bypass node protection in the world=Povas malatenti monderajn protektojn de la mondo
Can ban and unban players=Povas forbari kaj malforbari ludantojn
Can kick players=Povas elĵeti ludantojn
Can use /give and /giveme=Povas uzi /give kaj /giveme
Can use /setpassword and /clearpassword=Povas uzi /setpassword kaj /clearpassword
Can use fly mode=Povas ŝalti flugan reĝimon
Can use fast mode=Povas ŝalti rapidegan reĝimon
Can fly through solid nodes using noclip mode=Povas traflugi monderojn per trapasa reĝimo
Can use the rollback functionality=Povas uzi malfarajn funkciojn
Can enable wireframe=Povas ŝalti ĉirkaŭkadron
Unknown Item=Nekonata portaĵo
Air=Aero
Ignore=Malatenti
You can't place 'ignore' nodes!=Vi ne povas meti «malatentajn» monderojn!
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<filtrilo>] | dump [<filtrilo>] | save [<formo> [<filtrilo>]] | reset
Handle the profiler and profiling data=Trakti la analizilon kaj analizajn datumojn
Statistics written to action log.=Analizoj skribitaj al agoprotokolo.
Statistics were reset.=Analizoj forviŝitaj.
Usage: @1=Uzado: @1
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=Formo povas estas txt, csv, lua, json, aŭ json_pretty (struktuoj eble iam ŝanĝiĝos).
Values below show absolute/relative times spend per server step by the instrumented function.=Valoroj subaj montras la malrelativan/relativan tempon pasigitan de la servilo je ĉiu paŝo de la funkcio.
A total of @1 sample(s) were taken.=Sume @1 ekzemplero(j) konserviĝis.
The output is limited to '@1'.=La eligo estas limigita al «@1».
Saving of profile failed: @1=Konservado de profilo malsukcesis: @1
Profile saved to @1=Profilo konservita al @1

@ -1,9 +1,27 @@
# textdomain: __builtin
Invalid parameters (see /help @1).=Parameter tidak sah (lihat /help @1).
Too many arguments, try using just /help <command>=Terlalu banyak argumen. Coba hanya gunakan /help <perintah>
Available commands: @1=Perintah yang tersedia: @1
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Gunakan '/help <perintah>' untuk informasi lebih lanjut atau '/help all' untuk melihat daftar semuanya.
Available commands:=Perintah yang tersedia:
Command not available: @1=Perintah tidak tersedia: @1
[all | privs | <cmd>] [-t]=[all | privs | <perintah>] [-t]
Get help for commands or list privileges (-t: output in chat)=Ambil bantuan untuk perintah atau daftar hak (-t: keluaran di obrolan)
Available privileges:=Hak yang ada:
Command=Perintah
Parameters=Parameter
For more information, click on any entry in the list.=Untuk informasi lebih lanjut, klik pada entri apa pun dalam daftar.
Double-click to copy the entry to the chat history.=Klik ganda untuk menyalin entri ke riwayat obrolan.
Command: @1 @2=Perintah: @1 @2
Available commands: (see also: /help <cmd>)=Perintah yang tersedia: (lihat juga: /help <perintah>)
Close=Tutup
Privilege=Hak
Description=Deskripsi
Empty command.=Perintah kosong.
Invalid command: @1=Perintah tidak sah: @1
Invalid command usage.=Penggunaan perintah tidak sah.
(@1 s)= (@1 detik)
Command execution took @1 s=Pelaksanaan perintah memerlukan @1 detik
(@1 s)= (@1 detik)
Command execution took @1 s=Pelaksanaan perintah memerlukan @1 detik
You don't have permission to run this command (missing privileges: @1).=Anda tidak memiliki izin untuk menjalankan perintah ini (hak tidak ada: @1).
Unable to get position of player @1.=Tidak dapat mengambil letak pemain @1.
Incorrect area format. Expected: (x1,y1,z1) (x2,y2,z2)=Format daerah tidak sah. Diharapkan: (x1,y1,z1) (x2,y2,z2)
@ -189,30 +207,6 @@ You are already dead.=Anda telah mati.
@1 is already dead.=@1 telah mati.
@1 has been killed.=@1 telah dibunuh.
Kill player or yourself=Bunuh pemain atau diri Anda
Invalid parameters (see /help @1).=Parameter tidak sah (lihat /help @1).
Too many arguments, try using just /help <command>=Terlalu banyak argumen. Coba hanya gunakan /help <perintah>
Available commands: @1=Perintah yang tersedia: @1
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Gunakan '/help <perintah>' untuk informasi lebih lanjut atau '/help all' untuk melihat daftar semuanya.
Available commands:=Perintah yang tersedia:
Command not available: @1=Perintah tidak tersedia: @1
[all | privs | <cmd>] [-t]=[all | privs | <perintah>] [-t]
Get help for commands or list privileges (-t: output in chat)=Ambil bantuan untuk perintah atau daftar hak (-t: keluaran di obrolan)
Available privileges:=Hak yang ada:
Command=Perintah
Parameters=Parameter
For more information, click on any entry in the list.=Untuk informasi lebih lanjut, klik pada entri apa pun dalam daftar.
Double-click to copy the entry to the chat history.=Klik ganda untuk menyalin entri ke riwayat obrolan.
Command: @1 @2=Perintah: @1 @2
Available commands: (see also: /help <cmd>)=Perintah yang tersedia: (lihat juga: /help <perintah>)
Close=Tutup
Privilege=Hak
Description=Deskripsi
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset
Handle the profiler and profiling data=Menangani profiler dan data profiling
Statistics written to action log.=Statistik ditulis ke log action.
Statistics were reset.=Statistik diatur ulang.
Usage: @1=Penggunaan: @1
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=Format berupa salah satu dari txt, csv, lua, json, json_pretty (struktur mungkin berubah).
@1 joined the game.=@1 bergabung dalam permainan.
@1 left the game.=@1 keluar permainan.
@1 left the game (timed out).=@1 keluar permainan (kehabisan waktu).
@ -239,6 +233,12 @@ Unknown Item=Barang Tak Diketahui
Air=Udara
Ignore=Ignore
You can't place 'ignore' nodes!=Anda tidak dapat menaruh nodus 'ignore'!
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset
Handle the profiler and profiling data=Menangani profiler dan data profiling
Statistics written to action log.=Statistik ditulis ke log action.
Statistics were reset.=Statistik diatur ulang.
Usage: @1=Penggunaan: @1
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=Format berupa salah satu dari txt, csv, lua, json, json_pretty (struktur mungkin berubah).
Values below show absolute/relative times spend per server step by the instrumented function.=Nilai berikut menampilkan waktu mutlak/relatif yang dihabiskan tiap langkah server oleh fungsi instrumen.
A total of @1 sample(s) were taken.=Total @1 sampel yang diambil.
The output is limited to '@1'.=Keluaran dibatasi ke '@1'.

@ -1,78 +1,97 @@
# textdomain: __builtin
# note for transaltors: sono state seguite le norme di https://italianoinclusivo.it/ a parte per il suffisso -tore -trice, con l'intento di trasformarlo in un epiceno
Invalid parameters (see /help @1).=Parametri non validi (vedi /help @1)
Too many arguments, try using just /help <command>=Troppi argomenti, prova a usare /help <comando>
Available commands: @1=Comandi disponibili: @1
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Usa '/help <comando>' per ottenere più informazioni, o '/help all' per elencare tutti i comandi.
Available commands:=Comandi disponibili:
Command not available: @1=Comando non disponibile: @1
[all | privs | <cmd>] [-t]=[all | privs | <comando>] [-t]
Get help for commands or list privileges (-t: output in chat)=Richiama la finestra d'aiuto dei comandi o dei privilegi (-t: mostra in chat)
Available privileges:=Privilegi disponibili:
Command=Comando
Parameters=Parametri
For more information, click on any entry in the list.=Per più informazioni, clicca su una qualsiasi voce dell'elenco.
Double-click to copy the entry to the chat history.=Doppio clic per copiare la voce nella cronologia della chat.
Command: @1 @2=Comando: @1 @2
Available commands: (see also: /help <cmd>)=Comandi disponibili: (vedi anche /help <comando>)
Close=Chiudi
Privilege=Privilegio
Description=Descrizione
Empty command.=Comando vuoto.
Invalid command: @1=Comando non valido: @1
Invalid command usage.=Utilizzo del comando non valido.
(@1 s)=
Command execution took @1 s=
(@1 s)= (@1 s)
Command execution took @1 s=L'esecuzione del comando ha richiesto @1 s
You don't have permission to run this command (missing privileges: @1).=Non hai il permesso di eseguire questo comando (privilegi mancanti: @1).
Unable to get position of player @1.=Impossibile ottenere la posizione del giocatore @1.
Unable to get position of player @1.=Impossibile ottenere la posizione del giocatore @1.
Incorrect area format. Expected: (x1,y1,z1) (x2,y2,z2)=Formato dell'area non corretto. Richiesto: (x1,y1,z1) (x2,y2,z2)
<action>=<azione>
Show chat action (e.g., '/me orders a pizza' displays '<player name> orders a pizza')=Mostra un'azione in chat (es. `/me ordina una pizza` mostra `<nome giocatore> ordina una pizza`)
Show the name of the server owner=Mostra il nome del proprietario del server
Show the name of the server owner=Mostra il nome di chi possiede il server
The administrator of this server is @1.=L'amministratore di questo server è @1.
There's no administrator named in the config file.=Non c'è nessun amministratore nel file di configurazione.
@1 does not have any privileges.=
There's no administrator named in the config file.=Non c'è nessun*amministratore nel file di configurazione.
@1 does not have any privileges.=@1 non ha nessun privilegio
Privileges of @1: @2=Privilegi di @1: @2
[<name>]=[<nome>]
Show privileges of yourself or another player=Mostra i privilegi propri o di un altro giocatore
Player @1 does not exist.=Il giocatore @1 non esiste.
Show privileges of yourself or another player=Mostra i privilegi propri o di un*altrə giocatore
Player @1 does not exist.= giocatore @1 non esiste.
<privilege>=<privilegio>
Return list of all online players with privilege=Ritorna una lista di tutti i giocatori connessi col tale privilegio
Return list of all online players with privilege=Ritorna una lista di tuttɜ lɜ giocatori connessɜ col tale privilegio
Invalid parameters (see /help haspriv).=Parametri non validi (vedi /help haspriv).
Unknown privilege!=Privilegio sconosciuto!
No online player has the "@1" privilege.=
Players online with the "@1" privilege: @2=Giocatori connessi con il privilegio "@1": @2
No online player has the "@1" privilege.=Nessunə giocatore ha il privilegio "@1".
Players online with the "@1" privilege: @2=Giocatori connessɜ con il privilegio "@1": @2
Your privileges are insufficient.=I tuoi privilegi sono insufficienti.
Your privileges are insufficient. '@1' only allows you to grant: @2=
Your privileges are insufficient. '@1' only allows you to grant: @2=I tuoi privilegi sono insufficienti. '@1' ti permette soltanto di elargire: @2
Unknown privilege: @1=Privilegio sconosciuto: @1
@1 granted you privileges: @2=@1 ti ha assegnato i seguenti privilegi: @2
<name> (<privilege> [, <privilege2> [<...>]] | all)=
Give privileges to player=Dà privilegi al giocatore
<name> (<privilege> [, <privilege2> [<...>]] | all)=<nome> (<privilegio> [, <privilegio2> [<...>]] | all)
Give privileges to player=Dà privilegi al giocatore
Invalid parameters (see /help grant).=Parametri non validi (vedi /help grant).
<privilege> [, <privilege2> [<...>]] | all=
<privilege> [, <privilege2> [<...>]] | all=<privilegio> [, <privilegio2> [<...>]] | all
Grant privileges to yourself=Assegna dei privilegi a te stessǝ
Invalid parameters (see /help grantme).=Parametri non validi (vedi /help grantme).
Your privileges are insufficient. '@1' only allows you to revoke: @2=
Note: Cannot revoke in singleplayer: @1=
Note: Cannot revoke from admin: @1=
No privileges were revoked.=
Your privileges are insufficient. '@1' only allows you to revoke: @2=I tuoi privilegi sono insufficienti. '@1' ti permette soltanto di revocare: @2
Note: Cannot revoke in singleplayer: @1=N.B.: Non si può revocare in locale: @1
Note: Cannot revoke from admin: @1=N.B.: Non si può revocare a un*amministratore: @1
No privileges were revoked.=Nessun privilegio è stato revocato.
@1 revoked privileges from you: @2=@1 ti ha revocato i seguenti privilegi: @2
Remove privileges from player=Rimuove privilegi dal giocatore
Remove privileges from player=Rimuove privilegi dal giocatore
Invalid parameters (see /help revoke).=Parametri non validi (vedi /help revoke).
Revoke privileges from yourself=Revoca privilegi a te stessǝ
Invalid parameters (see /help revokeme).=Parametri non validi (vedi /help revokeme).
<name> <password>=<nome> <password>
Set player's password (sent unencrypted, thus insecure)=
Set player's password (sent unencrypted, thus insecure)=Imposta la password dellə giocatore (non crittografata, ergo insicura)
Name field required.=Campo "nome" richiesto.
Your password was cleared by @1.=La tua password è stata resettata da @1.
Password of player "@1" cleared.=Password del giocatore "@1" resettata.
Password of player "@1" cleared.=Password del giocatore "@1" resettata.
Your password was set by @1.=La tua password è stata impostata da @1.
Password of player "@1" set.=Password del giocatore "@1" impostata.
Password of player "@1" set.=Password del giocatore "@1" impostata.
<name>=<nome>
Set empty password for a player=Imposta una password vuota a un giocatore
Reload authentication data=Ricarica i dati d'autenticazione
Done.=Fatto.
Failed.=Errore.
Remove a player's data=Rimuove i dati di un giocatore
Remove a player's data=Rimuove i dati di unə giocatore
Player "@1" removed.=Giocatore "@1" rimosso.
No such player "@1" to remove.=Non è presente nessun giocatore "@1" da rimuovere.
Player "@1" is connected, cannot remove.=Il giocatore "@1" è connesso, non può essere rimosso.
No such player "@1" to remove.=Non è presente nessunə giocatore "@1" da rimuovere.
Player "@1" is connected, cannot remove.=Lə giocatore "@1" è connessə, non può essere rimossə.
Unhandled remove_player return code @1.=Codice ritornato da remove_player non gestito (@1).
Cannot teleport out of map bounds!=Non ci si può teletrasportare fuori dai limiti della mappa!
Cannot get player with name @1.=Impossibile trovare il giocatore chiamato @1.
Cannot teleport, @1 is attached to an object!=Impossibile teletrasportare, @1 è attaccato a un oggetto!
Cannot get player with name @1.=Impossibile trovare lə giocatore chiamato @1.
Cannot teleport, @1 is attached to an object!=Impossibile teletrasportare, @1 è attaccatə a un oggetto!
Teleporting @1 to @2.=Teletrasportando @1 da @2.
One does not teleport to oneself.=Non ci si può teletrasportare su se stessi.
Cannot get teleportee with name @1.=Impossibile trovare il giocatore chiamato @1 per il teletrasporto
Cannot get target player with name @1.=Impossibile trovare il giocatore chiamato @1 per il teletrasporto
One does not teleport to oneself.=Non ci si può teletrasportare su se stessɜ.
Cannot get teleportee with name @1.=Impossibile trovare lə giocatore chiamatə @1 per il teletrasporto
Cannot get target player with name @1.=Impossibile trovare lə giocatore chiamato @1 per il teletrasporto
Teleporting @1 to @2 at @3.=Teletrasportando @1 da @2 a @3
<X>,<Y>,<Z> | <to_name> | <name> <X>,<Y>,<Z> | <name> <to_name>=<X>,<Y>,<Z> | <da_nome> | <nome> <X>,<Y>,<Z> | <nome> <da_nome>
Teleport to position or player=Teletrasporta a una posizione o da un giocatore
You don't have permission to teleport other players (missing privilege: @1).=Non hai il permesso di teletrasportare altri giocatori (privilegio mancante: @1).
Teleport to position or player=Teletrasporta a una posizione o da unə giocatore
You don't have permission to teleport other players (missing privilege: @1).=Non hai il permesso di teletrasportare altrɜ giocatori (privilegio mancante: @1).
([-n] <name> <value>) | <name>=([-n] <nome> <valore>) | <nome>
Set or read server configuration setting=Imposta o ottieni le configurazioni del server
Failed. Cannot modify secure settings. Edit the settings file manually.=
Failed. Cannot modify secure settings. Edit the settings file manually.=Errore. Non è possibile modificare le impostazioni di sicurezza. Modifica manualmente il file d'impostazioni.
Failed. Use '/set -n <name> <value>' to create a new setting.=Errore. Usa 'set -n <nome> <valore>' per creare una nuova impostazione
@1 @= @2=@1 @= @2
<not set>=<non impostato>
@ -89,11 +108,11 @@ Resets lighting in the area between pos1 and pos2 (<pos1> and <pos2> must be in
Successfully reset light in the area ranging from @1 to @2.=Luce nell'area tra @1 e @2 reimpostata con successo.
Failed to load one or more blocks in area.=Errore nel caricare uno o più blocchi mappa nell'area.
List mods installed on the server=Elenca le mod installate nel server
No mods installed.=
No mods installed.=Nessuna mod installata.
Cannot give an empty item.=Impossibile dare un oggetto vuoto.
Cannot give an unknown item.=Impossibile dare un oggetto sconosciuto.
Giving 'ignore' is not allowed.=Non è permesso dare 'ignore'.
@1 is not a known player.=@1 non è un giocatore conosciuto.
@1 is not a known player.=@1 non è unə giocatore conosciutə.
@1 partially added to inventory.=@1 parzialmente aggiunto all'inventario.
@1 could not be added to inventory.=@1 non può essere aggiunto all'inventario.
@1 added to inventory.=@1 aggiunto all'inventario.
@ -101,7 +120,7 @@ Giving 'ignore' is not allowed.=Non è permesso dare 'ignore'.
@1 could not be added to inventory of @2.=Non è stato possibile aggiungere @1 all'inventario di @2.
@1 added to inventory of @2.=@1 aggiunto all'inventario di @2.
<name> <ItemString> [<count> [<wear>]]=<nome> <NomeOggetto> [<quantità> [<usura>]]
Give item to player=Dà oggetti ai giocatori
Give item to player=Dà oggetti allɜ giocatori
Name and ItemString required.=Richiesti nome e NomeOggetto.
<ItemString> [<count> [<wear>]]=<NomeOggetto> [<quantità> [<usura>]]
Give item to yourself=Dà oggetti a te stessǝ
@ -109,29 +128,29 @@ ItemString required.=Richiesto NomeOggetto.
<EntityName> [<X>,<Y>,<Z>]=<NomeEntità> [<X>,<Y>,<Z>]
Spawn entity at given (or your) position=Genera un'entità alla data coordinata (o la tua)
EntityName required.=Richiesto NomeEntità
Unable to spawn entity, player is nil.=Impossibile generare l'entità, il giocatore è nil.
Unable to spawn entity, player is nil.=Impossibile generare l'entità, lə giocatore è nil.
Cannot spawn an unknown entity.=Impossibile generare un'entità sconosciuta.
Invalid parameters (@1).=Parametri non validi (@1).
@1 spawned.=Generata entità @1.
@1 failed to spawn.=Errore nel generare @1
Destroy item in hand=Distrugge l'oggetto in mano
Unable to pulverize, no player.=Impossibile polverizzare, nessun giocatore.
Unable to pulverize, no player.=Impossibile polverizzare, nessunə giocatore.
Unable to pulverize, no item in hand.=Impossibile polverizzare, nessun oggetto in mano.
An item was pulverized.=Un oggetto è stato polverizzato.
[<range>] [<seconds>] [<limit>]=[<raggio>] [<secondi>] [<limite>]
Check who last touched a node or a node near it within the time specified by <seconds>. Default: range @= 0, seconds @= 86400 @= 24h, limit @= 5. Set <seconds> to inf for no time limit=Controlla chi è l'ultimo giocatore che ha toccato un nodo o un nodo nelle sue vicinanze, negli ultimi secondi indicati. Di base: raggio @= 0, secondi @= 86400 @= 24h, limite @= 5.
Check who last touched a node or a node near it within the time specified by <seconds>. Default: range @= 0, seconds @= 86400 @= 24h, limit @= 5. Set <seconds> to inf for no time limit=Controlla chi è l'ultimə giocatore che ha toccato un nodo o un nodo nelle sue vicinanze, negli ultimi secondi indicati. Di base: raggio @= 0, secondi @= 86400 @= 24h, limite @= 5.
Rollback functions are disabled.=Le funzioni di rollback sono disabilitate.
That limit is too high!=Il limite è troppo alto!
Checking @1 ...=Controllando @1 ...
Nobody has touched the specified location in @1 seconds.=Nessuno ha toccato il punto specificato negli ultimi @1 secondi.
Nobody has touched the specified location in @1 seconds.=Nessunə ha toccato il punto specificato negli ultimi @1 secondi.
@1 @2 @3 -> @4 @5 seconds ago.=@1 @2 @3 -> @4 @5 secondi fa.
Punch a node (range@=@1, seconds@=@2, limit@=@3).=Colpisce un nodo (raggio@=@1, secondi@=@2, limite@=@3)
(<name> [<seconds>]) | (:<actor> [<seconds>])=(<nome> [<secondi>]) | (:<attore> [<secondi>])
Revert actions of a player. Default for <seconds> is 60. Set <seconds> to inf for no time limit=Riavvolge le azioni di un giocatore. Di base, <secondi> è 60. Imposta <secondi> a inf per nessun limite di tempo
Revert actions of a player. Default for <seconds> is 60. Set <seconds> to inf for no time limit=Riavvolge le azioni di unə giocatore. Di base, <secondi> è 60. Imposta <secondi> a inf per nessun limite di tempo
Invalid parameters. See /help rollback and /help rollback_check.=Parametri non validi. Vedi /help rollback e /help rollback_check.
Reverting actions of player '@1' since @2 seconds.=Riavvolge le azioni del giocatore '@1' avvenute negli ultimi @2 secondi.
Reverting actions of player '@1' since @2 seconds.=Riavvolge le azioni del giocatore '@1' avvenute negli ultimi @2 secondi.
Reverting actions of @1 since @2 seconds.=Riavvolge le azioni di @1 avvenute negli ultimi @2 secondi.
(log is too long to show)=(il log è troppo lungo per essere mostrato)
(log is too long to show)=(lo storico è troppo lungo per essere mostrato)
Reverting actions succeeded.=Riavvolgimento azioni avvenuto con successo.
Reverting actions FAILED.=Errore nel riavvolgere le azioni.
Show server status=Mostra lo stato del server
@ -140,121 +159,89 @@ This command was disabled by a mod or game.=Questo comando è stato disabilitato
Show or set time of day=Mostra o imposta l'orario della giornata
Current time is @1:@2.=Orario corrente: @1:@2.
You don't have permission to run this command (missing privilege: @1).=Non hai il permesso di eseguire questo comando (privilegio mancante: @1)
Invalid time (must be between 0 and 24000).=
Invalid time (must be between 0 and 24000).=Orario non valido (deve essere tra 0 e 24000).
Time of day changed.=Orario della giornata cambiato.
Invalid hour (must be between 0 and 23 inclusive).=Ora non valida (deve essere tra 0 e 23 inclusi)
Invalid minute (must be between 0 and 59 inclusive).=Minuto non valido (deve essere tra 0 e 59 inclusi)
Show day count since world creation=Mostra il conteggio dei giorni da quando il mondo è stato creato
Current day is @1.=Giorno attuale: @1.
[<delay_in_seconds> | -1] [-r] [<message>]=
Shutdown server (-1 cancels a delayed shutdown, -r allows players to reconnect)=
[<delay_in_seconds> | -1] [-r] [<message>]=[<ritardo_in_secondi> | -1] [-r] [<messaggio>]
Shutdown server (-1 cancels a delayed shutdown, -r allows players to reconnect)=Arresta il server (-1 cancella un arresto programmato, -r permette allɜ giocatori di riconnettersi)
Server shutting down (operator request).=Arresto del server in corso (per richiesta dell'operatore)
Ban the IP of a player or show the ban list=Bandisce l'IP del giocatore o mostra la lista di quelli banditi
The ban list is empty.=La lista banditi è vuota.
Ban list: @1=Lista banditi: @1
You cannot ban players in singleplayer!=
Player is not online.=Il giocatore non è connesso.
Failed to ban player.=Errore nel bandire il giocatore.
Ban the IP of a player or show the ban list=Bandisce l'IP dellə giocatore o mostra la lista di quellɜ banditɜ
The ban list is empty.=La lista banditɜ è vuota.
Ban list: @1=Lista banditɜ: @1
You cannot ban players in singleplayer!=Non puoi bandire giocatori in locale!
Player is not online.=Lə giocatore non è connessə.
Failed to ban player.=Errore nel bandire lə giocatore.
Banned @1.=@1 banditǝ.
<name> | <IP_address>=<nome> | <indirizzo_IP>
Remove IP ban belonging to a player/IP=Perdona l'IP appartenente a un giocatore/IP
Failed to unban player/IP.=Errore nel perdonare il giocatore/IP
Remove IP ban belonging to a player/IP=Perdona l'IP appartenente a unə giocatore/IP
Failed to unban player/IP.=Errore nel perdonare lə giocatore/IP
Unbanned @1.=@1 perdonatǝ
<name> [<reason>]=<nome> [<ragione>]
Kick a player=Caccia un giocatore
Failed to kick player @1.=Errore nel cacciare il giocatore @1.
Kick a player=Caccia unə giocatore
Failed to kick player @1.=Errore nel cacciare lə giocatore @1.
Kicked @1.=@1 cacciatǝ.
[full | quick]=[full | quick]
Clear all objects in world=Elimina tutti gli oggetti/entità nel mondo
Invalid usage, see /help clearobjects.=Uso incorretto, vedi /help clearobjects.
Clearing all objects. This may take a long time. You may experience a timeout. (by @1)=Eliminando tutti gli oggetti/entità. Questo potrebbe richiedere molto tempo e farti eventualmente crashare. (di @1)
Clearing all objects. This may take a long time. You may experience a timeout. (by @1)=Eliminando tutti gli oggetti/entità. Questo potrebbe richiedere molto tempo e farti disconnettere. (di @1)
Cleared all objects.=Tutti gli oggetti sono stati eliminati.
<name> <message>=<nome> <messaggio>
Send a direct message to a player=Invia un messaggio privato al giocatore
Send a direct message to a player=Invia un messaggio privato a unə giocatore
Invalid usage, see /help msg.=Uso incorretto, vedi /help msg
The player @1 is not online.=Il giocatore @1 non è connesso.
The player @1 is not online.=Lə giocatore @1 non è connessə.
DM from @1: @2=Messaggio privato da @1: @2
Message sent.=Messaggio inviato.
Get the last login time of a player or yourself=Ritorna l'ultimo accesso di un giocatore o di te stessǝ
@1's last login time was @2.=L'ultimo accesso di @1 è avvenuto il @2
@1's last login time is unknown.=L'ultimo accesso di @1 non è conosciuto
Clear the inventory of yourself or another player=Svuota l'inventario tuo o di un altro giocatore
You don't have permission to clear another player's inventory (missing privilege: @1).=Non hai il permesso di svuotare l'inventario di un altro giocatore (privilegio mancante: @1).
Get the last login time of a player or yourself=Ritorna l'ultimo accesso di unə giocatore o di te stessǝ
@1's last login time was @2.=L'ultimo accesso di @1 è avvenuto il @2.
@1's last login time is unknown.=L'ultimo accesso di @1 è ignoto.
Clear the inventory of yourself or another player=Svuota l'inventario tuo o di un*altrə giocatore
You don't have permission to clear another player's inventory (missing privilege: @1).=Non hai il permesso di svuotare l'inventario di un*altrə giocatore (privilegio mancante: @1).
@1 cleared your inventory.=@1 ha svuotato il tuo inventario.
Cleared @1's inventory.=L'inventario di @1 è stato svuotato.
Player must be online to clear inventory!=Il giocatore deve essere connesso per svuotarne l'inventario!
Players can't be killed, damage has been disabled.=I giocatori non possono essere uccisi, il danno è disabilitato.
Player @1 is not online.=Il giocatore @1 non è connesso.
Player must be online to clear inventory!=Lə giocatore deve essere connessə per svuotarne l'inventario!
Players can't be killed, damage has been disabled.=Lɜ giocatori non possono essere uccisɜ, il danno è disabilitato.
Player @1 is not online.= giocatore @1 non è connesso.
You are already dead.=Sei già mortǝ.
@1 is already dead.=@1 è già mortǝ.
@1 has been killed.=@1 è stato uccisǝ.
Kill player or yourself=Uccide un giocatore o te stessǝ
Invalid parameters (see /help @1).=
Too many arguments, try using just /help <command>=
Available commands: @1=Comandi disponibili: @1
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Usa '/help <comando>' per ottenere più informazioni, o '/help all' per elencare tutti i comandi.
Available commands:=Comandi disponibili:
Command not available: @1=Comando non disponibile: @1
[all | privs | <cmd>] [-t]=
Get help for commands or list privileges (-t: output in chat)=
Available privileges:=Privilegi disponibili:
Command=Comando
Parameters=Parametri
For more information, click on any entry in the list.=Per più informazioni, clicca su una qualsiasi voce dell'elenco.
Double-click to copy the entry to the chat history.=Doppio click per copiare la voce nella cronologia della chat.
Command: @1 @2=Comando: @1 @2
Available commands: (see also: /help <cmd>)=Comandi disponibili: (vedi anche /help <comando>)
Close=Chiudi
Privilege=Privilegio
Description=Descrizione
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<filtro>] | dump [<filtro>] | save [<formato> [<filtro>]] | reset
Handle the profiler and profiling data=Gestisce il profiler e i dati da esso elaborati
Statistics written to action log.=Statistiche scritte nel log delle azioni.
Statistics were reset.=Le statistiche sono state resettate.
Usage: @1=Utilizzo: @1
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=I formati supportati sono txt, csv, lua, json e json_pretty (le strutture potrebbero essere soggetti a cambiamenti).
@1 joined the game.=
@1 left the game.=
@1 left the game (timed out).=
Kill player or yourself=Uccide unə giocatore o te stessǝ
@1 joined the game.=@1 si è connessə.
@1 left the game.=@1 si è disconnessə.
@1 left the game (timed out).=@1 si è disconnessə (connessione scaduta).
(no description)=(nessuna descrizione)
Can interact with things and modify the world=Si può interagire con le cose e modificare il mondo
Can speak in chat=Si può parlare in chat
Can modify basic privileges (@1)=
Can modify basic privileges (@1)=Si possono modificare i privilegi di base (@1)
Can modify privileges=Si possono modificare i privilegi
Can teleport self=Si può teletrasportare se stessз
Can teleport other players=Si possono teletrasportare gli altri giocatori
Can teleport other players=Si possono teletrasportare lɜ altrɜ giocatori
Can set the time of day using /time=Si può impostate l'orario della giornata tramite /time
Can do server maintenance stuff=Si possono eseguire operazioni di manutenzione del server
Can bypass node protection in the world=Si può aggirare la protezione dei nodi nel mondo
Can ban and unban players=Si possono bandire e perdonare i giocatori
Can kick players=Si possono cacciare i giocatori
Can ban and unban players=Si possono bandire e perdonare lɜ giocatori
Can kick players=Si possono cacciare lɜ giocatori
Can use /give and /giveme=Si possono usare /give e /give me
Can use /setpassword and /clearpassword=Si possono usare /setpassword e /clearpassword
Can use fly mode=Si può usare la modalità volo
Can use fast mode=Si può usare la modalità rapida
Can fly through solid nodes using noclip mode=Si può volare attraverso i nodi solidi con la modalità incorporea
Can use the rollback functionality=Si può usare la funzione di rollback
Can enable wireframe=
Can enable wireframe=Si può abilitare la vista reticolata
Unknown Item=Oggetto sconosciuto
Air=Aria
Ignore=Ignora
You can't place 'ignore' nodes!=Non puoi piazzare nodi 'ignore'!
Values below show absolute/relative times spend per server step by the instrumented function.=
A total of @1 sample(s) were taken.=
The output is limited to '@1'.=
Saving of profile failed: @1=
Profile saved to @1=
##### not used anymore #####
Set player's password=Imposta la password del giocatore
Invalid time.=Orario non valido.
[all | privs | <cmd>]=[all | privs | <comando>]
Get help for commands or list privileges=Richiama la finestra d'aiuto dei comandi o dei privilegi
Allows enabling various debug options that may affect gameplay=Permette di abilitare varie opzioni di debug che potrebbero influenzare l'esperienza di gioco
[<delay_in_seconds> | -1] [reconnect] [<message>]=[<ritardo_in_secondi> | -1] [reconnect] [<messaggio>]
Shutdown server (-1 cancels a delayed shutdown)=Arresta il server (-1 annulla un arresto programmato)
<name> (<privilege> | all)=<nome> (<privilegio> | all)
<privilege> | all=<privilegio> | all
Can modify 'shout' and 'interact' privileges=Si possono modificare i privilegi 'shout' e 'interact'
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<filtro>] | dump [<filtro>] | save [<formato> [<filtro>]] | reset
Handle the profiler and profiling data=Gestisce il profiler e i dati da esso elaborati
Statistics written to action log.=Statistiche scritte nel registro delle azioni.
Statistics were reset.=Le statistiche sono state resettate.
Usage: @1=Utilizzo: @1
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=I formati supportati sono txt, csv, lua, json e json_pretty (le strutture potrebbero essere soggetti a cambiamenti).
Values below show absolute/relative times spend per server step by the instrumented function.=I valori sottostanti mostrano i tempi assoluti/relativi impiegati su ogni singolo step dalla funzione analizzata
A total of @1 sample(s) were taken.=Son stati ottenuti campioni per un totale di @1.
The output is limited to '@1'.=L'output è limitato a '@1'.
Saving of profile failed: @1=Errore nel salvare il profilo: @1
Profile saved to @1=Profilo salvato in @1

@ -1,4 +1,22 @@
# textdomain: __builtin
Invalid parameters (see /help @1).=Parameter tidak sah (sila lihat /help @1).
Too many arguments, try using just /help <command>=Terlalu banyak argumen, cuba guna /help <perintah> sahaja
Available commands: @1=Perintah yang tersedia: @1
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Gunakan '/help <perintah>' untuk maklumat lanjut, atau '/help all' untuk senaraikan kesemuanya.
Available commands:=Perintah yang tersedia:
Command not available: @1=Perintah tidak tersedia: @1
[all | privs | <cmd>] [-t]=[all | privs | <perintah>] [-t]
Get help for commands or list privileges (-t: output in chat)=Dapatkan bantuan untuk perintah atau senaraikan keistimewaan (-t: output dalam sembang)
Available privileges:=Keistimewaan yang tersedia:
Command=Perintah
Parameters=Parameter
For more information, click on any entry in the list.=Untuk maklumat lanjut, klik pada mana-mana entri dalam senarai.
Double-click to copy the entry to the chat history.=Klik dua kali untuk salin entri ke sejarah sembang.
Command: @1 @2=Perintah: @1 @2
Available commands: (see also: /help <cmd>)=Perintah yang tersedua: (sila lihat juga: /help <perintah>)
Close=Tutup
Privilege=Keistimewaan
Description=Keterangan
Empty command.=Perintah kosong.
Invalid command: @1=Perintah tidak sah: @1
Invalid command usage.=Penggunaan perintah tidak sah.
@ -189,30 +207,6 @@ You are already dead.=Anda sudah pun mati.
@1 is already dead.=@1 sudah pun mati.
@1 has been killed.=@1 telah berjaya dibunuh.
Kill player or yourself=Bunuh pemain atau diri sendiri
Invalid parameters (see /help @1).=Parameter tidak sah (sila lihat /help @1).
Too many arguments, try using just /help <command>=Terlalu banyak argumen, cuba guna /help <perintah> sahaja
Available commands: @1=Perintah yang tersedia: @1
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Gunakan '/help <perintah>' untuk maklumat lanjut, atau '/help all' untuk senaraikan kesemuanya.
Available commands:=Perintah yang tersedia:
Command not available: @1=Perintah tidak tersedia: @1
[all | privs | <cmd>] [-t]=[all | privs | <perintah>] [-t]
Get help for commands or list privileges (-t: output in chat)=Dapatkan bantuan untuk perintah atau senaraikan keistimewaan (-t: output dalam sembang)
Available privileges:=Keistimewaan yang tersedia:
Command=Perintah
Parameters=Parameter
For more information, click on any entry in the list.=Untuk maklumat lanjut, klik pada mana-mana entri dalam senarai.
Double-click to copy the entry to the chat history.=Klik dua kali untuk salin entri ke sejarah sembang.
Command: @1 @2=Perintah: @1 @2
Available commands: (see also: /help <cmd>)=Perintah yang tersedua: (sila lihat juga: /help <perintah>)
Close=Tutup
Privilege=Keistimewaan
Description=Keterangan
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<tapisan>] | dump [<tapisan>] | save [<format> [<tapisan>]] | reset
Handle the profiler and profiling data=Uruskan pembukah dan data pembukahan
Statistics written to action log.=Statistik telah ditulis ke log perlakuan.
Statistics were reset.=Statistik telah ditetapkan semula.
Usage: @1=Kegunaan: @1
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=Format boleh jadi salah satu daripada txt, csv, lua, json, json_pretty (struktur boleh berubah kemudian).
@1 joined the game.=@1 telah menyertai permainan.
@1 left the game.=@1 telah meninggalkan permainan.
@1 left the game (timed out).=@1 telah meninggalkan permainan (tamat tempoh masa).
@ -239,6 +233,12 @@ Unknown Item=Item Tidak Diketahui
Air=Udara
Ignore=Abai
You can't place 'ignore' nodes!=Anda tidak boleh meletakkan nod 'abai'!
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<tapisan>] | dump [<tapisan>] | save [<format> [<tapisan>]] | reset
Handle the profiler and profiling data=Uruskan pembukah dan data pembukahan
Statistics written to action log.=Statistik telah ditulis ke log perlakuan.
Statistics were reset.=Statistik telah ditetapkan semula.
Usage: @1=Kegunaan: @1
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=Format boleh jadi salah satu daripada txt, csv, lua, json, json_pretty (struktur boleh berubah kemudian).
Values below show absolute/relative times spend per server step by the instrumented function.=Nilai di bawah menunjukkan masa mutlak/relatif yang digunakan oleh fungsi yang dipasangkan pada setiap langkah pelayan
A total of @1 sample(s) were taken.=Sebanyak @1 sampel telah diambil secara keseluruhan.
The output is limited to '@1'.=Output dihadkan kepada '@1'.

@ -0,0 +1,246 @@
# textdomain: __builtin
Invalid parameters (see /help @1).=Недопустимые параметры (см. /help @1).
Too many arguments, try using just /help <command>=Слишком много аргументов, попробуйте использовать просто /help <команда>
Available commands: @1=Доступные команды: @1
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Используйте '/help <команда>', чтобы получить дополнительную информацию, или '/help all', чтобы перечислить все.
Available commands:=Доступные команды:
Command not available: @1=Команда недоступна: @1
[all | privs | <cmd>] [-t]=[all | privs | <команда>] [-t]
Get help for commands or list privileges (-t: output in chat)=Получить справку по командам или списку привилегий (-t: вывод в чате)
Available privileges:=Доступные привилегии:
Command=Команда
Parameters=Параметры
For more information, click on any entry in the list.=Для получения дополнительной информации нажмите на любую запись в списке.
Double-click to copy the entry to the chat history.=Дважды щелкните, чтобы скопировать запись в историю чата.
Command: @1 @2=Команда: @1 @2
Available commands: (see also: /help <cmd>)=Доступные команды: (смотрите также: /help <команда>)
Close=Закрыть
Privilege=Привилегия
Description=Описание
Empty command.=Пустая команда.
Invalid command: @1=Недопустимая команда: @1
Invalid command usage.=Недопустимое использование команды.
(@1 s)= (@1 с)
Command execution took @1 s=Выполнение команды заняло @1 с
You don't have permission to run this command (missing privileges: @1).=У вас нет разрешения на запуск этой команды (отсутствуют привилегии: @1).
Unable to get position of player @1.=Не удалось получить позицию игрока @1.
Incorrect area format. Expected: (x1,y1,z1) (x2,y2,z2)=Неправильный формат области. Ожидаемое: (x1,y1,z1) (x2,y2,z2)
<action>=<действие>
Show chat action (e.g., '/me orders a pizza' displays '<player name> orders a pizza')=Показать действие в чате (например, '/me заказывает пиццу' отображает '<имя игрока> заказывает пиццу')
Show the name of the server owner=Показать имя владельца сервера
The administrator of this server is @1.=Администратором этого сервера является @1.
There's no administrator named in the config file.=В конфигурационном файле нет имени администратора.
@1 does not have any privileges.=@1 не имеет никаких привилегий.
Privileges of @1: @2=Привилегии @1: @2
[<name>]=[<имя>]
Show privileges of yourself or another player=Показать ваши привилегии или привилегии другого игрока
Player @1 does not exist.=Игрок @1 не существует.
<privilege>=<привилегия>
Return list of all online players with privilege=Возвращает список всех онлайн-игроков, имеющих указанную привилегию
Invalid parameters (see /help haspriv).=Недопустимые параметры (см. /help haspriv).
Unknown privilege!=Неизвестная привилегия!
No online player has the "@1" privilege.=Ни один онлайн-игрок не имеет привилегии "@1".
Players online with the "@1" privilege: @2=Игроки онлайн с привилегией "@1": @2
Your privileges are insufficient.=Ваших привилегий недостаточно.
Your privileges are insufficient. '@1' only allows you to grant: @2=Ваших привилегий недостаточно. '@1' позволяет вам предоставлять только: @2
Unknown privilege: @1=Неизвестная привилегия: @1
@1 granted you privileges: @2=@1 предоставил(а) вам привилегии: @2
<name> (<privilege> [, <privilege2> [<...>]] | all)=<имя> (<привилегия> [, <привилегия2> [<...>]] | all)
Give privileges to player=Предоставить привилегии игроку
Invalid parameters (see /help grant).=Недопустимые параметры (см. /help grant).
<privilege> [, <privilege2> [<...>]] | all=<привилегия> [, <привилегия2> [<...>]] | all
Grant privileges to yourself=Предоставить привилегии себе
Invalid parameters (see /help grantme).=Недопустимые параметры (см. /help grantme).
Your privileges are insufficient. '@1' only allows you to revoke: @2=Ваших привилегий недостаточно. '@1' позволяет вам отозвать только: @2
Note: Cannot revoke in singleplayer: @1=Примечание: Невозможно отозвать в одиночной игре: @1
Note: Cannot revoke from admin: @1=Примечание: Невозможно отозвать у администратора: @1
No privileges were revoked.=Никакие привилегии не были отозваны.
@1 revoked privileges from you: @2=@1 отозвал у вас привилегии: @2
Remove privileges from player=Отозвать привилегии у игрока
Invalid parameters (see /help revoke).=Недопустимые параметры (см. /help revoke).
Revoke privileges from yourself=Отозвать привилегии у себя
Invalid parameters (see /help revokeme).=Недопустимые параметры (см. /help revokeme).
<name> <password>=<имя> <пароль>
Set player's password (sent unencrypted, thus insecure)=Установить пароль игрока (отправка в незашифрованном виде, следовательно, небезопасно)
Name field required.=Обязательное поле "Имя".
Your password was cleared by @1.=@1 сбросил ваш пароль.
Password of player "@1" cleared.=Пароль игрока "@1" сброшен.
Your password was set by @1.=@1 установил вам пароль.
Password of player "@1" set.=Установлен пароль игрока "@1".
<name>=<имя>
Set empty password for a player=Установить пустой пароль для игрока
Reload authentication data=Перезагрузить аутентификационные данные
Done.=Готово.
Failed.=Не удалось.
Remove a player's data=Удалить данные игрока
Player "@1" removed.=Игрок "@1" удален.
No such player "@1" to remove.=Нет такого игрока "@1", которого можно было бы удалить.
Player "@1" is connected, cannot remove.=Игрок "@1" сейчас в игре, удалить его невозможно.
Unhandled remove_player return code @1.=Необработанный код возврата remove_player @1.
Cannot teleport out of map bounds!=Невозможно телепортироваться за пределы карты!
Cannot get player with name @1.=Не удается получить игрока с именем @1.
Cannot teleport, @1 is attached to an object!=Невозможно телепортироваться, @1 привязан к объекту!
Teleporting @1 to @2.=Телепортация @1 в @2.
One does not teleport to oneself.=Игрок не может телепортироваться к самому себе.
Cannot get teleportee with name @1.=Не удается найти телепортирующегося с именем @1.
Cannot get target player with name @1.=Не удается найти целевого игрока с именем @1.
Teleporting @1 to @2 at @3.=Телепортация @1 к @2 в @3.
<X>,<Y>,<Z> | <to_name> | <name> <X>,<Y>,<Z> | <name> <to_name>=<X>,<Y>,<Z> | <имя_к_кому> | <имя> <X>,<Y>,<Z> | <имя> <имя_к_кому>
Teleport to position or player=Телепортироваться на позицию или к игроку
You don't have permission to teleport other players (missing privilege: @1).=У вас нет разрешения на телепортацию других игроков (отсутствует привилегия: @1).
([-n] <name> <value>) | <name>=([-n] <имя> <значение>) | <имя>
Set or read server configuration setting=Установить или прочитать настройки конфигурации сервера
Failed. Cannot modify secure settings. Edit the settings file manually.=Ошибка. Не удается изменить настройки безопасности. Отредактируйте файл настроек вручную.
Failed. Use '/set -n <name> <value>' to create a new setting.=Ошибка. Используйте '/set -n <имя> <значение>', чтобы создать новую настройку.
@1 @= @2=@1 @= @2
<not set>=<не задано>
Invalid parameters (see /help set).=Недопустимые параметры (см. /help set).
Finished emerging @1 blocks in @2ms.=Загрузка/генерация @1 map-блоков завершена за @2мс.
emergeblocks update: @1/@2 blocks emerged (@3%)=обновление emergeblocks: загружено/сгенерировано: @1 из @2 блоков (@3%)
(here [<radius>]) | (<pos1> <pos2>)=(here [<радиус>]) | (<позиция1> <позиция2>)
Load (or, if nonexistent, generate) map blocks contained in area pos1 to pos2 (<pos1> and <pos2> must be in parentheses)=Загрузить (или сгенерировать, если не существуют) map-блоки, содержащиеся в области с позиции1 по позицию2 (<позиция1> и <позиция2> должны быть в круглых скобках)
Started emerge of area ranging from @1 to @2.=Загрузка/генерация области в диапазоне от @1 до @2 начата.
Delete map blocks contained in area pos1 to pos2 (<pos1> and <pos2> must be in parentheses)=Удалить map-блоки, содержащиеся в области с позиции1 по позицию2 (<позиция1> и <позиция2> должны быть заключены в круглые скобки)
Successfully cleared area ranging from @1 to @2.=Успешно очищена область в диапазоне от @1 до @2.
Failed to clear one or more blocks in area.=Не удалось очистить один или несколько блоков в области.
Resets lighting in the area between pos1 and pos2 (<pos1> and <pos2> must be in parentheses)=Сбрасывает освещение в области с позиции1 по позицию2 (<позиция1> и <позиция2> должны быть заключены в круглые скобки)
Successfully reset light in the area ranging from @1 to @2.=Успешно сброшено освещение в диапазоне от @1 до @2.
Failed to load one or more blocks in area.=Не удалось загрузить один или несколько блоков в области.
List mods installed on the server=Список модов, установленных на сервере
No mods installed.=Моды не установлены.
Cannot give an empty item.=Вы должны указать предмет.
Cannot give an unknown item.=Неизвестный предмет.
Giving 'ignore' is not allowed.=Выдача 'ignore' не допускается.
@1 is not a known player.=Неизвестный игрок: @1.
@1 partially added to inventory.=@1 частично добавлен в инвентарь.
@1 could not be added to inventory.=@1 не удалось добавить в инвентарь.
@1 added to inventory.=@1 добавлено в инвентарь.
@1 partially added to inventory of @2.=@1 частично добавлен в инвентарь @2.
@1 could not be added to inventory of @2.=@1 не удалось добавить в инвентарь @2.
@1 added to inventory of @2.=@1 добавлен в инвентарь @2.
<name> <ItemString> [<count> [<wear>]]=<имя> <ItemString> [<количество> [<износ>]]
Give item to player=Добавить предмет в инвентарь игрока
Name and ItemString required.=Требуется <имя> игрока и название предмета <ItemString>.
<ItemString> [<count> [<wear>]]=<ItemString> [<количество> [<износ>]]
Give item to yourself=Добавить предмет в свой инвентарь
ItemString required.=Требуется название предмета <ItemString>.
<EntityName> [<X>,<Y>,<Z>]=<EntityName> [<X>,<Y>,<Z>]
Spawn entity at given (or your) position=Спаун сущности (моб/стрела/...) в заданной (или вашей) позиции
EntityName required.=Требуется имя сущности <EntityName>.
Unable to spawn entity, player is nil.=Не удается создать сущность, игрок не найден.
Cannot spawn an unknown entity.=Не удается создать неизвестную сущность.
Invalid parameters (@1).=Недопустимые параметры (@1).
@1 spawned.=Сущность @1 создана.
@1 failed to spawn.=Сущность @1 не удалось создать.
Destroy item in hand=Уничтожить предмет, который у вас в руках
Unable to pulverize, no player.=Невозможно уничтожить предмет, нет игрока.
Unable to pulverize, no item in hand.=Невозможно уничтожить предмет, в руках нет предмета.
An item was pulverized.=Предмет был уничтожен.
[<range>] [<seconds>] [<limit>]=[<диапазон>] [<секунды>] [<ограничение>]
Check who last touched a node or a node near it within the time specified by <seconds>. Default: range @= 0, seconds @= 86400 @= 24h, limit @= 5. Set <seconds> to inf for no time limit=Проверить кто в последний раз прикасался к ноде или нодам рядом в течение времени, указанного в <секундах>. По умолчанию: диапазон @= 0, секунды @= 86400 @= 24 часа, ограничение @= 5. Установите <секунды> в значение inf для отключения ограничения по времени
Rollback functions are disabled.=Функции отката отключены.
That limit is too high!=Это <ограничение> слишком велико!
Checking @1 ...=Проверка @1 ...
Nobody has touched the specified location in @1 seconds.=Никто не прикасался к указанному местоположению в течение @1 секунд(ы).
@1 @2 @3 -> @4 @5 seconds ago.=@1 @2 @3 -> @4 @5 секунд назад.
Punch a node (range@=@1, seconds@=@2, limit@=@3).=Ударьте по ноде (диапазон@=@1, секунды@=@2, предел @=@3).
(<name> [<seconds>]) | (:<actor> [<seconds>])=(<имя> [<секунды>]) | (:<игрок> [<секунды>])
Revert actions of a player. Default for <seconds> is 60. Set <seconds> to inf for no time limit=Отменяет действия игрока. Значение по умолчанию для <секунд> равно 60. Установите <секунды> в значение inf для отключения ограничения по времени
Invalid parameters. See /help rollback and /help rollback_check.=Недопустимые параметры. Смотрите /help rollback и /help rollback_check.
Reverting actions of player '@1' since @2 seconds.=Отмена действия игрока "@1" за последние @2 секунд(у/ы).
Reverting actions of @1 since @2 seconds.=Отмена действий @1 за последние @2 секунд(у/ы).
(log is too long to show)=(журнал слишком длинный для отображения)
Reverting actions succeeded.=Отмена действий завершилась успешно.
Reverting actions FAILED.=Отменить действия НЕ УДАЛОСЬ.
Show server status=Показать статус сервера
This command was disabled by a mod or game.=Эта команда была отключена модом или игрой.
[<0..23>:<0..59> | <0..24000>]=[<0..23>:<0..59> | <0..24000>]
Show or set time of day=Показать или установить время суток
Current time is @1:@2.=Текущее время @1:@2.
You don't have permission to run this command (missing privilege: @1).=У вас нет разрешения на выполнение этой команды (отсутствует привилегия: @1).
Invalid time (must be between 0 and 24000).=Недопустимое время (должно быть в диапазоне от 0 до 24000).
Time of day changed.=Время суток изменено.
Invalid hour (must be between 0 and 23 inclusive).=Недопустимый час (должно быть от 0 до 23 включительно).
Invalid minute (must be between 0 and 59 inclusive).=Недопустимая минута (должно быть от 0 до 59 включительно).
Show day count since world creation=Показать количество дней с момента сотворения мира
Current day is @1.=Текущий день: @1.
[<delay_in_seconds> | -1] [-r] [<message>]=[<задержка_в_секундах> | -1] [-r] [<сообщение>]
Shutdown server (-1 cancels a delayed shutdown, -r allows players to reconnect)=Завершение работы сервера (-1 отменяет отложенное завершение работы, -r позволяет игрокам повторно подключиться)
Server shutting down (operator request).=Завершение работы сервера (по запросу администрации).
Ban the IP of a player or show the ban list=Заблокировать IP игрока или показать список заблокированных
The ban list is empty.=Список заблокированных пуст.
Ban list: @1=Список заблокированных: @1
You cannot ban players in singleplayer!=Вы не можете забанить игроков в одиночной игре!
Player is not online.=Игрок не подключен к Сети.
Failed to ban player.=Не удалось забанить игрока.
Banned @1.=@1 забанен.
<name> | <IP_address>=<имя> | <IP_адрес>
Remove IP ban belonging to a player/IP=Удалить IP-бан, принадлежащий игроку/IP-адресу
Failed to unban player/IP.=Не удалось разбанить игрока/IP.
Unbanned @1.=@1 разбанен.
<name> [<reason>]=<имя> [<причина>]
Kick a player=Кикнуть игрока
Failed to kick player @1.=Не удалось кикнуть @1.
Kicked @1.=@1 кикнут.
[full | quick]=[full | quick]
Clear all objects in world=Удалить ВСЕ объекты/сущности во ВСЁМ мире
Invalid usage, see /help clearobjects.=Недопустимое использование, см. /help clearobjects.
Clearing all objects. This may take a long time. You may experience a timeout. (by @1)=Удаление всех объектов. Это может занять много времени. Ваш клиент может отключиться из-за тайм-аута. (запущено: @1)
Cleared all objects.=Все объекты удалены.
<name> <message>=<имя> <сообщение>
Send a direct message to a player=Отправить прямое сообщение игроку
Invalid usage, see /help msg.=Недопустимое использование, см. /help msg.
The player @1 is not online.=Игрок @1 не находится в игре.
DM from @1: @2=DM от @1: @2
Message sent.=Сообщение отправлено.
Get the last login time of a player or yourself=Вывести время последнего входа игрока или своё
@1's last login time was @2.=Время последнего входа @1: @2.
@1's last login time is unknown.=Время последнего входа @1 неизвестно.
Clear the inventory of yourself or another player=Очистить свой инвентарь или инвентарь другого игрока
You don't have permission to clear another player's inventory (missing privilege: @1).=У вас нет разрешения на очистку инвентаря другого игрока (отсутствует привилегия: @1).
@1 cleared your inventory.=@1 очистил ваш инвентарь.
Cleared @1's inventory.=Инвентарь @1 очищен.
Player must be online to clear inventory!=Игрок должен быть онлайн, чтобы очистить его инвентарь!
Players can't be killed, damage has been disabled.=Игроки не могут быть убиты, урон отключен.
Player @1 is not online.=Игрок @1 не находится в игре.
You are already dead.=Ты уже мертв.
@1 is already dead.=@1 уже мертв.
@1 has been killed.=@1 был убит.
Kill player or yourself=Убить игрока или себя
@1 joined the game.=@1 присоединился к игре.
@1 left the game.=@1 вышел из игры.
@1 left the game (timed out).=@1 вышел из игры (тайм-аут).
(no description)=(без описания)
Can interact with things and modify the world=Может взаимодействовать (с объектами/игроками/...) и изменять мир
Can speak in chat=Может общаться в чате
Can modify basic privileges (@1)=Может изменять базовые привилегии (@1)
Can modify privileges=Может изменять привилегии
Can teleport self=Может самостоятельно телепортироваться
Can teleport other players=Может телепортировать других игроков
Can set the time of day using /time=Может устанавливать время суток с помощью /time
Can do server maintenance stuff=Может заниматься обслуживанием сервера
Can bypass node protection in the world=Может обходить защиту нод во всем мире
Can ban and unban players=Может банить и разбанивать игроков
Can kick players=Может кикать игроков
Can use /give and /giveme=Может использовать /give и /giveme
Can use /setpassword and /clearpassword=Может использовать /setpassword и /clearpassword
Can use fly mode=Может использовать режим полета
Can use fast mode=Может использовать быстрый режим (ускорение)
Can fly through solid nodes using noclip mode=Может пролетать сквозь ноды, используя режим noclip
Can use the rollback functionality=Может использовать функцию отката
Can enable wireframe=Может использовать режим каркаса в отладке
Unknown Item=Неизвестный предмет
Air=Воздух
Ignore=Игнорируемая встроенная нода (":ignore")
You can't place 'ignore' nodes!=Вы не можете установить ноду 'ignore'!
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset
Handle the profiler and profiling data=Работа с профайлером и данными профилирования
Statistics written to action log.=Статистика записывается в журнал действий.
Statistics were reset.=Статистика была сброшена.
Usage: @1=Использование: @1
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=Формат может быть одним из txt, csv, lua, json, json_pretty (структуры могут быть изменены).
Values below show absolute/relative times spend per server step by the instrumented function.=Приведенные ниже значения показывают абсолютное/относительное время, затрачиваемое функцией на каждый шаг сервера.
A total of @1 sample(s) were taken.=Всего было взято @1 образец(ов).
The output is limited to '@1'.=Вывод ограничен значением '@1'.
Saving of profile failed: @1=Не удалось сохранить данные профилирования: @1
Profile saved to @1=Данные профилирования сохранены в @1

@ -1,4 +1,22 @@
# textdomain: __builtin
Invalid parameters (see /help @1).=
Too many arguments, try using just /help <command>=
Available commands: @1=
Use '/help <cmd>' to get more information, or '/help all' to list everything.=
Available commands:=
Command not available: @1=
[all | privs | <cmd>] [-t]=
Get help for commands or list privileges (-t: output in chat)=
Available privileges:=
Command=
Parameters=
For more information, click on any entry in the list.=
Double-click to copy the entry to the chat history.=
Command: @1 @2=
Available commands: (see also: /help <cmd>)=
Close=
Privilege=
Description=
Empty command.=
Invalid command: @1=
Invalid command usage.=
@ -189,30 +207,6 @@ You are already dead.=
@1 is already dead.=
@1 has been killed.=
Kill player or yourself=
Invalid parameters (see /help @1).=
Too many arguments, try using just /help <command>=
Available commands: @1=
Use '/help <cmd>' to get more information, or '/help all' to list everything.=
Available commands:=
Command not available: @1=
[all | privs | <cmd>] [-t]=
Get help for commands or list privileges (-t: output in chat)=
Available privileges:=
Command=
Parameters=
For more information, click on any entry in the list.=
Double-click to copy the entry to the chat history.=
Command: @1 @2=
Available commands: (see also: /help <cmd>)=
Close=
Privilege=
Description=
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=
Handle the profiler and profiling data=
Statistics written to action log.=
Statistics were reset.=
Usage: @1=
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=
@1 joined the game.=
@1 left the game.=
@1 left the game (timed out).=
@ -239,6 +233,12 @@ Unknown Item=
Air=
Ignore=
You can't place 'ignore' nodes!=
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=
Handle the profiler and profiling data=
Statistics written to action log.=
Statistics were reset.=
Usage: @1=
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=
Values below show absolute/relative times spend per server step by the instrumented function.=
A total of @1 sample(s) were taken.=
The output is limited to '@1'.=

@ -23,9 +23,18 @@ if not core.get_http_api then
return
end
-- Unordered preserves the original order of the ContentDB API,
-- before the package list is ordered based on installed state.
local store = { packages = {}, packages_full = {}, packages_full_unordered = {}, aliases = {} }
local store = {
loading = false,
load_ok = false,
load_error = false,
-- Unordered preserves the original order of the ContentDB API,
-- before the package list is ordered based on installed state.
packages = {},
packages_full = {},
packages_full_unordered = {},
aliases = {},
}
local http = core.get_http_api()
@ -47,6 +56,9 @@ local filter_types_titles = {
fgettext("Texture packs"),
}
-- Automatic package installation
local auto_install_spec = nil
local number_downloading = 0
local download_queue = {}
@ -62,15 +74,6 @@ local REASON_UPDATE = "update"
local REASON_DEPENDENCY = "dependency"
-- encodes for use as URL parameter or path component
local function urlencode(str)
return str:gsub("[^%a%d()._~-]", function(char)
return string.format("%%%02X", string.byte(char))
end)
end
assert(urlencode("sample text?") == "sample%20text%3F")
local function get_download_url(package, reason)
local base_url = core.settings:get("contentdb_url")
local ret = base_url .. ("/packages/%s/releases/%d/download/"):format(
@ -89,7 +92,7 @@ local function download_and_extract(param)
if filename == "" or not core.download_file(param.url, filename) then
core.log("error", "Downloading " .. dump(param.url) .. " failed")
return {
msg = fgettext("Failed to download \"$1\"", package.title)
msg = fgettext_ne("Failed to download \"$1\"", package.title)
}
end
@ -105,7 +108,7 @@ local function download_and_extract(param)
os.remove(filename)
if not tempfolder then
return {
msg = fgettext("Failed to extract \"$1\" (unsupported file type or broken archive)", package.title),
msg = fgettext_ne("Failed to extract \"$1\" (unsupported file type or broken archive)", package.title),
}
end
@ -129,7 +132,7 @@ local function start_install(package, reason)
local path, msg = pkgmgr.install_dir(package.type, result.path, package.name, package.path)
core.delete_dir(result.path)
if not path then
gamedata.errormessage = fgettext("Error installing \"$1\": $2", package.title, msg)
gamedata.errormessage = fgettext_ne("Error installing \"$1\": $2", package.title, msg)
else
core.log("action", "Installed package to " .. path)
@ -151,7 +154,9 @@ local function start_install(package, reason)
if conf_path then
local conf = Settings(conf_path)
conf:set("title", package.title)
if not conf:get("title") then
conf:set("title", package.title)
end
if not name_is_title then
conf:set("name", package.name)
end
@ -184,12 +189,16 @@ local function start_install(package, reason)
if not core.handle_async(download_and_extract, params, callback) then
core.log("error", "ERROR: async event failed")
gamedata.errormessage = fgettext("Failed to download $1", package.name)
gamedata.errormessage = fgettext_ne("Failed to download $1", package.name)
return
end
end
local function queue_download(package, reason)
if package.queued or package.downloading then
return
end
local max_concurrent_downloads = tonumber(core.settings:get("contentdb_max_concurrent_downloads"))
if number_downloading < math.max(max_concurrent_downloads, 1) then
start_install(package, reason)
@ -200,6 +209,9 @@ local function queue_download(package, reason)
end
local function get_raw_dependencies(package)
if package.type ~= "mod" then
return {}
end
if package.raw_deps then
return package.raw_deps
end
@ -207,7 +219,7 @@ local function get_raw_dependencies(package)
local url_fmt = "/api/packages/%s/dependencies/?only_hard=1&protocol_version=%s&engine_version=%s"
local version = core.get_version()
local base_url = core.settings:get("contentdb_url")
local url = base_url .. url_fmt:format(package.url_part, core.get_max_supp_proto(), urlencode(version.string))
local url = base_url .. url_fmt:format(package.url_part, core.get_max_supp_proto(), core.urlencode(version.string))
local response = http.fetch_sync({ url = url })
if not response.succeeded then
@ -429,7 +441,7 @@ function install_dialog.get_formspec()
"container_end[]",
}
return table.concat(formspec, "")
return table.concat(formspec)
end
function install_dialog.handle_submit(this, fields)
@ -520,6 +532,50 @@ function confirm_overwrite.create(package, callback)
nil)
end
local function install_or_update_package(this, package)
local install_parent
if package.type == "mod" then
install_parent = core.get_modpath()
elseif package.type == "game" then
install_parent = core.get_gamepath()
elseif package.type == "txp" then
install_parent = core.get_texturepath()
else
error("Unknown package type: " .. package.type)
end
if package.queued or package.downloading then
return
end
local function on_confirm()
local deps = get_raw_dependencies(package)
if deps and has_hard_deps(deps) then
local dlg = install_dialog.create(package, deps)
dlg:set_parent(this)
this:hide()
dlg:show()
else
queue_download(package, package.path and REASON_UPDATE or REASON_NEW)
end
end
if package.type == "mod" and #pkgmgr.games == 0 then
local dlg = messagebox("install_game",
fgettext("You need to install a game before you can install a mod"))
dlg:set_parent(this)
this:hide()
dlg:show()
elseif not package.path and core.is_dir(install_parent .. DIR_DELIM .. package.name) then
local dlg = confirm_overwrite.create(package, on_confirm)
dlg:set_parent(this)
this:hide()
dlg:show()
else
on_confirm()
end
end
local function get_file_extension(path)
local parts = path:split(".")
@ -574,29 +630,46 @@ local function get_screenshot(package)
return defaulttexturedir .. "loading_screenshot.png"
end
function store.load()
local function fetch_pkgs()
local version = core.get_version()
local base_url = core.settings:get("contentdb_url")
local url = base_url ..
"/api/packages/?type=mod&type=game&type=txp&protocol_version=" ..
core.get_max_supp_proto() .. "&engine_version=" .. urlencode(version.string)
core.get_max_supp_proto() .. "&engine_version=" .. core.urlencode(version.string)
for _, item in pairs(core.settings:get("contentdb_flag_blacklist"):split(",")) do
item = item:trim()
if item ~= "" then
url = url .. "&hide=" .. urlencode(item)
url = url .. "&hide=" .. core.urlencode(item)
end
end
local response = http.fetch_sync({ url = url })
local languages
local current_language = core.get_language()
if current_language ~= "" then
languages = { current_language, "en;q=0.8" }
else
languages = { "en" }
end
local http = core.get_http_api()
local response = http.fetch_sync({
url = url,
extra_headers = {
"Accept-Language: " .. table.concat(languages, ", ")
},
})
if not response.succeeded then
return
end
store.packages_full = core.parse_json(response.data) or {}
store.aliases = {}
local packages = core.parse_json(response.data)
if not packages or #packages == 0 then
return
end
local aliases = {}
for _, package in pairs(store.packages_full) do
for _, package in pairs(packages) do
local name_len = #package.name
-- This must match what store.update_paths() does!
package.id = package.author:lower() .. "/"
@ -606,48 +679,137 @@ function store.load()
package.id = package.id .. package.name
end
package.url_part = urlencode(package.author) .. "/" .. urlencode(package.name)
package.url_part = core.urlencode(package.author) .. "/" .. core.urlencode(package.name)
if package.aliases then
for _, alias in ipairs(package.aliases) do
-- We currently don't support name changing
local suffix = "/" .. package.name
if alias:sub(-#suffix) == suffix then
store.aliases[alias:lower()] = package.id
aliases[alias:lower()] = package.id
end
end
end
end
store.packages_full_unordered = store.packages_full
store.packages = store.packages_full
store.loaded = true
return { packages = packages, aliases = aliases }
end
local function sort_and_filter_pkgs()
store.update_paths()
store.sort_packages()
store.filter_packages(search_string)
end
-- Resolves the package specification stored in auto_install_spec into an actual package.
-- May only be called after the package list has been loaded successfully.
local function resolve_auto_install_spec()
assert(store.load_ok)
if not auto_install_spec then
return nil
end
local spec = store.aliases[auto_install_spec] or auto_install_spec
local resolved = nil
for _, pkg in ipairs(store.packages_full_unordered) do
if pkg.id == spec then
resolved = pkg
break
end
end
if not resolved then
gamedata.errormessage = fgettext("The package $1 was not found.", auto_install_spec)
ui.update()
auto_install_spec = nil
end
return resolved
end
-- Installs the package specified by auto_install_spec.
-- Only does something if:
-- a. The package list has been loaded successfully.
-- b. The store dialog is currently visible.
local function do_auto_install()
if not store.load_ok then
return
end
local pkg = resolve_auto_install_spec()
if not pkg then
return
end
local store_dlg = ui.find_by_name("store")
if not store_dlg or store_dlg.hidden then
return
end
install_or_update_package(store_dlg, pkg)
auto_install_spec = nil
end
function store.load()
if store.load_ok then
sort_and_filter_pkgs()
return
end
if store.loading then
return
end
store.loading = true
core.handle_async(
fetch_pkgs,
nil,
function(result)
if result then
store.load_ok = true
store.load_error = false
store.packages = result.packages
store.packages_full = result.packages
store.packages_full_unordered = result.packages
store.aliases = result.aliases
sort_and_filter_pkgs()
do_auto_install()
else
store.load_error = true
end
store.loading = false
ui.update()
end
)
end
function store.update_paths()
local mod_hash = {}
pkgmgr.refresh_globals()
for _, mod in pairs(pkgmgr.global_mods:get_list()) do
if mod.author and mod.release > 0 then
local id = mod.author:lower() .. "/" .. mod.name
mod_hash[store.aliases[id] or id] = mod
local cdb_id = pkgmgr.get_contentdb_id(mod)
if cdb_id then
mod_hash[store.aliases[cdb_id] or cdb_id] = mod
end
end
local game_hash = {}
pkgmgr.update_gamelist()
for _, game in pairs(pkgmgr.games) do
if game.author ~= "" and game.release > 0 then
local id = game.author:lower() .. "/" .. game.id
game_hash[store.aliases[id] or id] = game
local cdb_id = pkgmgr.get_contentdb_id(game)
if cdb_id then
game_hash[store.aliases[cdb_id] or cdb_id] = game
end
end
local txp_hash = {}
for _, txp in pairs(pkgmgr.get_texture_packs()) do
if txp.author and txp.release > 0 then
local id = txp.author:lower() .. "/" .. txp.name
txp_hash[store.aliases[id] or id] = txp
local cdb_id = pkgmgr.get_contentdb_id(txp)
if cdb_id then
txp_hash[store.aliases[cdb_id] or cdb_id] = txp
end
end
@ -666,6 +828,7 @@ function store.update_paths()
package.installed_release = content.release or 0
else
package.path = nil
package.installed_release = nil
end
end
end
@ -673,27 +836,40 @@ end
function store.sort_packages()
local ret = {}
local auto_install_pkg = resolve_auto_install_spec() -- can be nil
-- Add installed content
for i=1, #store.packages_full_unordered do
local package = store.packages_full_unordered[i]
if package.path then
ret[#ret + 1] = package
for _, pkg in ipairs(store.packages_full_unordered) do
if pkg.path and pkg ~= auto_install_pkg then
ret[#ret + 1] = pkg
end
end
-- Sort installed content by title
-- Sort installed content first by "is there an update available?", then by title
table.sort(ret, function(a, b)
local a_updatable = a.installed_release < a.release
local b_updatable = b.installed_release < b.release
if a_updatable and not b_updatable then
return true
elseif b_updatable and not a_updatable then
return false
end
return a.title < b.title
end)
-- Add uninstalled content
for i=1, #store.packages_full_unordered do
local package = store.packages_full_unordered[i]
if not package.path then
ret[#ret + 1] = package
for _, pkg in ipairs(store.packages_full_unordered) do
if not pkg.path and pkg ~= auto_install_pkg then
ret[#ret + 1] = pkg
end
end
-- Put the package that will be auto-installed at the very top
if auto_install_pkg then
table.insert(ret, 1, auto_install_pkg)
end
store.packages_full = ret
end
@ -732,7 +908,29 @@ function store.filter_packages(query)
end
end
local function get_info_formspec(text)
local H = 9.5
return table.concat({
"formspec_version[6]",
"size[15.75,9.5]",
core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "position[0.5,0.55]",
"label[4,4.35;", text, "]",
"container[0,", H - 0.8 - 0.375, "]",
"button[0.375,0;5,0.8;back;", fgettext("Back to Main Menu"), "]",
"container_end[]",
})
end
function store.get_formspec(dlgdata)
if store.loading then
return get_info_formspec(fgettext("Loading..."))
end
if store.load_error then
return get_info_formspec(fgettext("No packages could be retrieved"))
end
assert(store.load_ok)
store.update_paths()
dlgdata.pagemax = math.max(math.ceil(#store.packages / num_per_page), 1)
@ -742,82 +940,70 @@ function store.get_formspec(dlgdata)
local W = 15.75
local H = 9.5
local formspec
if #store.packages_full > 0 then
formspec = {
"formspec_version[3]",
"size[15.75,9.5]",
"position[0.5,0.55]",
local formspec = {
"formspec_version[6]",
"size[15.75,9.5]",
core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "position[0.5,0.55]",
"style[status,downloading,queued;border=false]",
"style[status,downloading,queued;border=false]",
"container[0.375,0.375]",
"field[0,0;7.225,0.8;search_string;;", core.formspec_escape(search_string), "]",
"field_close_on_enter[search_string;false]",
"image_button[7.3,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]",
"image_button[8.125,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";clear;]",
"dropdown[9.6,0;2.4,0.8;type;", table.concat(filter_types_titles, ","), ";", filter_type, "]",
"container_end[]",
"container[0.375,0.375]",
"field[0,0;7.225,0.8;search_string;;", core.formspec_escape(search_string), "]",
"field_enter_after_edit[search_string;true]",
"image_button[7.3,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]",
"image_button[8.125,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";clear;]",
"dropdown[9.175,0;2.7875,0.8;type;", table.concat(filter_types_titles, ","), ";", filter_type, "]",
"container_end[]",
-- Page nav buttons
"container[0,", H - 0.8 - 0.375, "]",
"button[0.375,0;4,0.8;back;", fgettext("Back to Main Menu"), "]",
-- Page nav buttons
"container[0,", H - 0.8 - 0.375, "]",
"button[0.375,0;5,0.8;back;", fgettext("Back to Main Menu"), "]",
"container[", W - 0.375 - 0.8*4 - 2, ",0]",
"image_button[0,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "start_icon.png;pstart;]",
"image_button[0.8,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "prev_icon.png;pback;]",
"style[pagenum;border=false]",
"button[1.6,0;2,0.8;pagenum;", tonumber(cur_page), " / ", tonumber(dlgdata.pagemax), "]",
"image_button[3.6,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "next_icon.png;pnext;]",
"image_button[4.4,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "end_icon.png;pend;]",
"container_end[]",
"container[", W - 0.375 - 0.8*4 - 2, ",0]",
"image_button[0,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "start_icon.png;pstart;]",
"image_button[0.8,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "prev_icon.png;pback;]",
"style[pagenum;border=false]",
"button[1.6,0;2,0.8;pagenum;", tonumber(cur_page), " / ", tonumber(dlgdata.pagemax), "]",
"image_button[3.6,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "next_icon.png;pnext;]",
"image_button[4.4,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "end_icon.png;pend;]",
"container_end[]",
"container_end[]",
}
"container_end[]",
}
if number_downloading > 0 then
formspec[#formspec + 1] = "button[12.75,0.375;2.625,0.8;downloading;"
if #download_queue > 0 then
formspec[#formspec + 1] = fgettext("$1 downloading,\n$2 queued", number_downloading, #download_queue)
else
formspec[#formspec + 1] = fgettext("$1 downloading...", number_downloading)
if number_downloading > 0 then
formspec[#formspec + 1] = "button[12.5875,0.375;2.7875,0.8;downloading;"
if #download_queue > 0 then
formspec[#formspec + 1] = fgettext("$1 downloading,\n$2 queued", number_downloading, #download_queue)
else
formspec[#formspec + 1] = fgettext("$1 downloading...", number_downloading)
end
formspec[#formspec + 1] = "]"
else
local num_avail_updates = 0
for i=1, #store.packages_full do
local package = store.packages_full[i]
if package.path and package.installed_release < package.release and
not (package.downloading or package.queued) then
num_avail_updates = num_avail_updates + 1
end
end
if num_avail_updates == 0 then
formspec[#formspec + 1] = "button[12.5875,0.375;2.7875,0.8;status;"
formspec[#formspec + 1] = fgettext("No updates")
formspec[#formspec + 1] = "]"
else
local num_avail_updates = 0
for i=1, #store.packages_full do
local package = store.packages_full[i]
if package.path and package.installed_release < package.release and
not (package.downloading or package.queued) then
num_avail_updates = num_avail_updates + 1
end
end
if num_avail_updates == 0 then
formspec[#formspec + 1] = "button[12.75,0.375;2.625,0.8;status;"
formspec[#formspec + 1] = fgettext("No updates")
formspec[#formspec + 1] = "]"
else
formspec[#formspec + 1] = "button[12.75,0.375;2.625,0.8;update_all;"
formspec[#formspec + 1] = fgettext("Update All [$1]", num_avail_updates)
formspec[#formspec + 1] = "]"
end
end
if #store.packages == 0 then
formspec[#formspec + 1] = "label[4,3;"
formspec[#formspec + 1] = fgettext("No results")
formspec[#formspec + 1] = "button[12.5875,0.375;2.7875,0.8;update_all;"
formspec[#formspec + 1] = fgettext("Update All [$1]", num_avail_updates)
formspec[#formspec + 1] = "]"
end
else
formspec = {
"size[12,7]",
"position[0.5,0.55]",
"label[4,3;", fgettext("No packages could be retrieved"), "]",
"container[0,", H - 0.8 - 0.375, "]",
"button[0,0;4,0.8;back;", fgettext("Back to Main Menu"), "]",
"container_end[]",
}
end
if #store.packages == 0 then
formspec[#formspec + 1] = "label[4,4.75;"
formspec[#formspec + 1] = fgettext("No results")
formspec[#formspec + 1] = "]"
end
-- download/queued tooltips always have the same message
@ -846,7 +1032,10 @@ function store.get_formspec(dlgdata)
formspec[#formspec + 1] = "]"
-- buttons
local left_base = "image_button[-1.55,0;0.7,0.7;" .. core.formspec_escape(defaulttexturedir)
local description_width = W - 2.625 - 2 * 0.7 - 2 * 0.15
local second_base = "image_button[-1.55,0;0.7,0.7;" .. core.formspec_escape(defaulttexturedir)
local third_base = "image_button[-2.4,0;0.7,0.7;" .. core.formspec_escape(defaulttexturedir)
formspec[#formspec + 1] = "container["
formspec[#formspec + 1] = W - 0.375*2
formspec[#formspec + 1] = ",0.1]"
@ -856,28 +1045,28 @@ function store.get_formspec(dlgdata)
formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
formspec[#formspec + 1] = "cdb_downloading.png;3;400;]"
elseif package.queued then
formspec[#formspec + 1] = left_base
formspec[#formspec + 1] = second_base
formspec[#formspec + 1] = "cdb_queued.png;queued;]"
elseif not package.path then
local elem_name = "install_" .. i .. ";"
formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#71aa34]"
formspec[#formspec + 1] = left_base .. "cdb_add.png;" .. elem_name .. "]"
formspec[#formspec + 1] = second_base .. "cdb_add.png;" .. elem_name .. "]"
formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Install") .. tooltip_colors
else
if package.installed_release < package.release then
-- The install_ action also handles updating
local elem_name = "install_" .. i .. ";"
formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#28ccdf]"
formspec[#formspec + 1] = left_base .. "cdb_update.png;" .. elem_name .. "]"
formspec[#formspec + 1] = third_base .. "cdb_update.png;" .. elem_name .. "]"
formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Update") .. tooltip_colors
else
local elem_name = "uninstall_" .. i .. ";"
formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#a93b3b]"
formspec[#formspec + 1] = left_base .. "cdb_clear.png;" .. elem_name .. "]"
formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Uninstall") .. tooltip_colors
description_width = description_width - 0.7 - 0.15
end
local elem_name = "uninstall_" .. i .. ";"
formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#a93b3b]"
formspec[#formspec + 1] = second_base .. "cdb_clear.png;" .. elem_name .. "]"
formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Uninstall") .. tooltip_colors
end
local web_elem_name = "view_" .. i .. ";"
@ -888,7 +1077,6 @@ function store.get_formspec(dlgdata)
formspec[#formspec + 1] = "container_end[]"
-- description
local description_width = W - 0.375*5 - 0.85 - 2*0.7
formspec[#formspec + 1] = "textarea[1.855,0.3;"
formspec[#formspec + 1] = tostring(description_width)
formspec[#formspec + 1] = ",0.8;;;"
@ -898,7 +1086,7 @@ function store.get_formspec(dlgdata)
formspec[#formspec + 1] = "container_end[]"
end
return table.concat(formspec, "")
return table.concat(formspec)
end
function store.handle_submit(this, fields)
@ -952,6 +1140,7 @@ function store.handle_submit(this, fields)
local new_type = table.indexof(filter_types_titles, fields.type)
if new_type ~= filter_type then
filter_type = new_type
cur_page = 1
store.filter_packages(search_string)
return true
end
@ -975,39 +1164,7 @@ function store.handle_submit(this, fields)
assert(package)
if fields["install_" .. i] then
local install_parent
if package.type == "mod" then
install_parent = core.get_modpath()
elseif package.type == "game" then
install_parent = core.get_gamepath()
elseif package.type == "txp" then
install_parent = core.get_texturepath()
else
error("Unknown package type: " .. package.type)
end
local function on_confirm()
local deps = get_raw_dependencies(package)
if deps and has_hard_deps(deps) then
local dlg = install_dialog.create(package, deps)
dlg:set_parent(this)
this:hide()
dlg:show()
else
queue_download(package, package.path and REASON_UPDATE or REASON_NEW)
end
end
if not package.path and core.is_dir(install_parent .. DIR_DELIM .. package.name) then
local dlg = confirm_overwrite.create(package, on_confirm)
dlg:set_parent(this)
this:hide()
dlg:show()
else
on_confirm()
end
install_or_update_package(this, package)
return true
end
@ -1031,17 +1188,30 @@ function store.handle_submit(this, fields)
return false
end
function create_store_dlg(type)
if not store.loaded or #store.packages_full == 0 then
store.load()
function store.handle_events(event)
if event == "DialogShow" then
-- On touchscreen, don't show the "MINETEST" header behind the dialog.
mm_game_theme.set_engine(core.settings:get_bool("enable_touch"))
-- If the store is already loaded, auto-install packages here.
do_auto_install()
return true
end
store.update_paths()
store.sort_packages()
return false
end
--- Creates a ContentDB dialog.
---
--- @param type string | nil
--- Sets initial package filter. "game", "mod", "txp" or nil (no filter).
--- @param install_spec table | nil
--- ContentDB ID of package as returned by pkgmgr.get_contentdb_id().
--- Sets package to install or update automatically.
function create_store_dlg(type, install_spec)
search_string = ""
cur_page = 1
if type then
-- table.indexof does not work on tables that contain `nil`
for i, v in pairs(filter_types_type) do
@ -1050,12 +1220,19 @@ function create_store_dlg(type)
break
end
end
else
filter_type = 1
end
store.filter_packages(search_string)
-- Keep the old auto_install_spec if the caller doesn't specify one.
if install_spec then
auto_install_spec = install_spec
end
store.load()
return dialog_create("store",
store.get_formspec,
store.handle_submit,
nil)
store.handle_events)
end

@ -0,0 +1,22 @@
--Minetest
--Copyright (C) 2023 rubenwardy
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
local path = core.get_mainmenu_path() .. DIR_DELIM .. "content"
dofile(path .. DIR_DELIM .. "pkgmgr.lua")
dofile(path .. DIR_DELIM .. "update_detector.lua")
dofile(path .. DIR_DELIM .. "dlg_contentstore.lua")

@ -150,6 +150,8 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack)
toadd.virtual_path = mod_virtual_path
toadd.type = "mod"
pkgmgr.update_translations({ toadd })
-- Check modpack.txt
-- Note: modpack.conf is already checked above
local modpackfile = io.open(mod_path .. DIR_DELIM .. "modpack.txt")
@ -177,6 +179,7 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack)
end
end
--------------------------------------------------------------------------------
function pkgmgr.get_texture_packs()
local txtpath = core.get_texturepath()
local txtpath_system = core.get_texturepath_share()
@ -188,6 +191,8 @@ function pkgmgr.get_texture_packs()
load_texture_packs(txtpath_system, retval)
end
pkgmgr.update_translations(retval)
table.sort(retval, function(a, b)
return a.title:lower() < b.title:lower()
end)
@ -195,6 +200,23 @@ function pkgmgr.get_texture_packs()
return retval
end
--------------------------------------------------------------------------------
function pkgmgr.get_all()
local result = {}
for _, mod in pairs(pkgmgr.global_mods:get_list()) do
result[#result + 1] = mod
end
for _, game in pairs(pkgmgr.games) do
result[#result + 1] = game
end
for _, txp in pairs(pkgmgr.get_texture_packs()) do
result[#result + 1] = txp
end
return result
end
--------------------------------------------------------------------------------
function pkgmgr.get_folder_type(path)
local testfile = io.open(path .. DIR_DELIM .. "init.lua","r")
@ -260,7 +282,10 @@ function pkgmgr.is_valid_modname(modpath)
end
--------------------------------------------------------------------------------
function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
--- @param render_list filterlist
--- @param use_technical_names boolean to show technical names instead of human-readable titles
--- @param with_icon table or nil, from virtual path to icon object
function pkgmgr.render_packagelist(render_list, use_technical_names, with_icon)
if not render_list then
if not pkgmgr.global_mods then
pkgmgr.refresh_globals()
@ -273,10 +298,10 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
for i, v in ipairs(list) do
local color = ""
local icon = 0
local error = with_error and with_error[v.virtual_path]
local function update_error(val)
if val and (not error or (error.type == "warning" and val.type == "error")) then
error = val
local icon_info = with_icon and with_icon[v.virtual_path or v.path]
local function update_icon_info(val)
if val and (not icon_info or (icon_info.type == "warning" and val.type == "error")) then
icon_info = val
end
end
@ -286,8 +311,8 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
for j = 1, #rawlist do
if rawlist[j].modpack == list[i].name then
if with_error then
update_error(with_error[rawlist[j].virtual_path])
if with_icon then
update_icon_info(with_icon[rawlist[j].virtual_path or rawlist[j].path])
end
if rawlist[j].enabled then
@ -303,10 +328,10 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
color = mt_color_blue
local rawlist = render_list:get_raw_list()
if v.type == "game" and with_error then
if v.type == "game" and with_icon then
for j = 1, #rawlist do
if rawlist[j].is_game_content then
update_error(with_error[rawlist[j].virtual_path])
update_icon_info(with_icon[rawlist[j].virtual_path or rawlist[j].path])
end
end
end
@ -315,13 +340,17 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
color = mt_color_green
end
if error then
if error.type == "warning" then
if icon_info then
if icon_info.type == "warning" then
color = mt_color_orange
icon = 2
else
elseif icon_info.type == "error" then
color = mt_color_red
icon = 3
elseif icon_info.type == "update" then
icon = 4
else
error("Unknown icon type " .. icon_info.type)
end
end
@ -332,7 +361,7 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
retval[#retval + 1] = "0"
end
if with_error then
if with_icon then
retval[#retval + 1] = icon
end
@ -534,7 +563,7 @@ function pkgmgr.install_dir(expected_type, path, basename, targetpath)
-- There's no good way to detect a texture pack, so let's just assume
-- it's correct for now.
if basefolder and basefolder.type ~= "invalid" and basefolder.type ~= "txp" then
return nil, fgettext("Unable to install a $1 as a texture pack", basefolder.type)
return nil, fgettext_ne("Unable to install a $1 as a texture pack", basefolder.type)
end
local from = basefolder and basefolder.path or path
@ -544,17 +573,17 @@ function pkgmgr.install_dir(expected_type, path, basename, targetpath)
core.delete_dir(targetpath)
if not core.copy_dir(from, targetpath, false) then
return nil,
fgettext("Failed to install $1 to $2", basename, targetpath)
fgettext_ne("Failed to install $1 to $2", basename, targetpath)
end
return targetpath, nil
elseif not basefolder then
return nil, fgettext("Unable to find a valid mod, modpack, or game")
return nil, fgettext_ne("Unable to find a valid mod, modpack, or game")
end
-- Check type
if basefolder.type ~= expected_type and (basefolder.type ~= "modpack" or expected_type ~= "mod") then
return nil, fgettext("Unable to install a $1 as a $2", basefolder.type, expected_type)
return nil, fgettext_ne("Unable to install a $1 as a $2", basefolder.type, expected_type)
end
-- Set targetpath if not predetermined
@ -575,7 +604,7 @@ function pkgmgr.install_dir(expected_type, path, basename, targetpath)
targetpath = content_path .. DIR_DELIM .. basename
else
return nil,
fgettext("Install: Unable to find suitable folder name for $1", path)
fgettext_ne("Install: Unable to find suitable folder name for $1", path)
end
end
@ -583,7 +612,7 @@ function pkgmgr.install_dir(expected_type, path, basename, targetpath)
core.delete_dir(targetpath)
if not core.copy_dir(basefolder.path, targetpath, false) then
return nil,
fgettext("Failed to install $1 to $2", basename, targetpath)
fgettext_ne("Failed to install $1 to $2", basename, targetpath)
end
if basefolder.type == "game" then
@ -750,6 +779,53 @@ function pkgmgr.update_gamelist()
table.sort(pkgmgr.games, function(a, b)
return a.title:lower() < b.title:lower()
end)
pkgmgr.update_translations(pkgmgr.games)
end
--------------------------------------------------------------------------------
function pkgmgr.update_translations(list)
for _, item in ipairs(list) do
local info = core.get_content_info(item.path)
assert(info.path)
assert(info.textdomain)
assert(not item.is_translated)
item.is_translated = true
if info.title and info.title ~= "" then
item.title = core.get_content_translation(info.path, info.textdomain,
core.translate(info.textdomain, info.title))
end
if info.description and info.description ~= "" then
item.description = core.get_content_translation(info.path, info.textdomain,
core.translate(info.textdomain, info.description))
end
end
end
--------------------------------------------------------------------------------
-- Returns the ContentDB ID for an installed piece of content.
function pkgmgr.get_contentdb_id(content)
-- core.get_games() will return "" instead of nil if there is no "author" field.
if content.author and content.author ~= "" and content.release > 0 then
if content.type == "game" then
return content.author:lower() .. "/" .. content.id
end
return content.author:lower() .. "/" .. content.name
end
-- Until Minetest 5.8.0, Minetest Game was bundled with Minetest.
-- Unfortunately, the bundled MTG was not versioned (missing "release"
-- field in game.conf).
-- Therefore, we consider any installation of MTG that is not versioned,
-- has not been cloned from Git, and is not system-wide to be updatable.
if content.type == "game" and content.id == "minetest" and content.release == 0 and
not core.is_dir(content.path .. "/.git") and core.may_modify_path(content.path) then
return "minetest/minetest"
end
return nil
end
--------------------------------------------------------------------------------

@ -52,12 +52,12 @@ local function reset()
function core.get_gamepath()
return games_dir
end
function env.fgettext(fmt, ...)
function env.fgettext_ne(fmt, ...)
return fmt
end
setfenv(loadfile("builtin/common/misc_helpers.lua"), env)()
setfenv(loadfile("builtin/mainmenu/pkgmgr.lua"), env)()
setfenv(loadfile("builtin/mainmenu/content/pkgmgr.lua"), env)()
function env.pkgmgr.update_gamelist()
table.insert(calls, { "update_gamelist" })

@ -0,0 +1,147 @@
--Minetest
--Copyright (C) 2023 rubenwardy
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
update_detector = {}
if not core.get_http_api then
update_detector.get_all = function() return {} end
update_detector.get_count = function() return 0 end
return
end
local has_fetched = false
local latest_releases
do
local tmp = core.get_once("cdb_latest_releases")
if tmp then
latest_releases = core.deserialize(tmp, true)
has_fetched = latest_releases ~= nil
end
end
local function fetch_latest_releases()
local version = core.get_version()
local base_url = core.settings:get("contentdb_url")
local url = base_url ..
"/api/updates/?type=mod&type=game&type=txp&protocol_version=" ..
core.get_max_supp_proto() .. "&engine_version=" .. core.urlencode(version.string)
local http = core.get_http_api()
local response = http.fetch_sync({ url = url })
if not response.succeeded then
return
end
return core.parse_json(response.data)
end
--- Get a table from package key (author/name) to latest release id
---
--- @param callback function that takes a single argument, table or nil
local function get_latest_releases(callback)
core.handle_async(fetch_latest_releases, nil, callback)
end
local function has_packages_from_cdb()
pkgmgr.refresh_globals()
pkgmgr.update_gamelist()
for _, content in pairs(pkgmgr.get_all()) do
if pkgmgr.get_contentdb_id(content) then
return true
end
end
return false
end
--- @returns a new table with all keys lowercase
local function lowercase_keys(tab)
local ret = {}
for key, value in pairs(tab) do
ret[key:lower()] = value
end
return ret
end
local function fetch()
if has_fetched or not has_packages_from_cdb() then
return
end
has_fetched = true
get_latest_releases(function(releases)
if not releases then
has_fetched = false
return
end
latest_releases = lowercase_keys(releases)
core.set_once("cdb_latest_releases", core.serialize(latest_releases))
if update_detector.get_count() > 0 then
local maintab = ui.find_by_name("maintab")
if not maintab.hidden then
ui.update()
end
end
end)
end
--- @returns a list of content with an update available
function update_detector.get_all()
if latest_releases == nil then
fetch()
return {}
end
pkgmgr.refresh_globals()
pkgmgr.update_gamelist()
local ret = {}
local all_content = pkgmgr.get_all()
for _, content in ipairs(all_content) do
local cdb_id = pkgmgr.get_contentdb_id(content)
if cdb_id then
-- The backend will account for aliases in `latest_releases`
local latest_release = latest_releases[cdb_id]
if not latest_release and content.type == "game" then
latest_release = latest_releases[cdb_id .. "_game"]
end
if latest_release and latest_release > content.release then
ret[#ret + 1] = content
end
end
end
return ret
end
--- @return number of packages with updates available
function update_detector.get_count()
return #update_detector.get_all()
end

@ -245,7 +245,7 @@ local function get_formspec(data)
return retval ..
"tablecolumns[color;tree;image,align=inline,width=1.5,0=" .. core.formspec_escape(defaulttexturedir .. "blank.png") ..
",1=" .. core.formspec_escape(defaulttexturedir .. "checkbox_16_white.png") ..
",1=" .. core.formspec_escape(defaulttexturedir .. "checkbox_16.png") ..
",2=" .. core.formspec_escape(defaulttexturedir .. "error_icon_orange.png") ..
",3=" .. core.formspec_escape(defaulttexturedir .. "error_icon_red.png") .. ";text]" ..
"table[5.5,0.75;5.75,6;world_config_modlist;" ..

@ -70,6 +70,8 @@ local flag_checkboxes = {
{ "trees", fgettext("Trees and jungle grass") },
{ "flat", fgettext("Flat terrain") },
{ "mudflow", fgettext("Mud flow"), fgettext("Terrain surface erosion") },
{ "temples", fgettext("Desert temples"),
fgettext("Different dungeon variant generated in desert biomes (only if dungeons enabled)") },
-- Biome settings are in mgv6_biomes below
},
}
@ -91,16 +93,6 @@ local mgv6_biomes = {
local function create_world_formspec(dialogdata)
-- Point the player to ContentDB when no games are found
if #pkgmgr.games == 0 then
return "size[8,2.5,true]" ..
"style[label_button;border=false]" ..
"button[0.5,0.5;7,0.5;label_button;" ..
fgettext("You have no games installed.") .. "]" ..
"button[0.5,1.5;2.5,0.5;world_create_open_cdb;" .. fgettext("Install a game") .. "]" ..
"button[5.0,1.5;2.5,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]"
end
local current_mg = dialogdata.mg
local mapgens = core.get_mapgen_names()
@ -289,7 +281,7 @@ local function create_world_formspec(dialogdata)
end
local retval =
"size[12.25,7,true]" ..
"size[12.25,7.4,true]" ..
-- Left side
"container[0,0]"..
@ -310,8 +302,8 @@ local function create_world_formspec(dialogdata)
"label[0,2;" .. fgettext("Mapgen") .. "]"..
"dropdown[0,2.5;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]"
-- Warning if only devtest is installed
if #pkgmgr.games == 1 and pkgmgr.games[1].id == "devtest" then
-- Warning when making a devtest world
if game.id == "devtest" then
retval = retval ..
"container[0,3.5]" ..
"box[0,0;5.8,1.7;#ff8800]" ..
@ -331,8 +323,10 @@ local function create_world_formspec(dialogdata)
"container_end[]"..
-- Menu buttons
"button[3.25,6.5;3,0.5;world_create_confirm;" .. fgettext("Create") .. "]" ..
"button[6.25,6.5;3,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]"
"container[0,6.9]"..
"button[3.25,0;3,0.5;world_create_confirm;" .. fgettext("Create") .. "]" ..
"button[6.25,0;3,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]" ..
"container_end[]"
return retval
@ -363,7 +357,7 @@ local function create_world_buttonhandler(this, fields)
local message
if game == nil then
message = fgettext("No game selected")
message = fgettext_ne("No game selected")
end
if message == nil then
@ -382,7 +376,7 @@ local function create_world_buttonhandler(this, fields)
end
if menudata.worldlist:uid_exists_raw(worldname) then
message = fgettext("A world named \"$1\" already exists", worldname)
message = fgettext_ne("A world named \"$1\" already exists", worldname)
end
end

@ -34,7 +34,7 @@ local function delete_content_buttonhandler(this, fields)
this.data.content.path ~= core.get_gamepath() and
this.data.content.path ~= core.get_texturepath() then
if not core.delete_dir(this.data.content.path) then
gamedata.errormessage = fgettext("pkgmgr: failed to delete \"$1\"", this.data.content.path)
gamedata.errormessage = fgettext_ne("pkgmgr: failed to delete \"$1\"", this.data.content.path)
end
if this.data.content.type == "game" then
@ -43,7 +43,7 @@ local function delete_content_buttonhandler(this, fields)
pkgmgr.refresh_globals()
end
else
gamedata.errormessage = fgettext("pkgmgr: invalid path \"$1\"", this.data.content.path)
gamedata.errormessage = fgettext_ne("pkgmgr: invalid path \"$1\"", this.data.content.path)
end
this:delete()
return true

@ -0,0 +1,115 @@
--Minetest
--Copyright (C) 2023 Gregor Parzefall
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
function check_reinstall_mtg()
if core.settings:get_bool("no_mtg_notification") then
return
end
local games = core.get_games()
for _, game in ipairs(games) do
if game.id == "minetest" then
core.settings:set_bool("no_mtg_notification", true)
return
end
end
local mtg_world_found = false
local worlds = core.get_worlds()
for _, world in ipairs(worlds) do
if world.gameid == "minetest" then
mtg_world_found = true
break
end
end
if not mtg_world_found then
core.settings:set_bool("no_mtg_notification", true)
return
end
local maintab = ui.find_by_name("maintab")
local dlg = create_reinstall_mtg_dlg()
dlg:set_parent(maintab)
maintab:hide()
dlg:show()
ui.update()
end
local function get_formspec(dialogdata)
local markup = table.concat({
"<big>", fgettext("Minetest Game is no longer installed by default"), "</big>\n",
fgettext("For a long time, the Minetest engine shipped with a default game called \"Minetest Game\". " ..
"Since Minetest 5.8.0, Minetest ships without a default game."), "\n",
fgettext("If you want to continue playing in your Minetest Game worlds, you need to reinstall Minetest Game."),
})
return table.concat({
"formspec_version[6]",
"size[12.8,7]",
"hypertext[0.375,0.375;12.05,5.2;text;", minetest.formspec_escape(markup), "]",
"container[0.375,5.825]",
"style[dismiss;bgcolor=red]",
"button[0,0;4,0.8;dismiss;", fgettext("Dismiss"), "]",
"button[4.25,0;8,0.8;reinstall;", fgettext("Reinstall Minetest Game"), "]",
"container_end[]",
})
end
local function buttonhandler(this, fields)
if fields.reinstall then
-- Don't set "no_mtg_notification" here so that the dialog will be shown
-- again if downloading MTG fails for whatever reason.
this:delete()
local maintab = ui.find_by_name("maintab")
local dlg = create_store_dlg(nil, "minetest/minetest")
dlg:set_parent(maintab)
maintab:hide()
dlg:show()
return true
end
if fields.dismiss then
core.settings:set_bool("no_mtg_notification", true)
this:delete()
return true
end
end
local function eventhandler(event)
if event == "DialogShow" then
mm_game_theme.set_engine()
return true
elseif event == "MenuQuit" then
-- Don't allow closing the dialog with ESC, but still allow exiting
-- Minetest.
core.close()
return true
end
return false
end
function create_reinstall_mtg_dlg()
local dlg = dialog_create("dlg_reinstall_mtg", get_formspec,
buttonhandler, eventhandler)
return dlg
end

File diff suppressed because it is too large Load Diff

@ -71,6 +71,15 @@ local function version_info_buttonhandler(this, fields)
return false
end
local function version_info_eventhandler(event)
if event == "DialogShow" then
mm_game_theme.set_engine()
return true
end
return false
end
local function create_version_info_dlg(new_version, url)
assert(type(new_version) == "string")
assert(type(url) == "string")
@ -78,7 +87,7 @@ local function create_version_info_dlg(new_version, url)
local retval = dialog_create("version_info",
version_info_formspec,
version_info_buttonhandler,
nil)
version_info_eventhandler)
retval.data.new_version = new_version
retval.data.url = url

@ -20,10 +20,6 @@ mm_game_theme = {}
--------------------------------------------------------------------------------
function mm_game_theme.init()
mm_game_theme.defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" ..
DIR_DELIM .. "pack" .. DIR_DELIM
mm_game_theme.basetexturedir = mm_game_theme.defaulttexturedir
mm_game_theme.texturepack = core.settings:get("texture_path")
mm_game_theme.gameid = nil
@ -32,35 +28,27 @@ function mm_game_theme.init()
end
--------------------------------------------------------------------------------
function mm_game_theme.update(tab,gamedetails)
if tab ~= "singleplayer" then
mm_game_theme.reset()
return
end
if gamedetails == nil then
return
end
mm_game_theme.update_game(gamedetails)
end
--------------------------------------------------------------------------------
function mm_game_theme.reset()
function mm_game_theme.set_engine(hide_decorations)
mm_game_theme.gameid = nil
mm_game_theme.stop_music()
core.set_topleft_text("")
local have_bg = false
local have_overlay = mm_game_theme.set_generic("overlay")
local have_overlay = mm_game_theme.set_engine_single("overlay")
if not have_overlay then
have_bg = mm_game_theme.set_generic("background")
have_bg = mm_game_theme.set_engine_single("background")
end
mm_game_theme.clear("header")
mm_game_theme.clear("footer")
mm_game_theme.clear_single("header")
mm_game_theme.clear_single("footer")
core.set_clouds(false)
mm_game_theme.set_generic("footer")
mm_game_theme.set_generic("header")
if not hide_decorations then
mm_game_theme.set_engine_single("header")
mm_game_theme.set_engine_single("footer")
end
if not have_bg then
if core.settings:get_bool("menu_clouds") then
@ -69,51 +57,50 @@ function mm_game_theme.reset()
mm_game_theme.set_dirt_bg()
end
end
if mm_game_theme.music_handle ~= nil then
core.sound_stop(mm_game_theme.music_handle)
end
end
--------------------------------------------------------------------------------
function mm_game_theme.update_game(gamedetails)
function mm_game_theme.set_game(gamedetails)
assert(gamedetails ~= nil)
if mm_game_theme.gameid == gamedetails.id then
return
end
mm_game_theme.gameid = gamedetails.id
mm_game_theme.set_music(gamedetails)
core.set_topleft_text(gamedetails.name)
local have_bg = false
local have_overlay = mm_game_theme.set_game("overlay",gamedetails)
local have_overlay = mm_game_theme.set_game_single("overlay", gamedetails)
if not have_overlay then
have_bg = mm_game_theme.set_game("background",gamedetails)
have_bg = mm_game_theme.set_game_single("background", gamedetails)
end
mm_game_theme.clear("header")
mm_game_theme.clear("footer")
mm_game_theme.clear_single("header")
mm_game_theme.clear_single("footer")
core.set_clouds(false)
if not have_bg then
mm_game_theme.set_game_single("header", gamedetails)
mm_game_theme.set_game_single("footer", gamedetails)
if not have_bg then
if core.settings:get_bool("menu_clouds") then
core.set_clouds(true)
else
mm_game_theme.set_dirt_bg()
end
end
mm_game_theme.set_game("footer",gamedetails)
mm_game_theme.set_game("header",gamedetails)
mm_game_theme.gameid = gamedetails.id
end
--------------------------------------------------------------------------------
function mm_game_theme.clear(identifier)
function mm_game_theme.clear_single(identifier)
core.set_background(identifier,"")
end
--------------------------------------------------------------------------------
function mm_game_theme.set_generic(identifier)
function mm_game_theme.set_engine_single(identifier)
--try texture pack first
if mm_game_theme.texturepack ~= nil then
local path = mm_game_theme.texturepack .. DIR_DELIM .."menu_" ..
@ -123,25 +110,17 @@ function mm_game_theme.set_generic(identifier)
end
end
if mm_game_theme.defaulttexturedir ~= nil then
local path = mm_game_theme.defaulttexturedir .. DIR_DELIM .."menu_" ..
identifier .. ".png"
if core.set_background(identifier,path) then
return true
end
local path = defaulttexturedir .. DIR_DELIM .. "menu_" .. identifier .. ".png"
if core.set_background(identifier, path) then
return true
end
return false
end
--------------------------------------------------------------------------------
function mm_game_theme.set_game(identifier, gamedetails)
if gamedetails == nil then
return false
end
mm_game_theme.set_music(gamedetails)
function mm_game_theme.set_game_single(identifier, gamedetails)
assert(gamedetails ~= nil)
if mm_game_theme.texturepack ~= nil then
local path = mm_game_theme.texturepack .. DIR_DELIM ..
@ -194,10 +173,18 @@ function mm_game_theme.set_dirt_bg()
end
--------------------------------------------------------------------------------
function mm_game_theme.set_music(gamedetails)
function mm_game_theme.stop_music()
if mm_game_theme.music_handle ~= nil then
core.sound_stop(mm_game_theme.music_handle)
end
end
--------------------------------------------------------------------------------
function mm_game_theme.set_music(gamedetails)
mm_game_theme.stop_music()
assert(gamedetails ~= nil)
local music_path = gamedetails.path .. DIR_DELIM .. "menu" .. DIR_DELIM .. "theme"
mm_game_theme.music_handle = core.sound_play(music_path, true)
end

@ -28,6 +28,8 @@ local basepath = core.get_builtin_path()
defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" ..
DIR_DELIM .. "pack" .. DIR_DELIM
dofile(menupath .. DIR_DELIM .. "misc.lua")
dofile(basepath .. "common" .. DIR_DELIM .. "filterlist.lua")
dofile(basepath .. "fstk" .. DIR_DELIM .. "buttonbar.lua")
dofile(basepath .. "fstk" .. DIR_DELIM .. "dialog.lua")
@ -35,27 +37,26 @@ dofile(basepath .. "fstk" .. DIR_DELIM .. "tabview.lua")
dofile(basepath .. "fstk" .. DIR_DELIM .. "ui.lua")
dofile(menupath .. DIR_DELIM .. "async_event.lua")
dofile(menupath .. DIR_DELIM .. "common.lua")
dofile(menupath .. DIR_DELIM .. "pkgmgr.lua")
dofile(menupath .. DIR_DELIM .. "serverlistmgr.lua")
dofile(menupath .. DIR_DELIM .. "game_theme.lua")
dofile(menupath .. DIR_DELIM .. "content" .. DIR_DELIM .. "init.lua")
dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua")
dofile(menupath .. DIR_DELIM .. "dlg_settings_advanced.lua")
dofile(menupath .. DIR_DELIM .. "dlg_contentstore.lua")
dofile(menupath .. DIR_DELIM .. "settings" .. DIR_DELIM .. "init.lua")
dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua")
dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua")
dofile(menupath .. DIR_DELIM .. "dlg_register.lua")
dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua")
dofile(menupath .. DIR_DELIM .. "dlg_version_info.lua")
dofile(menupath .. DIR_DELIM .. "dlg_reinstall_mtg.lua")
local tabs = {}
tabs.settings = dofile(menupath .. DIR_DELIM .. "tab_settings.lua")
tabs.content = dofile(menupath .. DIR_DELIM .. "tab_content.lua")
tabs.about = dofile(menupath .. DIR_DELIM .. "tab_about.lua")
tabs.local_game = dofile(menupath .. DIR_DELIM .. "tab_local.lua")
tabs.play_online = dofile(menupath .. DIR_DELIM .. "tab_online.lua")
local tabs = {
content = dofile(menupath .. DIR_DELIM .. "tab_content.lua"),
about = dofile(menupath .. DIR_DELIM .. "tab_about.lua"),
local_game = dofile(menupath .. DIR_DELIM .. "tab_local.lua"),
play_online = dofile(menupath .. DIR_DELIM .. "tab_online.lua")
}
--------------------------------------------------------------------------------
local function main_event_handler(tabview, event)
@ -86,26 +87,16 @@ local function init_globals()
menudata.worldlist:add_sort_mechanism("alphabetic", sort_worlds_alphabetic)
menudata.worldlist:set_sortmode("alphabetic")
local gameid = core.settings:get("menu_last_game")
local game = gameid and pkgmgr.find_by_gameid(gameid)
if not game then
gameid = core.settings:get("default_game") or "minetest"
game = pkgmgr.find_by_gameid(gameid)
core.settings:set("menu_last_game", gameid)
end
mm_game_theme.init()
mm_game_theme.set_engine() -- This is just a fallback.
-- Create main tabview
local tv_main = tabview_create("maintab", {x = 12, y = 5.4}, {x = 0, y = 0})
-- note: size would be 15.5,7.1 in real coordinates mode
local tv_main = tabview_create("maintab", {x = 15.5, y = 7.1}, {x = 0, y = 0})
tv_main:set_autosave_tab(true)
tv_main:add(tabs.local_game)
tv_main:add(tabs.play_online)
tv_main:add(tabs.content)
tv_main:add(tabs.settings)
tv_main:add(tabs.about)
tv_main:set_global_event_handler(main_event_handler)
@ -116,16 +107,25 @@ local function init_globals()
tv_main:set_tab(last_tab)
end
-- In case the folder of the last selected game has been deleted,
-- display "Minetest" as a header
if tv_main.current_tab == "local" and not game then
mm_game_theme.reset()
end
tv_main:set_end_button({
icon = defaulttexturedir .. "settings_btn.png",
label = fgettext("Settings"),
name = "open_settings",
on_click = function(tabview)
local dlg = create_settings_dlg()
dlg:set_parent(tabview)
tabview:hide()
dlg:show()
return true
end,
})
ui.set_default("maintab")
check_new_version()
tv_main:show()
ui.update()
check_reinstall_mtg()
check_new_version()
end
init_globals()

@ -0,0 +1,6 @@
-- old non-method sound function
function core.sound_stop(handle, ...)
return handle:stop(...)
end

@ -0,0 +1,407 @@
--Minetest
--Copyright (C) 2022 rubenwardy
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
local make = {}
-- This file defines various component constructors, of the form:
--
-- make.component(setting)
--
-- `setting` is a table representing the settingtype.
--
-- A component is a table with the following:
--
-- * `full_width`: (Optional) true if the component shouldn't reserve space for info / reset.
-- * `info_text`: (Optional) string, informational text shown in an info icon.
-- * `setting`: (Optional) the setting.
-- * `max_w`: (Optional) maximum width, `avail_w` will never exceed this.
-- * `resettable`: (Optional) if this is true, a reset button is shown.
-- * `get_formspec = function(self, avail_w)`:
-- * `avail_w` is the available width for the component.
-- * Returns `fs, used_height`.
-- * `fs` is a string for the formspec.
-- Components should be relative to `0,0`, and not exceed `avail_w` or the returned `used_height`.
-- * `used_height` is the space used by components in `fs`.
-- * `on_submit = function(self, fields, parent)`:
-- * `fields`: submitted formspec fields
-- * `parent`: the fstk element for the settings UI, use to show dialogs
-- * Return true if the event was handled, to prevent future components receiving it.
local function get_label(setting)
local show_technical_names = core.settings:get_bool("show_technical_names")
if not show_technical_names and setting.readable_name then
return fgettext(setting.readable_name)
end
return setting.name
end
local function is_valid_number(value)
return type(value) == "number" and not (value ~= value or value >= math.huge or value <= -math.huge)
end
function make.heading(text)
return {
full_width = true,
get_formspec = function(self, avail_w)
return ("label[0,0.6;%s]box[0,0.9;%f,0.05;#ccc6]"):format(core.formspec_escape(text), avail_w), 1.2
end,
}
end
--- Used for string and numeric style fields
---
--- @param converter Function to coerce values from strings.
--- @param validator Validator function, optional. Returns true when valid.
--- @param stringifier Function to convert values to strings, optional.
local function make_field(converter, validator, stringifier)
return function(setting)
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
local value = core.settings:get(setting.name) or setting.default
self.resettable = core.settings:has(setting.name)
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
avail_w - 1.5, setting.name, get_label(setting), core.formspec_escape(value))
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name)
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))
return fs, 1.1
end,
on_submit = function(self, fields)
if fields["set_" .. setting.name] or fields.key_enter_field == setting.name then
local value = converter(fields[setting.name])
if value == nil or (validator and not validator(value)) then
return true
end
if setting.min then
value = math.max(value, setting.min)
end
if setting.max then
value = math.min(value, setting.max)
end
core.settings:set(setting.name, (stringifier or tostring)(value))
return true
end
end,
}
end
end
make.float = make_field(tonumber, is_valid_number, function(x)
local str = tostring(x)
if str:match("^[+-]?%d+$") then
str = str .. ".0"
end
return str
end)
make.int = make_field(function(x)
local value = tonumber(x)
return value and math.floor(value)
end, is_valid_number)
make.string = make_field(tostring, nil)
function make.bool(setting)
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
local value = core.settings:get_bool(setting.name, core.is_yes(setting.default))
self.resettable = core.settings:has(setting.name)
local fs = ("checkbox[0,0.25;%s;%s;%s]"):format(
setting.name, get_label(setting), tostring(value))
return fs, 0.5
end,
on_submit = function(self, fields)
if fields[setting.name] == nil then
return false
end
core.settings:set_bool(setting.name, core.is_yes(fields[setting.name]))
return true
end,
}
end
function make.enum(setting)
return {
info_text = setting.comment,
setting = setting,
max_w = 4.5,
get_formspec = function(self, avail_w)
local value = core.settings:get(setting.name) or setting.default
self.resettable = core.settings:has(setting.name)
local labels = setting.option_labels or {}
local items = {}
for i, option in ipairs(setting.values) do
items[i] = core.formspec_escape(labels[option] or option)
end
local selected_idx = table.indexof(setting.values, value)
local fs = "label[0,0.1;" .. get_label(setting) .. "]"
fs = fs .. ("dropdown[0,0.3;%f,0.8;%s;%s;%d;true]"):format(
avail_w, setting.name, table.concat(items, ","), selected_idx, value)
return fs, 1.1
end,
on_submit = function(self, fields)
local old_value = core.settings:get(setting.name) or setting.default
local idx = tonumber(fields[setting.name]) or 0
local value = setting.values[idx]
if value == nil or value == old_value then
return false
end
core.settings:set(setting.name, value)
return true
end,
}
end
local function make_path(setting)
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
local value = core.settings:get(setting.name) or setting.default
self.resettable = core.settings:has(setting.name)
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
avail_w - 3, setting.name, get_label(setting), core.formspec_escape(value))
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 3, "pick_" .. setting.name, fgettext("Browse"))
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))
return fs, 1.1
end,
on_submit = function(self, fields)
local dialog_name = "dlg_path_" .. setting.name
if fields["pick_" .. setting.name] then
local is_file = setting.type ~= "path"
core.show_path_select_dialog(dialog_name,
is_file and fgettext_ne("Select file") or fgettext_ne("Select directory"), is_file)
return true
end
if fields[dialog_name .. "_accepted"] then
local value = fields[dialog_name .. "_accepted"]
if value ~= nil then
core.settings:set(setting.name, value)
end
return true
end
if fields["set_" .. setting.name] or fields.key_enter_field == setting.name then
local value = fields[setting.name]
if value ~= nil then
core.settings:set(setting.name, value)
end
return true
end
end,
}
end
if PLATFORM == "Android" then
-- The Irrlicht file picker doesn't work on Android.
make.path = make.string
make.filepath = make.string
else
make.path = make_path
make.filepath = make_path
end
function make.v3f(setting)
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
local value = vector.from_string(core.settings:get(setting.name) or setting.default)
self.resettable = core.settings:has(setting.name)
-- Allocate space for "Set" button
avail_w = avail_w - 1
local fs = "label[0,0.1;" .. get_label(setting) .. "]"
local field_width = (avail_w - 3*0.25) / 3
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
0, field_width, setting.name .. "_x", "X", value.x)
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
field_width + 0.25, field_width, setting.name .. "_y", "Y", value.y)
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
2 * (field_width + 0.25), field_width, setting.name .. "_z", "Z", value.z)
fs = fs .. ("button[%f,0.6;1,0.8;%s;%s]"):format(avail_w, "set_" .. setting.name, fgettext("Set"))
return fs, 1.4
end,
on_submit = function(self, fields)
if fields["set_" .. setting.name] or
fields.key_enter_field == setting.name .. "_x" or
fields.key_enter_field == setting.name .. "_y" or
fields.key_enter_field == setting.name .. "_z" then
local x = tonumber(fields[setting.name .. "_x"])
local y = tonumber(fields[setting.name .. "_y"])
local z = tonumber(fields[setting.name .. "_z"])
if is_valid_number(x) and is_valid_number(y) and is_valid_number(z) then
core.settings:set(setting.name, vector.new(x, y, z):to_string())
else
core.log("error", "Invalid vector: " .. dump({x, y, z}))
end
return true
end
end,
}
end
function make.flags(setting)
local checkboxes = {}
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
local fs = {
"label[0,0.1;" .. get_label(setting) .. "]",
}
local value = core.settings:get(setting.name) or setting.default
self.resettable = core.settings:has(setting.name)
checkboxes = {}
for _, name in ipairs(value:split(",")) do
name = name:trim()
if name:sub(1, 2) == "no" then
checkboxes[name:sub(3)] = false
elseif name ~= "" then
checkboxes[name] = true
end
end
local columns = math.max(math.floor(avail_w / 2.5), 1)
local column_width = avail_w / columns
local x = 0
local y = 0.55
for _, possible in ipairs(setting.possible) do
if possible:sub(1, 2) ~= "no" then
if x >= avail_w then
x = 0
y = y + 0.5
end
local is_checked = checkboxes[possible]
fs[#fs + 1] = ("checkbox[%f,%f;%s;%s;%s]"):format(
x, y, setting.name .. "_" .. possible,
core.formspec_escape(possible), tostring(is_checked))
x = x + column_width
end
end
return table.concat(fs, ""), y + 0.25
end,
on_submit = function(self, fields)
local changed = false
for name, _ in pairs(checkboxes) do
local value = fields[setting.name .. "_" .. name]
if value ~= nil then
checkboxes[name] = core.is_yes(value)
changed = true
end
end
if changed then
local values = {}
for _, name in ipairs(setting.possible) do
if name:sub(1, 2) ~= "no" then
if checkboxes[name] then
table.insert(values, name)
else
table.insert(values, "no" .. name)
end
end
end
core.settings:set(setting.name, table.concat(values, ","))
end
return changed
end
}
end
local function make_noise_params(setting)
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
-- The "defaults" noise parameter flag doesn't reset a noise
-- setting to its default value, so we offer a regular reset button.
self.resettable = core.settings:has(setting.name)
local fs = "label[0,0.4;" .. get_label(setting) .. "]" ..
("button[%f,0;2.5,0.8;%s;%s]"):format(avail_w - 2.5, "edit_" .. setting.name, fgettext("Edit"))
return fs, 0.8
end,
on_submit = function(self, fields, tabview)
if fields["edit_" .. setting.name] then
local dlg = create_change_mapgen_flags_dlg(setting)
dlg:set_parent(tabview)
tabview:hide()
dlg:show()
return true
end
end,
}
end
make.noise_params_2d = make_noise_params
make.noise_params_3d = make_noise_params
return make

@ -0,0 +1,252 @@
--Minetest
--Copyright (C) 2015 PilzAdam
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
local checkboxes = {}
local function flags_to_table(flags)
return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split
end
local function get_current_np_group(setting)
local value = core.settings:get_np_group(setting.name)
if value == nil then
return setting.values
end
local p = "%g"
return {
p:format(value.offset),
p:format(value.scale),
p:format(value.spread.x),
p:format(value.spread.y),
p:format(value.spread.z),
p:format(value.seed),
p:format(value.octaves),
p:format(value.persistence),
p:format(value.lacunarity),
value.flags
}
end
local function get_formspec(dialogdata)
local setting = dialogdata.setting
-- Final formspec will be created at the end of this function
-- Default values below, may be changed depending on setting type
local width = 10
local height = 2
local description_height = 1.5
local t = get_current_np_group(setting)
local dimension = 3
if setting.type == "noise_params_2d" then
dimension = 2
end
local fields = {}
local function add_field(x, name, label, value)
fields[#fields + 1] = ("field[%f,%f;3.3,1;%s;%s;%s]"):format(
x, height, name, label, core.formspec_escape(value or "")
)
end
-- First row
height = height + 0.3
add_field(0.3, "te_offset", fgettext("Offset"), t[1])
add_field(3.6, "te_scale", fgettext("Scale"), t[2])
add_field(6.9, "te_seed", fgettext("Seed"), t[6])
height = height + 1.1
-- Second row
add_field(0.3, "te_spreadx", fgettext("X spread"), t[3])
if dimension == 3 then
add_field(3.6, "te_spready", fgettext("Y spread"), t[4])
else
fields[#fields + 1] = "label[4," .. height - 0.2 .. ";" ..
fgettext("2D Noise") .. "]"
end
add_field(6.9, "te_spreadz", fgettext("Z spread"), t[5])
height = height + 1.1
-- Third row
add_field(0.3, "te_octaves", fgettext("Octaves"), t[7])
add_field(3.6, "te_persist", fgettext("Persistence"), t[8])
add_field(6.9, "te_lacun", fgettext("Lacunarity"), t[9])
height = height + 1.1
local enabled_flags = flags_to_table(t[10])
local flags = {}
for _, name in ipairs(enabled_flags) do
-- Index by name, to avoid iterating over all enabled_flags for every possible flag.
flags[name] = true
end
for _, name in ipairs(setting.flags) do
local checkbox_name = "cb_" .. name
local is_enabled = flags[name] == true -- to get false if nil
checkboxes[checkbox_name] = is_enabled
end
local formspec = table.concat(fields)
.. "checkbox[0.5," .. height - 0.6 .. ";cb_defaults;"
--[[~ "defaults" is a noise parameter flag.
It describes the default processing options
for noise settings in the settings menu. ]]
.. fgettext("defaults") .. ";" -- defaults
.. tostring(flags["defaults"] == true) .. "]" -- to get false if nil
.. "checkbox[5," .. height - 0.6 .. ";cb_eased;"
--[[~ "eased" is a noise parameter flag.
It is used to make the map smoother and
can be enabled in noise settings in
the settings menu. ]]
.. fgettext("eased") .. ";" -- eased
.. tostring(flags["eased"] == true) .. "]"
.. "checkbox[5," .. height - 0.15 .. ";cb_absvalue;"
--[[~ "absvalue" is a noise parameter flag.
It is short for "absolute value".
It can be enabled in noise settings in
the settings menu. ]]
.. fgettext("absvalue") .. ";" -- absvalue
.. tostring(flags["absvalue"] == true) .. "]"
height = height + 1
-- Box good, textarea bad. Calculate textarea size from box.
local function create_textfield(size, label, text, bg_color)
local textarea = {
x = size.x + 0.3,
y = size.y,
w = size.w + 0.25,
h = size.h * 1.16 + 0.12
}
return ("box[%f,%f;%f,%f;%s]textarea[%f,%f;%f,%f;;%s;%s]"):format(
size.x, size.y, size.w, size.h, bg_color or "#000",
textarea.x, textarea.y, textarea.w, textarea.h,
core.formspec_escape(label), core.formspec_escape(text)
)
end
-- When there's an error: Shrink description textarea and add error below
if dialogdata.error_message then
local error_box = {
x = 0,
y = description_height - 0.4,
w = width - 0.25,
h = 0.5
}
formspec = formspec ..
create_textfield(error_box, "", dialogdata.error_message, "#600")
description_height = description_height - 0.75
end
-- Get description field
local description_box = {
x = 0,
y = 0.2,
w = width - 0.25,
h = description_height
}
local setting_name = setting.name
if setting.readable_name then
setting_name = fgettext_ne(setting.readable_name) ..
" (" .. setting.name .. ")"
end
local comment_text
if setting.comment == "" then
comment_text = fgettext_ne("(No description of setting given)")
else
comment_text = fgettext_ne(setting.comment)
end
return (
"size[" .. width .. "," .. height + 0.25 .. ",true]" ..
create_textfield(description_box, setting_name, comment_text) ..
formspec ..
"button[" .. width / 2 - 2.5 .. "," .. height - 0.4 .. ";2.5,1;btn_done;" ..
fgettext("Save") .. "]" ..
"button[" .. width / 2 .. "," .. height - 0.4 .. ";2.5,1;btn_cancel;" ..
fgettext("Cancel") .. "]"
)
end
local function buttonhandler(this, fields)
local setting = this.data.setting
if fields["btn_done"] or fields["key_enter"] then
local np_flags = {}
for _, name in ipairs(setting.flags) do
if checkboxes["cb_" .. name] then
table.insert(np_flags, name)
end
end
checkboxes = {}
if setting.type == "noise_params_2d" then
fields["te_spready"] = fields["te_spreadz"]
end
local new_value = {
offset = fields["te_offset"],
scale = fields["te_scale"],
spread = {
x = fields["te_spreadx"],
y = fields["te_spready"],
z = fields["te_spreadz"]
},
seed = fields["te_seed"],
octaves = fields["te_octaves"],
persistence = fields["te_persist"],
lacunarity = fields["te_lacun"],
flags = table.concat(np_flags, ", ")
}
core.settings:set_np_group(setting.name, new_value)
core.settings:write()
this:delete()
return true
end
if fields["btn_cancel"] then
this:delete()
return true
end
for name, value in pairs(fields) do
if name:sub(1, 3) == "cb_" then
checkboxes[name] = core.is_yes(value)
return false -- Don't update the formspec!
end
end
return false
end
function create_change_mapgen_flags_dlg(setting)
assert(type(setting) == "table")
local retval = dialog_create("dlg_change_mapgen_flags",
get_formspec,
buttonhandler,
nil)
retval.data.setting = setting
return retval
end

@ -0,0 +1,737 @@
--Minetest
--Copyright (C) 2022 rubenwardy
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
local component_funcs = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
"settings" .. DIR_DELIM .. "components.lua")
local shadows_component = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
"settings" .. DIR_DELIM .. "shadows_component.lua")
local full_settings = settingtypes.parse_config_file(false, true)
local info_icon_path = core.formspec_escape(defaulttexturedir .. "settings_info.png")
local reset_icon_path = core.formspec_escape(defaulttexturedir .. "settings_reset.png")
local all_pages = {}
local page_by_id = {}
local filtered_pages = all_pages
local filtered_page_by_id = page_by_id
local function add_page(page)
assert(type(page.id) == "string")
assert(type(page.title) == "string")
assert(page.section == nil or type(page.section) == "string")
assert(type(page.content) == "table")
assert(not page_by_id[page.id], "Page " .. page.id .. " already registered")
all_pages[#all_pages + 1] = page
page_by_id[page.id] = page
return page
end
local change_keys = {
query_text = "Controls",
requires = {
keyboard_mouse = true,
},
get_formspec = function(self, avail_w)
local btn_w = math.min(avail_w, 3)
return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Controls")), 0.8
end,
on_submit = function(self, fields)
if fields.btn_change_keys then
core.show_keys_menu()
end
end,
}
add_page({
id = "accessibility",
title = fgettext_ne("Accessibility"),
content = {
"language",
{ heading = fgettext_ne("General") },
"font_size",
"chat_font_size",
"gui_scaling",
"hud_scaling",
"show_nametag_backgrounds",
{ heading = fgettext_ne("Chat") },
"console_height",
"console_alpha",
"console_color",
{ heading = fgettext_ne("Controls") },
"autojump",
"safe_dig_and_place",
{ heading = fgettext_ne("Movement") },
"arm_inertia",
"view_bobbing_amount",
"fall_bobbing_amount",
},
})
local function load_settingtypes()
local page = nil
local section = nil
local function ensure_page_started()
if not page then
page = add_page({
id = (section or "general"):lower():gsub(" ", "_"),
title = section or fgettext_ne("General"),
section = section,
content = {},
})
end
end
for _, entry in ipairs(full_settings) do
if entry.type == "category" then
if entry.level == 0 then
section = entry.name
page = nil
elseif entry.level == 1 then
page = {
id = ((section and section .. "_" or "") .. entry.name):lower():gsub(" ", "_"),
title = entry.readable_name or entry.name,
section = section,
content = {},
}
page = add_page(page)
elseif entry.level == 2 then
ensure_page_started()
page.content[#page.content + 1] = {
heading = fgettext_ne(entry.readable_name or entry.name),
}
end
else
ensure_page_started()
page.content[#page.content + 1] = entry.name
end
end
end
load_settingtypes()
table.insert(page_by_id.controls_keyboard_and_mouse.content, 1, change_keys)
do
local content = page_by_id.graphics_and_audio_shaders.content
local idx = table.indexof(content, "enable_dynamic_shadows")
table.insert(content, idx, shadows_component)
end
local function get_setting_info(name)
for _, entry in ipairs(full_settings) do
if entry.type ~= "category" and entry.name == name then
return entry
end
end
return nil
end
-- These must not be translated, as they need to show in the local
-- language no matter the user's current language.
-- This list must be kept in sync with src/unsupported_language_list.txt.
get_setting_info("language").option_labels = {
[""] = fgettext_ne("(Use system language)"),
--ar = " [ar]", blacklisted
be = "Беларуская [be]",
bg = "Български [bg]",
ca = "Català [ca]",
cs = "Česky [cs]",
cy = "Cymraeg [cy]",
da = "Dansk [da]",
de = "Deutsch [de]",
--dv = " [dv]", blacklisted
el = "Ελληνικά [el]",
en = "English [en]",
eo = "Esperanto [eo]",
es = "Español [es]",
et = "Eesti [et]",
eu = "Euskara [eu]",
fi = "Suomi [fi]",
fil = "Wikang Filipino [fil]",
fr = "Français [fr]",
gd = "Gàidhlig [gd]",
gl = "Galego [gl]",
--he = " [he]", blacklisted
--hi = " [hi]", blacklisted
hu = "Magyar [hu]",
id = "Bahasa Indonesia [id]",
it = "Italiano [it]",
ja = "日本語 [ja]",
jbo = "Lojban [jbo]",
kk = "Қазақша [kk]",
--kn = " [kn]", blacklisted
ko = "한국어 [ko]",
ky = "Kırgızca / Кыргызча [ky]",
lt = "Lietuvių [lt]",
lv = "Latviešu [lv]",
mn = "Монгол [mn]",
mr = "मराठी [mr]",
ms = "Bahasa Melayu [ms]",
--ms_Arab = " [ms_Arab]", blacklisted
nb = "Norsk Bokmål [nb]",
nl = "Nederlands [nl]",
nn = "Norsk Nynorsk [nn]",
oc = "Occitan [oc]",
pl = "Polski [pl]",
pt = "Português [pt]",
pt_BR = "Português do Brasil [pt_BR]",
ro = "Română [ro]",
ru = "Русский [ru]",
sk = "Slovenčina [sk]",
sl = "Slovenščina [sl]",
sr_Cyrl = "Српски [sr_Cyrl]",
sr_Latn = "Srpski (Latinica) [sr_Latn]",
sv = "Svenska [sv]",
sw = "Kiswahili [sw]",
--th = " [th]", blacklisted
tr = "Türkçe [tr]",
tt = "Tatarça [tt]",
uk = "Українська [uk]",
vi = "Tiếng Việt [vi]",
zh_CN = "中文 (简体) [zh_CN]",
zh_TW = "正體中文 (繁體) [zh_TW]",
}
-- See if setting matches keywords
local function get_setting_match_weight(entry, query_keywords)
local setting_score = 0
for _, keyword in ipairs(query_keywords) do
if string.find(entry.name:lower(), keyword, 1, true) then
setting_score = setting_score + 1
end
if entry.readable_name and
string.find(fgettext(entry.readable_name):lower(), keyword, 1, true) then
setting_score = setting_score + 1
end
if entry.comment and
string.find(fgettext_ne(entry.comment):lower(), keyword, 1, true) then
setting_score = setting_score + 1
end
end
return setting_score
end
local function filter_page_content(page, query_keywords)
if #query_keywords == 0 then
return page.content, 0
end
local retval = {}
local i = 1
local max_weight = 0
for _, content in ipairs(page.content) do
if type(content) == "string" then
local setting = get_setting_info(content)
assert(setting, "Unknown setting: " .. content)
local weight = get_setting_match_weight(setting, query_keywords)
if weight > 0 then
max_weight = math.max(max_weight, weight)
retval[i] = content
i = i + 1
end
elseif type(content) == "table" and content.query_text then
for _, keyword in ipairs(query_keywords) do
if string.find(fgettext(content.query_text), keyword, 1, true) then
max_weight = math.max(max_weight, 1)
retval[i] = content
i = i + 1
break
end
end
end
end
return retval, max_weight
end
local function update_filtered_pages(query)
filtered_pages = {}
filtered_page_by_id = {}
local query_keywords = {}
for word in query:lower():gmatch("%S+") do
table.insert(query_keywords, word)
end
local best_page = nil
local best_page_weight = -1
for _, page in ipairs(all_pages) do
local content, page_weight = filter_page_content(page, query_keywords)
if page_has_contents(page, content) then
local new_page = table.copy(page)
new_page.content = content
filtered_pages[#filtered_pages + 1] = new_page
filtered_page_by_id[new_page.id] = new_page
if page_weight > best_page_weight then
best_page = new_page
best_page_weight = page_weight
end
end
end
return best_page and best_page.id or nil
end
local function check_requirements(name, requires)
if requires == nil then
return true
end
local video_driver = core.get_active_driver()
local shaders_support = video_driver == "opengl" or video_driver == "opengl3" or video_driver == "ogles2"
local special = {
android = PLATFORM == "Android",
desktop = PLATFORM ~= "Android",
touchscreen_gui = core.settings:get_bool("enable_touch"),
keyboard_mouse = not core.settings:get_bool("enable_touch"),
shaders_support = shaders_support,
shaders = core.settings:get_bool("enable_shaders") and shaders_support,
opengl = video_driver == "opengl",
gles = video_driver:sub(1, 5) == "ogles",
}
for req_key, req_value in pairs(requires) do
if special[req_key] == nil then
local required_setting = get_setting_info(req_key)
if required_setting == nil then
core.log("warning", "Unknown setting " .. req_key .. " required by " .. name)
end
local actual_value = core.settings:get_bool(req_key,
required_setting and core.is_yes(required_setting.default))
if actual_value ~= req_value then
return false
end
elseif special[req_key] ~= req_value then
return false
end
end
return true
end
function page_has_contents(page, actual_content)
local is_advanced =
page.id:sub(1, #"client_and_server") == "client_and_server" or
page.id:sub(1, #"mapgen") == "mapgen" or
page.id:sub(1, #"advanced") == "advanced"
local show_advanced = core.settings:get_bool("show_advanced")
if is_advanced and not show_advanced then
return false
end
for _, item in ipairs(actual_content) do
if item == false or item.heading then --luacheck: ignore
-- skip
elseif type(item) == "string" then
local setting = get_setting_info(item)
assert(setting, "Unknown setting: " .. item)
if check_requirements(setting.name, setting.requires) then
return true
end
elseif item.get_formspec then
if check_requirements(item.id, item.requires) then
return true
end
else
error("Unknown content in page: " .. dump(item))
end
end
return false
end
local function build_page_components(page)
-- Filter settings based on requirements
local content = {}
local last_heading
for _, item in ipairs(page.content) do
if item == false then --luacheck: ignore
-- skip
elseif item.heading then
last_heading = item
else
local name, requires
if type(item) == "string" then
local setting = get_setting_info(item)
assert(setting, "Unknown setting: " .. item)
name = setting.name
requires = setting.requires
elseif item.get_formspec then
name = item.id
requires = item.requires
else
error("Unknown content in page: " .. dump(item))
end
if check_requirements(name, requires) then
if last_heading then
content[#content + 1] = last_heading
last_heading = nil
end
content[#content + 1] = item
end
end
end
-- Create components
local retval = {}
for i, item in ipairs(content) do
if type(item) == "string" then
local setting = get_setting_info(item)
local component_func = component_funcs[setting.type]
assert(component_func, "Unknown setting type: " .. setting.type)
retval[i] = component_func(setting)
elseif item.get_formspec then
retval[i] = item
elseif item.heading then
retval[i] = component_funcs.heading(item.heading)
end
end
return retval
end
--- Creates a scrollbaroptions for a scroll_container
--
-- @param visible_l the length of the scroll_container and scrollbar
-- @param total_l length of the scrollable area
-- @param scroll_factor as passed to scroll_container
local function make_scrollbaroptions_for_scroll_container(visible_l, total_l, scroll_factor)
assert(total_l >= visible_l)
local max = total_l - visible_l
local thumb_size = (visible_l / total_l) * max
return ("scrollbaroptions[min=0;max=%f;thumbsize=%f]"):format(max / scroll_factor, thumb_size / scroll_factor)
end
local formspec_show_hack = false
local function get_formspec(dialogdata)
local page_id = dialogdata.page_id or "accessibility"
local page = filtered_page_by_id[page_id]
local extra_h = 1 -- not included in tabsize.height
local tabsize = {
width = core.settings:get_bool("enable_touch") and 16.5 or 15.5,
height = core.settings:get_bool("enable_touch") and (10 - extra_h) or 12,
}
local scrollbar_w = core.settings:get_bool("enable_touch") and 0.6 or 0.4
local left_pane_width = core.settings:get_bool("enable_touch") and 4.5 or 4.25
local left_pane_padding = 0.25
local search_width = left_pane_width + scrollbar_w - (0.75 * 2)
local back_w = 3
local checkbox_w = (tabsize.width - back_w - 2*0.2) / 2
local show_technical_names = core.settings:get_bool("show_technical_names")
local show_advanced = core.settings:get_bool("show_advanced")
formspec_show_hack = not formspec_show_hack
local fs = {
"formspec_version[6]",
"size[", tostring(tabsize.width), ",", tostring(tabsize.height + extra_h), "]",
core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "",
"bgcolor[#0000]",
-- HACK: this is needed to allow resubmitting the same formspec
formspec_show_hack and " " or "",
"box[0,0;", tostring(tabsize.width), ",", tostring(tabsize.height), ";#0000008C]",
("button[0,%f;%f,0.8;back;%s]"):format(
tabsize.height + 0.2, back_w, fgettext("Back")),
("box[%f,%f;%f,0.8;#0000008C]"):format(
back_w + 0.2, tabsize.height + 0.2, checkbox_w),
("checkbox[%f,%f;show_technical_names;%s;%s]"):format(
back_w + 2*0.2, tabsize.height + 0.6,
fgettext("Show technical names"), tostring(show_technical_names)),
("box[%f,%f;%f,0.8;#0000008C]"):format(
back_w + 2*0.2 + checkbox_w, tabsize.height + 0.2, checkbox_w),
("checkbox[%f,%f;show_advanced;%s;%s]"):format(
back_w + 3*0.2 + checkbox_w, tabsize.height + 0.6,
fgettext("Show advanced settings"), tostring(show_advanced)),
"field[0.25,0.25;", tostring(search_width), ",0.75;search_query;;",
core.formspec_escape(dialogdata.query or ""), "]",
"field_enter_after_edit[search_query;true]",
"container[", tostring(search_width + 0.25), ", 0.25]",
"image_button[0,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]",
"image_button[0.75,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";search_clear;]",
"tooltip[search;", fgettext("Search"), "]",
"tooltip[search_clear;", fgettext("Clear"), "]",
"container_end[]",
"scroll_container[0.25,1.25;", tostring(left_pane_width), ",",
tostring(tabsize.height - 1.5), ";leftscroll;vertical;0.1]",
"style_type[button;border=false;bgcolor=#3333]",
"style_type[button:hover;border=false;bgcolor=#6663]",
}
local y = 0
local last_section = nil
for _, other_page in ipairs(filtered_pages) do
if other_page.section ~= last_section then
fs[#fs + 1] = ("label[0.1,%f;%s]"):format(
y + 0.41, core.colorize("#ff0", fgettext(other_page.section)))
last_section = other_page.section
y = y + 0.82
end
fs[#fs + 1] = ("box[0,%f;%f,0.8;%s]"):format(
y, left_pane_width-left_pane_padding, other_page.id == page_id and "#467832FF" or "#3339")
fs[#fs + 1] = ("button[0,%f;%f,0.8;page_%s;%s]")
:format(y, left_pane_width-left_pane_padding, other_page.id, fgettext(other_page.title))
y = y + 0.82
end
if #filtered_pages == 0 then
fs[#fs + 1] = "label[0.1,0.41;"
fs[#fs + 1] = fgettext("No results")
fs[#fs + 1] = "]"
end
fs[#fs + 1] = "scroll_container_end[]"
if y >= tabsize.height - 1.25 then
fs[#fs + 1] = make_scrollbaroptions_for_scroll_container(tabsize.height - 1.5, y, 0.1)
fs[#fs + 1] = ("scrollbar[%f,1.25;%f,%f;vertical;leftscroll;%f]"):format(
left_pane_width + 0.25, scrollbar_w, tabsize.height - 1.5, dialogdata.leftscroll or 0)
end
fs[#fs + 1] = "style_type[button;border=;bgcolor=]"
if not dialogdata.components then
dialogdata.components = page and build_page_components(page) or {}
end
local right_pane_width = tabsize.width - left_pane_width - 0.375 - 2*scrollbar_w - 0.25
fs[#fs + 1] = ("scroll_container[%f,0;%f,%f;rightscroll;vertical;0.1]"):format(
tabsize.width - right_pane_width - scrollbar_w, right_pane_width, tabsize.height)
y = 0.25
for i, comp in ipairs(dialogdata.components) do
fs[#fs + 1] = ("container[0,%f]"):format(y)
local avail_w = right_pane_width - 0.25
if not comp.full_width then
avail_w = avail_w - 1.4
end
if comp.max_w then
avail_w = math.min(avail_w, comp.max_w)
end
local comp_fs, used_h = comp:get_formspec(avail_w)
fs[#fs + 1] = comp_fs
fs[#fs + 1] = "style_type[image_button;border=false;padding=]"
local show_reset = comp.resettable and comp.setting
local show_info = comp.info_text and comp.info_text ~= ""
if show_reset or show_info then
-- ensure there's enough space for reset/info
used_h = math.max(used_h, 0.5)
end
local info_reset_y = used_h / 2 - 0.25
if show_reset then
local default = comp.setting.default
local reset_tooltip = default and
fgettext("Reset setting to default ($1)", tostring(default)) or
fgettext("Reset setting to default")
fs[#fs + 1] = ("image_button[%f,%f;0.5,0.5;%s;%s;]"):format(
right_pane_width - 1.4, info_reset_y, reset_icon_path, "reset_" .. i)
fs[#fs + 1] = ("tooltip[%s;%s]"):format("reset_" .. i, reset_tooltip)
end
if show_info then
local info_x = right_pane_width - 0.75
fs[#fs + 1] = ("image[%f,%f;0.5,0.5;%s]"):format(info_x, info_reset_y, info_icon_path)
fs[#fs + 1] = ("tooltip[%f,%f;0.5,0.5;%s]"):format(info_x, info_reset_y, fgettext(comp.info_text))
end
fs[#fs + 1] = "style_type[image_button;border=;padding=]"
fs[#fs + 1] = "container_end[]"
if used_h > 0 then
y = y + used_h + 0.25
end
end
fs[#fs + 1] = "scroll_container_end[]"
if y >= tabsize.height then
fs[#fs + 1] = make_scrollbaroptions_for_scroll_container(tabsize.height, y + 0.375, 0.1)
fs[#fs + 1] = ("scrollbar[%f,0;%f,%f;vertical;rightscroll;%f]"):format(
tabsize.width - scrollbar_w, scrollbar_w, tabsize.height, dialogdata.rightscroll or 0)
end
return table.concat(fs, "")
end
-- On Android, closing the app via the "Recents screen" won't result in a clean
-- exit, discarding any setting changes made by the user.
-- To avoid that, we write the settings file in more cases on Android.
function write_settings_early()
if PLATFORM == "Android" then
core.settings:write()
end
end
local function buttonhandler(this, fields)
local dialogdata = this.data
dialogdata.leftscroll = core.explode_scrollbar_event(fields.leftscroll).value or dialogdata.leftscroll
dialogdata.rightscroll = core.explode_scrollbar_event(fields.rightscroll).value or dialogdata.rightscroll
dialogdata.query = fields.search_query
if fields.back then
this:delete()
return true
end
if fields.show_technical_names ~= nil then
local value = core.is_yes(fields.show_technical_names)
core.settings:set_bool("show_technical_names", value)
write_settings_early()
return true
end
if fields.show_advanced ~= nil then
local value = core.is_yes(fields.show_advanced)
core.settings:set_bool("show_advanced", value)
write_settings_early()
end
-- enable_touch is a checkbox in a setting component. We handle this
-- setting differently so we can hide/show pages using the next if-statement
if fields.enable_touch ~= nil then
local value = core.is_yes(fields.enable_touch)
core.settings:set_bool("enable_touch", value)
write_settings_early()
end
if fields.show_advanced ~= nil or fields.enable_touch ~= nil then
local suggested_page_id = update_filtered_pages(dialogdata.query)
dialogdata.components = nil
if not filtered_page_by_id[dialogdata.page_id] then
dialogdata.leftscroll = 0
dialogdata.rightscroll = 0
dialogdata.page_id = suggested_page_id
end
return true
end
if fields.search or fields.key_enter_field == "search_query" then
dialogdata.components = nil
dialogdata.leftscroll = 0
dialogdata.rightscroll = 0
dialogdata.page_id = update_filtered_pages(dialogdata.query)
return true
end
if fields.search_clear then
dialogdata.query = ""
dialogdata.components = nil
dialogdata.leftscroll = 0
dialogdata.rightscroll = 0
dialogdata.page_id = update_filtered_pages("")
return true
end
for _, page in ipairs(all_pages) do
if fields["page_" .. page.id] then
dialogdata.page_id = page.id
dialogdata.components = nil
dialogdata.rightscroll = 0
return true
end
end
for i, comp in ipairs(dialogdata.components) do
if comp.on_submit and comp:on_submit(fields, this) then
write_settings_early()
-- Clear components so they regenerate
dialogdata.components = nil
return true
end
if comp.setting and fields["reset_" .. i] then
core.settings:remove(comp.setting.name)
write_settings_early()
-- Clear components so they regenerate
dialogdata.components = nil
return true
end
end
return false
end
local function eventhandler(event)
if event == "DialogShow" then
-- Don't show the "MINETEST" header behind the dialog.
mm_game_theme.set_engine(true)
return true
end
return false
end
function create_settings_dlg()
local dlg = dialog_create("dlg_settings", get_formspec, buttonhandler, eventhandler)
dlg.data.page_id = update_filtered_pages("")
return dlg
end

@ -1,5 +1,3 @@
local settings = ...
local concat = table.concat
local insert = table.insert
local sprintf = string.format
@ -36,7 +34,7 @@ local group_format_template = [[
]]
local function create_minetest_conf_example()
local function create_minetest_conf_example(settings)
local result = { minetest_example_header }
for _, entry in ipairs(settings) do
if entry.type == "category" then
@ -108,14 +106,11 @@ local translation_file_header = [[
fake_function() {]]
local function create_translation_file()
local function create_translation_file(settings)
local result = { translation_file_header }
for _, entry in ipairs(settings) do
if entry.type == "category" then
insert(result, sprintf("\tgettext(%q);", entry.name))
elseif entry.type == "key" then --luacheck: ignore
-- Neither names nor descriptions of keys are used since we have a
-- dedicated menu for them.
else
if entry.readable_name then
insert(result, sprintf("\tgettext(%q);", entry.readable_name))
@ -132,12 +127,13 @@ local function create_translation_file()
end
local file = assert(io.open("minetest.conf.example", "w"))
file:write(create_minetest_conf_example())
file:write(create_minetest_conf_example(settingtypes.parse_config_file(true, false)))
file:close()
file = assert(io.open("src/settings_translation_file.cpp", "w"))
-- If 'minetest.conf.example' appears in the 'bin' folder, the line below may have to be
-- used instead. The file will also appear in the 'bin' folder.
--file = assert(io.open("settings_translation_file.cpp", "w"))
file:write(create_translation_file())
-- We don't want hidden settings to be translated, so we set read_all to false.
file:write(create_translation_file(settingtypes.parse_config_file(false, false)))
file:close()

@ -0,0 +1,28 @@
--Minetest
--Copyright (C) 2022 rubenwardy
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
local path = core.get_mainmenu_path() .. DIR_DELIM .. "settings"
dofile(path .. DIR_DELIM .. "settingtypes.lua")
dofile(path .. DIR_DELIM .. "dlg_change_mapgen_flags.lua")
dofile(path .. DIR_DELIM .. "dlg_settings.lua")
-- Uncomment to generate 'minetest.conf.example' and 'settings_translation_file.cpp'.
-- For RUN_IN_PLACE the generated files may appear in the 'bin' folder.
-- See comment and alternative line at the end of 'generate_from_settingtypes.lua'.
-- dofile(path .. DIR_DELIM .. "generate_from_settingtypes.lua")

@ -0,0 +1,507 @@
--Minetest
--Copyright (C) 2015 PilzAdam
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
settingtypes = {}
-- A Setting type is a table with the following keys:
--
-- name: Identifier
-- readable_name: Readable title
-- type: Category
--
-- name = mod.name,
-- readable_name = mod.title,
-- level = 1,
-- type = "category", "int", "string", ""
-- }
local FILENAME = "settingtypes.txt"
local CHAR_CLASSES = {
SPACE = "[%s]",
VARIABLE = "[%w_%-%.]",
INTEGER = "[+-]?[%d]",
FLOAT = "[+-]?[%d%.]",
FLAGS = "[%w_%-%.,]",
}
local function flags_to_table(flags)
return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split
end
-- returns error message, or nil
local function parse_setting_line(settings, line, read_all, base_level, allow_secure)
-- strip carriage returns (CR, /r)
line = line:gsub("\r", "")
-- comment
local comment_match = line:match("^#" .. CHAR_CLASSES.SPACE .. "*(.*)$")
if comment_match then
settings.current_comment[#settings.current_comment + 1] = comment_match
return
end
-- clear current_comment so only comments directly above a setting are bound to it
-- but keep a local reference to it for variables in the current line
local current_comment = settings.current_comment
settings.current_comment = {}
-- empty lines
if line:match("^" .. CHAR_CLASSES.SPACE .. "*$") then
return
end
-- category
local stars, category = line:match("^%[([%*]*)([^%]]+)%]$")
if category then
local category_level = stars:len() + base_level
if settings.current_hide_level then
if settings.current_hide_level < category_level then
-- Skip this category, it's inside a hidden category.
return
else
-- The start of this category marks the end of a hidden category.
settings.current_hide_level = nil
end
end
if not read_all and category:sub(1, 5) == "Hide:" then
-- This category is hidden.
settings.current_hide_level = category_level
return
end
table.insert(settings, {
name = category,
level = category_level,
type = "category",
})
return
end
if settings.current_hide_level then
-- Ignore this line, we're inside a hidden category.
return
end
-- settings
local first_part, name, readable_name, setting_type = line:match("^"
-- this first capture group matches the whole first part,
-- so we can later strip it from the rest of the line
.. "("
.. "([" .. CHAR_CLASSES.VARIABLE .. "+)" -- variable name
.. CHAR_CLASSES.SPACE .. "*"
.. "%(([^%)]*)%)" -- readable name
.. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.VARIABLE .. "+)" -- type
.. CHAR_CLASSES.SPACE .. "*"
.. ")")
if not first_part then
return "Invalid line"
end
if name:match("secure%.[.]*") and not allow_secure then
return "Tried to add \"secure.\" setting"
end
local requires = {}
local last_line = #current_comment > 0 and current_comment[#current_comment]:trim()
if last_line and last_line:lower():sub(1, 9) == "requires:" then
local parts = last_line:sub(10):split(",")
current_comment[#current_comment] = nil
for _, part in ipairs(parts) do
part = part:trim()
local value = true
if part:sub(1, 1) == "!" then
value = false
part = part:sub(2):trim()
end
requires[part] = value
end
end
if readable_name == "" then
readable_name = nil
end
local remaining_line = line:sub(first_part:len() + 1)
local comment = table.concat(current_comment, "\n"):trim()
if setting_type == "int" then
local default, min, max = remaining_line:match("^"
-- first int is required, the last 2 are optional
.. "(" .. CHAR_CLASSES.INTEGER .. "+)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.INTEGER .. "*)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.INTEGER .. "*)"
.. "$")
if not default or not tonumber(default) then
return "Invalid integer setting"
end
min = tonumber(min)
max = tonumber(max)
table.insert(settings, {
name = name,
readable_name = readable_name,
type = "int",
default = default,
min = min,
max = max,
requires = requires,
comment = comment,
})
return
end
if setting_type == "string"
or setting_type == "key" or setting_type == "v3f" then
local default = remaining_line:match("^(.*)$")
if not default then
return "Invalid string setting"
end
if setting_type == "key" and not read_all then
-- ignore key type if read_all is false
return
end
table.insert(settings, {
name = name,
readable_name = readable_name,
type = setting_type,
default = default,
requires = requires,
comment = comment,
})
return
end
if setting_type == "noise_params_2d"
or setting_type == "noise_params_3d" then
local default = remaining_line:match("^(.*)$")
if not default then
return "Invalid string setting"
end
local values = {}
local ti = 1
local index = 1
for match in default:gmatch("[+-]?[%d.-e]+") do -- All numeric characters
index = default:find("[+-]?[%d.-e]+", index) + match:len()
table.insert(values, match)
ti = ti + 1
if ti > 9 then
break
end
end
index = default:find("[^, ]", index)
local flags = ""
if index then
flags = default:sub(index)
end
table.insert(values, flags)
table.insert(settings, {
name = name,
readable_name = readable_name,
type = setting_type,
default = default,
default_table = {
offset = values[1],
scale = values[2],
spread = {
x = values[3],
y = values[4],
z = values[5]
},
seed = values[6],
octaves = values[7],
persistence = values[8],
lacunarity = values[9],
flags = values[10]
},
values = values,
requires = requires,
comment = comment,
noise_params = true,
flags = flags_to_table("defaults,eased,absvalue")
})
return
end
if setting_type == "bool" then
if remaining_line ~= "false" and remaining_line ~= "true" then
return "Invalid boolean setting"
end
table.insert(settings, {
name = name,
readable_name = readable_name,
type = "bool",
default = remaining_line,
requires = requires,
comment = comment,
})
return
end
if setting_type == "float" then
local default, min, max = remaining_line:match("^"
-- first float is required, the last 2 are optional
.. "(" .. CHAR_CLASSES.FLOAT .. "+)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.FLOAT .. "*)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.FLOAT .. "*)"
.."$")
if not default or not tonumber(default) then
return "Invalid float setting"
end
min = tonumber(min)
max = tonumber(max)
table.insert(settings, {
name = name,
readable_name = readable_name,
type = "float",
default = default,
min = min,
max = max,
requires = requires,
comment = comment,
})
return
end
if setting_type == "enum" then
local default, values = remaining_line:match("^"
-- first value (default) may be empty (i.e. is optional)
.. "(" .. CHAR_CLASSES.VARIABLE .. "*)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.FLAGS .. "+)"
.. "$")
if not default or values == "" then
return "Invalid enum setting"
end
table.insert(settings, {
name = name,
readable_name = readable_name,
type = "enum",
default = default,
values = values:split(",", true),
requires = requires,
comment = comment,
})
return
end
if setting_type == "path" or setting_type == "filepath" then
local default = remaining_line:match("^(.*)$")
if not default then
return "Invalid path setting"
end
table.insert(settings, {
name = name,
readable_name = readable_name,
type = setting_type,
default = default,
requires = requires,
comment = comment,
})
return
end
if setting_type == "flags" then
local default, possible = remaining_line:match("^"
-- first value (default) may be empty (i.e. is optional)
-- this is implemented by making the last value optional, and
-- swapping them around if it turns out empty.
.. "(" .. CHAR_CLASSES.FLAGS .. "+)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.FLAGS .. "*)"
.. "$")
if not default or not possible then
return "Invalid flags setting"
end
if possible == "" then
possible = default
default = ""
end
table.insert(settings, {
name = name,
readable_name = readable_name,
type = "flags",
default = default,
possible = flags_to_table(possible),
requires = requires,
comment = comment,
})
return
end
return "Invalid setting type \"" .. setting_type .. "\""
end
local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure)
-- store this helper variable in the table so it's easier to pass to parse_setting_line()
result.current_comment = {}
result.current_hide_level = nil
local line = file:read("*line")
while line do
local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure)
if error_msg then
core.log("error", error_msg .. " in " .. filepath .. " \"" .. line .. "\"")
end
line = file:read("*line")
end
result.current_comment = nil
result.current_hide_level = nil
end
--- Returns table of setting types
--
-- @param read_all Whether to ignore certain setting types for GUI or not
-- @parse_mods Whether to parse settingtypes.txt in mods and games
function settingtypes.parse_config_file(read_all, parse_mods)
local settings = {}
do
local builtin_path = core.get_builtin_path() .. FILENAME
local file = io.open(builtin_path, "r")
if not file then
core.log("error", "Can't load " .. FILENAME)
return settings
end
parse_single_file(file, builtin_path, read_all, settings, 0, true)
file:close()
end
if parse_mods then
-- Parse games
local games_category_initialized = false
for _, game in ipairs(pkgmgr.games) do
local path = game.path .. DIR_DELIM .. FILENAME
local file = io.open(path, "r")
if file then
if not games_category_initialized then
fgettext_ne("Content: Games") -- not used, but needed for xgettext
table.insert(settings, {
name = "Content: Games",
level = 0,
type = "category",
})
games_category_initialized = true
end
table.insert(settings, {
name = game.path,
readable_name = game.title,
level = 1,
type = "category",
})
parse_single_file(file, path, read_all, settings, 2, false)
file:close()
end
end
-- Parse mods
local mods_category_initialized = false
local mods = {}
pkgmgr.get_mods(core.get_modpath(), "mods", mods)
table.sort(mods, function(a, b) return a.name < b.name end)
for _, mod in ipairs(mods) do
local path = mod.path .. DIR_DELIM .. FILENAME
local file = io.open(path, "r")
if file then
if not mods_category_initialized then
fgettext_ne("Content: Mods") -- not used, but needed for xgettext
table.insert(settings, {
name = "Content: Mods",
level = 0,
type = "category",
})
mods_category_initialized = true
end
table.insert(settings, {
name = mod.path,
readable_name = mod.title or mod.name,
level = 1,
type = "category",
})
parse_single_file(file, path, read_all, settings, 2, false)
file:close()
end
end
-- Parse client mods
local clientmods_category_initialized = false
local clientmods = {}
pkgmgr.get_mods(core.get_clientmodpath(), "clientmods", clientmods)
for _, mod in ipairs(clientmods) do
local path = mod.path .. DIR_DELIM .. FILENAME
local file = io.open(path, "r")
if file then
if not clientmods_category_initialized then
fgettext_ne("Client Mods") -- not used, but needed for xgettext
table.insert(settings, {
name = "Client Mods",
level = 0,
type = "category",
})
clientmods_category_initialized = true
end
table.insert(settings, {
name = mod.path,
readable_name = mod.title or mod.name,
level = 1,
type = "category",
})
parse_single_file(file, path, read_all, settings, 2, false)
file:close()
end
end
end
return settings
end

@ -0,0 +1,121 @@
--Minetest
--Copyright (C) 2021-2 x2048
--Copyright (C) 2022-3 rubenwardy
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
local shadow_levels_labels = {
fgettext("Disabled"),
fgettext("Very Low"),
fgettext("Low"),
fgettext("Medium"),
fgettext("High"),
fgettext("Very High"),
fgettext("Custom"),
}
local PRESET_DISABLED = 1
local PRESET_CUSTOM = #shadow_levels_labels
-- max distance, texture size, texture_32bit, filters, map color
local shadow_presets = {
[2] = { 62, 512, true, 0, false },
[3] = { 93, 1024, true, 0, false },
[4] = { 140, 2048, true, 1, false },
[5] = { 210, 4096, true, 2, true },
[6] = { 300, 8192, true, 2, true },
}
local function detect_mapping_idx()
if not core.settings:get_bool("enable_dynamic_shadows", false) then
return PRESET_DISABLED
end
local shadow_map_max_distance = tonumber(core.settings:get("shadow_map_max_distance"))
local shadow_map_texture_size = tonumber(core.settings:get("shadow_map_texture_size"))
local shadow_map_texture_32bit = core.settings:get_bool("shadow_map_texture_32bit", false)
local shadow_filters = tonumber(core.settings:get("shadow_filters"))
local shadow_map_color = core.settings:get_bool("shadow_map_color", false)
for i = 2, 6 do
local preset = shadow_presets[i]
if preset[1] == shadow_map_max_distance and
preset[2] == shadow_map_texture_size and
preset[3] == shadow_map_texture_32bit and
preset[4] == shadow_filters and
preset[5] == shadow_map_color then
return i
end
end
return PRESET_CUSTOM
end
local function apply_preset(preset)
if preset then
core.settings:set_bool("enable_dynamic_shadows", true)
core.settings:set("shadow_map_max_distance", preset[1])
core.settings:set("shadow_map_texture_size", preset[2])
core.settings:set_bool("shadow_map_texture_32bit", preset[3])
core.settings:set("shadow_filters", preset[4])
core.settings:set_bool("shadow_map_color", preset[5])
else
core.settings:set_bool("enable_dynamic_shadows", false)
end
end
return {
query_text = "Shadows",
requires = {
shaders = true,
opengl = true,
},
get_formspec = function(self, avail_w)
local labels = table.copy(shadow_levels_labels)
local idx = detect_mapping_idx()
-- Remove "custom" if not already selected
if idx ~= PRESET_CUSTOM then
table.remove(labels, PRESET_CUSTOM)
end
local fs =
"label[0,0.2;" .. fgettext("Dynamic shadows") .. "]" ..
"dropdown[0,0.4;3,0.8;dd_shadows;" .. table.concat(labels, ",") .. ";" .. idx .. ";true]" ..
"label[0,1.5;" .. core.colorize("#bbb", fgettext("(The game will need to enable shadows as well)")) .. "]"
return fs, 1.8
end,
on_submit = function(self, fields)
if fields.dd_shadows then
local old_shadow_level_idx = detect_mapping_idx()
local shadow_level_idx = tonumber(fields.dd_shadows)
if shadow_level_idx == nil or shadow_level_idx == old_shadow_level_idx then
return false
end
if shadow_level_idx == PRESET_CUSTOM then
core.settings:set_bool("enable_dynamic_shadows", true)
return true
end
local preset = shadow_presets[shadow_level_idx]
apply_preset(preset)
return true
end
end,
}

@ -27,9 +27,9 @@ local core_developers = {
"Krock/SmallJoker <mk939@ymail.com>",
"Lars Hofhansl <larsh@apache.org>",
"v-rob <robinsonvincent89@gmail.com>",
"Hugues Ross <hugues.ross@gmail.com>",
"Dmitry Kostenko (x2048) <codeforsmile@gmail.com>",
"Desour",
"Desour/DS",
"srifqi",
"Gregor Parzefall (grorp)",
}
-- currently only https://github.com/orgs/minetest/teams/triagers/members
@ -43,18 +43,19 @@ local core_team = {
-- For updating active/previous contributors, see the script in ./util/gather_git_credits.py
local active_contributors = {
"Wuzzy [Features, translations, devtest]",
"Lars Müller [Bugfixes and entity features]",
"paradust7 [Bugfixes]",
"ROllerozxa [Bugfixes, Android]",
"srifqi [Android, translations]",
"Lexi Hale [Particlespawner animation]",
"Wuzzy [Features, translations, documentation]",
"numzero [Optimizations, work on OpenGL driver]",
"ROllerozxa [Bugfixes, Mainmenu]",
"Lars Müller [Bugfixes]",
"AFCMS [Documentation]",
"savilli [Bugfixes]",
"fluxionary [Bugfixes]",
"Gregor Parzefall [Bugfixes]",
"Bradley Pierce (Thresher) [Documentation]",
"Stvk imension [Android]",
"JosiahWI [Code cleanups]",
"OgelGames [UI, Bugfixes]",
"ndren [Bugfixes]",
"Abdou-31 [Documentation]",
"pecksin [Bouncy physics]",
"Daroc Alden [Fixes]",
}
local previous_core_developers = {
@ -75,13 +76,14 @@ local previous_core_developers = {
"Pierre-Yves Rollo <dev@pyrollo.com>",
"hecks",
"Jude Melton-Houghton (TurkeyMcMac) [RIP]",
"Hugues Ross <hugues.ross@gmail.com>",
"Dmitry Kostenko (x2048) <codeforsmile@gmail.com>",
}
local previous_contributors = {
"Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net> [Minetest logo]",
"red-001 <red-001@outlook.ie>",
"Giuseppe Bilotta",
"numzero",
"HybridDog",
"ClobberXD",
"Dániel Juhász (juhdanad) <juhdanad@gmail.com>",
@ -99,83 +101,79 @@ local previous_contributors = {
}
local function prepare_credits(dest, source)
for _, s in ipairs(source) do
-- if there's text inside brackets make it gray-ish
s = s:gsub("%[.-%]", core.colorize("#aaa", "%1"))
dest[#dest+1] = s
end
end
local string = table.concat(source, "\n") .. "\n"
local function build_hacky_list(items, spacing)
spacing = spacing or 0.5
local y = spacing / 2
local ret = {}
for _, item in ipairs(items) do
if item ~= "" then
ret[#ret+1] = ("label[0,%f;%s]"):format(y, core.formspec_escape(item))
end
y = y + spacing
end
return table.concat(ret, ""), y
local hypertext_escapes = {
["\\"] = "\\\\",
["<"] = "\\<",
[">"] = "\\>",
}
string = string:gsub("[\\<>]", hypertext_escapes)
string = string:gsub("%[.-%]", "<gray>%1</gray>")
table.insert(dest, string)
end
return {
name = "about",
caption = fgettext("About"),
cbf_formspec = function(tabview, name, tabdata)
local logofile = defaulttexturedir .. "logo.png"
local version = core.get_version()
local credit_list = {}
table.insert_all(credit_list, {
core.colorize("#000", "Dedication of the current release"),
"The 5.7.0 release is dedicated to the memory of",
"Minetest developer Jude Melton-Houghton (TurkeyMcMac)",
"who died on February 1, 2023.",
"Our thoughts are with his family and friends.",
"",
core.colorize("#ff0", fgettext("Core Developers"))
local hypertext = {
"<tag name=heading color=#ff0>",
"<tag name=gray color=#aaa>",
}
table.insert_all(hypertext, {
"<heading>", fgettext_ne("Core Developers"), "</heading>\n",
})
prepare_credits(credit_list, core_developers)
table.insert_all(credit_list, {
"",
core.colorize("#ff0", fgettext("Core Team"))
prepare_credits(hypertext, core_developers)
table.insert_all(hypertext, {
"\n",
"<heading>", fgettext_ne("Core Team"), "</heading>\n",
})
prepare_credits(credit_list, core_team)
table.insert_all(credit_list, {
"",
core.colorize("#ff0", fgettext("Active Contributors"))
prepare_credits(hypertext, core_team)
table.insert_all(hypertext, {
"\n",
"<heading>", fgettext_ne("Active Contributors"), "</heading>\n",
})
prepare_credits(credit_list, active_contributors)
table.insert_all(credit_list, {
"",
core.colorize("#ff0", fgettext("Previous Core Developers"))
prepare_credits(hypertext, active_contributors)
table.insert_all(hypertext, {
"\n",
"<heading>", fgettext_ne("Previous Core Developers"), "</heading>\n",
})
prepare_credits(credit_list, previous_core_developers)
table.insert_all(credit_list, {
"",
core.colorize("#ff0", fgettext("Previous Contributors"))
prepare_credits(hypertext, previous_core_developers)
table.insert_all(hypertext, {
"\n",
"<heading>", fgettext_ne("Previous Contributors"), "</heading>\n",
})
prepare_credits(credit_list, previous_contributors)
local credit_fs, scroll_height = build_hacky_list(credit_list)
-- account for the visible portion
scroll_height = math.max(0, scroll_height - 6.9)
prepare_credits(hypertext, previous_contributors)
hypertext = table.concat(hypertext):sub(1, -2)
local fs = "image[1.5,0.6;2.5,2.5;" .. core.formspec_escape(logofile) .. "]" ..
"style[label_button;border=false]" ..
"button[0.1,3.4;5.3,0.5;label_button;" ..
core.formspec_escape(version.project .. " " .. version.string) .. "]" ..
"button[1.5,4.1;2.5,0.8;homepage;minetest.net]" ..
"scroll_container[5.5,0.1;9.5,6.9;scroll_credits;vertical;" ..
tostring(scroll_height / 1000) .. "]" .. credit_fs ..
"scroll_container_end[]"..
"scrollbar[15,0.1;0.4,6.9;vertical;scroll_credits;0]"
"hypertext[5.5,0.25;9.75,6.6;credits;" .. minetest.formspec_escape(hypertext) .. "]"
-- Render information
local active_renderer_info = fgettext("Active renderer:") .. " " ..
core.formspec_escape(core.get_active_renderer())
fs = fs .. "style[label_button2;border=false]" ..
"button[0.1,6;5.3,1;label_button2;" ..
fgettext("Active renderer:") .. "\n" ..
core.formspec_escape(core.get_active_renderer()) .. "]"
"button[0.1,6;5.3,0.5;label_button2;" .. active_renderer_info .. "]"..
"tooltip[label_button2;" .. active_renderer_info .. "]"
-- Irrlicht device information
local irrlicht_device_info = fgettext("Irrlicht device:") .. " " ..
core.formspec_escape(core.get_active_irrlicht_device())
fs = fs .. "style[label_button3;border=false]" ..
"button[0.1,6.5;5.3,0.5;label_button3;" .. irrlicht_device_info .. "]"..
"tooltip[label_button3;" .. irrlicht_device_info .. "]"
if PLATFORM == "Android" then
fs = fs .. "button[0.5,5.1;4.5,0.8;share_debug;" .. fgettext("Share debug log") .. "]"
@ -186,8 +184,9 @@ return {
fs = fs .. "button[0.5,5.1;4.5,0.8;userdata;" .. fgettext("Open User Data Directory") .. "]"
end
return fs, "size[15.5,7.1,false]real_coordinates[true]"
return fs
end,
cbf_button_handler = function(this, fields, name, tabdata)
if fields.homepage then
core.open_url("https://www.minetest.net")
@ -202,4 +201,10 @@ return {
core.open_dir(core.get_user_path())
end
end,
on_change = function(type)
if type == "ENTER" then
mm_game_theme.set_engine()
end
end,
}

@ -16,78 +16,110 @@
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
local packages_raw
local packages
--------------------------------------------------------------------------------
local function get_formspec(tabview, name, tabdata)
if pkgmgr.global_mods == nil then
local function get_content_icons(packages_with_updates)
local ret = {}
for _, content in ipairs(packages_with_updates) do
ret[content.virtual_path or content.path] = { type = "update" }
end
return ret
end
local packages_raw, packages
local function update_packages()
if not pkgmgr.global_mods then
pkgmgr.refresh_globals()
end
if pkgmgr.games == nil then
if not pkgmgr.games then
pkgmgr.update_gamelist()
end
if packages == nil then
packages_raw = {}
table.insert_all(packages_raw, pkgmgr.games)
table.insert_all(packages_raw, pkgmgr.get_texture_packs())
table.insert_all(packages_raw, pkgmgr.global_mods:get_list())
packages_raw = {}
table.insert_all(packages_raw, pkgmgr.games)
table.insert_all(packages_raw, pkgmgr.get_texture_packs())
table.insert_all(packages_raw, pkgmgr.global_mods:get_list())
local function get_data()
return packages_raw
end
local function is_equal(element, uid) --uid match
return (element.type == "game" and element.id == uid) or
element.name == uid
end
packages = filterlist.create(get_data, pkgmgr.compare_package,
is_equal, nil, {})
local function get_data()
return packages_raw
end
if tabdata.selected_pkg == nil then
local function is_equal(element, uid) --uid match
return (element.type == "game" and element.id == uid) or
element.name == uid
end
packages = filterlist.create(get_data, pkgmgr.compare_package,
is_equal, nil, {})
end
local function on_change(type)
if type == "ENTER" then
mm_game_theme.set_engine()
update_packages()
end
end
local function get_formspec(tabview, name, tabdata)
if not packages then
update_packages()
end
if not tabdata.selected_pkg then
tabdata.selected_pkg = 1
end
local use_technical_names = core.settings:get_bool("show_technical_names")
local packages_with_updates = update_detector.get_all()
local update_icons = get_content_icons(packages_with_updates)
local update_count = #packages_with_updates
local contentdb_label
if update_count == 0 then
contentdb_label = fgettext("Browse online content")
else
contentdb_label = fgettext("Browse online content [$1]", update_count)
end
local retval =
"label[0.05,-0.25;".. fgettext("Installed Packages:") .. "]" ..
"tablecolumns[color;tree;text]" ..
"table[0,0.25;5.1,4.3;pkglist;" ..
pkgmgr.render_packagelist(packages, use_technical_names) ..
";" .. tabdata.selected_pkg .. "]" ..
"button[0,4.85;5.25,0.5;btn_contentdb;".. fgettext("Browse online content") .. "]"
local retval = {
"label[0.4,0.4;", fgettext("Installed Packages:"), "]",
"tablecolumns[color;tree;image,align=inline,width=1.5",
",tooltip=", fgettext("Update available?"),
",0=", core.formspec_escape(defaulttexturedir .. "blank.png"),
",4=", core.formspec_escape(defaulttexturedir .. "cdb_update_cropped.png"),
";text]",
"table[0.4,0.8;6.3,4.8;pkglist;",
pkgmgr.render_packagelist(packages, use_technical_names, update_icons),
";", tabdata.selected_pkg, "]",
"button[0.4,5.8;6.3,0.9;btn_contentdb;", contentdb_label, "]"
}
local selected_pkg
if filterlist.size(packages) >= tabdata.selected_pkg then
selected_pkg = packages:get_list()[tabdata.selected_pkg]
end
if selected_pkg ~= nil then
--check for screenshot beeing available
if selected_pkg then
-- Check for screenshot being available
local screenshotfilename = selected_pkg.path .. DIR_DELIM .. "screenshot.png"
local screenshotfile, error = io.open(screenshotfilename, "r")
local modscreenshot
if error == nil then
if not error then
screenshotfile:close()
modscreenshot = screenshotfilename
else
modscreenshot = defaulttexturedir .. "no_screenshot.png"
end
if modscreenshot == nil then
modscreenshot = defaulttexturedir .. "no_screenshot.png"
local desc = fgettext("No package description available")
if selected_pkg.description and selected_pkg.description:trim() ~= "" then
desc = core.formspec_escape(selected_pkg.description)
end
local info = core.get_content_info(selected_pkg.path)
local desc = fgettext("No package description available")
if info.description and info.description:trim() ~= "" then
desc = info.description
end
local title_and_name
if selected_pkg.type == "game" then
@ -97,65 +129,76 @@ local function get_formspec(tabview, name, tabdata)
core.colorize("#BFBFBF", selected_pkg.name)
end
retval = retval ..
"image[5.5,0;3,2;" .. core.formspec_escape(modscreenshot) .. "]" ..
"label[8.25,0.6;" .. core.formspec_escape(title_and_name) .. "]" ..
"box[5.5,2.2;6.15,2.35;#000]"
local desc_height = 3.2
if selected_pkg.type == "mod" then
if selected_pkg.is_modpack then
retval = retval ..
"button[8.65,4.65;3.25,1;btn_mod_mgr_rename_modpack;" ..
fgettext("Rename") .. "]"
if selected_pkg.is_modpack then
desc_height = 2.1
table.insert_all(retval, {
"button[7.1,4.7;8,0.9;btn_mod_mgr_rename_modpack;",
fgettext("Rename"), "]"
})
elseif selected_pkg.type == "mod" then
-- Show dependencies for mods
desc = desc .. "\n\n"
local toadd_hard = table.concat(info.depends or {}, "\n")
local toadd_soft = table.concat(info.optional_depends or {}, "\n")
if toadd_hard == "" and toadd_soft == "" then
desc = desc .. fgettext("No dependencies.")
else
--show dependencies
desc = desc .. "\n\n"
local toadd_hard = table.concat(info.depends or {}, "\n")
local toadd_soft = table.concat(info.optional_depends or {}, "\n")
if toadd_hard == "" and toadd_soft == "" then
desc = desc .. fgettext("No dependencies.")
else
if toadd_hard ~= "" then
desc = desc ..fgettext("Dependencies:") ..
"\n" .. toadd_hard
end
if toadd_soft ~= "" then
if toadd_hard ~= "" then
desc = desc ..fgettext("Dependencies:") ..
"\n" .. toadd_hard
end
if toadd_soft ~= "" then
if toadd_hard ~= "" then
desc = desc .. "\n\n"
end
desc = desc .. fgettext("Optional dependencies:") ..
"\n" .. toadd_soft
desc = desc .. "\n\n"
end
desc = desc .. fgettext("Optional dependencies:") ..
"\n" .. toadd_soft
end
end
elseif selected_pkg.type == "txp" then
desc_height = 2.1
else
if selected_pkg.type == "txp" then
if selected_pkg.enabled then
retval = retval ..
"button[8.65,4.65;3.25,1;btn_mod_mgr_disable_txp;" ..
fgettext("Disable Texture Pack") .. "]"
else
retval = retval ..
"button[8.65,4.65;3.25,1;btn_mod_mgr_use_txp;" ..
fgettext("Use Texture Pack") .. "]"
end
if selected_pkg.enabled then
table.insert_all(retval, {
"button[7.1,4.7;8,0.9;btn_mod_mgr_disable_txp;",
fgettext("Disable Texture Pack"), "]"
})
else
table.insert_all(retval, {
"button[7.1,4.7;8,0.9;btn_mod_mgr_use_txp;",
fgettext("Use Texture Pack"), "]"
})
end
end
retval = retval .. "textarea[5.85,2.2;6.35,2.9;;" ..
fgettext("Information:") .. ";" .. desc .. "]"
table.insert_all(retval, {
"image[7.1,0.2;3,2;", core.formspec_escape(modscreenshot), "]",
"label[10.5,1;", core.formspec_escape(title_and_name), "]",
"box[7.1,2.4;8,", tostring(desc_height), ";#000]",
"textarea[7.1,2.4;8,", tostring(desc_height), ";;;", desc, "]",
})
if core.may_modify_path(selected_pkg.path) then
retval = retval ..
"button[5.5,4.65;3.25,1;btn_mod_mgr_delete_mod;" ..
fgettext("Uninstall Package") .. "]"
table.insert_all(retval, {
"button[7.1,5.8;4,0.9;btn_mod_mgr_delete_mod;",
fgettext("Uninstall"), "]"
})
end
if update_icons[selected_pkg.virtual_path or selected_pkg.path] then
table.insert_all(retval, {
"button[11.1,5.8;4,0.9;btn_mod_mgr_update;",
fgettext("Update"), "]"
})
end
end
return retval
return table.concat(retval)
end
--------------------------------------------------------------------------------
local function handle_doubleclick(pkg)
if pkg.type == "txp" then
if core.settings:get("texture_path") == pkg.path then
@ -166,14 +209,14 @@ local function handle_doubleclick(pkg)
packages = nil
mm_game_theme.init()
mm_game_theme.reset()
mm_game_theme.set_engine()
end
end
--------------------------------------------------------------------------------
local function handle_buttons(tabview, fields, tabname, tabdata)
if fields["pkglist"] ~= nil then
local event = core.explode_table_event(fields["pkglist"])
if fields.pkglist then
local event = core.explode_table_event(fields.pkglist)
tabdata.selected_pkg = event.row
if event.type == "DCL" then
handle_doubleclick(packages:get_list()[tabdata.selected_pkg])
@ -181,7 +224,7 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
return true
end
if fields["btn_contentdb"] ~= nil then
if fields.btn_contentdb then
local dlg = create_store_dlg()
dlg:set_parent(tabview)
tabview:hide()
@ -190,7 +233,7 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
return true
end
if fields["btn_mod_mgr_rename_modpack"] ~= nil then
if fields.btn_mod_mgr_rename_modpack then
local mod = packages:get_list()[tabdata.selected_pkg]
local dlg_renamemp = create_rename_modpack_dlg(mod)
dlg_renamemp:set_parent(tabview)
@ -200,7 +243,7 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
return true
end
if fields["btn_mod_mgr_delete_mod"] ~= nil then
if fields.btn_mod_mgr_delete_mod then
local mod = packages:get_list()[tabdata.selected_pkg]
local dlg_delmod = create_delete_content_dlg(mod)
dlg_delmod:set_parent(tabview)
@ -210,6 +253,16 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
return true
end
if fields.btn_mod_mgr_update then
local pkg = packages:get_list()[tabdata.selected_pkg]
local dlg = create_store_dlg(nil, pkgmgr.get_contentdb_id(pkg))
dlg:set_parent(tabview)
tabview:hide()
dlg:show()
packages = nil
return true
end
if fields.btn_mod_mgr_use_txp or fields.btn_mod_mgr_disable_txp then
local txp_path = ""
if fields.btn_mod_mgr_use_txp then
@ -220,18 +273,24 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
packages = nil
mm_game_theme.init()
mm_game_theme.reset()
mm_game_theme.set_engine()
return true
end
return false
end
--------------------------------------------------------------------------------
return {
name = "content",
caption = fgettext("Content"),
caption = function()
local update_count = update_detector.get_count()
if update_count == 0 then
return fgettext("Content")
else
return fgettext("Content [$1]", update_count)
end
end,
cbf_formspec = get_formspec,
cbf_button_handler = handle_buttons,
on_change = pkgmgr.update_gamelist
on_change = on_change
}

@ -23,21 +23,40 @@ local valid_disabled_settings = {
["enable_server"]=true,
}
-- Name and port stored to persist when updating the formspec
local current_name = core.settings:get("name")
local current_port = core.settings:get("port")
-- Currently chosen game in gamebar for theming and filtering
function current_game()
local last_game_id = core.settings:get("menu_last_game")
local game = pkgmgr.find_by_gameid(last_game_id)
local gameid = core.settings:get("menu_last_game")
local game = gameid and pkgmgr.find_by_gameid(gameid)
-- Fall back to first game installed if one exists.
if not game and #pkgmgr.games > 0 then
-- If devtest is the first game in the list and there is another
-- game available, pick the other game instead.
local picked_game
if pkgmgr.games[1].id == "devtest" and #pkgmgr.games > 1 then
picked_game = 2
else
picked_game = 1
end
game = pkgmgr.games[picked_game]
gameid = game.id
core.settings:set("menu_last_game", gameid)
end
return game
end
-- Apply menu changes from given game
function apply_game(game)
core.set_topleft_text(game.name)
core.settings:set("menu_last_game", game.id)
menudata.worldlist:set_filtercriteria(game.id)
mm_game_theme.update("singleplayer", game) -- this refreshes the formspec
mm_game_theme.set_game(game)
local index = filterlist.get_current_index(menudata.worldlist,
tonumber(core.settings:get("mainmenu_last_selected_world")))
@ -59,16 +78,12 @@ function singleplayer_refresh_gamebar()
old_bar:delete()
end
local function game_buttonbar_button_handler(fields)
if fields.game_open_cdb then
local maintab = ui.find_by_name("maintab")
local dlg = create_store_dlg("game")
dlg:set_parent(maintab)
maintab:hide()
dlg:show()
return true
end
-- Hide gamebar if no games are installed
if #pkgmgr.games == 0 then
return false
end
local function game_buttonbar_button_handler(fields)
for _, game in ipairs(pkgmgr.games) do
if fields["game_btnbar_" .. game.id] then
apply_game(game)
@ -77,9 +92,12 @@ function singleplayer_refresh_gamebar()
end
end
local btnbar = buttonbar_create("game_button_bar",
game_buttonbar_button_handler,
{x=-0.3,y=5.9}, "horizontal", {x=12.4,y=1.15})
local btnbar = buttonbar_create(
"game_button_bar",
core.settings:get_bool("enable_touch") and {x = 0, y = 7.25} or {x = 0, y = 7.475},
{x = 15.5, y = 1.25},
"#000000",
game_buttonbar_button_handler)
for _, game in ipairs(pkgmgr.games) do
local btn_name = "game_btnbar_" .. game.id
@ -105,6 +123,7 @@ function singleplayer_refresh_gamebar()
local plus_image = core.formspec_escape(defaulttexturedir .. "plus.png")
btnbar:add_button("game_open_cdb", "", plus_image, fgettext("Install games from ContentDB"))
return true
end
local function get_disabled_settings(game)
@ -134,6 +153,15 @@ local function get_disabled_settings(game)
end
local function get_formspec(tabview, name, tabdata)
-- Point the player to ContentDB when no games are found
if #pkgmgr.games == 0 then
return table.concat({
"style[label_button;border=false]",
"button[2.75,1.5;10,1;label_button;", fgettext("You have no games installed."), "]",
"button[5.25,3.5;5,1.2;game_open_cdb;", fgettext("Install a game"), "]"})
end
local retval = ""
local index = filterlist.get_current_index(menudata.worldlist,
@ -151,8 +179,8 @@ local function get_formspec(tabview, name, tabdata)
local creative, damage, host = "", "", ""
-- Y offsets for game settings checkboxes
local y = -0.2
local yo = 0.45
local y = 0.2
local yo = 0.5625
if disabled_settings["creative_mode"] == nil then
creative = "checkbox[0,"..y..";cb_creative_mode;".. fgettext("Creative Mode") .. ";" ..
@ -171,41 +199,60 @@ local function get_formspec(tabview, name, tabdata)
end
retval = retval ..
"button[3.9,3.8;2.8,1;world_delete;".. fgettext("Delete") .. "]" ..
"button[6.55,3.8;2.8,1;world_configure;".. fgettext("Select Mods") .. "]" ..
"button[9.2,3.8;2.8,1;world_create;".. fgettext("New") .. "]" ..
"label[3.9,-0.05;".. fgettext("Select World:") .. "]"..
"container[5.25,4.875]" ..
"button[0,0;3.225,0.8;world_delete;".. fgettext("Delete") .. "]" ..
"button[3.325,0;3.225,0.8;world_configure;".. fgettext("Select Mods") .. "]" ..
"button[6.65,0;3.225,0.8;world_create;".. fgettext("New") .. "]" ..
"container_end[]" ..
"container[0.375,0.375]" ..
creative ..
damage ..
host ..
"textlist[3.9,0.4;7.9,3.45;sp_worlds;" ..
"container_end[]" ..
"container[5.25,0.375]" ..
"label[0,0.2;".. fgettext("Select World:") .. "]"..
"textlist[0,0.5;9.875,3.9;sp_worlds;" ..
menu_render_worldlist() ..
";" .. index .. "]"
";" .. index .. "]" ..
"container_end[]"
if core.settings:get_bool("enable_server") and disabled_settings["enable_server"] == nil then
retval = retval ..
"button[7.9,4.75;4.1,1;play;".. fgettext("Host Game") .. "]" ..
"button[10.1875,5.925;4.9375,0.8;play;".. fgettext("Host Game") .. "]" ..
"container[0.375,0.375]" ..
"checkbox[0,"..y..";cb_server_announce;" .. fgettext("Announce Server") .. ";" ..
dump(core.settings:get_bool("server_announce")) .. "]" ..
"field[0.3,2.85;3.8,0.5;te_playername;" .. fgettext("Name") .. ";" ..
core.formspec_escape(core.settings:get("name")) .. "]" ..
"pwdfield[0.3,4.05;3.8,0.5;te_passwd;" .. fgettext("Password") .. "]"
dump(core.settings:get_bool("server_announce")) .. "]"
-- Reset y so that the text fields always start at the same position,
-- regardless of whether some of the checkboxes are hidden.
y = 0.2 + 4 * yo + 0.35
retval = retval .. "field[0," .. y .. ";4.5,0.75;te_playername;" .. fgettext("Name") .. ";" ..
core.formspec_escape(current_name) .. "]"
y = y + 1.15 + 0.25
retval = retval .. "pwdfield[0," .. y .. ";4.5,0.75;te_passwd;" .. fgettext("Password") .. "]"
y = y + 1.15 + 0.25
local bind_addr = core.settings:get("bind_address")
if bind_addr ~= nil and bind_addr ~= "" then
retval = retval ..
"field[0.3,5.25;2.5,0.5;te_serveraddr;" .. fgettext("Bind Address") .. ";" ..
"field[0," .. y .. ";3,0.75;te_serveraddr;" .. fgettext("Bind Address") .. ";" ..
core.formspec_escape(core.settings:get("bind_address")) .. "]" ..
"field[2.85,5.25;1.25,0.5;te_serverport;" .. fgettext("Port") .. ";" ..
core.formspec_escape(core.settings:get("port")) .. "]"
"field[3.25," .. y .. ";1.25,0.75;te_serverport;" .. fgettext("Port") .. ";" ..
core.formspec_escape(current_port) .. "]"
else
retval = retval ..
"field[0.3,5.25;3.8,0.5;te_serverport;" .. fgettext("Server Port") .. ";" ..
core.formspec_escape(core.settings:get("port")) .. "]"
"field[0," .. y .. ";4.5,0.75;te_serverport;" .. fgettext("Server Port") .. ";" ..
core.formspec_escape(current_port) .. "]"
end
retval = retval .. "container_end[]"
else
retval = retval ..
"button[7.9,4.75;4.1,1;play;" .. fgettext("Play Game") .. "]"
"button[10.1875,5.925;4.9375,0.8;play;" .. fgettext("Play Game") .. "]"
end
return retval
@ -215,12 +262,29 @@ local function main_button_handler(this, fields, name, tabdata)
assert(name == "local")
if fields.game_open_cdb then
local maintab = ui.find_by_name("maintab")
local dlg = create_store_dlg("game")
dlg:set_parent(maintab)
maintab:hide()
dlg:show()
return true
end
if this.dlg_create_world_closed_at == nil then
this.dlg_create_world_closed_at = 0
end
local world_doubleclick = false
if fields["te_playername"] then
current_name = fields["te_playername"]
end
if fields["te_serverport"] then
current_port = fields["te_serverport"]
end
if fields["sp_worlds"] ~= nil then
local event = core.explode_textlist_event(fields["sp_worlds"])
local selected = core.get_textlist_index("sp_worlds")
@ -284,7 +348,7 @@ local function main_button_handler(this, fields, name, tabdata)
if selected == nil or gamedata.selected_world == 0 then
gamedata.errormessage =
fgettext("No world created or selected!")
fgettext_ne("No world created or selected!")
return true
end
@ -331,7 +395,6 @@ local function main_button_handler(this, fields, name, tabdata)
create_world_dlg:set_parent(this)
this:hide()
create_world_dlg:show()
mm_game_theme.update("singleplayer", current_game())
return true
end
@ -348,7 +411,6 @@ local function main_button_handler(this, fields, name, tabdata)
delete_world_dlg:set_parent(this)
this:hide()
delete_world_dlg:show()
mm_game_theme.update("singleplayer",current_game())
end
end
@ -366,7 +428,6 @@ local function main_button_handler(this, fields, name, tabdata)
configdialog:set_parent(this)
this:hide()
configdialog:show()
mm_game_theme.update("singleplayer",current_game())
end
end
@ -374,26 +435,24 @@ local function main_button_handler(this, fields, name, tabdata)
end
end
local function on_change(type, old_tab, new_tab)
if (type == "ENTER") then
local function on_change(type)
if type == "ENTER" then
local game = current_game()
if game then
apply_game(game)
else
mm_game_theme.set_engine()
end
singleplayer_refresh_gamebar()
ui.find_by_name("game_button_bar"):show()
else
if singleplayer_refresh_gamebar() then
ui.find_by_name("game_button_bar"):show()
end
elseif type == "LEAVE" then
menudata.worldlist:set_filtercriteria(nil)
local gamebar = ui.find_by_name("game_button_bar")
if gamebar then
gamebar:hide()
end
core.set_topleft_text("")
-- If new_tab is nil, a dialog is being shown; avoid resetting the theme
if new_tab then
mm_game_theme.update(new_tab,nil)
end
end
end

Some files were not shown because too many files have changed in this diff Show More