Compare commits
787 Commits
Author | SHA1 | Date |
---|---|---|
Cora de la Mouche | 6df1590803 | |
Cora de la Mouche | 2c050a42d7 | |
cora | 64e7dda46e | |
cora | 7a59bcda75 | |
Cora de la Mouche | f8fd5c11b6 | |
PrairieWind | c66ae6717a | |
Elias Fleckenstein | 296cce39d3 | |
Elias Fleckenstein | b11c0a6721 | |
Lars Müller | edc7df5480 | |
paradust7 | 951604e29f | |
Zughy | 381f84ee27 | |
sfan5 | 14c283a623 | |
rubenwardy | 4baf56520d | |
sfan5 | a69b7abe00 | |
rubenwardy | 03d86ea0b4 | |
ROllerozxa | 21323ef1ff | |
savilli | 1f39948bc3 | |
Lars Mueller | e82985c0a1 | |
JosiahWI | 8e5bd82c4d | |
sfan5 | 575caa8015 | |
sfan5 | 5f3af7d18b | |
Wuzzy | 6a6b579c54 | |
ROllerozxa | 6d163b72dc | |
Zughy | b72932b445 | |
Elias Fleckenstein | 827b9f8d70 | |
sfan5 | 9fc018ded1 | |
sfan5 | a9a207685a | |
sfan5 | c1d03695d4 | |
sfan5 | ea74680df4 | |
stefan | bb671c3089 | |
sfan5 | 1b68fb7683 | |
sfan5 | 303329f2d6 | |
sfan5 | 85c824ed13 | |
sfan5 | 998e4820c9 | |
sfan5 | 5cd7b0c6e4 | |
sfan5 | 8908a91016 | |
sfan5 | 261a8db9dd | |
sfan5 | f195db2d14 | |
sfan5 | da71e86633 | |
sfan5 | bccaf5fc2d | |
sfan5 | 0c6a029413 | |
x2048 | 25ba9d848d | |
Lars Müller | 76000e676b | |
Lars Müller | e8b2954586 | |
Wuzzy | fe299e24d6 | |
x2048 | ef22c0206f | |
sfan5 | 8b74257bf3 | |
sfan5 | 9a01581cdd | |
sfan5 | 5d26ac0088 | |
x2048 | ed26ed5a1f | |
sfan5 | 16a30556df | |
sfan5 | 5daafc9d33 | |
Richard Try | e16a470d59 | |
paradust7 | 367a2d4b29 | |
Wuzzy | 0f9c78c3eb | |
Zughy | c660218e43 | |
Wuzzy | fa682270a9 | |
Wuzzy | ac5e8176b9 | |
Elias Fleckenstein | 3ff3103e98 | |
paradust7 | 9f338f5a56 | |
paradust7 | 2742fef458 | |
sfan5 | bc59fcf5c5 | |
sfan5 | 2f32044273 | |
paradust7 | 371f21fb35 | |
Zughy | 8edc0fae5f | |
paradust7 | e1f707d7e1 | |
sfan5 | 9ee3dc71f1 | |
sfan5 | 70dc23f996 | |
rubenwardy | 4e9e230e34 | |
x2048 | dc45b85a54 | |
x2048 | a4ef62f5b2 | |
x2048 | 604fb2b738 | |
Elias Fleckenstein | 350b6d175c | |
Elias Fleckenstein | 393c839282 | |
Elias Fleckenstein | 8d19b99c5b | |
Elias Fleckenstein | ccd4c504d5 | |
paradust7 | 273bfee9a1 | |
Elias Fleckenstein | 147aaf326f | |
Elias Fleckenstein | 21df26984d | |
savilli | af37f9dc54 | |
JakobDev | db9b3aff75 | |
Wuzzy | eabf05758e | |
Elias Fleckenstein | b09fc5de5c | |
Dmitry Kostenko | 8756b7a735 | |
sfan5 | ec9f157512 | |
Jude Melton-Houghton | 7f58887ae3 | |
Jude Melton-Houghton | d17d7eba14 | |
Zughy | 4fb4991f5e | |
Octavian | 0f8c46771e | |
sfan5 | f5a8593b11 | |
Lars Müller | c2898f53bc | |
Lars Müller | 089797dbe6 | |
Lars Müller | 53c70b5f27 | |
sfan5 | a66e6d4dff | |
sfan5 | 1fa4f58080 | |
sfan5 | 7fff9da71d | |
ROllerozxa | f128f4cba1 | |
Zughy | 6f0c966877 | |
Jude Melton-Houghton | f10a260301 | |
Jude Melton-Houghton | 54bc8a7627 | |
rubenwardy | 9824a451bb | |
rubenwardy | e0e897832c | |
paradust7 | 87472150bc | |
Froggo | 45d318a773 | |
sfan5 | 4e1de06782 | |
sfan5 | 8735a85a30 | |
sfan5 | e108954633 | |
LoneWolfHT | 47cf257c40 | |
x2048 | cc56ebd90d | |
Lars Müller | 89c82035d8 | |
SmallJoker | 3ce5a68cd1 | |
paradust7 | 0704ca0550 | |
Lars Müller | ae7664597e | |
rubenwardy | e9e671078c | |
sfan5 | 71a56c3552 | |
sfan5 | e7659883cc | |
sfan5 | 663c936428 | |
sfan5 | 56a558baf8 | |
sfan5 | e6385e2ab7 | |
sfan5 | 5362f472ff | |
JakobDev | 41e79d902d | |
x2048 | c7bcebb628 | |
sfan5 | a89afe1229 | |
sfan5 | faecff570c | |
x2048 | 828461c193 | |
qwerty123a2 | ec4a789b4f | |
sfan5 | a65f6f07f3 | |
sfan5 | 00f71c3b9d | |
sfan5 | 3d2bf8fb02 | |
sfan5 | 391eec9ee7 | |
Oblomov | 0d91ef78dd | |
paradust7 | 7e18a1f1be | |
Wuzzy | 7f4fc6f8a7 | |
Wuzzy | a2f13e479b | |
Lars Müller | fccf1e2eac | |
rubenwardy | 480d5f2d51 | |
Alex | a6170963b8 | |
Wuzzy | 77325b92fb | |
Giuseppe Bilotta | b55d7cd45a | |
Giuseppe Bilotta | 23f981c458 | |
Lars Müller | 48d1bca9b8 | |
olive | a13cf0e3ce | |
SmallJoker | 1c8614ac9a | |
Lars Müller | 4558793caf | |
Lars Müller | 583257f093 | |
paradust7 | 7cea688a1c | |
olive | 062dd8dabc | |
SmallJoker | 1d07a36552 | |
x2048 | a5d29fa1d4 | |
Lars Mueller | 9aabd911eb | |
Lars Müller | 1f27bf6380 | |
ShadowNinja | 2d8eac4e0a | |
ShadowNinja | 833538cc90 | |
ShadowNinja | 80db8804c7 | |
ShadowNinja | d9effbb179 | |
ShadowNinja | 24a0f55c9c | |
ShadowNinja | f5e54cd398 | |
ShadowNinja | c9317a16c5 | |
ShadowNinja | dae6fe91a1 | |
ShadowNinja | 65fdc7ae50 | |
ShadowNinja | 00ebedad93 | |
ShadowNinja | 35bfffb556 | |
ShadowNinja | 8af332c9a7 | |
ShadowNinja | 7993909fab | |
ShadowNinja | 88b21a72f1 | |
ShadowNinja | ea2fba877a | |
ShadowNinja | 5683bb76cc | |
Dmitry Kostenko | 3a87fab6c8 | |
Dmitry Kostenko | 23516acd0b | |
x2048 | 48f7c5603e | |
Jude Melton-Houghton | 0b5b2b2633 | |
x2048 | 1348d9aaf8 | |
Jude Melton-Houghton | 21f17e871e | |
sfan5 | 837cea6b4a | |
x2048 | b0b9732359 | |
Dmitry Kostenko | 26c046a563 | |
Dmitry Kostenko | cf650fcaac | |
x2048 | 3dd7d7867b | |
x2048 | 31578303a4 | |
Jude Melton-Houghton | 06d197cdd0 | |
Jude Melton-Houghton | 11aab4198b | |
DS | 8d387433b1 | |
x2048 | 0f25fa7af6 | |
DS | 8d55702d13 | |
Daroc Alden | e54f5e544f | |
Gregor Parzefall | 289c3ff377 | |
Daroc Alden | 11f3f72f1c | |
sfan5 | ad7c72c164 | |
sfan5 | 51294163bb | |
Daroc Alden | 598efbf7f9 | |
Dmitry Kostenko | b651bbf446 | |
Dmitry Kostenko | 4801bdf45a | |
Dmitry Kostenko | 25c1974e0d | |
Dmitry Kostenko | 12896b22d8 | |
Dmitry Kostenko | e531c59606 | |
Dmitry Kostenko | 8f652f4e31 | |
Dmitry Kostenko | 97cb404822 | |
Dmitry Kostenko | d2a3bed240 | |
Dmitry Kostenko | e4583cb9b7 | |
Dmitry Kostenko | a684a91bf5 | |
Dmitry Kostenko | 1175f48d05 | |
Dmitry Kostenko | 54dccc480e | |
Dmitry Kostenko | f2cccf8da7 | |
Dmitry Kostenko | 10be033791 | |
Dmitry Kostenko | 4e39cdef94 | |
Dmitry Kostenko | 2bba53b2c3 | |
Lars Müller | b9e886726c | |
Zughy | 44fc888bd6 | |
sfan5 | f2d1295fe6 | |
sfan5 | 04bd253390 | |
rubenwardy | 7db751df3b | |
SmallJoker | f7311e0d97 | |
DS | 633e23bd65 | |
Nils Dagsson Moskopp | 7c227d2a00 | |
Lars Müller | 0a0fb11c21 | |
sfan5 | c31b301722 | |
pecksin | 5d0b18a0d0 | |
ROllerozxa | 258ae99491 | |
Wuzzy | 10cf2f3edd | |
Dennis Jenkins | 0cd9c5b5be | |
DS | a8707158a5 | |
Lars Müller | ad1da994b2 | |
sfan5 | ba6fbc417e | |
sfan5 | ce199d6f9e | |
Gaël C | 0dd8e8c242 | |
Lars Müller | b9ee29a945 | |
Zughy | be05c9022d | |
sfan5 | afb061c374 | |
Lars Müller | 1ee37148a8 | |
Jude Melton-Houghton | 1c73902005 | |
DS | d387e9b6d3 | |
sfan5 | 163d3547e6 | |
rubenwardy | c61998bd20 | |
Lars Mueller | 1e4d6672be | |
Lars Mueller | 80812b86d6 | |
rubenwardy | 128f6359e9 | |
sfan5 | 8c0331d244 | |
sfan5 | 54b805ffd0 | |
sfan5 | 484a4b518f | |
sfan5 | 5e4a01f2de | |
sfan5 | 5da204f5bc | |
sfan5 | b66477c29f | |
poi | a0e4b2bf54 | |
Nikita Epifanov | 98982065ed | |
Wuzzy | 49adce1a63 | |
Andrij Mizyk | 7c393f8658 | |
waxtatect | fcd06d99c6 | |
BreadW | f3e23ae972 | |
Marian | 39f5b05ae9 | |
Yaya - Nurul Azeera Hidayah @ Muhammad Nur Hidayat Yasuyoshi | 9205f10208 | |
Balázs Kovács | 8b9e5b47df | |
Thomas Wagner Nielsen | 17bb2712cb | |
Wuzzy | 9d3135a21b | |
Wuzzy | 777fb616b6 | |
ROllerozxa | 172acce352 | |
sfan5 | f69eead62e | |
sfan5 | a9bccb964f | |
SmallJoker | 74a384de0a | |
SmallJoker | a27362de6a | |
sfan5 | 66e8aae9f2 | |
sfan5 | 91c6728eb8 | |
sfan5 | 22f0c66abb | |
sfan5 | 7aea5cb88f | |
sfan5 | 058846d687 | |
Jude Melton-Houghton | fc161e757c | |
SmallJoker | 47735c273c | |
Lars Müller | fe0b2d02bf | |
updatepo.sh | 48e508052a | |
updatepo.sh | 0d0786e414 | |
poi | 4f0d7738b3 | |
Balázs Kovács | 76d7816749 | |
rubenwardy | 8c486aaeae | |
Gao Tiesuan | 2c338032ed | |
Gao Tiesuan | 94128924b2 | |
Gao Tiesuan | 319c4d1274 | |
Wuzzy | 7a8efa7f4f | |
Mehmet Ali | 6c3c2b3f92 | |
Yiu Man Ho | f1ac573e9a | |
Kisbenedek Márton | 0be76c0bf5 | |
pampogo kiraly | c07aeb0075 | |
Sebastian Jasiński | 4e5de9607b | |
Yaya - Nurul Azeera Hidayah @ Muhammad Nur Hidayat Yasuyoshi | c6f3dcc009 | |
Mikita Wiśniewski | 620ba51375 | |
Imre Kristoffer Eilertsen | 1a98351212 | |
Muhammad Rifqi Priyo Susanto | d4746b349d | |
AFCMS | d9caa1bd48 | |
Joaquín Villalba | c3a82b9a49 | |
debiankaios | c4b47269a4 | |
109247019824 | b4cc25f25b | |
Allan Nordhøy | 7645c95110 | |
Minetest-j45 | 4decded6ab | |
waxtatect | 471031e516 | |
AFCMS | c8d621e0b4 | |
ssantos | b1ea3a6199 | |
Mateusz Mendel | bb2d79f930 | |
Gert-dev | c416394ab2 | |
pesder | 02478d2db6 | |
Stas Kies | 9740a35685 | |
xerxstirb | 7f1db70919 | |
ROllerozxa | 02c288fa13 | |
Nikita Epifanov | f6ae8b6350 | |
Linerly | e59e16161f | |
IAmOlive | 7b46bcbbb8 | |
Gabriel Cardoso | 9b2dd0c7b3 | |
BreadW | 8eeab37473 | |
Marian | c745e711ec | |
abidin toumi | f022f030d0 | |
Yaya - Nurul Azeera Hidayah @ Muhammad Nur Hidayat Yasuyoshi | 8ab369475f | |
Oğuz Ersen | 9d1aed01fc | |
Andrei Stepanov | a2cdc6de33 | |
Simone Starace | 828ecac826 | |
Muhammad Rifqi Priyo Susanto | a0edf2849a | |
Wuzzy | e7fe20ada6 | |
waxtatect | 37aa8468a0 | |
Jude Melton-Houghton | 1b2176a426 | |
Vincent Robinson | 95a775cd3a | |
Dmitry Kostenko | 71317b8579 | |
sfan5 | f8cef52ea0 | |
Zughy | 37d80784dd | |
sfan5 | f66ed2c27f | |
sfan5 | 4f6f09590c | |
SmallJoker | 7c321ad7f5 | |
sfan5 | 42839fa1db | |
sfan5 | b02b381af2 | |
Dmitry Kostenko | b6555ee6af | |
Alex | 7c93b2d7a3 | |
sfan5 | 9a12e4499e | |
sfan5 | 379473b670 | |
savilli | 72b14bd994 | |
sfan5 | 76e97e85a0 | |
sfan5 | a90b2a4d4f | |
Dmitry Kostenko | 97248c6957 | |
SmallJoker | b2eb44afc5 | |
sfan5 | 4c8c649779 | |
Lars Mueller | b164e16d1b | |
SmallJoker | 8fab406c28 | |
sfan5 | 5eb45e1ea0 | |
sfan5 | 76dbd0d2d0 | |
Jude Melton-Houghton | bf22569019 | |
Wuzzy | b81948a14c | |
Aritz Erkiaga | 85da2e284b | |
Vincent Robinson | e39b159845 | |
SmallJoker | d33ab97434 | |
Desour | 1965628705 | |
Wuzzy | 84fdd369d4 | |
x2048 | 835524654e | |
x2048 | e030d9cff0 | |
Zughy | 8910c7f8ae | |
ShadowNinja | 29d2b2ccd0 | |
Vincent Robinson | 544b9d5c72 | |
Vincent Robinson | 4a16ab3585 | |
SmallJoker | 14c7fae378 | |
sfan5 | 0ea8df4d64 | |
ROllerozxa | 05573d6d8d | |
Vitaliy | 9b650b9efb | |
SmallJoker | 481bb90eac | |
savilli | 0fa54531d4 | |
William L. DeRieux IV | cc64a0405a | |
JosiahWI | 7f6306ca96 | |
sfan5 | 0c4929f025 | |
Jude Melton-Houghton | 1b664dd870 | |
sfan5 | 49f7d2494c | |
sfan5 | b2409b14d0 | |
sfan5 | f405459548 | |
sfan5 | 8c99f2232b | |
sfan5 | 8472141b79 | |
sfan5 | 1c5ece8334 | |
sfan5 | 378175497a | |
ROllerozxa | fcf86ded8f | |
sfan5 | 84efe279bb | |
Wuzzy | f71091bf52 | |
Wuzzy | 76aa6103e3 | |
Wuzzy | 1ab3eadd87 | |
Francisco | a8c58d5cbb | |
sfan5 | d9d219356a | |
sfan5 | ff934d538c | |
Richard Liu | 7a043b3ebb | |
Wuzzy | 80c3c7e642 | |
SmallJoker | 57a59ae92d | |
Elias Fleckenstein | d404517d2b | |
updatepo.sh | 1dc1305ada | |
updatepo.sh | a157256706 | |
ROllerozxa | 3ac102c93b | |
Ondřej Pfrogner | 3332592905 | |
Johann Lau | 42b8167f3d | |
Molly | fc0897682d | |
Vancha March | 8d99cddecc | |
Joaquín Villalba | 6f9d803d67 | |
Manuel González | c24b02852b | |
109247019824 | 88731631ab | |
phlostically | 714d4e4a81 | |
Markus Mikkonen | 1935783cc6 | |
Ronoaldo Pereira | 9f5d35e2aa | |
Jiri Grönroos | 6569056bfc | |
Heitor | 62c3c90120 | |
Tirifto | 7d2c99e2c2 | |
A M | dc73d441b5 | |
GunChleoc | 4866e06e62 | |
Zhaolin Lau | cd3a8f96c6 | |
Thiago Carmona Monteiro | 0eb829f5de | |
Marian | c062daccc9 | |
phlostically | f57b8f3e8a | |
Allan Nordhøy | efc99a8d42 | |
abidin toumi | ecd4fbc0de | |
Oğuz Ersen | 3590261c78 | |
Emily Ellis | f8dba7e5cf | |
David Leal | dd23991a19 | |
Riceball LEE | 6328b2274a | |
Yangjun Wang | 2e0780b437 | |
Чтабс | dfb1e62bc3 | |
Stefan Vukanovic | c264e47ee7 | |
BreadW | 19d5a5066c | |
Lin Happy 666 | 82f8e24d83 | |
Liet Kynes | 756261bafe | |
Petter Reinholdtsen | a8745948c4 | |
Liet Kynes | 0ce549f49e | |
Er2 | 7445a72f76 | |
Ács Zoltán | 5cb9a36916 | |
Mateusz Mendel | 3dbf52a871 | |
ResuUman | 9ca08800e1 | |
Mateusz Mendel | 1556e7eb09 | |
ResuUman | f5ab059c0f | |
Mateusz Mendel | 8ac17b877a | |
ResuUman | 9da60be08e | |
Mateusz Mendel | 0fc3684613 | |
ResuUman | c0b977bf32 | |
Mateusz Mendel | d3fab295ad | |
ResuUman | 58170e36ef | |
Чтабс | 60d6be1329 | |
Janar Leas | 5929c8f5ca | |
Wuzzy | 28851ca9b2 | |
Nicolae Crefelean | 5f79e9552d | |
Jordan Irwin | 891290a79f | |
Riceball LEE | e1fbf4795b | |
Yangjun Wang | 48bb0bb5bb | |
waxtatect | b9c1a999ff | |
Yaya - Nurul Azeera Hidayah @ Muhammad Nur Hidayat Yasuyoshi | 70ece73da9 | |
rubenwardy | 51cfb57b4d | |
Corey Powell | 413be76c63 | |
ROllerozxa | c85aa0030f | |
sfan5 | 87ab97da2a | |
Lejo | b9051386ae | |
HybridDog | 7a1464d783 | |
Elias Fleckenstein | 1ccf88e80a | |
sfan5 | 206e131854 | |
ExeVirus | 52bfbf6ed0 | |
Andrew Kerr | e35cfa589a | |
savilli | c9070e54bc | |
sfan5 | c510037e9a | |
Elijah Duffy | cbf658f83d | |
sfan5 | 6db914780e | |
Riceball LEE | 693f98373b | |
Wuzzy | 6910c8d920 | |
JosiahWI | 38ba813c55 | |
sfan5 | 0b95da7ad3 | |
sfan5 | ea1396f856 | |
sfan5 | 4114e3047b | |
x2048 | cef016d393 | |
Isabelle COWAN-BERGMAN | 532d5b21fd | |
rubenwardy | 8dfeba02b9 | |
sfan5 | 4ee643f472 | |
Jude Melton-Houghton | 1e26e45530 | |
sfan5 | 660e63dbae | |
Wuzzy | d4b89eb106 | |
sfan5 | a78124831f | |
Lars Müller | 0d345dc1bd | |
Wuzzy | 86b44ecd82 | |
LoneWolfHT | c82ec8b210 | |
rubenwardy | 6901c5fae5 | |
Wuzzy | fe7195badb | |
hecks | 02292e03e4 | |
sfan5 | fe5cb2cdfb | |
savilli | 6ea558f8ac | |
SmallJoker | ecc6f4ba25 | |
sfan5 | 6de8d77e17 | |
sfan5 | 2b5075f0e2 | |
sfan5 | 2d5b7b5fb4 | |
emixa-d | 9fab5d594c | |
HybridDog | 53e126ac49 | |
lhofhansl | b4b9bee5f2 | |
Wuzzy | bc71622d21 | |
Jude Melton-Houghton | 5aa95fef10 | |
Wuzzy | 4fca601e0c | |
Pedro Gimeno | d7e7ade0f6 | |
x2048 | 982e03f60d | |
Wuzzy | 21113ad410 | |
x2048 | f5040707fe | |
SmallJoker | d51d0f3a5a | |
Wuzzy | 918fbe3ec1 | |
nia | 2dc73d239a | |
Wuzzy | bc7d05581b | |
TheBrokenRail | 3dcf9e963e | |
sfan5 | 9f85862b7c | |
Elias Fleckenstein | c8900e169a | |
nia | 2628316842 | |
DS | e79d6154fc | |
Lars Müller | 40ea4ddef1 | |
Elias Fleckenstein | 950d2c9b3e | |
sfan5 | e0529da5c8 | |
sfan5 | 16a62426d6 | |
sfan5 | ad076ede85 | |
sfan5 | fd8a8501bc | |
sfan5 | ea250ff5c5 | |
HybridDog | d1e0f73b77 | |
ROllerozxa | 6fedee16f0 | |
Lars Müller | 719a12ecac | |
sfan5 | 4feb799b7e | |
sfan5 | b480a3e9fd | |
sfan5 | 75bf9b75ca | |
sfan5 | 766e885a1b | |
Jude Melton-Houghton | 7423c4c11e | |
DS | 2cefe51d3b | |
sfan5 | bbfae0cc67 | |
Buckaroo Banzai | bcb6565483 | |
NeroBurner | 7f3401412e | |
20kdc | a3e32d81c5 | |
Wuzzy | ff9945dc6e | |
sfan5 | e912008cb3 | |
sfan5 | 31d2b9edcd | |
sfan5 | e5edda28ce | |
sfan5 | 70dafcf5da | |
lhofhansl | d1624a5521 | |
sfan5 | beac4a2c98 | |
Elias Fleckenstein | fb4815c660 | |
Elias Fleckenstein | f122888148 | |
Minetest-j45 | f3082146c3 | |
Minetest-j45 | a3925db226 | |
Minetest-j45 | 7824a4956b | |
pecksin | 040aed37ab | |
sfan5 | 6a1424f2b1 | |
fn ⌃ ⌥ | 0f8a6d78a7 | |
savilli | 3f1adb49ae | |
NeroBurner | 1d69a23ba4 | |
Treer | 149d8fc8d6 | |
Lean Rada | d36dca3aba | |
Loïc Blot | a7188bd6f5 | |
Loïc Blot | ff3aa18436 | |
JosiahWI | ef84c3b8b9 | |
Wuzzy | 63e8224636 | |
SmallJoker | eea488ed75 | |
DS | dad87a360b | |
sfan5 | fad835cf64 | |
SmallJoker | 0c1e9603db | |
sfan5 | a72d13064f | |
Wuzzy | 6fd8aede48 | |
sfan5 | e7b05beb7d | |
Wuzzy | 1320c51d8e | |
rubenwardy | 24b66dede0 | |
SmallJoker | 3b842a7e02 | |
Wuzzy | 2eec997e97 | |
Lean Rada | 328d949225 | |
sfan5 | 4419e311a9 | |
Treer | 963fbd1572 | |
SmallJoker | b3b075ea02 | |
Hugues Ross | 47c146120a | |
x2048 | 442e48b84f | |
DS | 0709946c75 | |
hecks | eefa39e47b | |
DS | 1ab29f1716 | |
Pevernow | c6eddb0bae | |
SmallJoker | 4a3728d828 | |
rubenwardy | bee50ca7fa | |
SmallJoker | 32cb9d0828 | |
Wuzzy | e7cd4cfa25 | |
sfan5 | 0257e7150f | |
hecks | 1e2b638881 | |
hecktest | 28c98f9fa5 | |
hecks | 80d12dbedb | |
sfan5 | 2866918f32 | |
sfan5 | 6e8aebf432 | |
JosiahWI | cf136914cf | |
Wuzzy | 216728cc5e | |
rubenwardy | 9c145ba0d8 | |
x2048 | bf3acbf388 | |
x2048 | ff2d2a6e93 | |
random-geek | 5d27cc5096 | |
hecks | a049e8267f | |
hecks | 850293bae6 | |
sfan5 | 6caed7073c | |
SmallJoker | 40bee27e56 | |
Wuzzy | f4d8cc0f0b | |
Hugues Ross | 68143ed8ec | |
Wuzzy | 6cdb150c8b | |
Wuzzy | b7b5aad027 | |
sfan5 | 5c89a0e12a | |
x2048 | effb5356ca | |
x2048 | f5706d444b | |
hecks | 1d25d1f7ad | |
hecktest | 29522017a3 | |
SmallJoker | b93bbfde2c | |
Lean Rada | 42fbc757b1 | |
Warr1024 | 52128ae11e | |
hecks | e9bc59e376 | |
SmallJoker | 062fd2190e | |
hecks | 827a7852e2 | |
AFCMS | 8cc04e0cb4 | |
SmallJoker | f2fd443262 | |
sfan5 | 72927b73ca | |
NeroBurner | fa4dee0e62 | |
Wuzzy | 63fc728a84 | |
Wuzzy | 51bf4a6e26 | |
sfan5 | c60a146e22 | |
Juozas | cec0dfcbbd | |
SmallJoker | a8b7c8ff38 | |
Wuzzy | b5c09ada79 | |
NeroBurner | a7143c2a8c | |
Wuzzy | 7fdbf3f231 | |
William L. DeRieux IV | 9d2e7fc983 | |
Bensuperpc | 4b9a51ff0d | |
Wuzzy | b28523bf38 | |
SmallJoker | 2db6b07de1 | |
sfence | b10091be9b | |
pecksin | 1805775f3d | |
rubenwardy | e1b297a14b | |
updatepo.sh | 88bda3d914 | |
updatepo.sh | cb5dd0dae4 | |
Riceball LEE | ce0541fc0e | |
David Leal | 165986beb0 | |
Tirifto | 3474b97921 | |
Yiu Man Ho | 5915941eba | |
Allan Nordhøy | 8575d68d49 | |
telmo bruno silva seabra | 42aa81befd | |
Avyukt More | 392408401c | |
Avyukt More | dd3409c961 | |
Andrei Stepanov | 1a5279e911 | |
Andrij Mizyk | a3b480e300 | |
Nicolae Crefelean | 79a2c6f49d | |
THANOS SIOURDAKIS | 07e7d71bac | |
Gian M | 873feb2619 | |
Omer I.S | a189ce20b1 | |
phlostically | 53e82d0b25 | |
ferrumcccp | dfe9f2c925 | |
ludemys | fd63faa3f3 | |
David Leal | 72ae97d8ef | |
ludemys | 3c761f86c2 | |
Kornelijus Tvarijanavičius | a09db258ee | |
waxtatect | 875e9d4c6f | |
Brian Gaucher | d3fb83db6b | |
Markus Mikkonen | 3cc14d58d4 | |
Tviljan | 33509b13b7 | |
Edward | 686dcc7c59 | |
ssantos | 3a2f9859fe | |
waxtatect | ca254e4e4c | |
David Leal | a66b4f69e9 | |
Timur Seber | 398815c0c5 | |
François Delpierre | bc49e3a8d1 | |
waxtatect | 45ee5c9f03 | |
Timur Seber | 36af92443c | |
BreadW | 20dd05b343 | |
GnuPGを使うべきだ | 34da979ef6 | |
ItsWidee | 5b11a6a800 | |
François Delpierre | f8e9ea38da | |
Dainis | 6405188f68 | |
Konstantin Yeliseyev | 4b6402a005 | |
ResuUman | f922a78d13 | |
Mateusz Mendel | ac46a4e7b3 | |
gnu-ewm | 6393d32332 | |
Alessandro Mandelli | aac0d36a1d | |
Hatlábú Farkas | 9c8c7cfa13 | |
ItsWidee | a016189bb0 | |
matiasC | 116957e131 | |
Joaquín Villalba | e64c299983 | |
AnthonyDe | dc6c43db7d | |
Michalis | 900996c9f1 | |
Yangjun Wang | 2e21b4a1b4 | |
Liu Tao | 9352c26404 | |
Yangjun Wang | 5b2b3464c0 | |
David Leal | 2bc00862d3 | |
Joaquín Villalba | 819fbefc12 | |
David Leal | 1784d5f741 | |
Agustin Calderon | b9456c1a0c | |
abidin toumi | 6e8e0d10c8 | |
Tirifto | 0403de57ed | |
Oğuz Ersen | a70dfcaed9 | |
THANOS SIOURDAKIS | 67b6f9771c | |
Yaya - Nurul Azeera Hidayah @ Muhammad Nur Hidayat Yasuyoshi | afecda0a7d | |
winniepee | fa303ae12e | |
Ayes | 4bc56034cf | |
Wuzzy | a065e22f97 | |
Marian | 8231516bca | |
narrnika | af7160b859 | |
Mateusz Mendel | ec5e149c7a | |
Giov4 | d6debece16 | |
sfan5 | edf098db63 | |
Wuzzy | dc165fe942 | |
benrob0329 | fbcf0fab8e | |
Liso | c47313db65 | |
sfan5 | 46f42e15c4 | |
Muhammad Rifqi Priyo Susanto | 40acfc938c | |
DS | 8f085e02a1 | |
sfan5 | e15cae9fa0 | |
SmallJoker | c9144ae5e2 | |
Lars Müller | 89f3991351 | |
sfan5 | 1bc753f655 | |
sfan5 | f30dcdb504 | |
sfan5 | 2c53f03c18 | |
sfan5 | 758e3aa1ca | |
sfan5 | a0047d6edc | |
sfan5 | a12017c564 | |
Wuzzy | d7a4479eb3 | |
SmallJoker | 5bf72468f3 | |
Wuzzy | ff48619a85 | |
savilli | 673c29f7ea | |
SmallJoker | 93f43c890b | |
sfan5 | 4152227f17 | |
sfan5 | b56a028d6b | |
Lejo | d44f1aabbb | |
Dmitry Marakasov | 53dca4f95f | |
Elias Fleckenstein | 35445d24f4 | |
Elias Fleckenstein | 5131675a60 | |
Elias Fleckenstein | f3e741dad1 | |
Elias Fleckenstein | 96a37aed31 | |
Elias Fleckenstein | d082423166 | |
Elias Fleckenstein | ce0d81a825 | |
sfan5 | 69c70dd319 | |
sfan5 | 2443f1e235 | |
lhofhansl | 7c2826cbc0 | |
Wuzzy | 1bb8449734 | |
Loic Blot | 225d4541ff | |
sfan5 | ba40b39500 | |
sfan5 | 08f1a7fbed | |
Loic Blot | de85bc9227 | |
Loic Blot | 48d5abd5be | |
Loic Blot | a93712458b | |
Loic Blot | 5a02c376ea | |
Loic Blot | ccdd886e27 | |
Loic Blot | a47a00228b | |
Loic Blot | 809e68fdc0 | |
Loic Blot | 1bc855646e | |
Loic Blot | 258101a910 | |
Loic Blot | 74125a74d3 | |
Loic Blot | e0716384d6 | |
Loic Blot | e34d28af9f | |
Loic Blot | bc1888ff21 | |
sfan5 | 83a7b48bb1 | |
Wuzzy | 228f1c6770 | |
sfan5 | 734fb2c811 | |
sfan5 | 9660ae288a | |
Wuzzy | 776015c350 | |
Vincent Robinson | 074e6a67de | |
sfan5 | 3e2145d662 | |
sfan5 | daf862a38a | |
sfan5 | a24899bf2d | |
sfan5 | 1da73418cd | |
Wuzzy | 90a7bd6a0a | |
Seth Traverse | 16e5b39e1d | |
sfan5 | 0077982fb7 | |
sfan5 | 623f0a8613 | |
sfan5 | 52c0384bd1 | |
benrob0329 | a106bfd456 | |
SmallJoker | bbe120308f | |
sfan5 | 4d0fef8ae8 | |
Wuzzy | 4b8209d9a4 | |
sfan5 | 0abc1e98ed | |
Wuzzy | a0e7a4a0df | |
Wuzzy | 8c7e214875 | |
Wuzzy | e89e6c8380 | |
sfan5 | 57218aa9d1 | |
arydev | ea8fa30b67 | |
yw05 | 85163b531f | |
DS | 2332527765 | |
sfan5 | 19c283546c | |
SmallJoker | c11208c4b5 | |
sfan5 | f0bad0e2ba | |
Wuzzy | 3e1904fa8c | |
Loic Blot | 88783679cf | |
Loic Blot | 5de849713e | |
Loic Blot | 78da79b60f | |
Loic Blot | c4b048fbb3 | |
sfan5 | 024d47e0d3 | |
sfan5 | 34888a914e | |
Vincent Robinson | 3560691c0a | |
sfan5 | 1e4913cd76 | |
sfan5 | 0d90ed6d92 | |
Lars Müller | 88d1fcfe23 | |
SmallJoker | f4118a4fde | |
sfan5 | f345d00a43 | |
Wuzzy | 6c9be39db0 | |
Vitaliy | 3b78a22371 | |
Emojigit | fde2785fe3 | |
Wuzzy | 7ad8ca62dc | |
Wuzzy | 7c24a9ebef | |
sfan5 | 8d89f5f0cc | |
sfan5 | 5f4c78a77d | |
HybridDog | fc1512cca6 | |
sfan5 | 6a26d6d15a | |
Loic Blot | 298bb3d8f7 |
|
@ -1,6 +1,7 @@
|
|||
BasedOnStyle: LLVM
|
||||
IndentWidth: 8
|
||||
IndentWidth: 4
|
||||
UseTab: Always
|
||||
TabWidth: 4
|
||||
BreakBeforeBraces: Custom
|
||||
Standard: Cpp11
|
||||
BraceWrapping:
|
||||
|
@ -16,7 +17,7 @@ BraceWrapping:
|
|||
FixNamespaceComments: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
IndentCaseLabels: false
|
||||
AccessModifierOffset: -8
|
||||
AccessModifierOffset: -4
|
||||
ColumnLimit: 90
|
||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||
SortIncludes: false
|
||||
|
@ -26,7 +27,7 @@ IncludeCategories:
|
|||
- Regex: '^<.*'
|
||||
Priority: 1
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
ContinuationIndentWidth: 16
|
||||
ConstructorInitializerIndentWidth: 16
|
||||
ContinuationIndentWidth: 8
|
||||
ConstructorInitializerIndentWidth: 8
|
||||
BreakConstructorInitializers: AfterColon
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
./cmake-build-*
|
||||
./build/*
|
||||
./cache/*
|
||||
Dockerfile
|
|
@ -0,0 +1,9 @@
|
|||
[*]
|
||||
end_of_line = lf
|
||||
|
||||
[*.{cpp,h,lua,txt,glsl,md,c,cmake,java,gradle}]
|
||||
charset = utf8
|
||||
indent_size = 4
|
||||
indent_style = tab
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
|
@ -10,13 +10,31 @@ Contributions are welcome! Here's how you can help:
|
|||
|
||||
## Code
|
||||
|
||||
1. [Fork](https://help.github.com/articles/fork-a-repo/) the repository and [clone](https://help.github.com/articles/cloning-a-repository/) your fork.
|
||||
1. [Fork](https://help.github.com/articles/fork-a-repo/) the repository and
|
||||
[clone](https://help.github.com/articles/cloning-a-repository/) your fork.
|
||||
|
||||
2. Before you start coding, consider opening an [issue at Github](https://github.com/minetest/minetest/issues) to discuss the suitability and implementation of your intended contribution with the core developers. If you are planning to start some very significant coding, you would benefit from first discussing on our IRC development channel [#minetest-dev](http://www.minetest.net/irc/). Note that a proper IRC client is required to speak on this channel.
|
||||
2. Before you start coding, consider opening an
|
||||
[issue at Github](https://github.com/minetest/minetest/issues) to discuss the
|
||||
suitability and implementation of your intended contribution with the core
|
||||
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
|
||||
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.
|
||||
|
||||
You may also benefit from discussing on our IRC development channel
|
||||
[#minetest-dev](http://www.minetest.net/irc/). Note that a proper IRC client
|
||||
is required to speak on this channel.
|
||||
|
||||
3. Start coding!
|
||||
- Refer to the [Lua API](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt), [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 [Lua](http://dev.minetest.net/Lua_code_style_guidelines) code style guidelines.
|
||||
- Refer to the
|
||||
[Lua API](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt),
|
||||
[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
|
||||
[Lua](http://dev.minetest.net/Lua_code_style_guidelines) code style guidelines.
|
||||
- Check your code works as expected and document any changes to the Lua API.
|
||||
|
||||
4. Commit & [push](https://help.github.com/articles/pushing-to-a-remote/) your changes to a new branch (not `master`, one change per branch)
|
||||
|
@ -33,67 +51,115 @@ Contributions are welcome! Here's how you can help:
|
|||
|
||||
5. Once you are happy with your changes, submit a pull request.
|
||||
- Open the [pull-request form](https://github.com/minetest/minetest/pull/new/master).
|
||||
- Add a description explaining what you've done (or if it's a work-in-progress - what you need to do).
|
||||
- Add a description explaining what you've done (or if it's a
|
||||
work-in-progress - what you need to do).
|
||||
- Make sure to fill out the pull request template.
|
||||
|
||||
### A pull-request is considered merge-able when:
|
||||
|
||||
1. It follows the roadmap in some way and fits the whole picture of the project: [roadmap introduction](http://c55.me/blog/?p=1491), [roadmap continued](https://forum.minetest.net/viewtopic.php?t=9177)
|
||||
1. It follows [the roadmap](../doc/direction.md) in some way and fits the whole
|
||||
picture of the project.
|
||||
2. It works.
|
||||
3. It follows the code style for [C/C++](http://dev.minetest.net/Code_style_guidelines) or [Lua](http://dev.minetest.net/Lua_code_style_guidelines).
|
||||
4. The code's interfaces are well designed, regardless of other aspects that might need more work in the future.
|
||||
3. It follows the code style for
|
||||
[C/C++](http://dev.minetest.net/Code_style_guidelines) or
|
||||
[Lua](http://dev.minetest.net/Lua_code_style_guidelines).
|
||||
4. The code's interfaces are well designed, regardless of other aspects that
|
||||
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.
|
||||
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.
|
||||
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 a stable release is on the way.
|
||||
If you experience an issue, we would like to know the details - especially when
|
||||
a stable release is on the way.
|
||||
|
||||
1. Do a quick search on GitHub to check if the issue has already been reported.
|
||||
2. Is it an issue with the Minetest *engine*? If not, report it [elsewhere](http://www.minetest.net/development/#reporting-issues).
|
||||
3. [Open an issue](https://github.com/minetest/minetest/issues/new) and describe the issue you are having - you could include:
|
||||
2. Is it an issue with the Minetest *engine*? If not, report it
|
||||
[elsewhere](http://www.minetest.net/development/#reporting-issues).
|
||||
3. [Open an issue](https://github.com/minetest/minetest/issues/new) and describe
|
||||
the issue you are having - you could include:
|
||||
- Error logs (check the bottom of the `debug.txt` file).
|
||||
- Screenshots.
|
||||
- Ways you have tried to solve the issue, and whether they worked or not.
|
||||
- Your Minetest version and the content (games, mods or texture packs) you have installed.
|
||||
- Your platform (e.g. Windows 10 or Ubuntu 15.04 x64).
|
||||
|
||||
After reporting you should aim to answer questions or clarifications as this helps pinpoint the cause of the issue (if you don't do this your issue may be closed after 1 month).
|
||||
After reporting you should aim to answer questions or clarifications as this
|
||||
helps pinpoint the cause of the issue (if you don't do this your issue may be
|
||||
closed after 1 month).
|
||||
|
||||
## Feature requests
|
||||
|
||||
Feature requests are welcome but take a moment to see if your idea follows the roadmap in some way and fits the whole picture of the project: [roadmap introduction](http://c55.me/blog/?p=1491), [roadmap continued](https://forum.minetest.net/viewtopic.php?t=9177). You should provide a clear explanation with as much detail as possible.
|
||||
Feature requests are welcome but take a moment to see if your idea follows
|
||||
[the roadmap](../doc/direction.md) in some way and fits the whole picture of
|
||||
the project. You should provide a clear explanation with as much detail as
|
||||
possible.
|
||||
|
||||
## Translations
|
||||
|
||||
Translations of Minetest are performed using Weblate. You can access the project page with a list of current languages [here](https://hosted.weblate.org/projects/minetest/minetest/).
|
||||
The core translations of Minetest are performed using Weblate. You can access
|
||||
the project page with a list of current languages
|
||||
[here](https://hosted.weblate.org/projects/minetest/minetest/).
|
||||
|
||||
Builtin (the component which contains things like server messages, chat command
|
||||
descriptions, privilege descriptions) is translated separately; it needs to be
|
||||
translated by editing a `.tr` text file. See
|
||||
[Translation](https://dev.minetest.net/Translation) for more information.
|
||||
|
||||
## Donations
|
||||
|
||||
If you'd like to monetarily support Minetest development, you can find donation methods on [our website](http://www.minetest.net/development/#donate).
|
||||
If you'd like to monetarily support Minetest development, you can find donation
|
||||
methods on [our website](http://www.minetest.net/development/#donate).
|
||||
|
||||
# Maintaining
|
||||
|
||||
*This is a concise version of the [Rules & Guidelines](http://dev.minetest.net/Category:Rules_and_Guidelines) on the developer wiki.*
|
||||
* This is a concise version of the
|
||||
[Rules & Guidelines](http://dev.minetest.net/Category:Rules_and_Guidelines) on the developer wiki.*
|
||||
|
||||
These notes are for those who have push access Minetest (core developers / maintainers).
|
||||
|
||||
- See the [project organisation](http://dev.minetest.net/Organisation) for the people involved.
|
||||
|
||||
## Concept approvals and roadmaps
|
||||
|
||||
If a Pull Request is not a bug fix:
|
||||
|
||||
* If it matches a goal in [the roadmap](../doc/direction.md), then the PR should
|
||||
be labelled as "Roadmap" and the goal stated by number in the description.
|
||||
* If it doesn't match a goal, then it needs to receive a concept approval within
|
||||
a week of being opened to remain open. This 1 week deadline does not apply to
|
||||
PRs opened before the roadmap was adopted; instead, they may remain open or be
|
||||
closed as needed. Use the "Concept Approved" label. Issues can be marked as
|
||||
"Concept Approved" to give preapproval to future PRs.
|
||||
|
||||
## Reviewing pull requests
|
||||
|
||||
Pull requests should be reviewed and, if appropriate, checked if they achieve their intended purpose. You can show that you are in the process of, or will review the pull request by commenting *"Looks good"* or something similar.
|
||||
Pull requests should be reviewed and, if appropriate, checked if they achieve
|
||||
their intended purpose. You can show that you are in the process of, or will
|
||||
review the pull request by commenting *"Looks good"* or something similar.
|
||||
|
||||
**If the pull-request is not [merge-able](#a-pull-request-is-considered-merge-able-when):**
|
||||
|
||||
Submit a comment explaining to the author what they need to change to make the pull-request merge-able.
|
||||
Submit a comment explaining to the author what they need to change to make the
|
||||
pull-request merge-able.
|
||||
|
||||
- If the author comments or makes changes to the pull-request, it can be reviewed again.
|
||||
- If no response is made from the author within 1 month (when improvements are suggested or a question is asked), it can be closed.
|
||||
- If the author comments or makes changes to the pull-request, it can be
|
||||
reviewed again.
|
||||
- If no response is made from the author within 1 month (when improvements are
|
||||
suggested or a question is asked), it can be closed.
|
||||
|
||||
**If the pull-request is [merge-able](#a-pull-request-is-considered-merge-able-when):**
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ assignees: ''
|
|||
|
||||
##### Minetest version
|
||||
<!--
|
||||
Paste Minetest version between quotes below
|
||||
If you are on a devel version, please add git commit hash
|
||||
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.
|
||||
-->
|
||||
```
|
||||
|
@ -29,4 +29,4 @@ OpenGL version:
|
|||
<!-- Describe your problem here -->
|
||||
|
||||
##### Steps to reproduce
|
||||
<!-- For bug reports or build issues, explain how the problem happened -->
|
||||
<!-- Explain how the problem has happened, providing a minimal test (i.e. a code snippet reduced to the bone) where possible -->
|
||||
|
|
|
@ -3,6 +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)?
|
||||
- If not a bug fix, why is this PR needed? What usecases does it solve?
|
||||
|
||||
## To do
|
||||
|
|
|
@ -8,7 +8,7 @@ on:
|
|||
- 'lib/**.cpp'
|
||||
- 'src/**.[ch]'
|
||||
- 'src/**.cpp'
|
||||
- 'build/android/**'
|
||||
- 'android/**'
|
||||
- '.github/workflows/android.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
|
@ -16,27 +16,27 @@ on:
|
|||
- 'lib/**.cpp'
|
||||
- 'src/**.[ch]'
|
||||
- 'src/**.cpp'
|
||||
- 'build/android/**'
|
||||
- 'android/**'
|
||||
- '.github/workflows/android.yml'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends gettext openjdk-11-jdk-headless
|
||||
- name: Build with Gradle
|
||||
run: cd build/android; ./gradlew assemblerelease
|
||||
run: cd android; ./gradlew assemblerelease
|
||||
- name: Save armeabi artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Minetest-armeabi-v7a.apk
|
||||
path: build/android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk
|
||||
path: android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk
|
||||
- name: Save arm64 artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Minetest-arm64-v8a.apk
|
||||
path: build/android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
|
||||
path: android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
|
||||
|
|
|
@ -13,6 +13,8 @@ on:
|
|||
- 'util/buildbot/**'
|
||||
- 'util/ci/**'
|
||||
- '.github/workflows/**.yml'
|
||||
- 'Dockerfile'
|
||||
- '.dockerignore'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'lib/**.[ch]'
|
||||
|
@ -24,59 +26,61 @@ on:
|
|||
- 'util/buildbot/**'
|
||||
- 'util/ci/**'
|
||||
- '.github/workflows/**.yml'
|
||||
- 'Dockerfile'
|
||||
- '.dockerignore'
|
||||
|
||||
jobs:
|
||||
# This is our minor gcc compiler
|
||||
gcc_6:
|
||||
# Older gcc version (should be close to our minimum supported version)
|
||||
gcc_5:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps g++-6
|
||||
install_linux_deps g++-5
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./util/ci/build.sh
|
||||
env:
|
||||
CC: gcc-6
|
||||
CXX: g++-6
|
||||
CC: gcc-5
|
||||
CXX: g++-5
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
# This is the current gcc compiler (available in bionic)
|
||||
gcc_8:
|
||||
runs-on: ubuntu-18.04
|
||||
# Current gcc version
|
||||
gcc_10:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps g++-8
|
||||
install_linux_deps g++-10
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./util/ci/build.sh
|
||||
env:
|
||||
CC: gcc-8
|
||||
CXX: g++-8
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
# This is our minor clang compiler
|
||||
# Older clang version (should be close to our minimum supported version)
|
||||
clang_3_9:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps clang-3.9
|
||||
install_linux_deps clang-3.9 gdb
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
|
@ -85,26 +89,30 @@ jobs:
|
|||
CC: clang-3.9
|
||||
CXX: clang++-3.9
|
||||
|
||||
- name: Test
|
||||
- name: Unittest
|
||||
run: |
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
# This is the current clang version
|
||||
clang_9:
|
||||
runs-on: ubuntu-18.04
|
||||
- name: Integration test + devtest
|
||||
run: |
|
||||
./util/test_multiplayer.sh
|
||||
|
||||
# Current clang version
|
||||
clang_10:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps clang-9 valgrind libluajit-5.1-dev
|
||||
install_linux_deps clang-10 valgrind libluajit-5.1-dev
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./util/ci/build.sh
|
||||
env:
|
||||
CC: clang-9
|
||||
CXX: clang++-9
|
||||
CC: clang-10
|
||||
CXX: clang++-10
|
||||
CMAKE_FLAGS: "-DREQUIRE_LUAJIT=1"
|
||||
|
||||
- name: Test
|
||||
|
@ -118,13 +126,13 @@ jobs:
|
|||
# Build with prometheus-cpp (server-only)
|
||||
clang_9_prometheus:
|
||||
name: "clang_9 (PROMETHEUS=1)"
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps --old-irr clang-9
|
||||
install_linux_deps clang-9
|
||||
|
||||
- name: Build prometheus-cpp
|
||||
run: |
|
||||
|
@ -142,47 +150,25 @@ jobs:
|
|||
run: |
|
||||
./bin/minetestserver --run-unittests
|
||||
|
||||
# Build without freetype (client-only)
|
||||
clang_9_no_freetype:
|
||||
name: "clang_9 (FREETYPE=0)"
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps clang-9
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./util/ci/build.sh
|
||||
env:
|
||||
CC: clang-9
|
||||
CXX: clang++-9
|
||||
CMAKE_FLAGS: "-DENABLE_FREETYPE=0 -DBUILD_SERVER=0"
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
docker:
|
||||
name: "Docker image"
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build docker image
|
||||
run: |
|
||||
docker build .
|
||||
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-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install compiler
|
||||
run: |
|
||||
sudo apt-get update -q && sudo apt-get install gettext -qyy
|
||||
wget http://minetest.kitsunemimi.pw/mingw-w64-i686_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
|
||||
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
|
||||
|
@ -194,13 +180,13 @@ jobs:
|
|||
|
||||
win64:
|
||||
name: "MinGW cross-compiler (64-bit)"
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install compiler
|
||||
run: |
|
||||
sudo apt-get update -q && sudo apt-get install gettext -qyy
|
||||
wget http://minetest.kitsunemimi.pw/mingw-w64-x86_64_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
|
||||
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
|
||||
|
@ -213,13 +199,10 @@ jobs:
|
|||
msvc:
|
||||
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
|
||||
runs-on: windows-2019
|
||||
#### Disabled due to Irrlicht switch
|
||||
if: false
|
||||
#### Disabled due to Irrlicht switch
|
||||
env:
|
||||
VCPKG_VERSION: 0bf3923f9fab4001c00f0f429682a0853b5749e0
|
||||
# 2020.11
|
||||
vcpkg_packages: irrlicht zlib curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit
|
||||
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:
|
||||
|
@ -240,11 +223,17 @@ jobs:
|
|||
# Enable it, when working on the installer.
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Checkout IrrlichtMt
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: minetest/irrlicht
|
||||
path: lib/irrlichtmt/
|
||||
ref: "1.9.0mt6"
|
||||
|
||||
- name: Restore from cache and run vcpkg
|
||||
uses: lukka/run-vcpkg@v5
|
||||
uses: lukka/run-vcpkg@v7
|
||||
with:
|
||||
vcpkgArguments: ${{env.vcpkg_packages}}
|
||||
vcpkgDirectory: '${{ github.workspace }}\vcpkg'
|
||||
|
@ -252,7 +241,7 @@ jobs:
|
|||
vcpkgGitCommitId: ${{ env.VCPKG_VERSION }}
|
||||
vcpkgTriplet: ${{ matrix.config.vcpkg_triplet }}
|
||||
|
||||
- name: CMake
|
||||
- name: Minetest CMake
|
||||
run: |
|
||||
cmake ${{matrix.config.generator}} `
|
||||
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
|
||||
|
@ -260,7 +249,7 @@ jobs:
|
|||
-DENABLE_POSTGRESQL=OFF `
|
||||
-DRUN_IN_PLACE=${{ contains(matrix.type, 'portable') }} .
|
||||
|
||||
- name: Build
|
||||
- name: Build Minetest
|
||||
run: cmake --build . --config Release
|
||||
|
||||
- name: CPack
|
||||
|
@ -279,7 +268,7 @@ jobs:
|
|||
- name: Package Clean
|
||||
run: rm -r $env:GITHUB_WORKSPACE\Package\_CPack_Packages
|
||||
|
||||
- uses: actions/upload-artifact@v1
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: msvc-${{ matrix.config.arch }}-${{ matrix.type }}
|
||||
path: .\Package\
|
||||
|
|
|
@ -26,12 +26,13 @@ on:
|
|||
jobs:
|
||||
|
||||
# clang_format:
|
||||
# runs-on: ubuntu-18.04
|
||||
# runs-on: ubuntu-20.04
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - uses: actions/checkout@v3
|
||||
# - name: Install clang-format
|
||||
# run: |
|
||||
# sudo apt-get install clang-format-9 -qyy
|
||||
# sudo apt-get update
|
||||
# sudo apt-get install -y clang-format-9
|
||||
#
|
||||
# - name: Run clang-format
|
||||
# run: |
|
||||
|
@ -41,14 +42,13 @@ jobs:
|
|||
# CLANG_FORMAT: clang-format-9
|
||||
|
||||
clang_tidy:
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt-get install clang-tidy-9 -qyy
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps
|
||||
install_linux_deps clang-tidy-9
|
||||
|
||||
- name: Run clang-tidy
|
||||
run: |
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
name: lua_lint
|
||||
|
||||
# Lint on lua changes on builtin or if workflow changed
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'builtin/**.lua'
|
||||
- 'games/devtest/**.lua'
|
||||
- '.github/workflows/**.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'builtin/**.lua'
|
||||
- 'games/devtest/**.lua'
|
||||
- '.github/workflows/**.yml'
|
||||
|
||||
jobs:
|
||||
# Note that the integration tests are also run build.yml, but only when C++ code is changed.
|
||||
integration_tests:
|
||||
name: "Compile and run multiplayer tests"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps clang-10 gdb libluajit-5.1-dev
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./util/ci/build.sh
|
||||
env:
|
||||
CC: clang-10
|
||||
CXX: clang++-10
|
||||
CMAKE_FLAGS: "-DENABLE_GETTEXT=0 -DBUILD_SERVER=0"
|
||||
|
||||
- name: Integration test + devtest
|
||||
run: |
|
||||
./util/test_multiplayer.sh
|
||||
|
||||
luacheck:
|
||||
name: "Builtin Luacheck and Unit Tests"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: leafo/gh-actions-lua@v9
|
||||
with:
|
||||
luaVersion: "5.1.5"
|
||||
- uses: leafo/gh-actions-luarocks@v4
|
||||
|
||||
- name: Install luarocks tools
|
||||
run: |
|
||||
luarocks install --local luacheck
|
||||
luarocks install --local busted
|
||||
|
||||
- name: Run checks (builtin)
|
||||
run: |
|
||||
$HOME/.luarocks/bin/luacheck builtin
|
||||
$HOME/.luarocks/bin/busted builtin
|
||||
|
||||
- name: Run checks (devtest)
|
||||
run: |
|
||||
$HOME/.luarocks/bin/luacheck --config=games/devtest/.luacheckrc games/devtest
|
|
@ -1,32 +0,0 @@
|
|||
name: lua_lint
|
||||
|
||||
# Lint on lua changes on builtin or if workflow changed
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'builtin/**.lua'
|
||||
- '.github/workflows/**.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'builtin/**.lua'
|
||||
- '.github/workflows/**.yml'
|
||||
|
||||
jobs:
|
||||
luacheck:
|
||||
name: "Builtin Luacheck and Unit Tests"
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install luarocks
|
||||
run: |
|
||||
sudo apt-get install luarocks -qyy
|
||||
|
||||
- name: Install luarocks tools
|
||||
run: |
|
||||
luarocks install --local luacheck
|
||||
luarocks install --local busted
|
||||
|
||||
- name: Run checks
|
||||
run: |
|
||||
$HOME/.luarocks/bin/luacheck builtin
|
||||
$HOME/.luarocks/bin/busted builtin
|
|
@ -0,0 +1,66 @@
|
|||
name: macos
|
||||
|
||||
# build on c/cpp changes or workflow changes
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'lib/**.[ch]'
|
||||
- 'lib/**.cpp'
|
||||
- 'src/**.[ch]'
|
||||
- 'src/**.cpp'
|
||||
- '**/CMakeLists.txt'
|
||||
- 'cmake/Modules/**'
|
||||
- '.github/workflows/macos.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'lib/**.[ch]'
|
||||
- 'lib/**.cpp'
|
||||
- 'src/**.[ch]'
|
||||
- 'src/**.cpp'
|
||||
- '**/CMakeLists.txt'
|
||||
- 'cmake/Modules/**'
|
||||
- '.github/workflows/macos.yml'
|
||||
|
||||
env:
|
||||
IRRLICHT_TAG: 1.9.0mt6
|
||||
MINETEST_GAME_REPO: https://github.com/minetest/minetest_game.git
|
||||
MINETEST_GAME_BRANCH: master
|
||||
MINETEST_GAME_NAME: minetest_game
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-10.15
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
pkgs=(cmake freetype gettext gmp hiredis jpeg jsoncpp leveldb libogg libpng libvorbis luajit zstd)
|
||||
brew update
|
||||
brew install ${pkgs[@]}
|
||||
brew unlink $(brew ls --formula)
|
||||
brew link ${pkgs[@]}
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
git clone -b $MINETEST_GAME_BRANCH $MINETEST_GAME_REPO games/$MINETEST_GAME_NAME
|
||||
rm -rvf games/$MINETEST_GAME_NAME/.git
|
||||
git clone https://github.com/minetest/irrlicht -b $IRRLICHT_TAG lib/irrlichtmt
|
||||
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_FREETYPE=TRUE -DENABLE_GETTEXT=TRUE
|
||||
make -j2
|
||||
make install
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
./build/macos/minetest.app/Contents/MacOS/minetest --run-unittests
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: minetest-macos
|
||||
path: ./build/macos/
|
|
@ -76,6 +76,7 @@ doc/mkdocs/docs/*.md
|
|||
doc/mkdocs/mkdocs.yml
|
||||
|
||||
## Build files
|
||||
build/
|
||||
CMakeFiles
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
|
@ -86,6 +87,7 @@ 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
|
||||
|
@ -105,3 +107,13 @@ CMakeDoxy*
|
|||
compile_commands.json
|
||||
*.apk
|
||||
*.zip
|
||||
# Visual Studio
|
||||
*.vcxproj*
|
||||
*.sln
|
||||
.vs/
|
||||
|
||||
# Optional user provided library folder
|
||||
lib/irrlichtmt
|
||||
|
||||
# Generated mod storage database
|
||||
client/mod_storage.sqlite
|
||||
|
|
129
.gitlab-ci.yml
|
@ -9,7 +9,7 @@ stages:
|
|||
- deploy
|
||||
|
||||
variables:
|
||||
IRRLICHT_TAG: "1.9.0mt0"
|
||||
IRRLICHT_TAG: "1.9.0mt6"
|
||||
MINETEST_GAME_REPO: "https://github.com/minetest/minetest_game.git"
|
||||
CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH
|
||||
|
||||
|
@ -17,18 +17,12 @@ variables:
|
|||
stage: build
|
||||
before_script:
|
||||
- apt-get update
|
||||
- apt-get -y install build-essential git cmake libpng-dev libjpeg-dev libxxf86vm-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
|
||||
- DEBIAN_FRONTEND=noninteractive apt-get -y install build-essential git cmake libpng-dev libjpeg-dev libxxf86vm-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
|
||||
script:
|
||||
- git clone https://github.com/minetest/irrlicht -b $IRRLICHT_TAG
|
||||
- cd irrlicht
|
||||
- cmake . -DBUILD_SHARED_LIBS=OFF
|
||||
- make -j2
|
||||
- cd ..
|
||||
- mkdir cmakebuild
|
||||
- cd cmakebuild
|
||||
- cmake -DIRRLICHT_LIBRARY=$PWD/../irrlicht/lib/Linux/libIrrlicht.a -DIRRLICHT_INCLUDE_DIR=$PWD/../irrlicht/include -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DBUILD_SERVER=TRUE ..
|
||||
- make -j2
|
||||
- make install
|
||||
- git clone https://github.com/minetest/irrlicht -b $IRRLICHT_TAG lib/irrlichtmt
|
||||
- cmake -B build -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DBUILD_SERVER=TRUE ..
|
||||
- cmake --build build --parallel $(($(nproc) + 1))
|
||||
- cmake --install build
|
||||
artifacts:
|
||||
when: on_success
|
||||
expire_in: 1h
|
||||
|
@ -49,6 +43,7 @@ variables:
|
|||
- sed -i 's/DATEPLACEHOLDER/'$(date +%y.%m.%d)'/g' build/deb/minetest/DEBIAN/control
|
||||
- sed -i 's/JPEG_PLACEHOLDER/'$JPEG_PKG'/g' build/deb/minetest/DEBIAN/control
|
||||
- sed -i 's/LEVELDB_PLACEHOLDER/'$LEVELDB_PKG'/g' build/deb/minetest/DEBIAN/control
|
||||
- sed -i 's/JSONCPP_PLACEHOLDER/'$JSONCPP_PKG'/g' build/deb/minetest/DEBIAN/control
|
||||
- cd build/deb/ && dpkg-deb -b minetest/ && mv minetest.deb ../../
|
||||
artifacts:
|
||||
expire_in: 90 day
|
||||
|
@ -58,7 +53,7 @@ variables:
|
|||
.debpkg_install:
|
||||
stage: deploy
|
||||
before_script:
|
||||
- apt-get update
|
||||
- apt-get update -qy
|
||||
script:
|
||||
- apt-get install -y ./*.deb
|
||||
- minetest --version
|
||||
|
@ -79,6 +74,7 @@ package:debian-9:
|
|||
needs:
|
||||
- build:debian-9
|
||||
variables:
|
||||
JSONCPP_PKG: libjsoncpp1
|
||||
LEVELDB_PKG: libleveldb1v5
|
||||
JPEG_PKG: libjpeg62-turbo
|
||||
|
||||
|
@ -100,6 +96,7 @@ package:debian-10:
|
|||
needs:
|
||||
- build:debian-10
|
||||
variables:
|
||||
JSONCPP_PKG: libjsoncpp1
|
||||
LEVELDB_PKG: libleveldb1d
|
||||
JPEG_PKG: libjpeg62-turbo
|
||||
|
||||
|
@ -109,31 +106,32 @@ deploy:debian-10:
|
|||
needs:
|
||||
- package:debian-10
|
||||
|
||||
# Bullseye
|
||||
|
||||
build:debian-11:
|
||||
extends: .build_template
|
||||
image: debian:11
|
||||
|
||||
package:debian-11:
|
||||
extends: .debpkg_template
|
||||
image: debian:11
|
||||
needs:
|
||||
- build:debian-11
|
||||
variables:
|
||||
JSONCPP_PKG: libjsoncpp24
|
||||
LEVELDB_PKG: libleveldb1d
|
||||
JPEG_PKG: libjpeg62-turbo
|
||||
|
||||
deploy:debian-11:
|
||||
extends: .debpkg_install
|
||||
image: debian:11
|
||||
needs:
|
||||
- package:debian-11
|
||||
|
||||
##
|
||||
## Ubuntu
|
||||
##
|
||||
|
||||
# Xenial
|
||||
|
||||
build:ubuntu-16.04:
|
||||
extends: .build_template
|
||||
image: ubuntu:xenial
|
||||
|
||||
package:ubuntu-16.04:
|
||||
extends: .debpkg_template
|
||||
image: ubuntu:xenial
|
||||
needs:
|
||||
- build:ubuntu-16.04
|
||||
variables:
|
||||
LEVELDB_PKG: libleveldb1v5
|
||||
JPEG_PKG: libjpeg-turbo8
|
||||
|
||||
deploy:ubuntu-16.04:
|
||||
extends: .debpkg_install
|
||||
image: ubuntu:xenial
|
||||
needs:
|
||||
- package:ubuntu-16.04
|
||||
|
||||
# Bionic
|
||||
|
||||
build:ubuntu-18.04:
|
||||
|
@ -146,6 +144,7 @@ package:ubuntu-18.04:
|
|||
needs:
|
||||
- build:ubuntu-18.04
|
||||
variables:
|
||||
JSONCPP_PKG: libjsoncpp1
|
||||
LEVELDB_PKG: libleveldb1v5
|
||||
JPEG_PKG: libjpeg-turbo8
|
||||
|
||||
|
@ -155,6 +154,28 @@ deploy:ubuntu-18.04:
|
|||
needs:
|
||||
- package:ubuntu-18.04
|
||||
|
||||
# Focal
|
||||
|
||||
build:ubuntu-20.04:
|
||||
extends: .build_template
|
||||
image: ubuntu:focal
|
||||
|
||||
package:ubuntu-20.04:
|
||||
extends: .debpkg_template
|
||||
image: ubuntu:focal
|
||||
needs:
|
||||
- build:ubuntu-20.04
|
||||
variables:
|
||||
JSONCPP_PKG: libjsoncpp1
|
||||
LEVELDB_PKG: libleveldb1d
|
||||
JPEG_PKG: libjpeg-turbo8
|
||||
|
||||
deploy:ubuntu-20.04:
|
||||
extends: .debpkg_install
|
||||
image: ubuntu:focal
|
||||
needs:
|
||||
- package:ubuntu-20.04
|
||||
|
||||
##
|
||||
## Fedora
|
||||
##
|
||||
|
@ -164,36 +185,23 @@ build:fedora-28:
|
|||
extends: .build_template
|
||||
image: fedora:28
|
||||
before_script:
|
||||
- dnf -y install make git gcc gcc-c++ kernel-devel cmake libjpeg-devel libpng-devel libcurl-devel openal-soft-devel libvorbis-devel libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel spatialindex-devel
|
||||
- dnf -y install make git gcc gcc-c++ kernel-devel cmake libjpeg-devel libpng-devel libcurl-devel openal-soft-devel libvorbis-devel libXxf86vm-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
|
||||
|
||||
##
|
||||
## MinGW for Windows
|
||||
##
|
||||
|
||||
.generic_win_template:
|
||||
image: ubuntu:bionic
|
||||
image: ubuntu:focal
|
||||
before_script:
|
||||
- apt-get update
|
||||
- apt-get install -y wget xz-utils unzip git cmake gettext
|
||||
- wget -nv http://minetest.kitsunemimi.pw/mingw-w64-${WIN_ARCH}_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
|
||||
- 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: 1h
|
||||
paths:
|
||||
- _build/*
|
||||
|
||||
.package_win_template:
|
||||
extends: .generic_win_template
|
||||
stage: package
|
||||
script:
|
||||
- unzip _build/minetest-*.zip
|
||||
- cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libgcc*.dll minetest-*-win*/bin/
|
||||
- cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libstdc++*.dll minetest-*-win*/bin/
|
||||
- cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libwinpthread*.dll minetest-*-win*/bin/
|
||||
artifacts:
|
||||
expire_in: 90 day
|
||||
paths:
|
||||
|
@ -203,28 +211,15 @@ 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"
|
||||
|
||||
package:win32:
|
||||
extends: .package_win_template
|
||||
needs:
|
||||
- build:win32
|
||||
variables:
|
||||
WIN_ARCH: "i686"
|
||||
|
||||
|
||||
build:win64:
|
||||
extends: .build_win_template
|
||||
script:
|
||||
- EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh build
|
||||
variables:
|
||||
WIN_ARCH: "x86_64"
|
||||
|
||||
package:win64:
|
||||
extends: .package_win_template
|
||||
needs:
|
||||
- build:win64
|
||||
- unzip -q build/build/*.zip
|
||||
variables:
|
||||
WIN_ARCH: "x86_64"
|
||||
|
||||
|
@ -275,7 +270,7 @@ package:appimage-client:
|
|||
- build:ubuntu-18.04
|
||||
before_script:
|
||||
- apt-get update -y
|
||||
- apt-get install -y git wget
|
||||
- apt-get install -y git
|
||||
# Collect files
|
||||
- mkdir AppDir
|
||||
- cp -a artifact/minetest/usr/ AppDir/usr/
|
||||
|
|
|
@ -20,7 +20,7 @@ read_globals = {
|
|||
|
||||
string = {fields = {"split", "trim"}},
|
||||
table = {fields = {"copy", "getn", "indexof", "insert_all", "combine"}},
|
||||
math = {fields = {"hypot"}},
|
||||
math = {fields = {"hypot", "round"}},
|
||||
}
|
||||
|
||||
globals = {
|
||||
|
|
|
@ -24,18 +24,21 @@ AppDir:
|
|||
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-security main universe
|
||||
|
||||
include:
|
||||
- libirrlicht1.8
|
||||
- libxxf86vm1
|
||||
- libgl1-mesa-glx
|
||||
- libsqlite3-0
|
||||
- libogg0
|
||||
- libvorbis0a
|
||||
- libopenal1
|
||||
- libc6
|
||||
- libcurl3-gnutls
|
||||
- libfreetype6
|
||||
- zlib1g
|
||||
- libgmp10
|
||||
- libgl1
|
||||
- libjpeg-turbo8
|
||||
- libjsoncpp1
|
||||
- libleveldb1v5
|
||||
- libopenal1
|
||||
- libpng16-16
|
||||
- libsqlite3-0
|
||||
- libstdc++6
|
||||
- libvorbisfile3
|
||||
- libx11-6
|
||||
- libxxf86vm1
|
||||
- zlib1g
|
||||
|
||||
files:
|
||||
exclude:
|
||||
|
|
124
CMakeLists.txt
|
@ -1,16 +1,24 @@
|
|||
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()
|
||||
|
||||
# This can be read from ${PROJECT_NAME} after project() is called
|
||||
project(minetest)
|
||||
set(PROJECT_NAME_CAPITALIZED "Dragonfire")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(GCC_MINIMUM_VERSION "4.8")
|
||||
set(CLANG_MINIMUM_VERSION "3.4")
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
set(GCC_MINIMUM_VERSION "5.1")
|
||||
set(CLANG_MINIMUM_VERSION "3.5")
|
||||
|
||||
# Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing
|
||||
set(VERSION_MAJOR 5)
|
||||
set(VERSION_MINOR 5)
|
||||
set(VERSION_MINOR 6)
|
||||
set(VERSION_PATCH 0)
|
||||
set(VERSION_EXTRA "dragonfire" CACHE STRING "Stuff to append to version string")
|
||||
|
||||
|
@ -19,7 +27,7 @@ set(DEVELOPMENT_BUILD FALSE)
|
|||
|
||||
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
|
||||
if(VERSION_EXTRA)
|
||||
set(VERSION_STRING ${VERSION_STRING}-${VERSION_EXTRA})
|
||||
set(VERSION_STRING "${VERSION_STRING}-${VERSION_EXTRA}")
|
||||
elseif(DEVELOPMENT_BUILD)
|
||||
set(VERSION_STRING "${VERSION_STRING}-dev")
|
||||
endif()
|
||||
|
@ -44,7 +52,7 @@ set(RUN_IN_PLACE ${DEFAULT_RUN_IN_PLACE} CACHE BOOL
|
|||
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(WARN_ALL TRUE CACHE BOOL "Enable -Wall for Release build")
|
||||
|
||||
|
@ -57,30 +65,60 @@ endif()
|
|||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
|
||||
|
||||
|
||||
# This is done here so that relative search paths are more reasonable
|
||||
find_package(Irrlicht)
|
||||
if(BUILD_CLIENT AND NOT IRRLICHT_FOUND)
|
||||
message(FATAL_ERROR "Irrlicht is required to build the client, but it was not found.")
|
||||
elseif(IRRLICHT_INCLUDE_DIR STREQUAL "")
|
||||
message(FATAL_ERROR "Irrlicht headers are required to build the server, but none found.")
|
||||
set(IRRLICHTMT_BUILD_DIR "" CACHE PATH "Path to IrrlichtMt build directory.")
|
||||
if(NOT "${IRRLICHTMT_BUILD_DIR}" STREQUAL "")
|
||||
find_package(IrrlichtMt QUIET
|
||||
PATHS "${IRRLICHTMT_BUILD_DIR}"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
|
||||
if(NOT TARGET IrrlichtMt::IrrlichtMt)
|
||||
# find_package() searches certain subdirectories. ${PATH}/cmake is not
|
||||
# the only one, but it is the one where IrrlichtMt is supposed to export
|
||||
# IrrlichtMtConfig.cmake
|
||||
message(FATAL_ERROR "Could not find IrrlichtMtConfig.cmake in ${IRRLICHTMT_BUILD_DIR}/cmake.")
|
||||
endif()
|
||||
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/lib/irrlichtmt")
|
||||
message(STATUS "Using user-provided IrrlichtMt at subdirectory 'lib/irrlichtmt'")
|
||||
if(BUILD_CLIENT)
|
||||
# tell IrrlichtMt to create a static library
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared library" FORCE)
|
||||
add_subdirectory(lib/irrlichtmt EXCLUDE_FROM_ALL)
|
||||
unset(BUILD_SHARED_LIBS CACHE)
|
||||
|
||||
if(NOT TARGET IrrlichtMt)
|
||||
message(FATAL_ERROR "IrrlichtMt project is missing a CMake target?!")
|
||||
endif()
|
||||
else()
|
||||
add_library(IrrlichtMt::IrrlichtMt INTERFACE IMPORTED)
|
||||
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/irrlichtmt/include")
|
||||
endif()
|
||||
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")
|
||||
if(BUILD_CLIENT)
|
||||
message(FATAL_ERROR "IrrlichtMt is required to build the client, but it was not found.\n${explanation_msg}")
|
||||
endif()
|
||||
|
||||
include(MinetestFindIrrlichtHeaders)
|
||||
if(NOT IRRLICHT_INCLUDE_DIR)
|
||||
message(FATAL_ERROR "IrrlichtMt headers are required to build the server, but none found.\n${explanation_msg}")
|
||||
endif()
|
||||
message(STATUS "Found IrrlichtMt headers: ${IRRLICHT_INCLUDE_DIR}")
|
||||
add_library(IrrlichtMt::IrrlichtMt INTERFACE IMPORTED)
|
||||
# Note that we can't use target_include_directories() since that doesn't work for IMPORTED targets before CMake 3.11
|
||||
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${IRRLICHT_INCLUDE_DIR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CheckSymbolExists)
|
||||
set(CMAKE_REQUIRED_INCLUDES ${IRRLICHT_INCLUDE_DIR})
|
||||
unset(HAS_FORKED_IRRLICHT CACHE)
|
||||
check_symbol_exists(IRRLICHT_VERSION_MT "IrrCompileConfig.h" HAS_FORKED_IRRLICHT)
|
||||
if(NOT HAS_FORKED_IRRLICHT)
|
||||
string(CONCAT EXPLANATION_MSG
|
||||
"Irrlicht found, but it is not Minetest's Irrlicht fork. "
|
||||
"The Minetest team has forked Irrlicht to make their own customizations. "
|
||||
"It can be found here: https://github.com/minetest/irrlicht")
|
||||
if(BUILD_CLIENT)
|
||||
message(FATAL_ERROR "${EXPLANATION_MSG}\n"
|
||||
"Building the client with upstream Irrlicht is no longer possible.")
|
||||
else()
|
||||
message(WARNING "${EXPLANATION_MSG}\n"
|
||||
"The server can still be built with upstream Irrlicht but this is DISCOURAGED.")
|
||||
endif()
|
||||
if(TARGET IrrlichtMt::IrrlichtMt)
|
||||
message(STATUS "Found IrrlichtMt ${IrrlichtMt_VERSION}")
|
||||
endif()
|
||||
|
||||
|
||||
|
@ -112,15 +150,16 @@ elseif(UNIX) # Linux, BSD etc
|
|||
set(ICONDIR "unix/icons")
|
||||
set(LOCALEDIR "locale")
|
||||
else()
|
||||
set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}")
|
||||
set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin")
|
||||
set(DOCDIR "${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}")
|
||||
set(MANDIR "${CMAKE_INSTALL_PREFIX}/share/man")
|
||||
include(GNUInstallDirs)
|
||||
set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}")
|
||||
set(BINDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}")
|
||||
set(DOCDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DOCDIR}")
|
||||
set(MANDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_MANDIR}")
|
||||
set(EXAMPLE_CONF_DIR ${DOCDIR})
|
||||
set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/share/applications")
|
||||
set(APPDATADIR "${CMAKE_INSTALL_PREFIX}/share/metainfo")
|
||||
set(ICONDIR "${CMAKE_INSTALL_PREFIX}/share/icons")
|
||||
set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/share/locale")
|
||||
set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/applications")
|
||||
set(APPDATADIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/metainfo")
|
||||
set(ICONDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/icons")
|
||||
set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALEDIR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
@ -223,10 +262,10 @@ endif()
|
|||
find_package(GMP REQUIRED)
|
||||
find_package(Json REQUIRED)
|
||||
find_package(Lua REQUIRED)
|
||||
|
||||
# JsonCpp doesn't compile well on GCC 4.8
|
||||
if(NOT USE_SYSTEM_JSONCPP)
|
||||
set(GCC_MINIMUM_VERSION "4.9")
|
||||
if(NOT USE_LUAJIT)
|
||||
set(LUA_BIT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/bitop)
|
||||
set(LUA_BIT_LIBRARY bitop)
|
||||
add_subdirectory(lib/bitop)
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
|
@ -241,9 +280,12 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?Clang")
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_BENCHMARKS)
|
||||
add_subdirectory(lib/catch2)
|
||||
endif()
|
||||
|
||||
# Subdirectories
|
||||
# Be sure to add all relevant definitions above this
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
|
||||
|
|
47
Dockerfile
|
@ -1,6 +1,8 @@
|
|||
FROM alpine:3.11
|
||||
ARG DOCKER_IMAGE=alpine:3.14
|
||||
FROM $DOCKER_IMAGE AS builder
|
||||
|
||||
ENV MINETEST_GAME_VERSION master
|
||||
ENV IRRLICHT_VERSION master
|
||||
|
||||
COPY .git /usr/src/minetest/.git
|
||||
COPY CMakeLists.txt /usr/src/minetest/CMakeLists.txt
|
||||
|
@ -18,48 +20,49 @@ COPY textures /usr/src/minetest/textures
|
|||
|
||||
WORKDIR /usr/src/minetest
|
||||
|
||||
RUN apk add --no-cache git build-base irrlicht-dev cmake bzip2-dev libpng-dev \
|
||||
jpeg-dev libxxf86vm-dev mesa-dev sqlite-dev libogg-dev \
|
||||
libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev \
|
||||
gmp-dev jsoncpp-dev postgresql-dev luajit-dev ca-certificates && \
|
||||
RUN apk add --no-cache git build-base cmake sqlite-dev curl-dev zlib-dev zstd-dev \
|
||||
gmp-dev jsoncpp-dev postgresql-dev ninja luajit-dev ca-certificates && \
|
||||
git clone --depth=1 -b ${MINETEST_GAME_VERSION} https://github.com/minetest/minetest_game.git ./games/minetest_game && \
|
||||
rm -fr ./games/minetest_game/.git
|
||||
|
||||
WORKDIR /usr/src/
|
||||
RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp/ && \
|
||||
mkdir prometheus-cpp/build && \
|
||||
cd prometheus-cpp/build && \
|
||||
cmake .. \
|
||||
cd prometheus-cpp && \
|
||||
cmake -B build \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DENABLE_TESTING=0 && \
|
||||
make -j2 && \
|
||||
make install
|
||||
-DENABLE_TESTING=0 \
|
||||
-GNinja && \
|
||||
cmake --build build && \
|
||||
cmake --install build
|
||||
|
||||
RUN git clone --depth=1 https://github.com/minetest/irrlicht/ -b ${IRRLICHT_VERSION} && \
|
||||
cp -r irrlicht/include /usr/include/irrlichtmt
|
||||
|
||||
WORKDIR /usr/src/minetest
|
||||
RUN mkdir build && \
|
||||
cd build && \
|
||||
cmake .. \
|
||||
RUN cmake -B build \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SERVER=TRUE \
|
||||
-DENABLE_PROMETHEUS=TRUE \
|
||||
-DBUILD_UNITTESTS=FALSE \
|
||||
-DBUILD_CLIENT=FALSE && \
|
||||
make -j2 && \
|
||||
make install
|
||||
-DBUILD_CLIENT=FALSE \
|
||||
-GNinja && \
|
||||
cmake --build build && \
|
||||
cmake --install build
|
||||
|
||||
FROM alpine:3.11
|
||||
ARG DOCKER_IMAGE=alpine:3.14
|
||||
FROM $DOCKER_IMAGE AS runtime
|
||||
|
||||
RUN apk add --no-cache sqlite-libs curl gmp libstdc++ libgcc libpq luajit jsoncpp && \
|
||||
RUN apk add --no-cache sqlite-libs curl gmp libstdc++ libgcc libpq luajit jsoncpp zstd-libs && \
|
||||
adduser -D minetest --uid 30000 -h /var/lib/minetest && \
|
||||
chown -R minetest:minetest /var/lib/minetest
|
||||
|
||||
WORKDIR /var/lib/minetest
|
||||
|
||||
COPY --from=0 /usr/local/share/minetest /usr/local/share/minetest
|
||||
COPY --from=0 /usr/local/bin/minetestserver /usr/local/bin/minetestserver
|
||||
COPY --from=0 /usr/local/share/doc/minetest/minetest.conf.example /etc/minetest/minetest.conf
|
||||
COPY --from=builder /usr/local/share/minetest /usr/local/share/minetest
|
||||
COPY --from=builder /usr/local/bin/minetestserver /usr/local/bin/minetestserver
|
||||
COPY --from=builder /usr/local/share/doc/minetest/minetest.conf.example /etc/minetest/minetest.conf
|
||||
|
||||
USER minetest:minetest
|
||||
|
||||
|
|
12
LICENSE.txt
|
@ -63,6 +63,15 @@ Zughy:
|
|||
|
||||
appgurueu:
|
||||
textures/base/pack/server_incompatible.png
|
||||
|
||||
erlehmann, Warr1024, rollerozxa:
|
||||
textures/base/pack/no_screenshot.png
|
||||
|
||||
kilbith:
|
||||
textures/base/pack/server_favorite.png
|
||||
|
||||
SmallJoker
|
||||
textures/base/pack/server_favorite_delete.png (based on server_favorite.png)
|
||||
|
||||
License of Minetest source code
|
||||
-------------------------------
|
||||
|
@ -87,7 +96,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
Irrlicht
|
||||
---------------
|
||||
|
||||
This program uses the Irrlicht Engine. http://irrlicht.sourceforge.net/
|
||||
This program uses IrrlichtMt, Minetest's fork of
|
||||
the Irrlicht Engine. http://irrlicht.sourceforge.net/
|
||||
|
||||
The Irrlicht Engine License
|
||||
|
||||
|
|
150
README.md
|
@ -7,12 +7,12 @@ Minetest
|
|||
|
||||
Minetest is a free open-source voxel game engine with easy modding and game creation.
|
||||
|
||||
Copyright (C) 2010-2020 Perttu Ahola <celeron55@gmail.com>
|
||||
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
|
||||
If you downloaded the Dragonfire Client 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.
|
||||
|
||||
|
@ -132,29 +132,31 @@ Compiling
|
|||
|
||||
| Dependency | Version | Commentary |
|
||||
|------------|---------|------------|
|
||||
| GCC | 4.9+ | Can be replaced with Clang 3.4+ |
|
||||
| CMake | 2.6+ | |
|
||||
| Irrlicht | - | Custom version required, see https://github.com/minetest/irrlicht |
|
||||
| SQLite3 | 3.0+ | |
|
||||
| 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 libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
|
||||
sudo apt install g++ make libc6-dev cmake libpng-dev libjpeg-dev libxxf86vm-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 libvorbis-devel libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel spatialindex-devel
|
||||
|
||||
sudo dnf install make automake gcc gcc-c++ kernel-devel cmake libcurl-devel openal-soft-devel libvorbis-devel libXxf86vm-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 libxxf86vm libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses
|
||||
sudo pacman -S base-devel libcurl-gnutls cmake libxxf86vm 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 libxxf86vm-dev mesa-dev sqlite-dev libogg-dev libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev gmp-dev jsoncpp-dev luajit-dev
|
||||
sudo apk add build-base cmake libpng-dev jpeg-dev libxxf86vm-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
|
||||
|
||||
|
@ -170,13 +172,17 @@ For Fedora users:
|
|||
|
||||
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
|
||||
git clone --depth 1 https://github.com/dragonfireclient/dragonfireclient
|
||||
cd dragonfireclient
|
||||
|
||||
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 --branch 1.9.0mt7 --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
|
||||
|
@ -191,6 +197,14 @@ Download minetest_game, without using Git:
|
|||
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:
|
||||
|
@ -209,8 +223,15 @@ Run it:
|
|||
- 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 have the Irrlicht library installed.
|
||||
- In that case use `-DIRRLICHT_INCLUDE_DIR=/some/where/irrlicht/include`.
|
||||
- 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
|
||||
|
||||
|
@ -219,6 +240,7 @@ 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
|
||||
|
@ -227,9 +249,8 @@ General options and their default values:
|
|||
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_FREETYPE=ON - Build with FreeType2; Allows using TTF fonts
|
||||
ENABLE_GETTEXT=ON - Build with Gettext; Allows using translations
|
||||
ENABLE_GLES=OFF - Build for OpenGL ES instead of OpenGL (requires support by Irrlicht)
|
||||
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
|
||||
|
@ -239,10 +260,10 @@ General options and their default values:
|
|||
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
|
||||
OPENGL_GL_PREFERENCE=LEGACY - Linux client build only; See CMake Policy CMP0072 for reference
|
||||
RUN_IN_PLACE=FALSE - Create a portable install (worlds, settings etc. in current directory)
|
||||
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:
|
||||
|
||||
|
@ -251,17 +272,17 @@ Library specific options:
|
|||
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
|
||||
FREETYPE_INCLUDE_DIR_freetype2 - Only if building with FreeType 2; directory that contains an freetype directory with files such as ftimage.h in it
|
||||
FREETYPE_INCLUDE_DIR_ft2build - Only if building with FreeType 2; directory that contains ft2build.h
|
||||
FREETYPE_LIBRARY - Only if building with FreeType 2; path to libfreetype.a/libfreetype.so/freetype.lib
|
||||
FREETYPE_DLL - Only if building with FreeType 2 on Windows; path to libfreetype.dll
|
||||
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 iconv.h
|
||||
GETTEXT_LIBRARY - Only when building with gettext on Windows; path to libintl.dll.a
|
||||
GETTEXT_MSGFMT - Only when building with gettext; path to msgfmt/msgfmt.exe
|
||||
IRRLICHT_DLL - Only on Windows; path to Irrlicht.dll
|
||||
IRRLICHT_INCLUDE_DIR - Directory that contains IrrCompileConfig.h
|
||||
IRRLICHT_LIBRARY - Path to libIrrlicht.a/libIrrlicht.so/libIrrlicht.dll.a/Irrlicht.lib
|
||||
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
|
||||
|
@ -279,20 +300,20 @@ Library specific options:
|
|||
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
|
||||
OPENGLES2_INCLUDE_DIR - Only if building with GLES; directory that contains gl2.h
|
||||
OPENGLES2_LIBRARY - Only if building with GLES; path to libGLESv2.a/libGLESv2.so
|
||||
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
|
||||
XXF86VM_LIBRARY - Only on Linux; path to libXXf86vm.a/libXXf86vm.so
|
||||
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
|
||||
### Compiling on Windows using MSVC
|
||||
|
||||
### Requirements
|
||||
|
||||
|
@ -305,17 +326,14 @@ Library specific options:
|
|||
|
||||
It is highly recommended to use vcpkg as package manager.
|
||||
|
||||
#### a) Using vcpkg to install dependencies
|
||||
|
||||
After you successfully built vcpkg you can easily install the required libraries:
|
||||
```powershell
|
||||
vcpkg install zlib curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit gmp jsoncpp --triplet x64-windows
|
||||
vcpkg install zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry --triplet x64-windows
|
||||
```
|
||||
|
||||
- **Note that you currently need to build irrlicht on your own**
|
||||
- **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.
|
||||
- `freetype` is optional, it allows true-type font rendering.
|
||||
- `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
|
||||
|
||||
|
@ -323,10 +341,6 @@ There are other optional libraries, but they are not tested if they can build an
|
|||
|
||||
Use `--triplet` to specify the target triplet, e.g. `x64-windows` or `x86-windows`.
|
||||
|
||||
#### b) Compile the dependencies on your own
|
||||
|
||||
This is outdated and not recommended. Follow the instructions on https://dev.minetest.net/Build_Win32_Minetest_including_all_required_libraries#VS2012_Build
|
||||
|
||||
### Compile Minetest
|
||||
|
||||
#### a) Using the vcpkg toolchain and CMake GUI
|
||||
|
@ -355,12 +369,6 @@ cmake --build . --config Release
|
|||
```
|
||||
Make sure that the right compiler is selected and the path to the vcpkg toolchain is correct.
|
||||
|
||||
#### c) Using your own compiled libraries
|
||||
|
||||
**This is outdated and not recommended**
|
||||
|
||||
Follow the instructions on https://dev.minetest.net/Build_Win32_Minetest_including_all_required_libraries#VS2012_Build
|
||||
|
||||
### Windows Installer using WiX Toolset
|
||||
|
||||
Requirements:
|
||||
|
@ -374,6 +382,60 @@ 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
|
||||
```
|
||||
|
||||
Docker
|
||||
------
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
apply plugin: 'com.android.application'
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion '30.0.3'
|
||||
ndkVersion '22.0.7026061'
|
||||
ndkVersion "$ndk_version"
|
||||
defaultConfig {
|
||||
applicationId 'net.minetest.minetest'
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 30
|
||||
versionName "${versionMajor}.${versionMinor}.${versionPatch}"
|
||||
versionCode project.versionCode
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ android {
|
|||
|
||||
task prepareAssets() {
|
||||
def assetsFolder = "build/assets"
|
||||
def projRoot = "../../.."
|
||||
def projRoot = "../.."
|
||||
def gameToCopy = "minetest_game"
|
||||
|
||||
copy {
|
||||
|
@ -68,7 +68,7 @@ task prepareAssets() {
|
|||
from "${projRoot}/client/shaders" into "${assetsFolder}/client/shaders"
|
||||
}
|
||||
copy {
|
||||
from "../native/deps/Android/Irrlicht/shaders" into "${assetsFolder}/client/shaders/Irrlicht"
|
||||
from "../native/deps/armeabi-v7a/Irrlicht/Shaders" into "${assetsFolder}/client/shaders/Irrlicht"
|
||||
}
|
||||
copy {
|
||||
from "${projRoot}/fonts" include "*.ttf" into "${assetsFolder}/fonts"
|
||||
|
@ -76,10 +76,13 @@ task prepareAssets() {
|
|||
copy {
|
||||
from "${projRoot}/games/${gameToCopy}" into "${assetsFolder}/games/${gameToCopy}"
|
||||
}
|
||||
/*copy {
|
||||
// ToDo: fix broken locales
|
||||
from "${projRoot}/po" into "${assetsFolder}/po"
|
||||
}*/
|
||||
fileTree("${projRoot}/po").include("**/*.po").forEach { poFile ->
|
||||
def moPath = "${assetsFolder}/locale/${poFile.parentFile.name}/LC_MESSAGES/"
|
||||
file(moPath).mkdirs()
|
||||
exec {
|
||||
commandLine 'msgfmt', '-o', "${moPath}/minetest.mo", poFile
|
||||
}
|
||||
}
|
||||
copy {
|
||||
from "${projRoot}/textures" into "${assetsFolder}/textures"
|
||||
}
|
||||
|
@ -109,5 +112,5 @@ android.applicationVariants.all { variant ->
|
|||
|
||||
dependencies {
|
||||
implementation project(':native')
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
}
|
|
@ -30,7 +30,8 @@
|
|||
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
|
||||
android:maxAspectRatio="3.0"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
@ -44,7 +45,8 @@
|
|||
android:launchMode="singleTask"
|
||||
android:maxAspectRatio="3.0"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
|
@ -57,6 +59,17 @@
|
|||
android:name=".UnzipService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
</application>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="net.minetest.minetest.fileprovider"
|
||||
android:grantUriPermissions="true"
|
||||
android:exported="false">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/filepaths" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -26,16 +26,25 @@ import android.net.Uri;
|
|||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
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;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Objects;
|
||||
|
||||
// Native code finds these methods by name (see porting_android.cpp).
|
||||
// This annotation prevents the minifier/Proguard from mangling them.
|
||||
@Keep
|
||||
public class GameActivity extends NativeActivity {
|
||||
static {
|
||||
System.loadLibrary("c++_shared");
|
||||
|
@ -85,9 +94,19 @@ public class GameActivity extends NativeActivity {
|
|||
|
||||
private void showDialogUI(String hint, String current, int editType) {
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
EditText editText = new CustomEditText(this);
|
||||
builder.setView(editText);
|
||||
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);
|
||||
}
|
||||
container.addView(editText);
|
||||
editText.setMaxLines(8);
|
||||
editText.requestFocus();
|
||||
editText.setHint(hint);
|
||||
editText.setText(current);
|
||||
|
@ -103,8 +122,9 @@ public class GameActivity extends NativeActivity {
|
|||
else
|
||||
editText.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
editText.setSelection(editText.getText().length());
|
||||
editText.setOnKeyListener((view, KeyCode, event) -> {
|
||||
if (KeyCode == KeyEvent.KEYCODE_ENTER) {
|
||||
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;
|
||||
messageReturnValue = editText.getText().toString();
|
||||
|
@ -113,6 +133,18 @@ public class GameActivity extends NativeActivity {
|
|||
}
|
||||
return false;
|
||||
});
|
||||
// For multi-line, add Done button since Enter key does not submit text
|
||||
if (editType == 1) {
|
||||
Button doneButton = new Button(this);
|
||||
container.addView(doneButton);
|
||||
doneButton.setText(R.string.ime_dialog_done);
|
||||
doneButton.setOnClickListener((view -> {
|
||||
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
||||
messageReturnCode = 0;
|
||||
messageReturnValue = editText.getText().toString();
|
||||
alertDialog.dismiss();
|
||||
}));
|
||||
}
|
||||
alertDialog.show();
|
||||
alertDialog.setOnCancelListener(dialog -> {
|
||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||
|
@ -146,4 +178,30 @@ public class GameActivity extends NativeActivity {
|
|||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
|
||||
startActivity(browserIntent);
|
||||
}
|
||||
|
||||
public String getUserDataPath() {
|
||||
return Utils.getUserDataDirectory(this).getAbsolutePath();
|
||||
}
|
||||
|
||||
public String getCachePath() {
|
||||
return Utils.getCacheDirectory(this).getAbsolutePath();
|
||||
}
|
||||
|
||||
public void shareFile(String path) {
|
||||
File file = new File(path);
|
||||
if (!file.exists()) {
|
||||
Log.e("GameActivity", "File " + file.getAbsolutePath() + " doesn't exist");
|
||||
return;
|
||||
}
|
||||
|
||||
Uri fileUri = FileProvider.getUriForFile(this, "net.minetest.minetest.fileprovider", file);
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_SEND, fileUri);
|
||||
intent.setDataAndType(fileUri, getContentResolver().getType(fileUri));
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
|
||||
|
||||
Intent shareIntent = Intent.createChooser(intent, null);
|
||||
startActivity(shareIntent);
|
||||
}
|
||||
}
|
|
@ -29,12 +29,14 @@ 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.StringRes;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
@ -43,11 +45,7 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static net.minetest.minetest.UnzipService.ACTION_FAILURE;
|
||||
import static net.minetest.minetest.UnzipService.ACTION_PROGRESS;
|
||||
import static net.minetest.minetest.UnzipService.ACTION_UPDATE;
|
||||
import static net.minetest.minetest.UnzipService.FAILURE;
|
||||
import static net.minetest.minetest.UnzipService.SUCCESS;
|
||||
import static net.minetest.minetest.UnzipService.*;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
private final static int versionCode = BuildConfig.VERSION_CODE;
|
||||
|
@ -56,26 +54,40 @@ public class MainActivity extends AppCompatActivity {
|
|||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
||||
private static final String SETTINGS = "MinetestSettings";
|
||||
private static final String TAG_VERSION_CODE = "versionCode";
|
||||
|
||||
private ProgressBar mProgressBar;
|
||||
private TextView mTextView;
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private final BroadcastReceiver myReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
int progress = 0;
|
||||
if (intent != null)
|
||||
@StringRes int message = 0;
|
||||
if (intent != null) {
|
||||
progress = intent.getIntExtra(ACTION_PROGRESS, 0);
|
||||
if (progress >= 0) {
|
||||
if (mProgressBar != null) {
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
mProgressBar.setProgress(progress);
|
||||
}
|
||||
mTextView.setVisibility(View.VISIBLE);
|
||||
} else if (progress == FAILURE) {
|
||||
message = intent.getIntExtra(ACTION_PROGRESS_MESSAGE, 0);
|
||||
}
|
||||
|
||||
if (progress == FAILURE) {
|
||||
Toast.makeText(MainActivity.this, intent.getStringExtra(ACTION_FAILURE), Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
} else if (progress == SUCCESS)
|
||||
} else if (progress == SUCCESS) {
|
||||
startNative();
|
||||
} else {
|
||||
if (mProgressBar != null) {
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
if (progress == INDETERMINATE) {
|
||||
mProgressBar.setIndeterminate(true);
|
||||
} else {
|
||||
mProgressBar.setIndeterminate(false);
|
||||
mProgressBar.setProgress(progress);
|
||||
}
|
||||
}
|
||||
mTextView.setVisibility(View.VISIBLE);
|
||||
if (message != 0)
|
||||
mTextView.setText(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -88,7 +100,9 @@ public class MainActivity extends AppCompatActivity {
|
|||
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)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
|
||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
|
||||
checkPermission();
|
||||
else
|
||||
checkAppVersion();
|
||||
|
@ -120,6 +134,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
if (grantResult != PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(this, R.string.not_granted, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
checkAppVersion();
|
||||
|
@ -127,10 +142,27 @@ public class MainActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
private void checkAppVersion() {
|
||||
if (sharedPreferences.getInt(TAG_VERSION_CODE, 0) == versionCode)
|
||||
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);
|
||||
mTextView.setVisibility(View.VISIBLE);
|
||||
} else if (sharedPreferences.getInt(TAG_VERSION_CODE, 0) == versionCode &&
|
||||
Utils.isInstallValid(this)) {
|
||||
startNative();
|
||||
else
|
||||
new CopyZipTask(this).execute(getCacheDir() + "/Minetest.zip");
|
||||
} else {
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
mProgressBar.setIndeterminate(true);
|
||||
mTextView.setVisibility(View.VISIBLE);
|
||||
|
||||
Intent intent = new Intent(this, UnzipService.class);
|
||||
startService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void startNative() {
|
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
|
||||
Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@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
|
||||
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.
|
||||
*/
|
||||
|
||||
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 androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class UnzipService extends IntentService {
|
||||
public static final String ACTION_UPDATE = "net.minetest.minetest.UPDATE";
|
||||
public static final String ACTION_PROGRESS = "net.minetest.minetest.PROGRESS";
|
||||
public static final String ACTION_PROGRESS_MESSAGE = "net.minetest.minetest.PROGRESS_MESSAGE";
|
||||
public static final String ACTION_FAILURE = "net.minetest.minetest.FAILURE";
|
||||
public static final int SUCCESS = -1;
|
||||
public static final int FAILURE = -2;
|
||||
public static final int INDETERMINATE = -3;
|
||||
private final int id = 1;
|
||||
private NotificationManager mNotifyManager;
|
||||
private boolean isSuccess = true;
|
||||
private String failureMessage;
|
||||
|
||||
private static boolean isRunning = false;
|
||||
public static synchronized boolean getIsRunning() {
|
||||
return isRunning;
|
||||
}
|
||||
private static synchronized void setIsRunning(boolean v) {
|
||||
isRunning = v;
|
||||
}
|
||||
|
||||
public UnzipService() {
|
||||
super("net.minetest.minetest.UnzipService");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
Notification.Builder notificationBuilder = createNotification();
|
||||
final File zipFile = new File(getCacheDir(), "Minetest.zip");
|
||||
try {
|
||||
setIsRunning(true);
|
||||
File userDataDirectory = Utils.getUserDataDirectory(this);
|
||||
if (userDataDirectory == null) {
|
||||
throw new IOException("Unable to find user data directory");
|
||||
}
|
||||
|
||||
try (InputStream in = this.getAssets().open(zipFile.getName())) {
|
||||
try (OutputStream out = new FileOutputStream(zipFile)) {
|
||||
int readLen;
|
||||
byte[] readBuffer = new byte[16384];
|
||||
while ((readLen = in.read(readBuffer)) != -1) {
|
||||
out.write(readBuffer, 0, readLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
migrate(notificationBuilder, userDataDirectory);
|
||||
unzip(notificationBuilder, zipFile, userDataDirectory);
|
||||
} catch (IOException e) {
|
||||
isSuccess = false;
|
||||
failureMessage = e.getLocalizedMessage();
|
||||
} finally {
|
||||
setIsRunning(false);
|
||||
zipFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
} else {
|
||||
builder = new Notification.Builder(this);
|
||||
}
|
||||
|
||||
Intent notificationIntent = new Intent(this, MainActivity.class);
|
||||
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
PendingIntent intent = PendingIntent.getActivity(this, 0,
|
||||
notificationIntent, 0);
|
||||
|
||||
builder.setContentTitle(getString(R.string.notification_title))
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setContentText(getString(R.string.notification_description))
|
||||
.setContentIntent(intent)
|
||||
.setOngoing(true)
|
||||
.setProgress(0, 0, true);
|
||||
|
||||
mNotifyManager.notify(id, builder.build());
|
||||
return builder;
|
||||
}
|
||||
|
||||
private void unzip(Notification.Builder notificationBuilder, File zipFile, File userDataDirectory) throws IOException {
|
||||
int per = 0;
|
||||
|
||||
int size;
|
||||
try (ZipFile zipSize = new ZipFile(zipFile)) {
|
||||
size = zipSize.size();
|
||||
}
|
||||
|
||||
int readLen;
|
||||
byte[] readBuffer = new byte[16384];
|
||||
try (FileInputStream fileInputStream = new FileInputStream(zipFile);
|
||||
ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) {
|
||||
ZipEntry ze;
|
||||
while ((ze = zipInputStream.getNextEntry()) != null) {
|
||||
if (ze.isDirectory()) {
|
||||
++per;
|
||||
Utils.createDirs(userDataDirectory, ze.getName());
|
||||
continue;
|
||||
}
|
||||
publishProgress(notificationBuilder, R.string.loading, 100 * ++per / size);
|
||||
try (OutputStream outputStream = new FileOutputStream(
|
||||
new File(userDataDirectory, ze.getName()))) {
|
||||
while ((readLen = zipInputStream.read(readBuffer)) != -1) {
|
||||
outputStream.write(readBuffer, 0, readLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void moveFileOrDir(@NonNull File src, @NonNull File dst) throws IOException {
|
||||
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);
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException("Move operation interrupted");
|
||||
}
|
||||
}
|
||||
|
||||
boolean recursivelyDeleteDirectory(@NonNull File loc) {
|
||||
try {
|
||||
Process p = new ProcessBuilder("/system/bin/rm", "-rf",
|
||||
loc.getAbsolutePath()).start();
|
||||
return p.waitFor() == 0;
|
||||
} catch (IOException | InterruptedException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
newLocation.mkdir();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
recursivelyDeleteDirectory(oldLocation);
|
||||
}
|
||||
|
||||
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);
|
||||
if (!isSuccess)
|
||||
intentUpdate.putExtra(ACTION_FAILURE, failureMessage);
|
||||
sendBroadcast(intentUpdate);
|
||||
|
||||
if (notificationBuilder != null) {
|
||||
notificationBuilder.setContentText(getString(message));
|
||||
if (progress == INDETERMINATE) {
|
||||
notificationBuilder.setProgress(100, 50, true);
|
||||
} else {
|
||||
notificationBuilder.setProgress(100, progress, false);
|
||||
}
|
||||
mNotifyManager.notify(id, notificationBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
mNotifyManager.cancel(id);
|
||||
publishProgress(null, R.string.loading, isSuccess ? SUCCESS : FAILURE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package net.minetest.minetest;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import java.io.File;
|
||||
|
||||
public class Utils {
|
||||
public static @NonNull File createDirs(File root, String dir) {
|
||||
File f = new File(root, dir);
|
||||
if (!f.isDirectory())
|
||||
f.mkdirs();
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
public static @Nullable File getUserDataDirectory(Context context) {
|
||||
File extDir = context.getExternalFilesDir(null);
|
||||
if (extDir == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return createDirs(extDir, "Minetest");
|
||||
}
|
||||
|
||||
public static @Nullable File getCacheDirectory(Context context) {
|
||||
return context.getCacheDir();
|
||||
}
|
||||
|
||||
public static boolean isInstallValid(Context context) {
|
||||
File userDataDirectory = getUserDataDirectory(context);
|
||||
return userDataDirectory != null && userDataDirectory.isDirectory() &&
|
||||
new File(userDataDirectory, "games").isDirectory() &&
|
||||
new File(userDataDirectory, "builtin").isDirectory() &&
|
||||
new File(userDataDirectory, "client").isDirectory() &&
|
||||
new File(userDataDirectory, "textures").isDirectory();
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 83 B After Width: | Height: | Size: 83 B |
|
@ -1,4 +1,5 @@
|
|||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
@ -14,7 +15,8 @@
|
|||
android:layout_marginRight="90dp"
|
||||
android:indeterminate="false"
|
||||
android:max="100"
|
||||
android:visibility="gone" />
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
|
@ -25,6 +27,7 @@
|
|||
android:background="@android:color/transparent"
|
||||
android:text="@string/loading"
|
||||
android:textColor="#FEFEFE"
|
||||
android:visibility="gone" />
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</RelativeLayout>
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
@ -3,8 +3,11 @@
|
|||
|
||||
<string name="label">Minetest</string>
|
||||
<string name="loading">Loading…</string>
|
||||
<string name="migrating">Migrating save data from old install… (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…</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>
|
|
@ -0,0 +1,3 @@
|
|||
<paths>
|
||||
<external-files-path path="Minetest/" name="minetest" />
|
||||
</paths>
|
|
@ -1,21 +1,22 @@
|
|||
// 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", 5) // Version Minor
|
||||
project.ext.set("versionMinor", 6) // Version Minor
|
||||
project.ext.set("versionPatch", 0) // Version Patch
|
||||
project.ext.set("versionExtra", "-dev") // Version Extra
|
||||
project.ext.set("versionCode", 32) // Android Version Code
|
||||
project.ext.set("versionCode", 38) // 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
|
||||
|
||||
buildscript {
|
||||
ext.ndk_version = '23.0.7599858'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.1'
|
||||
classpath 'com.android.tools.build:gradle:7.0.3'
|
||||
classpath 'de.undercouch:gradle-download-task:4.1.1'
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
|
@ -1,6 +1,5 @@
|
|||
#Fri Jan 08 17:52:00 UTC 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
|
@ -98,7 +98,7 @@ location of your Java installation."
|
|||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
command -v java >/dev/null || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
@ -0,0 +1,68 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'de.undercouch.download'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion '30.0.3'
|
||||
ndkVersion "$ndk_version"
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
arguments '-j' + Runtime.getRuntime().availableProcessors(),
|
||||
"versionMajor=${versionMajor}",
|
||||
"versionMinor=${versionMinor}",
|
||||
"versionPatch=${versionPatch}",
|
||||
"versionExtra=${versionExtra}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
path file('jni/Android.mk')
|
||||
}
|
||||
}
|
||||
|
||||
// supported architectures
|
||||
splits {
|
||||
abi {
|
||||
enable true
|
||||
reset()
|
||||
include 'armeabi-v7a', 'arm64-v8a'//, 'x86'
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
arguments 'NDEBUG=1'
|
||||
}
|
||||
}
|
||||
|
||||
ndk {
|
||||
debugSymbolLevel 'SYMBOL_TABLE'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get precompiled deps
|
||||
task downloadDeps(type: Download) {
|
||||
src 'https://github.com/minetest/minetest_android_deps/releases/download/latest/deps.zip'
|
||||
dest new File(buildDir, 'deps.zip')
|
||||
overwrite false
|
||||
}
|
||||
|
||||
task getDeps(dependsOn: downloadDeps, type: Copy) {
|
||||
def deps = new File(buildDir.parent, 'deps')
|
||||
if (!deps.exists()) {
|
||||
deps.mkdir()
|
||||
from zipTree(downloadDeps.dest)
|
||||
into deps
|
||||
}
|
||||
}
|
||||
|
||||
preBuild.dependsOn getDeps
|
|
@ -0,0 +1,290 @@
|
|||
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)
|
||||
|
||||
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} \
|
||||
$(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/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 \
|
||||
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
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
ifdef GPROF
|
||||
$(call import-module,android-ndk-profiler)
|
||||
endif
|
||||
$(call import-module,android/native_app_glue)
|
|
@ -0,0 +1,32 @@
|
|||
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
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
|
||||
Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@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
|
||||
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.
|
||||
*/
|
||||
|
||||
package net.minetest.minetest;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class CopyZipTask extends AsyncTask<String, Void, String> {
|
||||
|
||||
private final WeakReference<AppCompatActivity> activityRef;
|
||||
|
||||
CopyZipTask(AppCompatActivity activity) {
|
||||
activityRef = new WeakReference<>(activity);
|
||||
}
|
||||
|
||||
protected String doInBackground(String... params) {
|
||||
copyAsset(params[0]);
|
||||
return params[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String result) {
|
||||
startUnzipService(result);
|
||||
}
|
||||
|
||||
private void copyAsset(String zipName) {
|
||||
String filename = zipName.substring(zipName.lastIndexOf("/") + 1);
|
||||
try (InputStream in = activityRef.get().getAssets().open(filename);
|
||||
OutputStream out = new FileOutputStream(zipName)) {
|
||||
copyFile(in, out);
|
||||
} catch (IOException e) {
|
||||
AppCompatActivity activity = activityRef.get();
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(() -> Toast.makeText(activityRef.get(), e.getLocalizedMessage(), Toast.LENGTH_LONG).show());
|
||||
}
|
||||
cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyFile(InputStream in, OutputStream out) throws IOException {
|
||||
byte[] buffer = new byte[1024];
|
||||
int read;
|
||||
while ((read = in.read(buffer)) != -1)
|
||||
out.write(buffer, 0, read);
|
||||
}
|
||||
|
||||
private void startUnzipService(String file) {
|
||||
Intent intent = new Intent(activityRef.get(), UnzipService.class);
|
||||
intent.putExtra(UnzipService.EXTRA_KEY_IN_FILE, file);
|
||||
AppCompatActivity activity = activityRef.get();
|
||||
if (activity != null) {
|
||||
activity.startService(intent);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
|
||||
Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@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
|
||||
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.
|
||||
*/
|
||||
|
||||
package net.minetest.minetest;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class UnzipService extends IntentService {
|
||||
public static final String ACTION_UPDATE = "net.minetest.minetest.UPDATE";
|
||||
public static final String ACTION_PROGRESS = "net.minetest.minetest.PROGRESS";
|
||||
public static final String ACTION_FAILURE = "net.minetest.minetest.FAILURE";
|
||||
public static final String EXTRA_KEY_IN_FILE = "file";
|
||||
public static final int SUCCESS = -1;
|
||||
public static final int FAILURE = -2;
|
||||
private final int id = 1;
|
||||
private NotificationManager mNotifyManager;
|
||||
private boolean isSuccess = true;
|
||||
private String failureMessage;
|
||||
|
||||
public UnzipService() {
|
||||
super("net.minetest.minetest.UnzipService");
|
||||
}
|
||||
|
||||
private void isDir(String dir, String location) {
|
||||
File f = new File(location, dir);
|
||||
if (!f.isDirectory())
|
||||
f.mkdirs();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
createNotification();
|
||||
unzip(intent);
|
||||
}
|
||||
|
||||
private void 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);
|
||||
} else {
|
||||
builder = new Notification.Builder(this);
|
||||
}
|
||||
builder.setContentTitle(getString(R.string.notification_title))
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setContentText(getString(R.string.notification_description));
|
||||
mNotifyManager.notify(id, builder.build());
|
||||
}
|
||||
|
||||
private void unzip(Intent intent) {
|
||||
String zip = intent.getStringExtra(EXTRA_KEY_IN_FILE);
|
||||
isDir("Minetest", Environment.getExternalStorageDirectory().toString());
|
||||
String location = Environment.getExternalStorageDirectory() + File.separator + "Minetest" + File.separator;
|
||||
int per = 0;
|
||||
int size = getSummarySize(zip);
|
||||
File zipFile = new File(zip);
|
||||
int readLen;
|
||||
byte[] readBuffer = new byte[8192];
|
||||
try (FileInputStream fileInputStream = new FileInputStream(zipFile);
|
||||
ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) {
|
||||
ZipEntry ze;
|
||||
while ((ze = zipInputStream.getNextEntry()) != null) {
|
||||
if (ze.isDirectory()) {
|
||||
++per;
|
||||
isDir(ze.getName(), location);
|
||||
} else {
|
||||
publishProgress(100 * ++per / size);
|
||||
try (OutputStream outputStream = new FileOutputStream(location + ze.getName())) {
|
||||
while ((readLen = zipInputStream.read(readBuffer)) != -1) {
|
||||
outputStream.write(readBuffer, 0, readLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
zipFile.delete();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
isSuccess = false;
|
||||
failureMessage = e.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private void publishProgress(int progress) {
|
||||
Intent intentUpdate = new Intent(ACTION_UPDATE);
|
||||
intentUpdate.putExtra(ACTION_PROGRESS, progress);
|
||||
if (!isSuccess) intentUpdate.putExtra(ACTION_FAILURE, failureMessage);
|
||||
sendBroadcast(intentUpdate);
|
||||
}
|
||||
|
||||
private int getSummarySize(String zip) {
|
||||
int size = 0;
|
||||
try {
|
||||
ZipFile zipSize = new ZipFile(zip);
|
||||
size += zipSize.size();
|
||||
} catch (IOException e) {
|
||||
Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
mNotifyManager.cancel(id);
|
||||
publishProgress(isSuccess ? SUCCESS : FAILURE);
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'de.undercouch.download'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion '30.0.3'
|
||||
ndkVersion '22.0.7026061'
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
arguments '-j' + Runtime.getRuntime().availableProcessors(),
|
||||
"versionMajor=${versionMajor}",
|
||||
"versionMinor=${versionMinor}",
|
||||
"versionPatch=${versionPatch}",
|
||||
"versionExtra=${versionExtra}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
path file('jni/Android.mk')
|
||||
}
|
||||
}
|
||||
|
||||
// supported architectures
|
||||
splits {
|
||||
abi {
|
||||
enable true
|
||||
reset()
|
||||
include 'armeabi-v7a', 'arm64-v8a'//, 'x86'
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
arguments 'NDEBUG=1'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get precompiled deps
|
||||
def folder = 'minetest_android_deps_binaries'
|
||||
|
||||
task downloadDeps(type: Download) {
|
||||
src 'https://github.com/minetest/' + folder + '/archive/master.zip'
|
||||
dest new File(buildDir, 'deps.zip')
|
||||
overwrite false
|
||||
}
|
||||
|
||||
task getDeps(dependsOn: downloadDeps, type: Copy) {
|
||||
def deps = file('deps')
|
||||
def f = file("$buildDir/" + folder + "-master")
|
||||
|
||||
if (!deps.exists() && !f.exists()) {
|
||||
from zipTree(downloadDeps.dest)
|
||||
into buildDir
|
||||
}
|
||||
|
||||
doLast {
|
||||
if (!deps.exists()) {
|
||||
file(f).renameTo(file(deps))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get sqlite
|
||||
def sqlite_ver = '3340000'
|
||||
task downloadSqlite(dependsOn: getDeps, type: Download) {
|
||||
src 'https://www.sqlite.org/2020/sqlite-amalgamation-' + sqlite_ver + '.zip'
|
||||
dest new File(buildDir, 'sqlite.zip')
|
||||
overwrite false
|
||||
}
|
||||
|
||||
task getSqlite(dependsOn: downloadSqlite, type: Copy) {
|
||||
def sqlite = file('deps/Android/sqlite')
|
||||
def f = file("$buildDir/sqlite-amalgamation-" + sqlite_ver)
|
||||
|
||||
if (!sqlite.exists() && !f.exists()) {
|
||||
from zipTree(downloadSqlite.dest)
|
||||
into buildDir
|
||||
}
|
||||
|
||||
doLast {
|
||||
if (!sqlite.exists()) {
|
||||
file(f).renameTo(file(sqlite))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preBuild.dependsOn getDeps
|
||||
preBuild.dependsOn getSqlite
|
|
@ -1,219 +0,0 @@
|
|||
LOCAL_PATH := $(call my-dir)/..
|
||||
|
||||
#LOCAL_ADDRESS_SANITIZER:=true
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Curl
|
||||
LOCAL_SRC_FILES := deps/Android/Curl/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libcurl.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Freetype
|
||||
LOCAL_SRC_FILES := deps/Android/Freetype/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libfreetype.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Irrlicht
|
||||
LOCAL_SRC_FILES := deps/Android/Irrlicht/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libIrrlicht.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
#include $(CLEAR_VARS)
|
||||
#LOCAL_MODULE := LevelDB
|
||||
#LOCAL_SRC_FILES := deps/Android/LevelDB/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libleveldb.a
|
||||
#include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := LuaJIT
|
||||
LOCAL_SRC_FILES := deps/Android/LuaJIT/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libluajit.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := mbedTLS
|
||||
LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedtls.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := mbedx509
|
||||
LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedx509.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := mbedcrypto
|
||||
LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedcrypto.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := OpenAL
|
||||
LOCAL_SRC_FILES := deps/Android/OpenAL-Soft/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libopenal.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
# You can use `OpenSSL and Crypto` instead `mbedTLS mbedx509 mbedcrypto`,
|
||||
#but it increase APK size on ~0.7MB
|
||||
#include $(CLEAR_VARS)
|
||||
#LOCAL_MODULE := OpenSSL
|
||||
#LOCAL_SRC_FILES := deps/Android/OpenSSL/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libssl.a
|
||||
#include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
#include $(CLEAR_VARS)
|
||||
#LOCAL_MODULE := Crypto
|
||||
#LOCAL_SRC_FILES := deps/Android/OpenSSL/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libcrypto.a
|
||||
#include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Vorbis
|
||||
LOCAL_SRC_FILES := deps/Android/Vorbis/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libvorbis.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_FREETYPE=1 \
|
||||
-DUSE_LEVELDB=0 \
|
||||
-DUSE_LUAJIT=1 \
|
||||
-DVERSION_MAJOR=${versionMajor} \
|
||||
-DVERSION_MINOR=${versionMinor} \
|
||||
-DVERSION_PATCH=${versionPatch} \
|
||||
-DVERSION_EXTRA=${versionExtra} \
|
||||
$(GPROF_DEF)
|
||||
|
||||
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/Android/Curl/include \
|
||||
deps/Android/Freetype/include \
|
||||
deps/Android/Irrlicht/include \
|
||||
deps/Android/LevelDB/include \
|
||||
deps/Android/libiconv/include \
|
||||
deps/Android/libiconv/libcharset/include \
|
||||
deps/Android/LuaJIT/src \
|
||||
deps/Android/OpenAL-Soft/include \
|
||||
deps/Android/sqlite \
|
||||
deps/Android/Vorbis/include
|
||||
|
||||
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/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
|
||||
|
||||
# LevelDB backend is disabled
|
||||
# ../../../src/database/database-leveldb.cpp
|
||||
|
||||
# GMP
|
||||
LOCAL_SRC_FILES += ../../../lib/gmp/mini-gmp.c
|
||||
|
||||
# JSONCPP
|
||||
LOCAL_SRC_FILES += ../../../lib/jsoncpp/jsoncpp.cpp
|
||||
|
||||
# iconv
|
||||
LOCAL_SRC_FILES += \
|
||||
deps/Android/libiconv/lib/iconv.c \
|
||||
deps/Android/libiconv/libcharset/lib/localcharset.c
|
||||
|
||||
# SQLite3
|
||||
LOCAL_SRC_FILES += deps/Android/sqlite/sqlite3.c
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += Curl Freetype Irrlicht OpenAL mbedTLS mbedx509 mbedcrypto Vorbis LuaJIT android_native_app_glue $(PROFILER_LIBS) #LevelDB
|
||||
#OpenSSL Crypto
|
||||
|
||||
LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES
|
||||
|
||||
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 := -Ofast -fvisibility=hidden -fexceptions -Wno-deprecated-declarations -Wno-extra-tokens
|
||||
|
||||
ifeq ($(APP_ABI),armeabi-v7a)
|
||||
APP_CPPFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb
|
||||
endif
|
||||
|
||||
#ifeq ($(APP_ABI),x86)
|
||||
#APP_CPPFLAGS += -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32 -funroll-loops
|
||||
#endif
|
||||
|
||||
ifndef NDEBUG
|
||||
APP_CPPFLAGS := -g -D_DEBUG -O0 -fno-omit-frame-pointer -fexceptions
|
||||
endif
|
||||
|
||||
APP_CFLAGS := $(APP_CPPFLAGS) -Wno-parentheses-equality #-Werror=shorten-64-to-32
|
||||
APP_CXXFLAGS := $(APP_CPPFLAGS) -frtti -std=gnu++17
|
||||
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
|
|
@ -0,0 +1,46 @@
|
|||
core.log("info", "Initializing asynchronous environment (game)")
|
||||
|
||||
local function pack2(...)
|
||||
return {n=select('#', ...), ...}
|
||||
end
|
||||
|
||||
-- Entrypoint to run async jobs, called by C++
|
||||
function core.job_processor(func, params)
|
||||
local retval = pack2(func(unpack(params, 1, params.n)))
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
-- Import a bunch of individual files from builtin/game/
|
||||
local gamepath = core.get_builtin_path() .. "game" .. DIR_DELIM
|
||||
local commonpath = core.get_builtin_path() .. "common" .. DIR_DELIM
|
||||
|
||||
dofile(gamepath .. "constants.lua")
|
||||
dofile(gamepath .. "item_s.lua")
|
||||
dofile(gamepath .. "misc_s.lua")
|
||||
dofile(gamepath .. "features.lua")
|
||||
dofile(commonpath .. "voxelarea.lua")
|
||||
|
||||
-- Transfer of globals
|
||||
do
|
||||
local all = assert(core.transferred_globals)
|
||||
core.transferred_globals = nil
|
||||
|
||||
-- reassemble other tables
|
||||
all.registered_nodes = {}
|
||||
all.registered_craftitems = {}
|
||||
all.registered_tools = {}
|
||||
for k, v in pairs(all.registered_items) do
|
||||
if v.type == "node" then
|
||||
all.registered_nodes[k] = v
|
||||
elseif v.type == "craftitem" then
|
||||
all.registered_craftitems[k] = v
|
||||
elseif v.type == "tool" then
|
||||
all.registered_tools[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in pairs(all) do
|
||||
core[k] = v
|
||||
end
|
||||
end
|
|
@ -1,17 +0,0 @@
|
|||
|
||||
core.log("info", "Initializing Asynchronous environment")
|
||||
|
||||
function core.job_processor(serialized_func, serialized_param)
|
||||
local func = loadstring(serialized_func)
|
||||
local param = core.deserialize(serialized_param)
|
||||
local retval = nil
|
||||
|
||||
if type(func) == "function" then
|
||||
retval = core.serialize(func(param))
|
||||
else
|
||||
core.log("error", "ASYNC WORKER: Unable to deserialize function")
|
||||
end
|
||||
|
||||
return retval or core.serialize(nil)
|
||||
end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
core.log("info", "Initializing asynchronous environment")
|
||||
|
||||
function core.job_processor(func, serialized_param)
|
||||
local param = core.deserialize(serialized_param)
|
||||
|
||||
local retval = core.serialize(func(param))
|
||||
|
||||
return retval or core.serialize(nil)
|
||||
end
|
|
@ -12,6 +12,8 @@ core.cheats = {
|
|||
["NoSlow"] = "no_slow",
|
||||
["JetPack"] = "jetpack",
|
||||
["AntiSlip"] = "antislip",
|
||||
["AirJump"] = "airjump",
|
||||
["Spider"] = "spider",
|
||||
},
|
||||
["Render"] = {
|
||||
["Xray"] = "xray",
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
local death_formspec = ""
|
||||
.. "size[11,5.5]"
|
||||
.. "bgcolor[#320000b4;true]"
|
||||
.. "label[4.85,1.35;" .. "You died" .. "]"
|
||||
.. "button_exit[2,3;3,0.5;btn_respawn;" .. "Respawn" .. "]"
|
||||
.. "label[4.85,1.35;" .. fgettext("You died") .. "]"
|
||||
.. "button_exit[2,3;3,0.5;btn_respawn;" .. fgettext("Respawn") .. "]"
|
||||
.. "button_exit[6,3;3,0.5;btn_ghost_mode;" .. "Ghost Mode" .. "]"
|
||||
.. "set_focus[btn_respawn;true]"
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ dofile(clientpath .. "register.lua")
|
|||
dofile(commonpath .. "after.lua")
|
||||
dofile(commonpath .. "chatcommands.lua")
|
||||
dofile(commonpath .. "vector.lua")
|
||||
dofile(commonpath .. "voxelarea.lua")
|
||||
dofile(clientpath .. "util.lua")
|
||||
dofile(clientpath .. "chatcommands.lua")
|
||||
dofile(clientpath .. "death_formspec.lua")
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
core.callback_origins = {}
|
||||
|
||||
local getinfo = debug.getinfo
|
||||
|
|
|
@ -37,7 +37,14 @@ function core.after(after, func, ...)
|
|||
arg = {...},
|
||||
mod_origin = core.get_last_run_mod(),
|
||||
}
|
||||
|
||||
jobs[#jobs + 1] = new_job
|
||||
time_next = math.min(time_next, expire)
|
||||
return { cancel = function() new_job.func = function() end end }
|
||||
|
||||
return {
|
||||
cancel = function()
|
||||
new_job.func = function() end
|
||||
new_job.args = {}
|
||||
end
|
||||
}
|
||||
end
|
||||
|
|
|
@ -6,6 +6,42 @@ local S = core.get_translator("__builtin")
|
|||
|
||||
core.registered_chatcommands = {}
|
||||
|
||||
-- Interpret the parameters of a command, separating options and arguments.
|
||||
-- Input: command, param
|
||||
-- command: name of command
|
||||
-- param: parameters of command
|
||||
-- Returns: opts, args
|
||||
-- opts is a string of option letters, or false on error
|
||||
-- args is an array with the non-option arguments in order, or an error message
|
||||
-- Example: for this command line:
|
||||
-- /command a b -cd e f -g
|
||||
-- the function would receive:
|
||||
-- a b -cd e f -g
|
||||
-- and it would return:
|
||||
-- "cdg", {"a", "b", "e", "f"}
|
||||
-- Negative numbers are taken as arguments. Long options (--option) are
|
||||
-- currently rejected as reserved.
|
||||
local function getopts(command, param)
|
||||
local opts = ""
|
||||
local args = {}
|
||||
for match in param:gmatch("%S+") do
|
||||
if match:byte(1) == 45 then -- 45 = '-'
|
||||
local second = match:byte(2)
|
||||
if second == 45 then
|
||||
return false, S("Invalid parameters (see /help @1).", command)
|
||||
elseif second and (second < 48 or second > 57) then -- 48 = '0', 57 = '9'
|
||||
opts = opts .. match:sub(2)
|
||||
else
|
||||
-- numeric, add it to args
|
||||
args[#args + 1] = match
|
||||
end
|
||||
else
|
||||
args[#args + 1] = match
|
||||
end
|
||||
end
|
||||
return opts, args
|
||||
end
|
||||
|
||||
function core.register_chatcommand(cmd, def)
|
||||
def = def or {}
|
||||
def.params = def.params or ""
|
||||
|
@ -38,6 +74,7 @@ if INIT == "client" then
|
|||
local def = {}
|
||||
def.description = desc
|
||||
def.params = "del <item> | add <item> | list"
|
||||
def.list_setting = setting
|
||||
function def.func(param)
|
||||
local list = (minetest.settings:get(setting) or ""):split(",")
|
||||
if param == "list" then
|
||||
|
@ -78,22 +115,30 @@ if INIT == "client" then
|
|||
end
|
||||
end
|
||||
|
||||
local function do_help_cmd(name, param)
|
||||
local function format_help_line(cmd, def)
|
||||
local cmd_marker = "/"
|
||||
if INIT == "client" then
|
||||
cmd_marker = "."
|
||||
end
|
||||
local msg = core.colorize("#00ffff", cmd_marker .. cmd)
|
||||
if def.params and def.params ~= "" then
|
||||
msg = msg .. " " .. def.params
|
||||
end
|
||||
if def.description and def.description ~= "" then
|
||||
msg = msg .. ": " .. def.description
|
||||
end
|
||||
return msg
|
||||
local function format_help_line(cmd, def)
|
||||
local cmd_marker = INIT == "client" and "." or "/"
|
||||
local msg = core.colorize("#00ffff", cmd_marker .. cmd)
|
||||
if def.params and def.params ~= "" then
|
||||
msg = msg .. " " .. def.params
|
||||
end
|
||||
if param == "" then
|
||||
if def.description and def.description ~= "" then
|
||||
msg = msg .. ": " .. def.description
|
||||
end
|
||||
return msg
|
||||
end
|
||||
|
||||
local function do_help_cmd(name, param)
|
||||
local opts, args = getopts("help", param)
|
||||
if not opts then
|
||||
return false, args
|
||||
end
|
||||
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)
|
||||
use_gui = use_gui and not opts:find("t")
|
||||
|
||||
if #args == 0 and not use_gui then
|
||||
local cmds = {}
|
||||
for cmd, def in pairs(core.registered_chatcommands) do
|
||||
if INIT == "client" or core.check_player_privs(name, def.privs) then
|
||||
|
@ -116,7 +161,10 @@ local function do_help_cmd(name, param)
|
|||
.. "everything.")
|
||||
end
|
||||
return true, msg
|
||||
elseif param == "all" then
|
||||
elseif #args == 0 or (args[1] == "all" and use_gui) then
|
||||
core.show_general_help_formspec(name)
|
||||
return true
|
||||
elseif args[1] == "all" then
|
||||
local cmds = {}
|
||||
for cmd, def in pairs(core.registered_chatcommands) do
|
||||
if INIT == "client" or core.check_player_privs(name, def.privs) then
|
||||
|
@ -131,7 +179,11 @@ local function do_help_cmd(name, param)
|
|||
msg = core.gettext("Available commands:")
|
||||
end
|
||||
return true, msg.."\n"..table.concat(cmds, "\n")
|
||||
elseif INIT == "game" and param == "privs" then
|
||||
elseif INIT == "game" and args[1] == "privs" then
|
||||
if use_gui then
|
||||
core.show_privs_help_formspec(name)
|
||||
return true
|
||||
end
|
||||
local privs = {}
|
||||
for priv, def in pairs(core.registered_privileges) do
|
||||
privs[#privs + 1] = priv .. ": " .. def.description
|
||||
|
@ -139,7 +191,7 @@ local function do_help_cmd(name, param)
|
|||
table.sort(privs)
|
||||
return true, S("Available privileges:").."\n"..table.concat(privs, "\n")
|
||||
else
|
||||
local cmd = param
|
||||
local cmd = args[1]
|
||||
local def = core.registered_chatcommands[cmd]
|
||||
if not def then
|
||||
local msg
|
||||
|
@ -165,8 +217,8 @@ if INIT == "client" then
|
|||
})
|
||||
else
|
||||
core.register_chatcommand("help", {
|
||||
params = S("[all | privs | <cmd>]"),
|
||||
description = S("Get help for commands or list privileges"),
|
||||
params = S("[all | privs | <cmd>] [-t]"),
|
||||
description = S("Get help for commands or list privileges (-t: output in chat)"),
|
||||
func = do_help_cmd,
|
||||
})
|
||||
end
|
||||
|
|
|
@ -22,7 +22,6 @@ local LIST_FORMSPEC_DESCRIPTION = [[
|
|||
|
||||
local F = core.formspec_escape
|
||||
local S = core.get_translator("__builtin")
|
||||
local check_player_privs = core.check_player_privs
|
||||
|
||||
|
||||
-- CHAT COMMANDS FORMSPEC
|
||||
|
@ -58,10 +57,11 @@ local function build_chatcommands_formspec(name, sel, copy)
|
|||
.. "any entry in the list.").. "\n" ..
|
||||
S("Double-click to copy the entry to the chat history.")
|
||||
|
||||
local privs = core.get_player_privs(name)
|
||||
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 = privs[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))
|
||||
|
@ -125,30 +125,12 @@ core.register_on_player_receive_fields(function(player, formname, fields)
|
|||
end
|
||||
end)
|
||||
|
||||
|
||||
local help_command = core.registered_chatcommands["help"]
|
||||
local old_help_func = help_command.func
|
||||
|
||||
help_command.func = function(name, param)
|
||||
local admin = core.settings:get("name")
|
||||
|
||||
-- If the admin ran help, put the output in the chat buffer as well to
|
||||
-- work with the server terminal
|
||||
if param == "privs" then
|
||||
core.show_formspec(name, "__builtin:help_privs",
|
||||
build_privs_formspec(name))
|
||||
if name ~= admin then
|
||||
return true
|
||||
end
|
||||
end
|
||||
if param == "" or param == "all" then
|
||||
core.show_formspec(name, "__builtin:help_cmds",
|
||||
build_chatcommands_formspec(name))
|
||||
if name ~= admin then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return old_help_func(name, param)
|
||||
function core.show_general_help_formspec(name)
|
||||
core.show_formspec(name, "__builtin:help_cmds",
|
||||
build_chatcommands_formspec(name))
|
||||
end
|
||||
|
||||
function core.show_privs_help_formspec(name)
|
||||
core.show_formspec(name, "__builtin:help_privs",
|
||||
build_privs_formspec(name))
|
||||
end
|
||||
|
|
|
@ -204,19 +204,12 @@ end
|
|||
|
||||
--------------------------------------------------------------------------------
|
||||
function string:trim()
|
||||
return (self:gsub("^%s*(.-)%s*$", "%1"))
|
||||
return self:match("^%s*(.-)%s*$")
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function math.hypot(x, y)
|
||||
local t
|
||||
x = math.abs(x)
|
||||
y = math.abs(y)
|
||||
t = math.min(x, y)
|
||||
x = math.max(x, y)
|
||||
if x == 0 then return 0 end
|
||||
t = t / x
|
||||
return x * math.sqrt(1 + t * t)
|
||||
return math.sqrt(x * x + y * y)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
@ -244,15 +237,24 @@ function math.factorial(x)
|
|||
return v
|
||||
end
|
||||
|
||||
function core.formspec_escape(text)
|
||||
if text ~= nil then
|
||||
text = string.gsub(text,"\\","\\\\")
|
||||
text = string.gsub(text,"%]","\\]")
|
||||
text = string.gsub(text,"%[","\\[")
|
||||
text = string.gsub(text,";","\\;")
|
||||
text = string.gsub(text,",","\\,")
|
||||
|
||||
function math.round(x)
|
||||
if x >= 0 then
|
||||
return math.floor(x + 0.5)
|
||||
end
|
||||
return text
|
||||
return math.ceil(x - 0.5)
|
||||
end
|
||||
|
||||
local formspec_escapes = {
|
||||
["\\"] = "\\\\",
|
||||
["["] = "\\[",
|
||||
["]"] = "\\]",
|
||||
[";"] = "\\;",
|
||||
[","] = "\\,"
|
||||
}
|
||||
function core.formspec_escape(text)
|
||||
-- Use explicit character set instead of dot here because it doubles the performance
|
||||
return text and string.gsub(text, "[\\%[%];,]", formspec_escapes)
|
||||
end
|
||||
|
||||
|
||||
|
@ -263,18 +265,21 @@ function core.wrap_text(text, max_length, as_table)
|
|||
return as_table and {text} or text
|
||||
end
|
||||
|
||||
for word in text:gmatch('%S+') do
|
||||
local cur_length = #table.concat(line, ' ')
|
||||
if cur_length > 0 and cur_length + #word + 1 >= max_length then
|
||||
local line_length = 0
|
||||
for word in text:gmatch("%S+") do
|
||||
if line_length > 0 and line_length + #word + 1 >= max_length then
|
||||
-- word wouldn't fit on current line, move to next line
|
||||
table.insert(result, table.concat(line, ' '))
|
||||
line = {}
|
||||
table.insert(result, table.concat(line, " "))
|
||||
line = {word}
|
||||
line_length = #word
|
||||
else
|
||||
table.insert(line, word)
|
||||
line_length = line_length + 1 + #word
|
||||
end
|
||||
table.insert(line, word)
|
||||
end
|
||||
|
||||
table.insert(result, table.concat(line, ' '))
|
||||
return as_table and result or table.concat(result, '\n')
|
||||
table.insert(result, table.concat(line, " "))
|
||||
return as_table and result or table.concat(result, "\n")
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
@ -423,56 +428,50 @@ function core.string_to_pos(value)
|
|||
return nil
|
||||
end
|
||||
|
||||
local p = {}
|
||||
p.x, p.y, p.z = string.match(value, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
|
||||
if p.x and p.y and p.z then
|
||||
p.x = tonumber(p.x)
|
||||
p.y = tonumber(p.y)
|
||||
p.z = tonumber(p.z)
|
||||
return p
|
||||
end
|
||||
p = {}
|
||||
p.x, p.y, p.z = string.match(value, "^%( *([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *%)$")
|
||||
if p.x and p.y and p.z then
|
||||
p.x = tonumber(p.x)
|
||||
p.y = tonumber(p.y)
|
||||
p.z = tonumber(p.z)
|
||||
return p
|
||||
value = value:match("^%((.-)%)$") or value -- strip parentheses
|
||||
|
||||
local x, y, z = value:trim():match("^([%d.-]+)[,%s]%s*([%d.-]+)[,%s]%s*([%d.-]+)$")
|
||||
if x and y and z then
|
||||
x = tonumber(x)
|
||||
y = tonumber(y)
|
||||
z = tonumber(z)
|
||||
return vector.new(x, y, z)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function core.string_to_area(value)
|
||||
local p1, p2 = unpack(value:split(") ("))
|
||||
if p1 == nil or p2 == nil then
|
||||
return nil
|
||||
|
||||
do
|
||||
local rel_num_cap = "(~?-?%d*%.?%d*)" -- may be overly permissive as this will be tonumber'ed anyways
|
||||
local num_delim = "[,%s]%s*"
|
||||
local pattern = "^" .. table.concat({rel_num_cap, rel_num_cap, rel_num_cap}, num_delim) .. "$"
|
||||
|
||||
local function parse_area_string(pos, relative_to)
|
||||
local pp = {}
|
||||
pp.x, pp.y, pp.z = pos:trim():match(pattern)
|
||||
return core.parse_coordinates(pp.x, pp.y, pp.z, relative_to)
|
||||
end
|
||||
|
||||
p1 = core.string_to_pos(p1 .. ")")
|
||||
p2 = core.string_to_pos("(" .. p2)
|
||||
if p1 == nil or p2 == nil then
|
||||
return nil
|
||||
function core.string_to_area(value, relative_to)
|
||||
local p1, p2 = value:match("^%((.-)%)%s*%((.-)%)$")
|
||||
if not p1 then
|
||||
return
|
||||
end
|
||||
|
||||
p1 = parse_area_string(p1, relative_to)
|
||||
p2 = parse_area_string(p2, relative_to)
|
||||
|
||||
if p1 == nil or p2 == nil then
|
||||
return
|
||||
end
|
||||
|
||||
return p1, p2
|
||||
end
|
||||
|
||||
return p1, p2
|
||||
end
|
||||
|
||||
local function test_string_to_area()
|
||||
local p1, p2 = core.string_to_area("(10.0, 5, -2) ( 30.2, 4, -12.53)")
|
||||
assert(p1.x == 10.0 and p1.y == 5 and p1.z == -2)
|
||||
assert(p2.x == 30.2 and p2.y == 4 and p2.z == -12.53)
|
||||
|
||||
p1, p2 = core.string_to_area("(10.0, 5, -2 30.2, 4, -12.53")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(10.0, 5,) -2 fgdf2, 4, -12.53")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
end
|
||||
|
||||
test_string_to_area()
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function table.copy(t, seen)
|
||||
local n = {}
|
||||
|
@ -543,7 +542,7 @@ if INIT == "mainmenu" then
|
|||
end
|
||||
end
|
||||
|
||||
if INIT == "client" or INIT == "mainmenu" then
|
||||
if core.gettext then -- for client and mainmenu
|
||||
function fgettext_ne(text, ...)
|
||||
text = core.gettext(text)
|
||||
local arg = {n=select('#', ...), ...}
|
||||
|
@ -786,6 +785,74 @@ function core.is_nan(number)
|
|||
return number ~= number
|
||||
end
|
||||
|
||||
--[[ Helper function for parsing an optionally relative number
|
||||
of a chat command parameter, using the chat command tilde notation.
|
||||
|
||||
Parameters:
|
||||
* arg: String snippet containing the number; possible values:
|
||||
* "<number>": return as number
|
||||
* "~<number>": return relative_to + <number>
|
||||
* "~": return relative_to
|
||||
* Anything else will return `nil`
|
||||
* relative_to: Number to which the `arg` number might be relative to
|
||||
|
||||
Returns:
|
||||
A number or `nil`, depending on `arg.
|
||||
|
||||
Examples:
|
||||
* `core.parse_relative_number("5", 10)` returns 5
|
||||
* `core.parse_relative_number("~5", 10)` returns 15
|
||||
* `core.parse_relative_number("~", 10)` returns 10
|
||||
]]
|
||||
function core.parse_relative_number(arg, relative_to)
|
||||
if not arg then
|
||||
return nil
|
||||
elseif arg == "~" then
|
||||
return relative_to
|
||||
elseif string.sub(arg, 1, 1) == "~" then
|
||||
local number = tonumber(string.sub(arg, 2))
|
||||
if not number then
|
||||
return nil
|
||||
end
|
||||
if core.is_nan(number) or number == math.huge or number == -math.huge then
|
||||
return nil
|
||||
end
|
||||
return relative_to + number
|
||||
else
|
||||
local number = tonumber(arg)
|
||||
if core.is_nan(number) or number == math.huge or number == -math.huge then
|
||||
return nil
|
||||
end
|
||||
return number
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Helper function to parse coordinates that might be relative
|
||||
to another position; supports chat command tilde notation.
|
||||
Intended to be used in chat command parameter parsing.
|
||||
|
||||
Parameters:
|
||||
* x, y, z: Parsed x, y, and z coordinates as strings
|
||||
* relative_to: Position to which to compare the position
|
||||
|
||||
Syntax of x, y and z:
|
||||
* "<number>": return as number
|
||||
* "~<number>": return <number> + player position on this axis
|
||||
* "~": return player position on this axis
|
||||
|
||||
Returns: a vector or nil for invalid input or if player does not exist
|
||||
]]
|
||||
function core.parse_coordinates(x, y, z, relative_to)
|
||||
if not relative_to then
|
||||
x, y, z = tonumber(x), tonumber(y), tonumber(z)
|
||||
return x and y and z and { x = x, y = y, z = z }
|
||||
end
|
||||
local rx = core.parse_relative_number(x, relative_to.x)
|
||||
local ry = core.parse_relative_number(y, relative_to.y)
|
||||
local rz = core.parse_relative_number(z, relative_to.z)
|
||||
return rx and ry and rz and { x = rx, y = ry, z = rz }
|
||||
end
|
||||
|
||||
function core.inventorycube(img1, img2, img3)
|
||||
img2 = img2 or img1
|
||||
img3 = img3 or img1
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
|
||||
-- Always warn when creating a global variable, even outside of a function.
|
||||
-- This ignores mod namespaces (variables with the same name as the current mod).
|
||||
local WARN_INIT = false
|
||||
|
||||
local getinfo = debug.getinfo
|
||||
|
||||
function core.global_exists(name)
|
||||
|
@ -33,11 +28,6 @@ function meta:__newindex(name, value)
|
|||
end
|
||||
declared[name] = true
|
||||
end
|
||||
-- Ignore mod namespaces
|
||||
if WARN_INIT and name ~= core.get_current_modname() then
|
||||
core.log("warning", ("Global variable %q created at %s.")
|
||||
:format(name, desc))
|
||||
end
|
||||
rawset(self, name, value)
|
||||
end
|
||||
|
||||
|
@ -54,4 +44,3 @@ function meta:__index(name)
|
|||
end
|
||||
|
||||
setmetatable(_G, meta)
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
_G.core = {}
|
||||
_G.vector = {metatable = {}}
|
||||
dofile("builtin/common/vector.lua")
|
||||
dofile("builtin/common/misc_helpers.lua")
|
||||
|
||||
describe("string", function()
|
||||
|
@ -55,8 +57,8 @@ end)
|
|||
|
||||
describe("pos", function()
|
||||
it("from string", function()
|
||||
assert.same({ x = 10, y = 5.1, z = -2}, core.string_to_pos("10.0, 5.1, -2"))
|
||||
assert.same({ x = 10, y = 5.1, z = -2}, core.string_to_pos("( 10.0, 5.1, -2)"))
|
||||
assert.equal(vector.new(10, 5.1, -2), core.string_to_pos("10.0, 5.1, -2"))
|
||||
assert.equal(vector.new(10, 5.1, -2), core.string_to_pos("( 10.0, 5.1, -2)"))
|
||||
assert.is_nil(core.string_to_pos("asd, 5, -2)"))
|
||||
end)
|
||||
|
||||
|
@ -65,9 +67,107 @@ describe("pos", function()
|
|||
end)
|
||||
end)
|
||||
|
||||
describe("area parsing", function()
|
||||
describe("valid inputs", function()
|
||||
it("accepts absolute numbers", function()
|
||||
local p1, p2 = core.string_to_area("(10.0, 5, -2) ( 30.2 4 -12.53)")
|
||||
assert(p1.x == 10 and p1.y == 5 and p1.z == -2)
|
||||
assert(p2.x == 30.2 and p2.y == 4 and p2.z == -12.53)
|
||||
end)
|
||||
|
||||
it("accepts relative numbers", function()
|
||||
local p1, p2 = core.string_to_area("(1,2,3) (~5,~-5,~)", {x=10,y=10,z=10})
|
||||
assert(type(p1) == "table" and type(p2) == "table")
|
||||
assert(p1.x == 1 and p1.y == 2 and p1.z == 3)
|
||||
assert(p2.x == 15 and p2.y == 5 and p2.z == 10)
|
||||
|
||||
p1, p2 = core.string_to_area("(1 2 3) (~5 ~-5 ~)", {x=10,y=10,z=10})
|
||||
assert(type(p1) == "table" and type(p2) == "table")
|
||||
assert(p1.x == 1 and p1.y == 2 and p1.z == 3)
|
||||
assert(p2.x == 15 and p2.y == 5 and p2.z == 10)
|
||||
end)
|
||||
end)
|
||||
describe("invalid inputs", function()
|
||||
it("rejects too few numbers", function()
|
||||
local p1, p2 = core.string_to_area("(1,1) (1,1,1,1)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
end)
|
||||
|
||||
it("rejects too many numbers", function()
|
||||
local p1, p2 = core.string_to_area("(1,1,1,1) (1,1,1,1)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
end)
|
||||
|
||||
it("rejects nan & inf", function()
|
||||
local p1, p2 = core.string_to_area("(1,1,1) (1,1,nan)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(1,1,1) (1,1,~nan)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(1,1,1) (1,~nan,1)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(1,1,1) (1,1,inf)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(1,1,1) (1,1,~inf)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(1,1,1) (1,~inf,1)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(nan,nan,nan) (nan,nan,nan)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(nan,nan,nan) (nan,nan,nan)")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(inf,inf,inf) (-inf,-inf,-inf)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(inf,inf,inf) (-inf,-inf,-inf)")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
end)
|
||||
|
||||
it("rejects words", function()
|
||||
local p1, p2 = core.string_to_area("bananas", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("bananas", "foobar")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("bananas")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(bananas,bananas,bananas)")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(bananas,bananas,bananas) (bananas,bananas,bananas)")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
end)
|
||||
|
||||
it("requires parenthesis & valid numbers", function()
|
||||
local p1, p2 = core.string_to_area("(10.0, 5, -2 30.2, 4, -12.53")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(10.0, 5,) -2 fgdf2, 4, -12.53")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("table", function()
|
||||
it("indexof()", function()
|
||||
assert.equal(1, table.indexof({"foo", "bar"}, "foo"))
|
||||
assert.equal(-1, table.indexof({"foo", "bar"}, "baz"))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("formspec_escape", function()
|
||||
it("escapes", function()
|
||||
assert.equal(nil, core.formspec_escape(nil))
|
||||
assert.equal("", core.formspec_escape(""))
|
||||
assert.equal("\\[Hello\\\\\\[", core.formspec_escape("[Hello\\["))
|
||||
end)
|
||||
end)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
_G.core = {}
|
||||
_G.vector = {metatable = {}}
|
||||
|
||||
_G.setfenv = require 'busted.compatibility'.setfenv
|
||||
|
||||
dofile("builtin/common/serialize.lua")
|
||||
dofile("builtin/common/vector.lua")
|
||||
|
||||
describe("serialize", function()
|
||||
it("works", function()
|
||||
|
@ -53,4 +55,16 @@ describe("serialize", function()
|
|||
assert.is_nil(test_out.func)
|
||||
assert.equals(test_out.foo, "bar")
|
||||
end)
|
||||
|
||||
it("vectors work", function()
|
||||
local v = vector.new(1, 2, 3)
|
||||
assert.same({{x = 1, y = 2, z = 3}}, core.deserialize(core.serialize({v})))
|
||||
assert.same({x = 1, y = 2, z = 3}, core.deserialize(core.serialize(v)))
|
||||
|
||||
-- abuse
|
||||
v = vector.new(1, 2, 3)
|
||||
v.a = "bla"
|
||||
assert.same({x = 1, y = 2, z = 3, a = "bla"},
|
||||
core.deserialize(core.serialize(v)))
|
||||
end)
|
||||
end)
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
_G.vector = {}
|
||||
_G.vector = {metatable = {}}
|
||||
dofile("builtin/common/vector.lua")
|
||||
|
||||
describe("vector", function()
|
||||
describe("new()", function()
|
||||
it("constructs", function()
|
||||
assert.same({ x = 0, y = 0, z = 0 }, vector.new())
|
||||
assert.same({ x = 1, y = 2, z = 3 }, vector.new(1, 2, 3))
|
||||
assert.same({ x = 3, y = 2, z = 1 }, vector.new({ x = 3, y = 2, z = 1 }))
|
||||
assert.same({x = 0, y = 0, z = 0}, vector.new())
|
||||
assert.same({x = 1, y = 2, z = 3}, vector.new(1, 2, 3))
|
||||
assert.same({x = 3, y = 2, z = 1}, vector.new({x = 3, y = 2, z = 1}))
|
||||
|
||||
assert.is_true(vector.check(vector.new()))
|
||||
assert.is_true(vector.check(vector.new(1, 2, 3)))
|
||||
assert.is_true(vector.check(vector.new({x = 3, y = 2, z = 1})))
|
||||
|
||||
local input = vector.new({ x = 3, y = 2, z = 1 })
|
||||
local output = vector.new(input)
|
||||
assert.same(input, output)
|
||||
assert.are_not.equal(input, output)
|
||||
assert.equal(input, output)
|
||||
assert.is_false(rawequal(input, output))
|
||||
assert.equal(input, input:new())
|
||||
end)
|
||||
|
||||
it("throws on invalid input", function()
|
||||
|
@ -25,27 +31,294 @@ describe("vector", function()
|
|||
end)
|
||||
end)
|
||||
|
||||
it("equal()", function()
|
||||
local function assertE(a, b)
|
||||
assert.is_true(vector.equals(a, b))
|
||||
end
|
||||
local function assertNE(a, b)
|
||||
assert.is_false(vector.equals(a, b))
|
||||
end
|
||||
|
||||
assertE({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
|
||||
assertE({x = -1, y = 0, z = 1}, {x = -1, y = 0, z = 1})
|
||||
local a = { x = 2, y = 4, z = -10 }
|
||||
assertE(a, a)
|
||||
assertNE({x = -1, y = 0, z = 1}, a)
|
||||
it("zero()", function()
|
||||
assert.same({x = 0, y = 0, z = 0}, vector.zero())
|
||||
assert.same(vector.new(), vector.zero())
|
||||
assert.equal(vector.new(), vector.zero())
|
||||
assert.is_true(vector.check(vector.zero()))
|
||||
end)
|
||||
|
||||
it("add()", function()
|
||||
assert.same({ x = 2, y = 4, z = 6 }, vector.add(vector.new(1, 2, 3), { x = 1, y = 2, z = 3 }))
|
||||
it("copy()", function()
|
||||
local v = vector.new(1, 2, 3)
|
||||
assert.same(v, vector.copy(v))
|
||||
assert.same(vector.new(v), vector.copy(v))
|
||||
assert.equal(vector.new(v), vector.copy(v))
|
||||
assert.is_true(vector.check(vector.copy(v)))
|
||||
end)
|
||||
|
||||
it("indexes", function()
|
||||
local some_vector = vector.new(24, 42, 13)
|
||||
assert.equal(24, some_vector[1])
|
||||
assert.equal(24, some_vector.x)
|
||||
assert.equal(42, some_vector[2])
|
||||
assert.equal(42, some_vector.y)
|
||||
assert.equal(13, some_vector[3])
|
||||
assert.equal(13, some_vector.z)
|
||||
|
||||
some_vector[1] = 100
|
||||
assert.equal(100, some_vector.x)
|
||||
some_vector.x = 101
|
||||
assert.equal(101, some_vector[1])
|
||||
|
||||
some_vector[2] = 100
|
||||
assert.equal(100, some_vector.y)
|
||||
some_vector.y = 102
|
||||
assert.equal(102, some_vector[2])
|
||||
|
||||
some_vector[3] = 100
|
||||
assert.equal(100, some_vector.z)
|
||||
some_vector.z = 103
|
||||
assert.equal(103, some_vector[3])
|
||||
end)
|
||||
|
||||
it("direction()", function()
|
||||
local a = vector.new(1, 0, 0)
|
||||
local b = vector.new(1, 42, 0)
|
||||
assert.equal(vector.new(0, 1, 0), vector.direction(a, b))
|
||||
assert.equal(vector.new(0, 1, 0), a:direction(b))
|
||||
end)
|
||||
|
||||
it("distance()", function()
|
||||
local a = vector.new(1, 0, 0)
|
||||
local b = vector.new(3, 42, 9)
|
||||
assert.is_true(math.abs(43 - vector.distance(a, b)) < 1.0e-12)
|
||||
assert.is_true(math.abs(43 - a:distance(b)) < 1.0e-12)
|
||||
assert.equal(0, vector.distance(a, a))
|
||||
assert.equal(0, b:distance(b))
|
||||
end)
|
||||
|
||||
it("length()", function()
|
||||
local a = vector.new(0, 0, -23)
|
||||
assert.equal(0, vector.length(vector.new()))
|
||||
assert.equal(23, vector.length(a))
|
||||
assert.equal(23, a:length())
|
||||
end)
|
||||
|
||||
it("normalize()", function()
|
||||
local a = vector.new(0, 0, -23)
|
||||
assert.equal(vector.new(0, 0, -1), vector.normalize(a))
|
||||
assert.equal(vector.new(0, 0, -1), a:normalize())
|
||||
assert.equal(vector.new(), vector.normalize(vector.new()))
|
||||
end)
|
||||
|
||||
it("floor()", function()
|
||||
local a = vector.new(0.1, 0.9, -0.5)
|
||||
assert.equal(vector.new(0, 0, -1), vector.floor(a))
|
||||
assert.equal(vector.new(0, 0, -1), a:floor())
|
||||
end)
|
||||
|
||||
it("round()", function()
|
||||
local a = vector.new(0.1, 0.9, -0.5)
|
||||
assert.equal(vector.new(0, 1, -1), vector.round(a))
|
||||
assert.equal(vector.new(0, 1, -1), a:round())
|
||||
end)
|
||||
|
||||
it("apply()", function()
|
||||
local i = 0
|
||||
local f = function(x)
|
||||
i = i + 1
|
||||
return x + i
|
||||
end
|
||||
local a = vector.new(0.1, 0.9, -0.5)
|
||||
assert.equal(vector.new(1, 1, 0), vector.apply(a, math.ceil))
|
||||
assert.equal(vector.new(1, 1, 0), a:apply(math.ceil))
|
||||
assert.equal(vector.new(0.1, 0.9, 0.5), vector.apply(a, math.abs))
|
||||
assert.equal(vector.new(0.1, 0.9, 0.5), a:apply(math.abs))
|
||||
assert.equal(vector.new(1.1, 2.9, 2.5), vector.apply(a, f))
|
||||
assert.equal(vector.new(4.1, 5.9, 5.5), a:apply(f))
|
||||
end)
|
||||
|
||||
it("combine()", function()
|
||||
local a = vector.new(1, 2, 3)
|
||||
local b = vector.new(3, 2, 1)
|
||||
assert.equal(vector.add(a, b), vector.combine(a, b, function(x, y) return x + y end))
|
||||
assert.equal(vector.new(3, 2, 3), vector.combine(a, b, math.max))
|
||||
assert.equal(vector.new(1, 2, 1), vector.combine(a, b, math.min))
|
||||
end)
|
||||
|
||||
it("equals()", function()
|
||||
local function assertE(a, b)
|
||||
assert.is_true(vector.equals(a, b))
|
||||
end
|
||||
local function assertNE(a, b)
|
||||
assert.is_false(vector.equals(a, b))
|
||||
end
|
||||
|
||||
assertE({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
|
||||
assertE({x = -1, y = 0, z = 1}, {x = -1, y = 0, z = 1})
|
||||
assertE({x = -1, y = 0, z = 1}, vector.new(-1, 0, 1))
|
||||
local a = {x = 2, y = 4, z = -10}
|
||||
assertE(a, a)
|
||||
assertNE({x = -1, y = 0, z = 1}, a)
|
||||
|
||||
assert.equal(vector.new(1, 2, 3), vector.new(1, 2, 3))
|
||||
assert.is_true(vector.new(1, 2, 3):equals(vector.new(1, 2, 3)))
|
||||
assert.not_equal(vector.new(1, 2, 3), vector.new(1, 2, 4))
|
||||
assert.is_true(vector.new(1, 2, 3) == vector.new(1, 2, 3))
|
||||
assert.is_false(vector.new(1, 2, 3) == vector.new(1, 3, 3))
|
||||
end)
|
||||
|
||||
it("metatable is same", function()
|
||||
local a = vector.new()
|
||||
local b = vector.new(1, 2, 3)
|
||||
|
||||
assert.equal(true, vector.check(a))
|
||||
assert.equal(true, vector.check(b))
|
||||
|
||||
assert.equal(vector.metatable, getmetatable(a))
|
||||
assert.equal(vector.metatable, getmetatable(b))
|
||||
assert.equal(vector.metatable, a.metatable)
|
||||
end)
|
||||
|
||||
it("sort()", function()
|
||||
local a = vector.new(1, 2, 3)
|
||||
local b = vector.new(0.5, 232, -2)
|
||||
local sorted = {vector.new(0.5, 2, -2), vector.new(1, 232, 3)}
|
||||
assert.same(sorted, {vector.sort(a, b)})
|
||||
assert.same(sorted, {a:sort(b)})
|
||||
end)
|
||||
|
||||
it("angle()", function()
|
||||
assert.equal(math.pi, vector.angle(vector.new(-1, -2, -3), vector.new(1, 2, 3)))
|
||||
assert.equal(math.pi/2, vector.new(0, 1, 0):angle(vector.new(1, 0, 0)))
|
||||
end)
|
||||
|
||||
it("dot()", function()
|
||||
assert.equal(-14, vector.dot(vector.new(-1, -2, -3), vector.new(1, 2, 3)))
|
||||
assert.equal(0, vector.new():dot(vector.new(1, 2, 3)))
|
||||
end)
|
||||
|
||||
it("cross()", function()
|
||||
local a = vector.new(-1, -2, 0)
|
||||
local b = vector.new(1, 2, 3)
|
||||
assert.equal(vector.new(-6, 3, 0), vector.cross(a, b))
|
||||
assert.equal(vector.new(-6, 3, 0), a:cross(b))
|
||||
end)
|
||||
|
||||
it("offset()", function()
|
||||
assert.same({ x = 41, y = 52, z = 63 }, vector.offset(vector.new(1, 2, 3), 40, 50, 60))
|
||||
assert.same({x = 41, y = 52, z = 63}, vector.offset(vector.new(1, 2, 3), 40, 50, 60))
|
||||
assert.equal(vector.new(41, 52, 63), vector.offset(vector.new(1, 2, 3), 40, 50, 60))
|
||||
assert.equal(vector.new(41, 52, 63), vector.new(1, 2, 3):offset(40, 50, 60))
|
||||
end)
|
||||
|
||||
it("is()", function()
|
||||
local some_table1 = {foo = 13, [42] = 1, "bar", 2}
|
||||
local some_table2 = {1, 2, 3}
|
||||
local some_table3 = {x = 1, 2, 3}
|
||||
local some_table4 = {1, 2, z = 3}
|
||||
local old = {x = 1, y = 2, z = 3}
|
||||
local real = vector.new(1, 2, 3)
|
||||
|
||||
assert.is_false(vector.check(nil))
|
||||
assert.is_false(vector.check(1))
|
||||
assert.is_false(vector.check(true))
|
||||
assert.is_false(vector.check("foo"))
|
||||
assert.is_false(vector.check(some_table1))
|
||||
assert.is_false(vector.check(some_table2))
|
||||
assert.is_false(vector.check(some_table3))
|
||||
assert.is_false(vector.check(some_table4))
|
||||
assert.is_false(vector.check(old))
|
||||
assert.is_true(vector.check(real))
|
||||
assert.is_true(real:check())
|
||||
end)
|
||||
|
||||
it("global pairs", function()
|
||||
local out = {}
|
||||
local vec = vector.new(10, 20, 30)
|
||||
for k, v in pairs(vec) do
|
||||
out[k] = v
|
||||
end
|
||||
assert.same({x = 10, y = 20, z = 30}, out)
|
||||
end)
|
||||
|
||||
it("abusing works", function()
|
||||
local v = vector.new(1, 2, 3)
|
||||
v.a = 1
|
||||
assert.equal(1, v.a)
|
||||
|
||||
local a_is_there = false
|
||||
for key, value in pairs(v) do
|
||||
if key == "a" then
|
||||
a_is_there = true
|
||||
assert.equal(value, 1)
|
||||
break
|
||||
end
|
||||
end
|
||||
assert.is_true(a_is_there)
|
||||
end)
|
||||
|
||||
it("add()", function()
|
||||
local a = vector.new(1, 2, 3)
|
||||
local b = vector.new(1, 4, 3)
|
||||
local c = vector.new(2, 6, 6)
|
||||
assert.equal(c, vector.add(a, {x = 1, y = 4, z = 3}))
|
||||
assert.equal(c, vector.add(a, b))
|
||||
assert.equal(c, a:add(b))
|
||||
assert.equal(c, a + b)
|
||||
assert.equal(c, b + a)
|
||||
end)
|
||||
|
||||
it("subtract()", function()
|
||||
local a = vector.new(1, 2, 3)
|
||||
local b = vector.new(2, 4, 3)
|
||||
local c = vector.new(-1, -2, 0)
|
||||
assert.equal(c, vector.subtract(a, {x = 2, y = 4, z = 3}))
|
||||
assert.equal(c, vector.subtract(a, b))
|
||||
assert.equal(c, a:subtract(b))
|
||||
assert.equal(c, a - b)
|
||||
assert.equal(c, -b + a)
|
||||
end)
|
||||
|
||||
it("multiply()", function()
|
||||
local a = vector.new(1, 2, 3)
|
||||
local b = vector.new(2, 4, 3)
|
||||
local c = vector.new(2, 8, 9)
|
||||
local s = 2
|
||||
local d = vector.new(2, 4, 6)
|
||||
assert.equal(c, vector.multiply(a, {x = 2, y = 4, z = 3}))
|
||||
assert.equal(c, vector.multiply(a, b))
|
||||
assert.equal(d, vector.multiply(a, s))
|
||||
assert.equal(d, a:multiply(s))
|
||||
assert.equal(d, a * s)
|
||||
assert.equal(d, s * a)
|
||||
assert.equal(-a, -1 * a)
|
||||
end)
|
||||
|
||||
it("divide()", function()
|
||||
local a = vector.new(1, 2, 3)
|
||||
local b = vector.new(2, 4, 3)
|
||||
local c = vector.new(0.5, 0.5, 1)
|
||||
local s = 2
|
||||
local d = vector.new(0.5, 1, 1.5)
|
||||
assert.equal(c, vector.divide(a, {x = 2, y = 4, z = 3}))
|
||||
assert.equal(c, vector.divide(a, b))
|
||||
assert.equal(d, vector.divide(a, s))
|
||||
assert.equal(d, a:divide(s))
|
||||
assert.equal(d, a / s)
|
||||
assert.equal(d, 1/s * a)
|
||||
assert.equal(-a, a / -1)
|
||||
end)
|
||||
|
||||
it("to_string()", function()
|
||||
local v = vector.new(1, 2, 3.14)
|
||||
assert.same("(1, 2, 3.14)", vector.to_string(v))
|
||||
assert.same("(1, 2, 3.14)", v:to_string())
|
||||
assert.same("(1, 2, 3.14)", tostring(v))
|
||||
end)
|
||||
|
||||
it("from_string()", function()
|
||||
local v = vector.new(1, 2, 3.14)
|
||||
assert.is_true(vector.check(vector.from_string("(1, 2, 3.14)")))
|
||||
assert.same({v, 13}, {vector.from_string("(1, 2, 3.14)")})
|
||||
assert.same({v, 12}, {vector.from_string("(1,2 ,3.14)")})
|
||||
assert.same({v, 12}, {vector.from_string("(1,2,3.14,)")})
|
||||
assert.same({v, 11}, {vector.from_string("(1 2 3.14)")})
|
||||
assert.same({v, 15}, {vector.from_string("( 1, 2, 3.14 )")})
|
||||
assert.same({v, 15}, {vector.from_string(" ( 1, 2, 3.14) ")})
|
||||
assert.same({vector.new(), 8}, {vector.from_string("(0,0,0) ( 1, 2, 3.14) ")})
|
||||
assert.same({v, 22}, {vector.from_string("(0,0,0) ( 1, 2, 3.14) ", 8)})
|
||||
assert.same({v, 22}, {vector.from_string("(0,0,0) ( 1, 2, 3.14) ", 9)})
|
||||
assert.same(nil, vector.from_string("nothing"))
|
||||
end)
|
||||
|
||||
-- This function is needed because of floating point imprecision.
|
||||
|
|
|
@ -1,73 +1,132 @@
|
|||
--[[
|
||||
Vector helpers
|
||||
Note: The vector.*-functions must be able to accept old vectors that had no metatables
|
||||
]]
|
||||
|
||||
vector = {}
|
||||
-- localize functions
|
||||
local setmetatable = setmetatable
|
||||
|
||||
-- vector.metatable is set by C++.
|
||||
local metatable = vector.metatable
|
||||
|
||||
local xyz = {"x", "y", "z"}
|
||||
|
||||
-- only called when rawget(v, key) returns nil
|
||||
function metatable.__index(v, key)
|
||||
return rawget(v, xyz[key]) or vector[key]
|
||||
end
|
||||
|
||||
-- only called when rawget(v, key) returns nil
|
||||
function metatable.__newindex(v, key, value)
|
||||
rawset(v, xyz[key] or key, value)
|
||||
end
|
||||
|
||||
-- constructors
|
||||
|
||||
local function fast_new(x, y, z)
|
||||
return setmetatable({x = x, y = y, z = z}, metatable)
|
||||
end
|
||||
|
||||
function vector.new(a, b, c)
|
||||
if type(a) == "table" then
|
||||
assert(a.x and a.y and a.z, "Invalid vector passed to vector.new()")
|
||||
return {x=a.x, y=a.y, z=a.z}
|
||||
elseif a then
|
||||
assert(b and c, "Invalid arguments for vector.new()")
|
||||
return {x=a, y=b, z=c}
|
||||
if a and b and c then
|
||||
return fast_new(a, b, c)
|
||||
end
|
||||
|
||||
-- deprecated, use vector.copy and vector.zero directly
|
||||
if type(a) == "table" then
|
||||
return vector.copy(a)
|
||||
else
|
||||
assert(not a, "Invalid arguments for vector.new()")
|
||||
return vector.zero()
|
||||
end
|
||||
return {x=0, y=0, z=0}
|
||||
end
|
||||
|
||||
function vector.zero()
|
||||
return fast_new(0, 0, 0)
|
||||
end
|
||||
|
||||
function vector.copy(v)
|
||||
assert(v.x and v.y and v.z, "Invalid vector passed to vector.copy()")
|
||||
return fast_new(v.x, v.y, v.z)
|
||||
end
|
||||
|
||||
function vector.from_string(s, init)
|
||||
local x, y, z, np = string.match(s, "^%s*%(%s*([^%s,]+)%s*[,%s]%s*([^%s,]+)%s*[,%s]" ..
|
||||
"%s*([^%s,]+)%s*[,%s]?%s*%)()", init)
|
||||
x = tonumber(x)
|
||||
y = tonumber(y)
|
||||
z = tonumber(z)
|
||||
if not (x and y and z) then
|
||||
return nil
|
||||
end
|
||||
return fast_new(x, y, z), np
|
||||
end
|
||||
|
||||
function vector.to_string(v)
|
||||
return string.format("(%g, %g, %g)", v.x, v.y, v.z)
|
||||
end
|
||||
metatable.__tostring = vector.to_string
|
||||
|
||||
function vector.equals(a, b)
|
||||
return a.x == b.x and
|
||||
a.y == b.y and
|
||||
a.z == b.z
|
||||
end
|
||||
metatable.__eq = vector.equals
|
||||
|
||||
-- unary operations
|
||||
|
||||
function vector.length(v)
|
||||
return math.hypot(v.x, math.hypot(v.y, v.z))
|
||||
return math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
|
||||
end
|
||||
-- Note: we can not use __len because it is already used for primitive table length
|
||||
|
||||
function vector.normalize(v)
|
||||
local len = vector.length(v)
|
||||
if len == 0 then
|
||||
return {x=0, y=0, z=0}
|
||||
return fast_new(0, 0, 0)
|
||||
else
|
||||
return vector.divide(v, len)
|
||||
end
|
||||
end
|
||||
|
||||
function vector.floor(v)
|
||||
return {
|
||||
x = math.floor(v.x),
|
||||
y = math.floor(v.y),
|
||||
z = math.floor(v.z)
|
||||
}
|
||||
return vector.apply(v, math.floor)
|
||||
end
|
||||
|
||||
function vector.round(v)
|
||||
return {
|
||||
x = math.floor(v.x + 0.5),
|
||||
y = math.floor(v.y + 0.5),
|
||||
z = math.floor(v.z + 0.5)
|
||||
}
|
||||
return fast_new(
|
||||
math.round(v.x),
|
||||
math.round(v.y),
|
||||
math.round(v.z)
|
||||
)
|
||||
end
|
||||
|
||||
function vector.apply(v, func)
|
||||
return {
|
||||
x = func(v.x),
|
||||
y = func(v.y),
|
||||
z = func(v.z)
|
||||
}
|
||||
return fast_new(
|
||||
func(v.x),
|
||||
func(v.y),
|
||||
func(v.z)
|
||||
)
|
||||
end
|
||||
|
||||
function vector.combine(a, b, func)
|
||||
return fast_new(
|
||||
func(a.x, b.x),
|
||||
func(a.y, b.y),
|
||||
func(a.z, b.z)
|
||||
)
|
||||
end
|
||||
|
||||
function vector.distance(a, b)
|
||||
local x = a.x - b.x
|
||||
local y = a.y - b.y
|
||||
local z = a.z - b.z
|
||||
return math.hypot(x, math.hypot(y, z))
|
||||
return math.sqrt(x * x + y * y + z * z)
|
||||
end
|
||||
|
||||
function vector.direction(pos1, pos2)
|
||||
return vector.normalize({
|
||||
x = pos2.x - pos1.x,
|
||||
y = pos2.y - pos1.y,
|
||||
z = pos2.z - pos1.z
|
||||
})
|
||||
return vector.subtract(pos2, pos1):normalize()
|
||||
end
|
||||
|
||||
function vector.angle(a, b)
|
||||
|
@ -82,70 +141,137 @@ function vector.dot(a, b)
|
|||
end
|
||||
|
||||
function vector.cross(a, b)
|
||||
return {
|
||||
x = a.y * b.z - a.z * b.y,
|
||||
y = a.z * b.x - a.x * b.z,
|
||||
z = a.x * b.y - a.y * b.x
|
||||
}
|
||||
return fast_new(
|
||||
a.y * b.z - a.z * b.y,
|
||||
a.z * b.x - a.x * b.z,
|
||||
a.x * b.y - a.y * b.x
|
||||
)
|
||||
end
|
||||
|
||||
function metatable.__unm(v)
|
||||
return fast_new(-v.x, -v.y, -v.z)
|
||||
end
|
||||
|
||||
-- add, sub, mul, div operations
|
||||
|
||||
function vector.add(a, b)
|
||||
if type(b) == "table" then
|
||||
return {x = a.x + b.x,
|
||||
y = a.y + b.y,
|
||||
z = a.z + b.z}
|
||||
return fast_new(
|
||||
a.x + b.x,
|
||||
a.y + b.y,
|
||||
a.z + b.z
|
||||
)
|
||||
else
|
||||
return {x = a.x + b,
|
||||
y = a.y + b,
|
||||
z = a.z + b}
|
||||
return fast_new(
|
||||
a.x + b,
|
||||
a.y + b,
|
||||
a.z + b
|
||||
)
|
||||
end
|
||||
end
|
||||
function metatable.__add(a, b)
|
||||
return fast_new(
|
||||
a.x + b.x,
|
||||
a.y + b.y,
|
||||
a.z + b.z
|
||||
)
|
||||
end
|
||||
|
||||
function vector.subtract(a, b)
|
||||
if type(b) == "table" then
|
||||
return {x = a.x - b.x,
|
||||
y = a.y - b.y,
|
||||
z = a.z - b.z}
|
||||
return fast_new(
|
||||
a.x - b.x,
|
||||
a.y - b.y,
|
||||
a.z - b.z
|
||||
)
|
||||
else
|
||||
return {x = a.x - b,
|
||||
y = a.y - b,
|
||||
z = a.z - b}
|
||||
return fast_new(
|
||||
a.x - b,
|
||||
a.y - b,
|
||||
a.z - b
|
||||
)
|
||||
end
|
||||
end
|
||||
function metatable.__sub(a, b)
|
||||
return fast_new(
|
||||
a.x - b.x,
|
||||
a.y - b.y,
|
||||
a.z - b.z
|
||||
)
|
||||
end
|
||||
|
||||
function vector.multiply(a, b)
|
||||
if type(b) == "table" then
|
||||
return {x = a.x * b.x,
|
||||
y = a.y * b.y,
|
||||
z = a.z * b.z}
|
||||
return fast_new(
|
||||
a.x * b.x,
|
||||
a.y * b.y,
|
||||
a.z * b.z
|
||||
)
|
||||
else
|
||||
return {x = a.x * b,
|
||||
y = a.y * b,
|
||||
z = a.z * b}
|
||||
return fast_new(
|
||||
a.x * b,
|
||||
a.y * b,
|
||||
a.z * b
|
||||
)
|
||||
end
|
||||
end
|
||||
function metatable.__mul(a, b)
|
||||
if type(a) == "table" then
|
||||
return fast_new(
|
||||
a.x * b,
|
||||
a.y * b,
|
||||
a.z * b
|
||||
)
|
||||
else
|
||||
return fast_new(
|
||||
a * b.x,
|
||||
a * b.y,
|
||||
a * b.z
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
function vector.divide(a, b)
|
||||
if type(b) == "table" then
|
||||
return {x = a.x / b.x,
|
||||
y = a.y / b.y,
|
||||
z = a.z / b.z}
|
||||
return fast_new(
|
||||
a.x / b.x,
|
||||
a.y / b.y,
|
||||
a.z / b.z
|
||||
)
|
||||
else
|
||||
return {x = a.x / b,
|
||||
y = a.y / b,
|
||||
z = a.z / b}
|
||||
return fast_new(
|
||||
a.x / b,
|
||||
a.y / b,
|
||||
a.z / b
|
||||
)
|
||||
end
|
||||
end
|
||||
function metatable.__div(a, b)
|
||||
-- scalar/vector makes no sense
|
||||
return fast_new(
|
||||
a.x / b,
|
||||
a.y / b,
|
||||
a.z / b
|
||||
)
|
||||
end
|
||||
|
||||
-- misc stuff
|
||||
|
||||
function vector.offset(v, x, y, z)
|
||||
return {x = v.x + x,
|
||||
y = v.y + y,
|
||||
z = v.z + z}
|
||||
return fast_new(
|
||||
v.x + x,
|
||||
v.y + y,
|
||||
v.z + z
|
||||
)
|
||||
end
|
||||
|
||||
function vector.sort(a, b)
|
||||
return {x = math.min(a.x, b.x), y = math.min(a.y, b.y), z = math.min(a.z, b.z)},
|
||||
{x = math.max(a.x, b.x), y = math.max(a.y, b.y), z = math.max(a.z, b.z)}
|
||||
return fast_new(math.min(a.x, b.x), math.min(a.y, b.y), math.min(a.z, b.z)),
|
||||
fast_new(math.max(a.x, b.x), math.max(a.y, b.y), math.max(a.z, b.z))
|
||||
end
|
||||
|
||||
function vector.check(v)
|
||||
return getmetatable(v) == metatable
|
||||
end
|
||||
|
||||
local function sin(x)
|
||||
|
@ -213,7 +339,7 @@ end
|
|||
|
||||
function vector.dir_to_rotation(forward, up)
|
||||
forward = vector.normalize(forward)
|
||||
local rot = {x = math.asin(forward.y), y = -math.atan2(forward.x, forward.z), z = 0}
|
||||
local rot = vector.new(math.asin(forward.y), -math.atan2(forward.x, forward.z), 0)
|
||||
if not up then
|
||||
return rot
|
||||
end
|
||||
|
@ -221,7 +347,7 @@ function vector.dir_to_rotation(forward, up)
|
|||
"Invalid vectors passed to vector.dir_to_rotation().")
|
||||
up = vector.normalize(up)
|
||||
-- Calculate vector pointing up with roll = 0, just based on forward vector.
|
||||
local forwup = vector.rotate({x = 0, y = 1, z = 0}, rot)
|
||||
local forwup = vector.rotate(vector.new(0, 1, 0), rot)
|
||||
-- 'forwup' and 'up' are now in a plane with 'forward' as normal.
|
||||
-- The angle between them is the absolute of the roll value we're looking for.
|
||||
rot.z = vector.angle(forwup, up)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
VoxelArea = {
|
||||
MinEdge = {x=1, y=1, z=1},
|
||||
MaxEdge = {x=0, y=0, z=0},
|
||||
MinEdge = vector.new(1, 1, 1),
|
||||
MaxEdge = vector.new(0, 0, 0),
|
||||
ystride = 0,
|
||||
zstride = 0,
|
||||
}
|
||||
|
@ -19,11 +19,11 @@ end
|
|||
|
||||
function VoxelArea:getExtent()
|
||||
local MaxEdge, MinEdge = self.MaxEdge, self.MinEdge
|
||||
return {
|
||||
x = MaxEdge.x - MinEdge.x + 1,
|
||||
y = MaxEdge.y - MinEdge.y + 1,
|
||||
z = MaxEdge.z - MinEdge.z + 1,
|
||||
}
|
||||
return vector.new(
|
||||
MaxEdge.x - MinEdge.x + 1,
|
||||
MaxEdge.y - MinEdge.y + 1,
|
||||
MaxEdge.z - MinEdge.z + 1
|
||||
)
|
||||
end
|
||||
|
||||
function VoxelArea:getVolume()
|
|
@ -63,7 +63,7 @@ function ui.update()
|
|||
-- handle errors
|
||||
if gamedata ~= nil and gamedata.reconnect_requested then
|
||||
local error_message = core.formspec_escape(
|
||||
gamedata.errormessage or "<none available>")
|
||||
gamedata.errormessage or fgettext("<none available>"))
|
||||
formspec = {
|
||||
"size[14,8]",
|
||||
"real_coordinates[true]",
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
core.async_jobs = {}
|
||||
|
||||
function core.async_event_handler(jobid, retval)
|
||||
local callback = core.async_jobs[jobid]
|
||||
assert(type(callback) == "function")
|
||||
callback(unpack(retval, 1, retval.n))
|
||||
core.async_jobs[jobid] = nil
|
||||
end
|
||||
|
||||
function core.handle_async(func, callback, ...)
|
||||
assert(type(func) == "function" and type(callback) == "function",
|
||||
"Invalid minetest.handle_async invocation")
|
||||
local args = {n = select("#", ...), ...}
|
||||
local mod_origin = core.get_last_run_mod()
|
||||
|
||||
local jobid = core.do_async_callback(func, args, mod_origin)
|
||||
core.async_jobs[jobid] = callback
|
||||
|
||||
return true
|
||||
end
|
||||
|
|
@ -87,6 +87,10 @@ core.builtin_auth_handler = {
|
|||
core.settings:get("default_password")))
|
||||
end
|
||||
|
||||
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
|
||||
|
@ -100,9 +104,6 @@ core.builtin_auth_handler = {
|
|||
core.run_priv_callbacks(name, priv, nil, "revoke")
|
||||
end
|
||||
end
|
||||
|
||||
auth_entry.privileges = privileges
|
||||
core_auth.save(auth_entry)
|
||||
core.notify_authentication_modified(name)
|
||||
end,
|
||||
reload = function()
|
||||
|
|
|
@ -75,9 +75,9 @@ core.register_on_chat_message(function(name, message)
|
|||
local has_privs, missing_privs = core.check_player_privs(name, cmd_def.privs)
|
||||
if has_privs then
|
||||
core.set_last_run_mod(cmd_def.mod_origin)
|
||||
local t_before = minetest.get_us_time()
|
||||
local t_before = core.get_us_time()
|
||||
local success, result = cmd_def.func(name, param)
|
||||
local delay = (minetest.get_us_time() - t_before) / 1000000
|
||||
local delay = (core.get_us_time() - t_before) / 1000000
|
||||
if success == false and result == nil then
|
||||
core.chat_send_player(name, "-!- "..S("Invalid command usage."))
|
||||
local help_def = core.registered_chatcommands["help"]
|
||||
|
@ -91,11 +91,12 @@ core.register_on_chat_message(function(name, message)
|
|||
if delay > msg_time_threshold then
|
||||
-- Show how much time it took to execute the command
|
||||
if result then
|
||||
result = result ..
|
||||
minetest.colorize("#f3d2ff", " (%.5g s)"):format(delay)
|
||||
result = result .. core.colorize("#f3d2ff", S(" (@1 s)",
|
||||
string.format("%.5f", delay)))
|
||||
else
|
||||
result = minetest.colorize("#f3d2ff",
|
||||
"Command execution took %.5f s"):format(delay)
|
||||
result = core.colorize("#f3d2ff", S(
|
||||
"Command execution took @1 s",
|
||||
string.format("%.5f", delay)))
|
||||
end
|
||||
end
|
||||
if result then
|
||||
|
@ -129,8 +130,13 @@ local function parse_range_str(player_name, str)
|
|||
return false, S("Unable to get position of player @1.", player_name)
|
||||
end
|
||||
else
|
||||
p1, p2 = core.string_to_area(str)
|
||||
if p1 == nil then
|
||||
local player = core.get_player_by_name(player_name)
|
||||
local relpos
|
||||
if player then
|
||||
relpos = player:get_pos()
|
||||
end
|
||||
p1, p2 = core.string_to_area(str, relpos)
|
||||
if p1 == nil or p2 == nil then
|
||||
return false, S("Incorrect area format. "
|
||||
.. "Expected: (x1,y1,z1) (x2,y2,z2)")
|
||||
end
|
||||
|
@ -166,6 +172,18 @@ core.register_chatcommand("admin", {
|
|||
end,
|
||||
})
|
||||
|
||||
local function privileges_of(name, privs)
|
||||
if not privs then
|
||||
privs = core.get_player_privs(name)
|
||||
end
|
||||
local privstr = core.privs_to_string(privs, ", ")
|
||||
if privstr == "" then
|
||||
return S("@1 does not have any privileges.", name)
|
||||
else
|
||||
return S("Privileges of @1: @2", name, privstr)
|
||||
end
|
||||
end
|
||||
|
||||
core.register_chatcommand("privs", {
|
||||
params = S("[<name>]"),
|
||||
description = S("Show privileges of yourself or another player"),
|
||||
|
@ -175,9 +193,7 @@ core.register_chatcommand("privs", {
|
|||
if not core.player_exists(name) then
|
||||
return false, S("Player @1 does not exist.", name)
|
||||
end
|
||||
return true, S("Privileges of @1: @2", name,
|
||||
core.privs_to_string(
|
||||
core.get_player_privs(name), ", "))
|
||||
return true, privileges_of(name)
|
||||
end,
|
||||
})
|
||||
|
||||
|
@ -201,9 +217,14 @@ core.register_chatcommand("haspriv", {
|
|||
table.insert(players_with_priv, player_name)
|
||||
end
|
||||
end
|
||||
return true, S("Players online with the \"@1\" privilege: @2",
|
||||
param,
|
||||
table.concat(players_with_priv, ", "))
|
||||
if #players_with_priv == 0 then
|
||||
return true, S("No online player has the \"@1\" privilege.",
|
||||
param)
|
||||
else
|
||||
return true, S("Players online with the \"@1\" privilege: @2",
|
||||
param,
|
||||
table.concat(players_with_priv, ", "))
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
|
@ -226,7 +247,10 @@ local function handle_grant_command(caller, grantname, grantprivstr)
|
|||
core.string_to_privs(core.settings:get("basic_privs") or "interact,shout")
|
||||
for priv, _ in pairs(grantprivs) do
|
||||
if not basic_privs[priv] and not caller_privs.privs then
|
||||
return false, S("Your privileges are insufficient.")
|
||||
return false, S("Your privileges are insufficient. "..
|
||||
"'@1' only allows you to grant: @2",
|
||||
"basic_privs",
|
||||
core.privs_to_string(basic_privs, ', '))
|
||||
end
|
||||
if not core.registered_privileges[priv] then
|
||||
privs_unknown = privs_unknown .. S("Unknown privilege: @1", priv) .. "\n"
|
||||
|
@ -236,24 +260,22 @@ local function handle_grant_command(caller, grantname, grantprivstr)
|
|||
if privs_unknown ~= "" then
|
||||
return false, privs_unknown
|
||||
end
|
||||
core.set_player_privs(grantname, privs)
|
||||
for priv, _ in pairs(grantprivs) do
|
||||
-- call the on_grant callbacks
|
||||
core.run_priv_callbacks(grantname, priv, caller, "grant")
|
||||
end
|
||||
core.set_player_privs(grantname, privs)
|
||||
core.log("action", caller..' granted ('..core.privs_to_string(grantprivs, ', ')..') privileges to '..grantname)
|
||||
if grantname ~= caller then
|
||||
core.chat_send_player(grantname,
|
||||
S("@1 granted you privileges: @2", caller,
|
||||
core.privs_to_string(grantprivs, ' ')))
|
||||
core.privs_to_string(grantprivs, ', ')))
|
||||
end
|
||||
return true, S("Privileges of @1: @2", grantname,
|
||||
core.privs_to_string(
|
||||
core.get_player_privs(grantname), ' '))
|
||||
return true, privileges_of(grantname)
|
||||
end
|
||||
|
||||
core.register_chatcommand("grant", {
|
||||
params = S("<name> (<privilege> | all)"),
|
||||
params = S("<name> (<privilege> [, <privilege2> [<...>]] | all)"),
|
||||
description = S("Give privileges to player"),
|
||||
func = function(name, param)
|
||||
local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
|
||||
|
@ -265,7 +287,7 @@ core.register_chatcommand("grant", {
|
|||
})
|
||||
|
||||
core.register_chatcommand("grantme", {
|
||||
params = S("<privilege> | all"),
|
||||
params = S("<privilege> [, <privilege2> [<...>]] | all"),
|
||||
description = S("Grant privileges to yourself"),
|
||||
func = function(name, param)
|
||||
if param == "" then
|
||||
|
@ -285,46 +307,90 @@ local function handle_revoke_command(caller, revokename, revokeprivstr)
|
|||
return false, S("Player @1 does not exist.", revokename)
|
||||
end
|
||||
|
||||
local revokeprivs = core.string_to_privs(revokeprivstr)
|
||||
local privs = core.get_player_privs(revokename)
|
||||
|
||||
local revokeprivs = core.string_to_privs(revokeprivstr)
|
||||
local is_singleplayer = core.is_singleplayer()
|
||||
local is_admin = not is_singleplayer
|
||||
and revokename == core.settings:get("name")
|
||||
and revokename ~= ""
|
||||
if revokeprivstr == "all" then
|
||||
revokeprivs = table.copy(privs)
|
||||
end
|
||||
|
||||
local privs_unknown = ""
|
||||
local basic_privs =
|
||||
core.string_to_privs(core.settings:get("basic_privs") or "interact,shout")
|
||||
local irrevokable = {}
|
||||
local has_irrevokable_priv = false
|
||||
for priv, _ in pairs(revokeprivs) do
|
||||
if not basic_privs[priv] and not caller_privs.privs then
|
||||
return false, S("Your privileges are insufficient.")
|
||||
return false, S("Your privileges are insufficient. "..
|
||||
"'@1' only allows you to revoke: @2",
|
||||
"basic_privs",
|
||||
core.privs_to_string(basic_privs, ', '))
|
||||
end
|
||||
local def = core.registered_privileges[priv]
|
||||
if not def then
|
||||
-- Old/removed privileges might still be granted to certain players
|
||||
if not privs[priv] then
|
||||
privs_unknown = privs_unknown .. S("Unknown privilege: @1", priv) .. "\n"
|
||||
end
|
||||
elseif is_singleplayer and def.give_to_singleplayer then
|
||||
irrevokable[priv] = true
|
||||
elseif is_admin and def.give_to_admin then
|
||||
irrevokable[priv] = true
|
||||
end
|
||||
end
|
||||
for priv, _ in pairs(irrevokable) do
|
||||
revokeprivs[priv] = nil
|
||||
has_irrevokable_priv = true
|
||||
end
|
||||
if privs_unknown ~= "" then
|
||||
return false, privs_unknown
|
||||
end
|
||||
if has_irrevokable_priv then
|
||||
if is_singleplayer then
|
||||
core.chat_send_player(caller,
|
||||
S("Note: Cannot revoke in singleplayer: @1",
|
||||
core.privs_to_string(irrevokable, ', ')))
|
||||
elseif is_admin then
|
||||
core.chat_send_player(caller,
|
||||
S("Note: Cannot revoke from admin: @1",
|
||||
core.privs_to_string(irrevokable, ', ')))
|
||||
end
|
||||
end
|
||||
|
||||
if revokeprivstr == "all" then
|
||||
revokeprivs = privs
|
||||
privs = {}
|
||||
else
|
||||
for priv, _ in pairs(revokeprivs) do
|
||||
privs[priv] = nil
|
||||
end
|
||||
local revokecount = 0
|
||||
for priv, _ in pairs(revokeprivs) do
|
||||
privs[priv] = nil
|
||||
revokecount = revokecount + 1
|
||||
end
|
||||
|
||||
if revokecount == 0 then
|
||||
return false, S("No privileges were revoked.")
|
||||
end
|
||||
|
||||
core.set_player_privs(revokename, privs)
|
||||
for priv, _ in pairs(revokeprivs) do
|
||||
-- call the on_revoke callbacks
|
||||
core.run_priv_callbacks(revokename, priv, caller, "revoke")
|
||||
end
|
||||
local new_privs = core.get_player_privs(revokename)
|
||||
|
||||
core.set_player_privs(revokename, privs)
|
||||
core.log("action", caller..' revoked ('
|
||||
..core.privs_to_string(revokeprivs, ', ')
|
||||
..') privileges from '..revokename)
|
||||
if revokename ~= caller then
|
||||
core.chat_send_player(revokename,
|
||||
S("@1 revoked privileges from you: @2", caller,
|
||||
core.privs_to_string(revokeprivs, ' ')))
|
||||
core.privs_to_string(revokeprivs, ', ')))
|
||||
end
|
||||
return true, S("Privileges of @1: @2", revokename,
|
||||
core.privs_to_string(
|
||||
core.get_player_privs(revokename), ' '))
|
||||
return true, privileges_of(revokename, new_privs)
|
||||
end
|
||||
|
||||
core.register_chatcommand("revoke", {
|
||||
params = S("<name> (<privilege> | all)"),
|
||||
params = S("<name> (<privilege> [, <privilege2> [<...>]] | all)"),
|
||||
description = S("Remove privileges from player"),
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
|
@ -337,7 +403,7 @@ core.register_chatcommand("revoke", {
|
|||
})
|
||||
|
||||
core.register_chatcommand("revokeme", {
|
||||
params = S("<privilege> | all"),
|
||||
params = S("<privilege> [, <privilege2> [<...>]] | all"),
|
||||
description = S("Revoke privileges from yourself"),
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
|
@ -444,10 +510,10 @@ core.register_chatcommand("remove_player", {
|
|||
-- pos may be a non-integer position
|
||||
local function find_free_position_near(pos)
|
||||
local tries = {
|
||||
{x=1, y=0, z=0},
|
||||
{x=-1, y=0, z=0},
|
||||
{x=0, y=0, z=1},
|
||||
{x=0, y=0, z=-1},
|
||||
vector.new( 1, 0, 0),
|
||||
vector.new(-1, 0, 0),
|
||||
vector.new( 0, 0, 1),
|
||||
vector.new( 0, 0, -1),
|
||||
}
|
||||
for _, d in ipairs(tries) do
|
||||
local p = vector.add(pos, d)
|
||||
|
@ -464,7 +530,7 @@ end
|
|||
|
||||
-- Teleports player <name> to <p> if possible
|
||||
local function teleport_to_pos(name, p)
|
||||
local lm = 31000
|
||||
local lm = 31007 -- equals MAX_MAP_GENERATION_LIMIT in C++
|
||||
if p.x < -lm or p.x > lm or p.y < -lm or p.y > lm
|
||||
or p.z < -lm or p.z > lm then
|
||||
return false, S("Cannot teleport out of map bounds!")
|
||||
|
@ -509,10 +575,15 @@ core.register_chatcommand("teleport", {
|
|||
description = S("Teleport to position or player"),
|
||||
privs = {teleport=true},
|
||||
func = function(name, param)
|
||||
local player = core.get_player_by_name(name)
|
||||
local relpos
|
||||
if player then
|
||||
relpos = player:get_pos()
|
||||
end
|
||||
local p = {}
|
||||
p.x, p.y, p.z = param:match("^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
|
||||
p = vector.apply(p, tonumber)
|
||||
if p.x and p.y and p.z then
|
||||
p.x, p.y, p.z = string.match(param, "^([%d.~-]+)[, ] *([%d.~-]+)[, ] *([%d.~-]+)$")
|
||||
p = core.parse_coordinates(p.x, p.y, p.z, relpos)
|
||||
if p and p.x and p.y and p.z then
|
||||
return teleport_to_pos(name, p)
|
||||
end
|
||||
|
||||
|
@ -526,9 +597,19 @@ core.register_chatcommand("teleport", {
|
|||
"other players (missing privilege: @1).", "bring")
|
||||
|
||||
local teleportee_name
|
||||
p = {}
|
||||
teleportee_name, p.x, p.y, p.z = param:match(
|
||||
"^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
|
||||
"^([^ ]+) +([%d.~-]+)[, ] *([%d.~-]+)[, ] *([%d.~-]+)$")
|
||||
if teleportee_name then
|
||||
local teleportee = core.get_player_by_name(teleportee_name)
|
||||
if not teleportee then
|
||||
return
|
||||
end
|
||||
relpos = teleportee:get_pos()
|
||||
p = core.parse_coordinates(p.x, p.y, p.z, relpos)
|
||||
end
|
||||
p = vector.apply(p, tonumber)
|
||||
|
||||
if teleportee_name and p.x and p.y and p.z then
|
||||
if not has_bring_priv then
|
||||
return false, missing_bring_msg
|
||||
|
@ -561,6 +642,10 @@ core.register_chatcommand("set", {
|
|||
|
||||
setname, setvalue = string.match(param, "([^ ]+) (.+)")
|
||||
if setname and setvalue then
|
||||
if setname:sub(1, 7) == "secure." then
|
||||
return false, S("Failed. Cannot modify secure settings. "
|
||||
.. "Edit the settings file manually.")
|
||||
end
|
||||
if not core.settings:get(setname) then
|
||||
return false, S("Failed. Use '/set -n <name> <value>' "
|
||||
.. "to create a new setting.")
|
||||
|
@ -682,7 +767,12 @@ core.register_chatcommand("mods", {
|
|||
description = S("List mods installed on the server"),
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
return true, table.concat(core.get_modnames(), ", ")
|
||||
local mods = core.get_modnames()
|
||||
if #mods == 0 then
|
||||
return true, S("No mods installed.")
|
||||
else
|
||||
return true, table.concat(core.get_modnames(), ", ")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
|
@ -772,7 +862,7 @@ core.register_chatcommand("spawnentity", {
|
|||
description = S("Spawn entity at given (or your) position"),
|
||||
privs = {give=true, interact=true},
|
||||
func = function(name, param)
|
||||
local entityname, p = string.match(param, "^([^ ]+) *(.*)$")
|
||||
local entityname, pstr = string.match(param, "^([^ ]+) *(.*)$")
|
||||
if not entityname then
|
||||
return false, S("EntityName required.")
|
||||
end
|
||||
|
@ -786,11 +876,15 @@ core.register_chatcommand("spawnentity", {
|
|||
if not core.registered_entities[entityname] then
|
||||
return false, S("Cannot spawn an unknown entity.")
|
||||
end
|
||||
if p == "" then
|
||||
local p
|
||||
if pstr == "" then
|
||||
p = player:get_pos()
|
||||
else
|
||||
p = core.string_to_pos(p)
|
||||
if p == nil then
|
||||
p = {}
|
||||
p.x, p.y, p.z = string.match(pstr, "^([%d.~-]+)[, ] *([%d.~-]+)[, ] *([%d.~-]+)$")
|
||||
local relpos = player:get_pos()
|
||||
p = core.parse_coordinates(p.x, p.y, p.z, relpos)
|
||||
if not (p and p.x and p.y and p.z) then
|
||||
return false, S("Invalid parameters (@1).", param)
|
||||
end
|
||||
end
|
||||
|
@ -949,6 +1043,13 @@ core.register_chatcommand("status", {
|
|||
end,
|
||||
})
|
||||
|
||||
local function get_time(timeofday)
|
||||
local time = math.floor(timeofday * 1440)
|
||||
local minute = time % 60
|
||||
local hour = (time - minute) / 60
|
||||
return time, hour, minute
|
||||
end
|
||||
|
||||
core.register_chatcommand("time", {
|
||||
params = S("[<0..23>:<0..59> | <0..24000>]"),
|
||||
description = S("Show or set time of day"),
|
||||
|
@ -967,25 +1068,44 @@ core.register_chatcommand("time", {
|
|||
return false, S("You don't have permission to run "
|
||||
.. "this command (missing privilege: @1).", "settime")
|
||||
end
|
||||
local hour, minute = param:match("^(%d+):(%d+)$")
|
||||
if not hour then
|
||||
local new_time = tonumber(param)
|
||||
local relative, negative, hour, minute = param:match("^(~?)(%-?)(%d+):(%d+)$")
|
||||
if not relative then -- checking the first capture against nil suffices
|
||||
local new_time = core.parse_relative_number(param, core.get_timeofday() * 24000)
|
||||
if not new_time then
|
||||
return false, S("Invalid time.")
|
||||
new_time = tonumber(param) or -1
|
||||
else
|
||||
new_time = new_time % 24000
|
||||
end
|
||||
-- Backward compatibility.
|
||||
core.set_timeofday((new_time % 24000) / 24000)
|
||||
if new_time ~= new_time or new_time < 0 or new_time > 24000 then
|
||||
return false, S("Invalid time (must be between 0 and 24000).")
|
||||
end
|
||||
core.set_timeofday(new_time / 24000)
|
||||
core.log("action", name .. " sets time to " .. new_time)
|
||||
return true, S("Time of day changed.")
|
||||
end
|
||||
local new_time
|
||||
hour = tonumber(hour)
|
||||
minute = tonumber(minute)
|
||||
if hour < 0 or hour > 23 then
|
||||
return false, S("Invalid hour (must be between 0 and 23 inclusive).")
|
||||
elseif minute < 0 or minute > 59 then
|
||||
return false, S("Invalid minute (must be between 0 and 59 inclusive).")
|
||||
if relative == "" then
|
||||
if hour < 0 or hour > 23 then
|
||||
return false, S("Invalid hour (must be between 0 and 23 inclusive).")
|
||||
elseif minute < 0 or minute > 59 then
|
||||
return false, S("Invalid minute (must be between 0 and 59 inclusive).")
|
||||
end
|
||||
new_time = (hour * 60 + minute) / 1440
|
||||
else
|
||||
if minute < 0 or minute > 59 then
|
||||
return false, S("Invalid minute (must be between 0 and 59 inclusive).")
|
||||
end
|
||||
local current_time = core.get_timeofday()
|
||||
if negative == "-" then -- negative time
|
||||
hour, minute = -hour, -minute
|
||||
end
|
||||
new_time = (current_time + (hour * 60 + minute) / 1440) % 1
|
||||
local _
|
||||
_, hour, minute = get_time(new_time)
|
||||
end
|
||||
core.set_timeofday((hour * 60 + minute) / 1440)
|
||||
core.set_timeofday(new_time)
|
||||
core.log("action", ("%s sets time to %d:%02d"):format(name, hour, minute))
|
||||
return true, S("Time of day changed.")
|
||||
end,
|
||||
|
@ -998,24 +1118,58 @@ core.register_chatcommand("days", {
|
|||
end
|
||||
})
|
||||
|
||||
local function parse_shutdown_param(param)
|
||||
local delay, reconnect, message
|
||||
local one, two, three
|
||||
one, two, three = param:match("^(%S+) +(%-r) +(.*)")
|
||||
if one and two and three then
|
||||
-- 3 arguments: delay, reconnect and message
|
||||
return one, two, three
|
||||
end
|
||||
-- 2 arguments
|
||||
one, two = param:match("^(%S+) +(.*)")
|
||||
if one and two then
|
||||
if tonumber(one) then
|
||||
delay = one
|
||||
if two == "-r" then
|
||||
reconnect = two
|
||||
else
|
||||
message = two
|
||||
end
|
||||
elseif one == "-r" then
|
||||
reconnect, message = one, two
|
||||
end
|
||||
return delay, reconnect, message
|
||||
end
|
||||
-- 1 argument
|
||||
one = param:match("(.*)")
|
||||
if tonumber(one) then
|
||||
delay = one
|
||||
elseif one == "-r" then
|
||||
reconnect = one
|
||||
else
|
||||
message = one
|
||||
end
|
||||
return delay, reconnect, message
|
||||
end
|
||||
|
||||
core.register_chatcommand("shutdown", {
|
||||
params = S("[<delay_in_seconds> | -1] [reconnect] [<message>]"),
|
||||
description = S("Shutdown server (-1 cancels a delayed shutdown)"),
|
||||
params = S("[<delay_in_seconds> | -1] [-r] [<message>]"),
|
||||
description = S("Shutdown server (-1 cancels a delayed shutdown, -r allows players to reconnect)"),
|
||||
privs = {server=true},
|
||||
func = function(name, param)
|
||||
local delay, reconnect, message
|
||||
delay, param = param:match("^%s*(%S+)(.*)")
|
||||
if param then
|
||||
reconnect, param = param:match("^%s*(%S+)(.*)")
|
||||
local delay, reconnect, message = parse_shutdown_param(param)
|
||||
local bool_reconnect = reconnect == "-r"
|
||||
if not message then
|
||||
message = ""
|
||||
end
|
||||
message = param and param:match("^%s*(.+)") or ""
|
||||
delay = tonumber(delay) or 0
|
||||
|
||||
if delay == 0 then
|
||||
core.log("action", name .. " shuts down server")
|
||||
core.chat_send_all("*** "..S("Server shutting down (operator request)."))
|
||||
end
|
||||
core.request_shutdown(message:trim(), core.is_yes(reconnect), delay)
|
||||
core.request_shutdown(message:trim(), bool_reconnect, delay)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
|
@ -1033,6 +1187,9 @@ core.register_chatcommand("ban", {
|
|||
return true, S("Ban list: @1", ban_list)
|
||||
end
|
||||
end
|
||||
if core.is_singleplayer() then
|
||||
return false, S("You cannot ban players in singleplayer!")
|
||||
end
|
||||
if not core.get_player_by_name(param) then
|
||||
return false, S("Player is not online.")
|
||||
end
|
||||
|
@ -1184,7 +1341,7 @@ local function handle_kill_command(killer, victim)
|
|||
return false, S("@1 is already dead.", victim)
|
||||
end
|
||||
end
|
||||
if not killer == victim then
|
||||
if killer ~= victim then
|
||||
core.log("action", string.format("%s killed %s", killer, victim))
|
||||
end
|
||||
-- Kill victim
|
||||
|
|
|
@ -39,7 +39,7 @@ local gravity = tonumber(core.settings:get("movement_gravity")) or 9.81
|
|||
core.register_entity(":__builtin:falling_node", {
|
||||
initial_properties = {
|
||||
visual = "item",
|
||||
visual_size = {x = SCALE, y = SCALE, z = SCALE},
|
||||
visual_size = vector.new(SCALE, SCALE, SCALE),
|
||||
textures = {},
|
||||
physical = true,
|
||||
is_visible = false,
|
||||
|
@ -84,9 +84,6 @@ core.register_entity(":__builtin:falling_node", {
|
|||
local textures
|
||||
if def.tiles and def.tiles[1] then
|
||||
local tile = def.tiles[1]
|
||||
if def.drawtype == "torchlike" and def.paramtype2 ~= "wallmounted" then
|
||||
tile = def.tiles[2] or def.tiles[1]
|
||||
end
|
||||
if type(tile) == "table" then
|
||||
tile = tile.name
|
||||
end
|
||||
|
@ -99,7 +96,7 @@ core.register_entity(":__builtin:falling_node", {
|
|||
local vsize
|
||||
if def.visual_scale then
|
||||
local s = def.visual_scale
|
||||
vsize = {x = s, y = s, z = s}
|
||||
vsize = vector.new(s, s, s)
|
||||
end
|
||||
self.object:set_properties({
|
||||
is_visible = true,
|
||||
|
@ -114,15 +111,21 @@ core.register_entity(":__builtin:falling_node", {
|
|||
itemstring = core.itemstring_with_palette(itemstring, node.param2)
|
||||
end
|
||||
-- FIXME: solution needed for paramtype2 == "leveled"
|
||||
local vsize
|
||||
if def.visual_scale then
|
||||
local s = def.visual_scale * SCALE
|
||||
vsize = {x = s, y = s, z = s}
|
||||
-- Calculate size of falling node
|
||||
local s = {}
|
||||
s.x = (def.visual_scale or 1) * SCALE
|
||||
s.y = s.x
|
||||
s.z = s.x
|
||||
-- Compensate for wield_scale
|
||||
if def.wield_scale then
|
||||
s.x = s.x / def.wield_scale.x
|
||||
s.y = s.y / def.wield_scale.y
|
||||
s.z = s.z / def.wield_scale.z
|
||||
end
|
||||
self.object:set_properties({
|
||||
is_visible = true,
|
||||
wield_item = itemstring,
|
||||
visual_size = vsize,
|
||||
visual_size = s,
|
||||
glow = def.light_source,
|
||||
})
|
||||
end
|
||||
|
@ -147,11 +150,7 @@ core.register_entity(":__builtin:falling_node", {
|
|||
|
||||
-- Rotate entity
|
||||
if def.drawtype == "torchlike" then
|
||||
if def.paramtype2 == "wallmounted" then
|
||||
self.object:set_yaw(math.pi*0.25)
|
||||
else
|
||||
self.object:set_yaw(-math.pi*0.25)
|
||||
end
|
||||
self.object:set_yaw(math.pi*0.25)
|
||||
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"
|
||||
|
@ -165,8 +164,13 @@ core.register_entity(":__builtin:falling_node", {
|
|||
if euler then
|
||||
self.object:set_rotation(euler)
|
||||
end
|
||||
elseif (def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted") then
|
||||
elseif (def.drawtype ~= "plantlike" and def.drawtype ~= "plantlike_rooted" and
|
||||
(def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted" or def.drawtype == "signlike")) then
|
||||
local rot = node.param2 % 8
|
||||
if (def.drawtype == "signlike" and def.paramtype2 ~= "wallmounted" and def.paramtype2 ~= "colorwallmounted") then
|
||||
-- Change rotation to "floor" by default for non-wallmounted paramtype2
|
||||
rot = 1
|
||||
end
|
||||
local pitch, yaw, roll = 0, 0, 0
|
||||
if def.drawtype == "nodebox" or def.drawtype == "mesh" then
|
||||
if rot == 0 then
|
||||
|
@ -208,6 +212,14 @@ core.register_entity(":__builtin:falling_node", {
|
|||
end
|
||||
end
|
||||
self.object:set_rotation({x=pitch, y=yaw, z=roll})
|
||||
elseif (def.drawtype == "mesh" and def.paramtype2 == "degrotate") then
|
||||
local p2 = (node.param2 - (def.place_param2 or 0)) % 240
|
||||
local yaw = (p2 / 240) * (math.pi * 2)
|
||||
self.object:set_yaw(yaw)
|
||||
elseif (def.drawtype == "mesh" and def.paramtype2 == "colordegrotate") then
|
||||
local p2 = (node.param2 % 32 - (def.place_param2 or 0) % 32) % 24
|
||||
local yaw = (p2 / 24) * (math.pi * 2)
|
||||
self.object:set_yaw(yaw)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
@ -222,7 +234,7 @@ core.register_entity(":__builtin:falling_node", {
|
|||
|
||||
on_activate = function(self, staticdata)
|
||||
self.object:set_armor_groups({immortal = 1})
|
||||
self.object:set_acceleration({x = 0, y = -gravity, z = 0})
|
||||
self.object:set_acceleration(vector.new(0, -gravity, 0))
|
||||
|
||||
local ds = core.deserialize(staticdata)
|
||||
if ds and ds.node then
|
||||
|
@ -298,7 +310,7 @@ core.register_entity(":__builtin:falling_node", {
|
|||
if self.floats then
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
local bcp = vector.round({x = pos.x, y = pos.y - 0.7, z = pos.z})
|
||||
local bcp = pos:offset(0, -0.7, 0):round()
|
||||
local bcn = core.get_node(bcp)
|
||||
|
||||
local bcd = core.registered_nodes[bcn.name]
|
||||
|
@ -339,13 +351,12 @@ core.register_entity(":__builtin:falling_node", {
|
|||
-- TODO: this hack could be avoided in the future if objects
|
||||
-- could choose who to collide with
|
||||
local vel = self.object:get_velocity()
|
||||
self.object:set_velocity({
|
||||
x = vel.x,
|
||||
y = player_collision.old_velocity.y,
|
||||
z = vel.z
|
||||
})
|
||||
self.object:set_pos(vector.add(self.object:get_pos(),
|
||||
{x = 0, y = -0.5, z = 0}))
|
||||
self.object:set_velocity(vector.new(
|
||||
vel.x,
|
||||
player_collision.old_velocity.y,
|
||||
vel.z
|
||||
))
|
||||
self.object:set_pos(self.object:get_pos():offset(0, -0.5, 0))
|
||||
end
|
||||
return
|
||||
elseif bcn.name == "ignore" then
|
||||
|
@ -407,7 +418,7 @@ local function convert_to_falling_node(pos, node)
|
|||
|
||||
obj:get_luaentity():set_node(node, metatable)
|
||||
core.remove_node(pos)
|
||||
return true
|
||||
return true, obj
|
||||
end
|
||||
|
||||
function core.spawn_falling_node(pos)
|
||||
|
@ -425,7 +436,7 @@ local function drop_attached_node(p)
|
|||
if def and def.preserve_metadata then
|
||||
local oldmeta = core.get_meta(p):to_table().fields
|
||||
-- Copy pos and node because the callback can modify them.
|
||||
local pos_copy = {x=p.x, y=p.y, z=p.z}
|
||||
local pos_copy = vector.new(p)
|
||||
local node_copy = {name=n.name, param1=n.param1, param2=n.param2}
|
||||
local drop_stacks = {}
|
||||
for k, v in pairs(drops) do
|
||||
|
@ -450,14 +461,14 @@ end
|
|||
|
||||
function builtin_shared.check_attached_node(p, n)
|
||||
local def = core.registered_nodes[n.name]
|
||||
local d = {x = 0, y = 0, z = 0}
|
||||
local d = vector.new()
|
||||
if def.paramtype2 == "wallmounted" or
|
||||
def.paramtype2 == "colorwallmounted" then
|
||||
-- The fallback vector here is in case 'wallmounted to dir' is nil due
|
||||
-- to voxelmanip placing a wallmounted node without resetting a
|
||||
-- pre-existing param2 value that is out-of-range for wallmounted.
|
||||
-- The fallback vector corresponds to param2 = 0.
|
||||
d = core.wallmounted_to_dir(n.param2) or {x = 0, y = 1, z = 0}
|
||||
d = core.wallmounted_to_dir(n.param2) or vector.new(0, 1, 0)
|
||||
else
|
||||
d.y = -1
|
||||
end
|
||||
|
@ -477,7 +488,7 @@ end
|
|||
function core.check_single_for_falling(p)
|
||||
local n = core.get_node(p)
|
||||
if core.get_item_group(n.name, "falling_node") ~= 0 then
|
||||
local p_bottom = {x = p.x, y = p.y - 1, z = p.z}
|
||||
local p_bottom = vector.offset(p, 0, -1, 0)
|
||||
-- Only spawn falling node if node below is loaded
|
||||
local n_bottom = core.get_node_or_nil(p_bottom)
|
||||
local d_bottom = n_bottom and core.registered_nodes[n_bottom.name]
|
||||
|
@ -516,17 +527,17 @@ end
|
|||
-- Down first as likely case, but always before self. The same with sides.
|
||||
-- Up must come last, so that things above self will also fall all at once.
|
||||
local check_for_falling_neighbors = {
|
||||
{x = -1, y = -1, z = 0},
|
||||
{x = 1, y = -1, z = 0},
|
||||
{x = 0, y = -1, z = -1},
|
||||
{x = 0, y = -1, z = 1},
|
||||
{x = 0, y = -1, z = 0},
|
||||
{x = -1, y = 0, z = 0},
|
||||
{x = 1, y = 0, z = 0},
|
||||
{x = 0, y = 0, z = 1},
|
||||
{x = 0, y = 0, z = -1},
|
||||
{x = 0, y = 0, z = 0},
|
||||
{x = 0, y = 1, z = 0},
|
||||
vector.new(-1, -1, 0),
|
||||
vector.new( 1, -1, 0),
|
||||
vector.new( 0, -1, -1),
|
||||
vector.new( 0, -1, 1),
|
||||
vector.new( 0, -1, 0),
|
||||
vector.new(-1, 0, 0),
|
||||
vector.new( 1, 0, 0),
|
||||
vector.new( 0, 0, 1),
|
||||
vector.new( 0, 0, -1),
|
||||
vector.new( 0, 0, 0),
|
||||
vector.new( 0, 1, 0),
|
||||
}
|
||||
|
||||
function core.check_for_falling(p)
|
||||
|
|
|
@ -19,6 +19,10 @@ core.features = {
|
|||
object_step_has_moveresult = true,
|
||||
direct_velocity_on_players = true,
|
||||
use_texture_alpha_string_modes = true,
|
||||
degrotate_240_steps = true,
|
||||
abm_min_max_y = true,
|
||||
dynamic_add_media_table = true,
|
||||
get_sky_as_table = true,
|
||||
}
|
||||
|
||||
function core.has_feature(arg)
|
||||
|
|
|
@ -86,12 +86,6 @@ local function read_file(filename)
|
|||
return core.deserialize(t) or {}
|
||||
end
|
||||
|
||||
local function write_file(filename, table)
|
||||
local f = io.open(filename, "w")
|
||||
f:write(core.serialize(table))
|
||||
f:close()
|
||||
end
|
||||
|
||||
blocks_forceloaded = read_file(wpath.."/force_loaded.txt")
|
||||
for _, __ in pairs(blocks_forceloaded) do
|
||||
total_forceloaded = total_forceloaded + 1
|
||||
|
@ -106,7 +100,8 @@ end)
|
|||
|
||||
-- persists the currently forceloaded blocks to disk
|
||||
local function persist_forceloaded_blocks()
|
||||
write_file(wpath.."/force_loaded.txt", blocks_forceloaded)
|
||||
local data = core.serialize(blocks_forceloaded)
|
||||
core.safe_file_write(wpath.."/force_loaded.txt", data)
|
||||
end
|
||||
|
||||
-- periodical forceload persistence
|
||||
|
|
|
@ -7,9 +7,8 @@ local gamepath = scriptpath .. "game".. DIR_DELIM
|
|||
-- not exposed to outer context
|
||||
local builtin_shared = {}
|
||||
|
||||
dofile(commonpath .. "vector.lua")
|
||||
|
||||
dofile(gamepath .. "constants.lua")
|
||||
dofile(gamepath .. "item_s.lua")
|
||||
assert(loadfile(gamepath .. "item.lua"))(builtin_shared)
|
||||
dofile(gamepath .. "register.lua")
|
||||
|
||||
|
@ -18,8 +17,10 @@ if core.settings:get_bool("profiler.load") then
|
|||
end
|
||||
|
||||
dofile(commonpath .. "after.lua")
|
||||
dofile(commonpath .. "voxelarea.lua")
|
||||
dofile(gamepath .. "item_entity.lua")
|
||||
dofile(gamepath .. "deprecated.lua")
|
||||
dofile(gamepath .. "misc_s.lua")
|
||||
dofile(gamepath .. "misc.lua")
|
||||
dofile(gamepath .. "privileges.lua")
|
||||
dofile(gamepath .. "auth.lua")
|
||||
|
@ -30,9 +31,9 @@ dofile(gamepath .. "static_spawn.lua")
|
|||
dofile(gamepath .. "detached_inventory.lua")
|
||||
assert(loadfile(gamepath .. "falling.lua"))(builtin_shared)
|
||||
dofile(gamepath .. "features.lua")
|
||||
dofile(gamepath .. "voxelarea.lua")
|
||||
dofile(gamepath .. "forceloading.lua")
|
||||
dofile(gamepath .. "statbars.lua")
|
||||
dofile(gamepath .. "knockback.lua")
|
||||
dofile(gamepath .. "async.lua")
|
||||
|
||||
profiler = nil
|
||||
|
|
|
@ -15,140 +15,29 @@ end
|
|||
-- Item definition helpers
|
||||
--
|
||||
|
||||
function core.dir_to_facedir(dir, is6d)
|
||||
--account for y if requested
|
||||
if is6d and math.abs(dir.y) > math.abs(dir.x) and math.abs(dir.y) > math.abs(dir.z) then
|
||||
|
||||
--from above
|
||||
if dir.y < 0 then
|
||||
if math.abs(dir.x) > math.abs(dir.z) then
|
||||
if dir.x < 0 then
|
||||
return 19
|
||||
else
|
||||
return 13
|
||||
end
|
||||
else
|
||||
if dir.z < 0 then
|
||||
return 10
|
||||
else
|
||||
return 4
|
||||
end
|
||||
end
|
||||
|
||||
--from below
|
||||
else
|
||||
if math.abs(dir.x) > math.abs(dir.z) then
|
||||
if dir.x < 0 then
|
||||
return 15
|
||||
else
|
||||
return 17
|
||||
end
|
||||
else
|
||||
if dir.z < 0 then
|
||||
return 6
|
||||
else
|
||||
return 8
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--otherwise, place horizontally
|
||||
elseif math.abs(dir.x) > math.abs(dir.z) then
|
||||
if dir.x < 0 then
|
||||
return 3
|
||||
else
|
||||
return 1
|
||||
end
|
||||
else
|
||||
if dir.z < 0 then
|
||||
return 2
|
||||
else
|
||||
return 0
|
||||
function core.get_pointed_thing_position(pointed_thing, above)
|
||||
if pointed_thing.type == "node" then
|
||||
if above then
|
||||
-- The position where a node would be placed
|
||||
return pointed_thing.above
|
||||
end
|
||||
-- The position where a node would be dug
|
||||
return pointed_thing.under
|
||||
elseif pointed_thing.type == "object" then
|
||||
return pointed_thing.ref and pointed_thing.ref:get_pos()
|
||||
end
|
||||
end
|
||||
|
||||
-- Table of possible dirs
|
||||
local facedir_to_dir = {
|
||||
{x= 0, y=0, z= 1},
|
||||
{x= 1, y=0, z= 0},
|
||||
{x= 0, y=0, z=-1},
|
||||
{x=-1, y=0, z= 0},
|
||||
{x= 0, y=-1, z= 0},
|
||||
{x= 0, y=1, z= 0},
|
||||
}
|
||||
-- Mapping from facedir value to index in facedir_to_dir.
|
||||
local facedir_to_dir_map = {
|
||||
[0]=1, 2, 3, 4,
|
||||
5, 2, 6, 4,
|
||||
6, 2, 5, 4,
|
||||
1, 5, 3, 6,
|
||||
1, 6, 3, 5,
|
||||
1, 4, 3, 2,
|
||||
}
|
||||
function core.facedir_to_dir(facedir)
|
||||
return facedir_to_dir[facedir_to_dir_map[facedir % 32]]
|
||||
end
|
||||
|
||||
function core.dir_to_wallmounted(dir)
|
||||
if math.abs(dir.y) > math.max(math.abs(dir.x), math.abs(dir.z)) then
|
||||
if dir.y < 0 then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
elseif math.abs(dir.x) > math.abs(dir.z) then
|
||||
if dir.x < 0 then
|
||||
return 3
|
||||
else
|
||||
return 2
|
||||
end
|
||||
else
|
||||
if dir.z < 0 then
|
||||
return 5
|
||||
else
|
||||
return 4
|
||||
local function has_all_groups(tbl, required_groups)
|
||||
if type(required_groups) == "string" then
|
||||
return (tbl[required_groups] or 0) ~= 0
|
||||
end
|
||||
for _, group in ipairs(required_groups) do
|
||||
if (tbl[group] or 0) == 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- table of dirs in wallmounted order
|
||||
local wallmounted_to_dir = {
|
||||
[0] = {x = 0, y = 1, z = 0},
|
||||
{x = 0, y = -1, z = 0},
|
||||
{x = 1, y = 0, z = 0},
|
||||
{x = -1, y = 0, z = 0},
|
||||
{x = 0, y = 0, z = 1},
|
||||
{x = 0, y = 0, z = -1},
|
||||
}
|
||||
function core.wallmounted_to_dir(wallmounted)
|
||||
return wallmounted_to_dir[wallmounted % 8]
|
||||
end
|
||||
|
||||
function core.dir_to_yaw(dir)
|
||||
return -math.atan2(dir.x, dir.z)
|
||||
end
|
||||
|
||||
function core.yaw_to_dir(yaw)
|
||||
return {x = -math.sin(yaw), y = 0, z = math.cos(yaw)}
|
||||
end
|
||||
|
||||
function core.is_colored_paramtype(ptype)
|
||||
return (ptype == "color") or (ptype == "colorfacedir") or
|
||||
(ptype == "colorwallmounted")
|
||||
end
|
||||
|
||||
function core.strip_param2_color(param2, paramtype2)
|
||||
if not core.is_colored_paramtype(paramtype2) then
|
||||
return nil
|
||||
end
|
||||
if paramtype2 == "colorfacedir" then
|
||||
param2 = math.floor(param2 / 32) * 32
|
||||
elseif paramtype2 == "colorwallmounted" then
|
||||
param2 = math.floor(param2 / 8) * 8
|
||||
end
|
||||
-- paramtype2 == "color" requires no modification.
|
||||
return param2
|
||||
return true
|
||||
end
|
||||
|
||||
function core.get_node_drops(node, toolname)
|
||||
|
@ -190,7 +79,7 @@ function core.get_node_drops(node, toolname)
|
|||
if item.rarity ~= nil then
|
||||
good_rarity = item.rarity < 1 or math.random(item.rarity) == 1
|
||||
end
|
||||
if item.tools ~= nil then
|
||||
if item.tools ~= nil or item.tool_groups ~= nil then
|
||||
good_tool = false
|
||||
end
|
||||
if item.tools ~= nil and toolname then
|
||||
|
@ -205,6 +94,27 @@ function core.get_node_drops(node, toolname)
|
|||
end
|
||||
end
|
||||
end
|
||||
if item.tool_groups ~= nil and toolname then
|
||||
local tooldef = core.registered_items[toolname]
|
||||
if tooldef ~= nil and type(tooldef.groups) == "table" then
|
||||
if type(item.tool_groups) == "string" then
|
||||
-- tool_groups can be a string which specifies the required group
|
||||
good_tool = core.get_item_group(toolname, item.tool_groups) ~= 0
|
||||
else
|
||||
-- tool_groups can be a list of sufficient requirements.
|
||||
-- i.e. if any item in the list can be satisfied then the tool is good
|
||||
assert(type(item.tool_groups) == "table")
|
||||
for _, required_groups in ipairs(item.tool_groups) do
|
||||
-- required_groups can be either a string (a single group),
|
||||
-- or an array of strings where all must be in tooldef.groups
|
||||
good_tool = has_all_groups(tooldef.groups, required_groups)
|
||||
if good_tool then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if good_rarity and good_tool then
|
||||
got_count = got_count + 1
|
||||
for _, add_item in ipairs(item.items) do
|
||||
|
@ -266,12 +176,12 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2,
|
|||
end
|
||||
|
||||
-- Place above pointed node
|
||||
local place_to = {x = above.x, y = above.y, z = above.z}
|
||||
local place_to = vector.new(above)
|
||||
|
||||
-- If node under is buildable_to, place into it instead (eg. snow)
|
||||
if olddef_under.buildable_to then
|
||||
log("info", "node under is buildable to")
|
||||
place_to = {x = under.x, y = under.y, z = under.z}
|
||||
place_to = vector.new(under)
|
||||
end
|
||||
|
||||
if core.is_protected(place_to, playername) then
|
||||
|
@ -291,22 +201,14 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2,
|
|||
newnode.param2 = def.place_param2
|
||||
elseif (def.paramtype2 == "wallmounted" or
|
||||
def.paramtype2 == "colorwallmounted") and not param2 then
|
||||
local dir = {
|
||||
x = under.x - above.x,
|
||||
y = under.y - above.y,
|
||||
z = under.z - above.z
|
||||
}
|
||||
local dir = vector.subtract(under, above)
|
||||
newnode.param2 = core.dir_to_wallmounted(dir)
|
||||
-- Calculate the direction for furnaces and chests and stuff
|
||||
elseif (def.paramtype2 == "facedir" or
|
||||
def.paramtype2 == "colorfacedir") and not param2 then
|
||||
local placer_pos = placer and placer:get_pos()
|
||||
if placer_pos then
|
||||
local dir = {
|
||||
x = above.x - placer_pos.x,
|
||||
y = above.y - placer_pos.y,
|
||||
z = above.z - placer_pos.z
|
||||
}
|
||||
local dir = vector.subtract(above, placer_pos)
|
||||
newnode.param2 = core.dir_to_facedir(dir)
|
||||
log("info", "facedir: " .. newnode.param2)
|
||||
end
|
||||
|
@ -323,6 +225,8 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2,
|
|||
color_divisor = 8
|
||||
elseif def.paramtype2 == "colorfacedir" then
|
||||
color_divisor = 32
|
||||
elseif def.paramtype2 == "colordegrotate" then
|
||||
color_divisor = 32
|
||||
end
|
||||
if color_divisor then
|
||||
local color = math.floor(metatable.palette_index / color_divisor)
|
||||
|
@ -358,7 +262,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2,
|
|||
-- Run callback
|
||||
if def.after_place_node and not prevent_after_place then
|
||||
-- Deepcopy place_to and pointed_thing because callback can modify it
|
||||
local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
|
||||
local place_to_copy = vector.new(place_to)
|
||||
local pointed_thing_copy = copy_pointed_thing(pointed_thing)
|
||||
if def.after_place_node(place_to_copy, placer, itemstack,
|
||||
pointed_thing_copy) then
|
||||
|
@ -369,7 +273,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2,
|
|||
-- Run script hook
|
||||
for _, callback in ipairs(core.registered_on_placenodes) do
|
||||
-- Deepcopy pos, node and pointed_thing because callback can modify them
|
||||
local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
|
||||
local place_to_copy = vector.new(place_to)
|
||||
local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2}
|
||||
local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2}
|
||||
local pointed_thing_copy = copy_pointed_thing(pointed_thing)
|
||||
|
@ -448,34 +352,41 @@ function core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed
|
|||
return result
|
||||
end
|
||||
end
|
||||
-- read definition before potentially emptying the stack
|
||||
local def = itemstack:get_definition()
|
||||
if itemstack:take_item() ~= nil then
|
||||
user:set_hp(user:get_hp() + hp_change)
|
||||
if itemstack:take_item():is_empty() then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
if def and def.sound and def.sound.eat then
|
||||
core.sound_play(def.sound.eat, {
|
||||
pos = user:get_pos(),
|
||||
max_hear_distance = 16
|
||||
}, true)
|
||||
end
|
||||
if def and def.sound and def.sound.eat then
|
||||
core.sound_play(def.sound.eat, {
|
||||
pos = user:get_pos(),
|
||||
max_hear_distance = 16
|
||||
}, true)
|
||||
end
|
||||
|
||||
if replace_with_item then
|
||||
if itemstack:is_empty() then
|
||||
itemstack:add_item(replace_with_item)
|
||||
-- Changing hp might kill the player causing mods to do who-knows-what to the
|
||||
-- inventory, so do this before set_hp().
|
||||
if replace_with_item then
|
||||
if itemstack:is_empty() then
|
||||
itemstack:add_item(replace_with_item)
|
||||
else
|
||||
local inv = user:get_inventory()
|
||||
-- Check if inv is null, since non-players don't have one
|
||||
if inv and inv:room_for_item("main", {name=replace_with_item}) then
|
||||
inv:add_item("main", replace_with_item)
|
||||
else
|
||||
local inv = user:get_inventory()
|
||||
-- Check if inv is null, since non-players don't have one
|
||||
if inv and inv:room_for_item("main", {name=replace_with_item}) then
|
||||
inv:add_item("main", replace_with_item)
|
||||
else
|
||||
local pos = user:get_pos()
|
||||
pos.y = math.floor(pos.y + 0.5)
|
||||
core.add_item(pos, replace_with_item)
|
||||
end
|
||||
local pos = user:get_pos()
|
||||
pos.y = math.floor(pos.y + 0.5)
|
||||
core.add_item(pos, replace_with_item)
|
||||
end
|
||||
end
|
||||
end
|
||||
return itemstack
|
||||
user:set_wielded_item(itemstack)
|
||||
|
||||
user:set_hp(user:get_hp() + hp_change)
|
||||
|
||||
return nil -- don't overwrite wield item a second time
|
||||
end
|
||||
|
||||
function core.item_eat(hp_change, replace_with_item)
|
||||
|
@ -515,11 +426,11 @@ function core.handle_node_drops(pos, drops, digger)
|
|||
for _, dropped_item in pairs(drops) do
|
||||
local left = give_item(dropped_item)
|
||||
if not left:is_empty() then
|
||||
local p = {
|
||||
x = pos.x + math.random()/2-0.25,
|
||||
y = pos.y + math.random()/2-0.25,
|
||||
z = pos.z + math.random()/2-0.25,
|
||||
}
|
||||
local p = vector.offset(pos,
|
||||
math.random()/2-0.25,
|
||||
math.random()/2-0.25,
|
||||
math.random()/2-0.25
|
||||
)
|
||||
core.add_item(p, left)
|
||||
end
|
||||
end
|
||||
|
@ -556,7 +467,7 @@ function core.node_dig(pos, node, digger)
|
|||
if wielded then
|
||||
local wdef = wielded:get_definition()
|
||||
local tp = wielded:get_tool_capabilities()
|
||||
local dp = core.get_dig_params(def and def.groups, tp)
|
||||
local dp = core.get_dig_params(def and def.groups, tp, wielded:get_wear())
|
||||
if wdef and wdef.after_use then
|
||||
wielded = wdef.after_use(wielded, digger, node, dp) or wielded
|
||||
else
|
||||
|
@ -578,7 +489,7 @@ function core.node_dig(pos, node, digger)
|
|||
if def and def.preserve_metadata then
|
||||
local oldmeta = core.get_meta(pos):to_table().fields
|
||||
-- Copy pos and node because the callback can modify them.
|
||||
local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
|
||||
local pos_copy = vector.new(pos)
|
||||
local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
|
||||
local drop_stacks = {}
|
||||
for k, v in pairs(drops) do
|
||||
|
@ -610,7 +521,7 @@ function core.node_dig(pos, node, digger)
|
|||
-- Run callback
|
||||
if def and def.after_dig_node then
|
||||
-- Copy pos and node because callback can modify them
|
||||
local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
|
||||
local pos_copy = vector.new(pos)
|
||||
local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
|
||||
def.after_dig_node(pos_copy, node_copy, oldmetadata, digger)
|
||||
end
|
||||
|
@ -618,12 +529,10 @@ function core.node_dig(pos, node, digger)
|
|||
-- Run script hook
|
||||
for _, callback in ipairs(core.registered_on_dignodes) do
|
||||
local origin = core.callback_origins[callback]
|
||||
if origin then
|
||||
core.set_last_run_mod(origin.mod)
|
||||
end
|
||||
core.set_last_run_mod(origin.mod)
|
||||
|
||||
-- Copy pos and node because callback can modify them
|
||||
local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
|
||||
local pos_copy = vector.new(pos)
|
||||
local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
|
||||
callback(pos_copy, node_copy, digger)
|
||||
end
|
||||
|
@ -666,7 +575,7 @@ core.nodedef_default = {
|
|||
groups = {},
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
wield_scale = {x=1,y=1,z=1},
|
||||
wield_scale = vector.new(1, 1, 1),
|
||||
stack_max = default_stack_max,
|
||||
usable = false,
|
||||
liquids_pointable = false,
|
||||
|
@ -725,7 +634,7 @@ core.craftitemdef_default = {
|
|||
groups = {},
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
wield_scale = {x=1,y=1,z=1},
|
||||
wield_scale = vector.new(1, 1, 1),
|
||||
stack_max = default_stack_max,
|
||||
liquids_pointable = false,
|
||||
tool_capabilities = nil,
|
||||
|
@ -744,7 +653,7 @@ core.tooldef_default = {
|
|||
groups = {},
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
wield_scale = {x=1,y=1,z=1},
|
||||
wield_scale = vector.new(1, 1, 1),
|
||||
stack_max = 1,
|
||||
liquids_pointable = false,
|
||||
tool_capabilities = nil,
|
||||
|
@ -763,7 +672,7 @@ core.noneitemdef_default = { -- This is used for the hand and unknown items
|
|||
groups = {},
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
wield_scale = {x=1,y=1,z=1},
|
||||
wield_scale = vector.new(1, 1, 1),
|
||||
stack_max = default_stack_max,
|
||||
liquids_pointable = false,
|
||||
tool_capabilities = nil,
|
||||
|
|