Compare commits
728 Commits
5.7.0
...
5.8.1-andr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d445638265 | ||
|
|
f61f6626b7 | ||
|
|
857ba25115 | ||
|
|
00774bc28b | ||
|
|
f572266f19 | ||
|
|
c3893064a3 | ||
|
|
4183443f02 | ||
|
|
5715434d5e | ||
|
|
ab2419db24 | ||
|
|
35a83c3514 | ||
|
|
de09cac1e9 | ||
|
|
b64f4bdb3a | ||
|
|
008310aad9 | ||
|
|
4d024d737c | ||
|
|
50edb30a18 | ||
|
|
00f6bd0f08 | ||
|
|
81e40a1172 | ||
|
|
fe4f89f3fa | ||
|
|
0d372250c1 | ||
|
|
367720020c | ||
|
|
0dab82085d | ||
|
|
cb211d63cd | ||
|
|
49ce5a2de6 | ||
|
|
30769589bf | ||
|
|
6cf9b7472a | ||
|
|
4be8b77598 | ||
|
|
bae9f65411 | ||
|
|
0a20d30f83 | ||
|
|
7245bcc614 | ||
|
|
51136780d6 | ||
|
|
ea6eb0dfc8 | ||
|
|
ab88fc6835 | ||
|
|
30b28280eb | ||
|
|
e5672111d2 | ||
|
|
ce0aca49c2 | ||
|
|
0d3b71564f | ||
|
|
0a51fde971 | ||
|
|
a13a165e9b | ||
|
|
8a7d3d07de | ||
|
|
0977728ea0 | ||
|
|
92eb63c867 | ||
|
|
0c4a15fa16 | ||
|
|
01ac9e15ef | ||
|
|
dd3fc83777 | ||
|
|
6a5e480a58 | ||
|
|
419d971891 | ||
|
|
9e62cb5c04 | ||
|
|
bf53e7e1ca | ||
|
|
91134015e7 | ||
|
|
047520d91e | ||
|
|
6106e4e72b | ||
|
|
a7e5456099 | ||
|
|
d6a8b546e4 | ||
|
|
7f9326805c | ||
|
|
36f4953502 | ||
|
|
dc6452db1b | ||
|
|
cfe1953c2d | ||
|
|
dfe00f88e1 | ||
|
|
53886dcdb5 | ||
|
|
771da80bbb | ||
|
|
0f3ac7c956 | ||
|
|
71490a417e | ||
|
|
4255ac3022 | ||
|
|
6783734612 | ||
|
|
61db32beee | ||
|
|
31ee7af3ab | ||
|
|
72edfe3d04 | ||
|
|
7199ee4ff8 | ||
|
|
585e6aa80b | ||
|
|
1bc74b0ba1 | ||
|
|
73e85b2ebb | ||
|
|
0e4de28988 | ||
|
|
aa912e90a7 | ||
|
|
8cf76e004f | ||
|
|
7cb20dd6c2 | ||
|
|
2bc0d76f63 | ||
|
|
8abb5796ed | ||
|
|
7a658c1a6a | ||
|
|
1b0a34b9d1 | ||
|
|
8b5fc7f23a | ||
|
|
ee35d7df58 | ||
|
|
8d1f1b4704 | ||
|
|
50f48ce9df | ||
|
|
e14f905299 | ||
|
|
6980f516d4 | ||
|
|
52e66b6dfe | ||
|
|
645e4abf52 | ||
|
|
b0932ef458 | ||
|
|
330aee974e | ||
|
|
a1d7c25587 | ||
|
|
6c4352eaf9 | ||
|
|
2f279d2403 | ||
|
|
842b2bbd36 | ||
|
|
d056bb3ee7 | ||
|
|
adf9a3953b | ||
|
|
c7dd8c18ed | ||
|
|
d8c8bf1897 | ||
|
|
b0c92e885e | ||
|
|
80ae408eb9 | ||
|
|
9720eb50b3 | ||
|
|
1efa3a165e | ||
|
|
b730c0aa9a | ||
|
|
bc26bdc2bf | ||
|
|
3187aca3c9 | ||
|
|
c4b7876a1a | ||
|
|
904dbe730d | ||
|
|
8bf2031310 | ||
|
|
af474d10a4 | ||
|
|
fe8d04d0b3 | ||
|
|
394450758e | ||
|
|
56902745c8 | ||
|
|
9e952603b2 | ||
|
|
80c4c260ae | ||
|
|
570fc90bf6 | ||
|
|
7213ff7a00 | ||
|
|
adec16790b | ||
|
|
726326924d | ||
|
|
4d2227cfa5 | ||
|
|
2025dcffbd | ||
|
|
ec7a1f02e7 | ||
|
|
1d31533601 | ||
|
|
64104585c5 | ||
|
|
96197025b9 | ||
|
|
1363059416 | ||
|
|
b3988d964a | ||
|
|
2ad17136dc | ||
|
|
b2aa5d9261 | ||
|
|
4ee32c5441 | ||
|
|
ddce858c34 | ||
|
|
00be802c5c | ||
|
|
8d2e1289a4 | ||
|
|
454eb3901d | ||
|
|
a464b41d99 | ||
|
|
1a562ca144 | ||
|
|
03ba9370b9 | ||
|
|
2f16227302 | ||
|
|
15c3fb7b7a | ||
|
|
2ce14ce4eb | ||
|
|
906417cc0d | ||
|
|
2fbf5f4250 | ||
|
|
3491509b21 | ||
|
|
7e8831a414 | ||
|
|
341e53f2e2 | ||
|
|
c9655e54ce | ||
|
|
8a9855241c | ||
|
|
72fc564758 | ||
|
|
81fee2207e | ||
|
|
425db09ede | ||
|
|
0f2b196b32 | ||
|
|
bb7c0ceea0 | ||
|
|
24c2ef2996 | ||
|
|
f7775640d5 | ||
|
|
3127dd902a | ||
|
|
6445fbaadc | ||
|
|
4a4861c26f | ||
|
|
332f1af325 | ||
|
|
520cfaf13e | ||
|
|
7b14b867f5 | ||
|
|
8ab517d242 | ||
|
|
c0f0770f65 | ||
|
|
28e06f7d9c | ||
|
|
77f2c94395 | ||
|
|
17e0ec27eb | ||
|
|
d62abbc938 | ||
|
|
00b7208b5a | ||
|
|
92248e8018 | ||
|
|
78aad07be9 | ||
|
|
50cdf0e9bf | ||
|
|
a644e8c70a | ||
|
|
2e96f99e9c | ||
|
|
57cc054bb3 | ||
|
|
8df315378d | ||
|
|
bcfd1fcdba | ||
|
|
6f93853e65 | ||
|
|
c8b98d1eeb | ||
|
|
356ee9d2a9 | ||
|
|
db5a15e14c | ||
|
|
e2ab89d253 | ||
|
|
85884c15e7 | ||
|
|
fc5ff8d8c7 | ||
|
|
34e566f726 | ||
|
|
a18be49827 | ||
|
|
2871a9fee8 | ||
|
|
2817b9d84b | ||
|
|
4cc05906bd | ||
|
|
d04c1b5d73 | ||
|
|
2a518d8661 | ||
|
|
9b71b2f5d9 | ||
|
|
261bf52440 | ||
|
|
9b76946540 | ||
|
|
9c0b546942 | ||
|
|
ac3bb40692 | ||
|
|
54a39e3c4e | ||
|
|
def9db9d16 | ||
|
|
ed1c6b432c | ||
|
|
dc88e6f927 | ||
|
|
ce53230ab2 | ||
|
|
cd17caab7e | ||
|
|
d5bf34271f | ||
|
|
941896ef28 | ||
|
|
f362ed0880 | ||
|
|
be1c441157 | ||
|
|
8c3b8b7b4c | ||
|
|
be1e781399 | ||
|
|
b10fe4ddd6 | ||
|
|
d54d4b4618 | ||
|
|
09104e17a0 | ||
|
|
7a5247cc33 | ||
|
|
475809ed40 | ||
|
|
2f9742c64f | ||
|
|
85c9c27e42 | ||
|
|
8c9a4d9a05 | ||
|
|
1b2396ee9e | ||
|
|
dd587aa30d | ||
|
|
9ccc0bd27e | ||
|
|
b1dec37adb | ||
|
|
62eb6cfed0 | ||
|
|
6026003508 | ||
|
|
6fdc7e0dad | ||
|
|
3c41195986 | ||
|
|
5e0f14266d | ||
|
|
12e98678f6 | ||
|
|
352a403bd0 | ||
|
|
7e678b5686 | ||
|
|
b270c2bd68 | ||
|
|
11ec75c2ad | ||
|
|
929a13a9a0 | ||
|
|
2c74797d34 | ||
|
|
d05da513be | ||
|
|
26bb397852 | ||
|
|
01d26c0e0e | ||
|
|
e02bf9fb1a | ||
|
|
9ec40ce8e9 | ||
|
|
ac8a9f9502 | ||
|
|
c60d971bc4 | ||
|
|
8db4381304 | ||
|
|
5a5697273b | ||
|
|
de0036f4c1 | ||
|
|
33cc29bbda | ||
|
|
3a4bf14c20 | ||
|
|
56965bc814 | ||
|
|
94eba15c34 | ||
|
|
c90c545d33 | ||
|
|
bbc64a2eb5 | ||
|
|
606215fae9 | ||
|
|
8fa2ea71ef | ||
|
|
591e45657f | ||
|
|
b0d5cedeb6 | ||
|
|
d113636a43 | ||
|
|
5109fa7eda | ||
|
|
ff87be6e5f | ||
|
|
4cf900c779 | ||
|
|
c247761213 | ||
|
|
d57c936b08 | ||
|
|
9f47e123d2 | ||
|
|
c3114132d3 | ||
|
|
5949172735 | ||
|
|
e36b2226b9 | ||
|
|
a88e61c2cf | ||
|
|
5bfc5d44c0 | ||
|
|
4f735fba05 | ||
|
|
8ebaf753d3 | ||
|
|
2479d51cc6 | ||
|
|
033128d8dc | ||
|
|
4ef93fe25f | ||
|
|
833c324498 | ||
|
|
48ab1835da | ||
|
|
798b9eae4a | ||
|
|
010d08f6a4 | ||
|
|
7897450b27 | ||
|
|
2ad4c9e0ce | ||
|
|
95056f9783 | ||
|
|
1a568cc491 | ||
|
|
83b85ba16a | ||
|
|
294ad98776 | ||
|
|
f080aa29b5 | ||
|
|
4252f9d4d5 | ||
|
|
0cbf96cc83 | ||
|
|
7b56daa236 | ||
|
|
852d6a7976 | ||
|
|
bf9f831cb2 | ||
|
|
0ba899e239 | ||
|
|
660151572f | ||
|
|
54eacca287 | ||
|
|
f47b00426a | ||
|
|
7e4dccb3b5 | ||
|
|
f98726c516 | ||
|
|
aea9242a96 | ||
|
|
92b6ff4721 | ||
|
|
72ef90885d | ||
|
|
d0ee63c766 | ||
|
|
587e2b2526 | ||
|
|
a65cdbe66e | ||
|
|
7b3ed32003 | ||
|
|
2c987b66c1 | ||
|
|
e48f15c135 | ||
|
|
bf36a90579 | ||
|
|
43c9c38a28 | ||
|
|
f6bddc4e8d | ||
|
|
f9c881eb5a | ||
|
|
16da954bd7 | ||
|
|
45e7a80057 | ||
|
|
7e7aceb8c1 | ||
|
|
d75c956dbc | ||
|
|
f7f3aaf43c | ||
|
|
91c0439922 | ||
|
|
9d62abbe46 | ||
|
|
124d064015 | ||
|
|
2903f692ba | ||
|
|
7f9de5db0b | ||
|
|
14441a289e | ||
|
|
137e4ce866 | ||
|
|
526c5f2348 | ||
|
|
e4bedc7ea8 | ||
|
|
c6a0ead72d | ||
|
|
98f097dc2f | ||
|
|
c816aa5374 | ||
|
|
4d9a67682d | ||
|
|
d16d1a1341 | ||
|
|
752ce1a1b2 | ||
|
|
28fce8aad5 | ||
|
|
e0948f42ab | ||
|
|
21ecdd5681 | ||
|
|
20e9969313 | ||
|
|
3f2a10bb4b | ||
|
|
9f25378ddd | ||
|
|
cc8280426f | ||
|
|
ba6de431a2 | ||
|
|
c14e4d1795 | ||
|
|
e0192e256f | ||
|
|
53c594abe0 | ||
|
|
72ed8514c5 | ||
|
|
6f0d36c41a | ||
|
|
7473e4cafd | ||
|
|
6bf63d4b41 | ||
|
|
05ebe2418b | ||
|
|
9bef3c136a | ||
|
|
307e380f30 | ||
|
|
128d22e6ee | ||
|
|
50234b8e5c | ||
|
|
3552537fc4 | ||
|
|
f41e9e3e0f | ||
|
|
9b310a6e6f | ||
|
|
2061984313 | ||
|
|
3a47559e86 | ||
|
|
8e09077de8 | ||
|
|
1837a11c22 | ||
|
|
bf987bf58a | ||
|
|
4a14a18799 | ||
|
|
136a93f628 | ||
|
|
0218963f1b | ||
|
|
078bd95a49 | ||
|
|
869df17ddf | ||
|
|
26453df2f7 | ||
|
|
d71872af23 | ||
|
|
25ef8f3934 | ||
|
|
ff498fc206 | ||
|
|
0ade097e99 | ||
|
|
dde8f0e20a | ||
|
|
21035bf5d4 | ||
|
|
d7291e0600 | ||
|
|
aaae9d5a77 | ||
|
|
c09a3a52ac | ||
|
|
442d5fc75c | ||
|
|
3b74cc4a41 | ||
|
|
de77fe8ade | ||
|
|
2f6a9d12f1 | ||
|
|
aada2403c9 | ||
|
|
35ad3dabab | ||
|
|
4fb6754903 | ||
|
|
7e51e2dea6 | ||
|
|
84fb663d6c | ||
|
|
659828b142 | ||
|
|
610578e3e2 | ||
|
|
62629939ff | ||
|
|
20b10b5691 | ||
|
|
32ff832108 | ||
|
|
4fdd2dec59 | ||
|
|
524d446757 | ||
|
|
5b6bc8a12b | ||
|
|
6a328197a5 | ||
|
|
03ffc2618c | ||
|
|
7c26cb1c35 | ||
|
|
1102f92dac | ||
|
|
729671d6ae | ||
|
|
d676520526 | ||
|
|
c29d897854 | ||
|
|
b8ddde0a96 | ||
|
|
43c9647fe5 | ||
|
|
03dda13910 | ||
|
|
531122ee86 | ||
|
|
8f25f487fe | ||
|
|
f1feeb319c | ||
|
|
edcbfa31c9 | ||
|
|
8e1af25738 | ||
|
|
6b3deaa170 | ||
|
|
a4e69d6843 | ||
|
|
c549e84abb | ||
|
|
dade95e142 | ||
|
|
f947e2afec | ||
|
|
6a05d63993 | ||
|
|
5e6d144567 | ||
|
|
9c348d057e | ||
|
|
8b108ed5f2 | ||
|
|
e700182f44 | ||
|
|
34ad551efc | ||
|
|
5d863d7e9c | ||
|
|
28766d1879 | ||
|
|
ba80d1ce1f | ||
|
|
c91182e1b3 | ||
|
|
553dc02deb | ||
|
|
1b51ff333a | ||
|
|
1780d1bbde | ||
|
|
08ea467bfe | ||
|
|
cfb1b879e0 | ||
|
|
d0bcdff5ce | ||
|
|
8445c5fe60 | ||
|
|
a1463263b5 | ||
|
|
a857c46e6e | ||
|
|
d9f478cbfb | ||
|
|
252c79d53a | ||
|
|
23f7aab354 | ||
|
|
e5a5d5a672 | ||
|
|
29b7aea38b | ||
|
|
1ef9fc9d1f | ||
|
|
7221de6ede | ||
|
|
8cd1296049 | ||
|
|
a8ec6092e2 | ||
|
|
fc3d6c1dd9 | ||
|
|
6832bf044e | ||
|
|
394dd9ffa5 | ||
|
|
8cccd75e81 | ||
|
|
00c647e4cc | ||
|
|
f4cb16cc2d | ||
|
|
d6eb6ff973 | ||
|
|
b60d38b7f9 | ||
|
|
180ec92ef9 | ||
|
|
95a9f4ab7c | ||
|
|
f393214fef | ||
|
|
5ba70cf5ef | ||
|
|
35112f2453 | ||
|
|
15fb4cab15 | ||
|
|
15445a0fbe | ||
|
|
80574cdbe8 | ||
|
|
3de54039ae | ||
|
|
65692ad1b5 | ||
|
|
bc4fc6d648 | ||
|
|
ad37df7f2e | ||
|
|
d35672e78e | ||
|
|
a421a1d764 | ||
|
|
bec9c68bf3 | ||
|
|
b35aa10579 | ||
|
|
7f6b09dce8 | ||
|
|
8b73743baa | ||
|
|
e9e8eed360 | ||
|
|
062b4d036a | ||
|
|
d197ff0f9d | ||
|
|
9c90358912 | ||
|
|
0fb6dbab36 | ||
|
|
4158b72971 | ||
|
|
8c2c7fadbf | ||
|
|
b1786e88ac | ||
|
|
5cd6a22dd3 | ||
|
|
b89077187b | ||
|
|
c5fb50298a | ||
|
|
ccd696c49a | ||
|
|
50e91b882c | ||
|
|
ae7271b725 | ||
|
|
d49d80a4a0 | ||
|
|
0b08e1b1d2 | ||
|
|
f9b1176fa9 | ||
|
|
3d232e2345 | ||
|
|
9d1ae80e89 | ||
|
|
2a1bc82887 | ||
|
|
d1e5dbefc7 | ||
|
|
fc116ec950 | ||
|
|
7283d2495f | ||
|
|
e139749b5c | ||
|
|
fe75ec8d0d | ||
|
|
68f81ace97 | ||
|
|
2fc7eb3ea2 | ||
|
|
1dd13da37d | ||
|
|
b201c03625 | ||
|
|
ceec560779 | ||
|
|
d39a07efea | ||
|
|
73391013f7 | ||
|
|
1d88d85f1c | ||
|
|
4a742be73e | ||
|
|
8982998681 | ||
|
|
9d736e8b8b | ||
|
|
c26e122485 | ||
|
|
67068cfaf4 | ||
|
|
35929d27e3 | ||
|
|
2e497cc471 | ||
|
|
c2a9ac24ac | ||
|
|
587f6656a4 | ||
|
|
4377c03168 | ||
|
|
1e0520074c | ||
|
|
b9f6832347 | ||
|
|
f8bb0cd3d1 | ||
|
|
129aef758e | ||
|
|
94f55cf406 | ||
|
|
a20b758e19 | ||
|
|
128842becf | ||
|
|
d51331f51f | ||
|
|
4ef221a645 | ||
|
|
79010e972e | ||
|
|
0ca530e251 | ||
|
|
57b4d46dbc | ||
|
|
50df5e2f59 | ||
|
|
b6db2c7262 | ||
|
|
9441b69ad2 | ||
|
|
08e3d16a58 | ||
|
|
3f3049fdba | ||
|
|
41fb7a8a7e | ||
|
|
5b9828e094 | ||
|
|
96e35585b0 | ||
|
|
f035fe9336 | ||
|
|
44c2e33c78 | ||
|
|
2785dcbbbf | ||
|
|
9b03bd3243 | ||
|
|
8f30456ee3 | ||
|
|
38557ff635 | ||
|
|
7bc2cde4dd | ||
|
|
f065d3a06b | ||
|
|
21f7e3a987 | ||
|
|
9f688bc433 | ||
|
|
6e9d31d4fb | ||
|
|
e81c48526b | ||
|
|
b405985b80 | ||
|
|
8b010c5a9f | ||
|
|
1e7b5d6fdb | ||
|
|
a55982e7f0 | ||
|
|
1ac378063e | ||
|
|
d497c92684 | ||
|
|
677dc2c155 | ||
|
|
beea8deeb5 | ||
|
|
0d0f1a2fb2 | ||
|
|
439701ed7a | ||
|
|
d945d0129c | ||
|
|
cc91477308 | ||
|
|
ac139ec03d | ||
|
|
b4f0e834bf | ||
|
|
6e6cdc834f | ||
|
|
4b81ae1b35 | ||
|
|
d569dc45a8 | ||
|
|
23d49fda29 | ||
|
|
62ad2c3bc1 | ||
|
|
25373ad294 | ||
|
|
26d0c0fd8d | ||
|
|
3afffcd36b | ||
|
|
5440de1785 | ||
|
|
25a56f11c8 | ||
|
|
8271c6481f | ||
|
|
83e26f839d | ||
|
|
c3f7905d82 | ||
|
|
05b54a8d18 | ||
|
|
e5cfdd369e | ||
|
|
c61d8cfb85 | ||
|
|
27f4195471 | ||
|
|
b2596eda32 | ||
|
|
9379440fcb | ||
|
|
b7c502c8d1 | ||
|
|
3c16d2d9b4 | ||
|
|
6cbc03a418 | ||
|
|
15ecc0fa65 | ||
|
|
7f2f8cdad1 | ||
|
|
a64646cb7b | ||
|
|
05f531c538 | ||
|
|
7ca335446b | ||
|
|
99e96a6581 | ||
|
|
7200d8f90e | ||
|
|
7038837aca | ||
|
|
541dcc0e5a | ||
|
|
1060b5aabf | ||
|
|
6ea73c4982 | ||
|
|
cff847273a | ||
|
|
e669f8db9b | ||
|
|
461cc30842 | ||
|
|
13d7cb957c | ||
|
|
47a439a905 | ||
|
|
053fb96694 | ||
|
|
b72c1f7367 | ||
|
|
e90738575f | ||
|
|
35718eec9c | ||
|
|
8285a53152 | ||
|
|
116fe7815b | ||
|
|
4c2efd7da3 | ||
|
|
1a433e3185 | ||
|
|
fa98a00916 | ||
|
|
f4dd46ad60 | ||
|
|
41825ccfbb | ||
|
|
4d1a5f12c0 | ||
|
|
cc58d56c6d | ||
|
|
683ef07312 | ||
|
|
c77c78cef5 | ||
|
|
88517030a4 | ||
|
|
45471e8e63 | ||
|
|
4e620beb75 | ||
|
|
e7ab875b47 | ||
|
|
be74ed9ab6 | ||
|
|
52b2eacd37 | ||
|
|
6b4210a2ea | ||
|
|
740d0da5ee | ||
|
|
34883d356e | ||
|
|
aeafcce314 | ||
|
|
ae1d82c325 | ||
|
|
1c89a07226 | ||
|
|
43e262f13e | ||
|
|
e5f802ab5c | ||
|
|
847860fc5c | ||
|
|
77e936445f | ||
|
|
41beb74ef7 | ||
|
|
67be50b706 | ||
|
|
cd840b7c9d | ||
|
|
07903949ec | ||
|
|
d2156ddaad | ||
|
|
a84ff4b3ff | ||
|
|
c02f13d33f | ||
|
|
f6490859fd | ||
|
|
4b7816dbf4 | ||
|
|
86ffbc3ec5 | ||
|
|
67257f44a5 | ||
|
|
3086f5567a | ||
|
|
bb8acb095d | ||
|
|
24be3cbb5f | ||
|
|
9773191103 | ||
|
|
ddc703c3ec | ||
|
|
09ec204e4b | ||
|
|
8b6cafa0e0 | ||
|
|
1ee8be9d43 | ||
|
|
91bc190d21 | ||
|
|
4dc833b642 | ||
|
|
895e9f8d5c | ||
|
|
12906ff631 | ||
|
|
9c9bcff107 | ||
|
|
443ca5c63b | ||
|
|
1102b1fc4c | ||
|
|
0d089eab21 | ||
|
|
b44fada29e | ||
|
|
859ea160e6 | ||
|
|
1c49e2ffc0 | ||
|
|
140245c58d | ||
|
|
47adcced60 | ||
|
|
a2054deb12 | ||
|
|
30fdfd9ded | ||
|
|
a87a86eb92 | ||
|
|
49dfbcfbc8 | ||
|
|
c558a4f4f6 | ||
|
|
4f49a2248f | ||
|
|
4e57da42c1 | ||
|
|
4f6c9e206c | ||
|
|
699e1d4b77 | ||
|
|
fa2978b3b9 | ||
|
|
ffcdf741fc | ||
|
|
429d7f33d4 | ||
|
|
b6a229775e | ||
|
|
0a4580368a | ||
|
|
e5b191e1f1 | ||
|
|
a07acc067b | ||
|
|
01e763f879 | ||
|
|
a95e75261a | ||
|
|
dcaa7ac609 | ||
|
|
7d2ae10849 | ||
|
|
642cac3759 | ||
|
|
662af2edba | ||
|
|
6f97e15a8f | ||
|
|
ab2fa2ca9d | ||
|
|
4666c99b27 | ||
|
|
e38415c56d | ||
|
|
3f3fce4664 | ||
|
|
1407a7bc8a | ||
|
|
1d268f4b83 | ||
|
|
92aab79d27 | ||
|
|
0823bb1276 | ||
|
|
18e42efa01 | ||
|
|
0ea16328df | ||
|
|
b037efc5c6 | ||
|
|
839bb71389 | ||
|
|
2ef7df5cfe | ||
|
|
f5d2d79f75 | ||
|
|
8e5fa96ffc | ||
|
|
6dbbe07d50 | ||
|
|
3c23410753 | ||
|
|
8e35ab78b4 | ||
|
|
7f2911b7f0 | ||
|
|
d877e90301 | ||
|
|
93db4be726 | ||
|
|
1f419ad19f | ||
|
|
3c7f6508ad | ||
|
|
39449d4b63 | ||
|
|
28e05295fa | ||
|
|
60c18d3550 | ||
|
|
482ab186a2 | ||
|
|
4814195dc4 | ||
|
|
d215b7a10e | ||
|
|
0e52b78590 | ||
|
|
b3a9d607c4 | ||
|
|
c00081b62c | ||
|
|
5e4739b460 | ||
|
|
0d8f598df2 | ||
|
|
2ef04cc308 | ||
|
|
7a0884e2cd | ||
|
|
fa858530cc | ||
|
|
1c61fe5ed9 | ||
|
|
57409ef382 | ||
|
|
06ba826803 | ||
|
|
6e6ef9489f | ||
|
|
45bbe39d1f | ||
|
|
6c9fc13083 | ||
|
|
4c8a642388 | ||
|
|
6208c9d64f | ||
|
|
76325d0ba9 | ||
|
|
cf1802a6de | ||
|
|
538a7b12bd | ||
|
|
57e0f52aaa | ||
|
|
1ae0335b62 | ||
|
|
ca1bff6b66 | ||
|
|
10cc62d2ca | ||
|
|
dd451a8a00 | ||
|
|
444ec1e412 | ||
|
|
82739f4d7d | ||
|
|
19825d853e | ||
|
|
d8ece2e3e9 | ||
|
|
bf4deb0ce6 | ||
|
|
da4739a26c | ||
|
|
fc24bf0915 | ||
|
|
9329b99cba |
@@ -3,10 +3,10 @@ IndentWidth: 4
|
||||
UseTab: Always
|
||||
TabWidth: 4
|
||||
BreakBeforeBraces: Custom
|
||||
Standard: Cpp11
|
||||
Standard: c++17
|
||||
BraceWrapping:
|
||||
AfterClass: true
|
||||
AfterControlStatement: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
@@ -20,7 +20,7 @@ IndentCaseLabels: false
|
||||
AccessModifierOffset: -4
|
||||
ColumnLimit: 90
|
||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||
SortIncludes: false
|
||||
SortIncludes: Never
|
||||
IncludeCategories:
|
||||
- Regex: '^".*'
|
||||
Priority: 2
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
end_of_line = lf
|
||||
|
||||
[*.{cpp,h,lua,txt,glsl,md,c,cmake,java,gradle}]
|
||||
charset = utf8
|
||||
charset = utf-8
|
||||
indent_size = 4
|
||||
indent_style = tab
|
||||
insert_final_newline = true
|
||||
|
||||
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@@ -19,7 +19,7 @@ Contributions are welcome! Here's how you can help:
|
||||
developers.
|
||||
|
||||
Any Pull Request that isn't a bug fix and isn't covered by
|
||||
[the roadmap](../doc/direction.md) will be closed within a week unless it
|
||||
[the roadmap](../doc/direction.md) will be closed within a month unless it
|
||||
receives a concept approval from a Core Developer. For this reason, it is
|
||||
recommended that you open an issue for any such pull requests before doing
|
||||
the work, to avoid disappointment.
|
||||
@@ -30,7 +30,7 @@ Contributions are welcome! Here's how you can help:
|
||||
|
||||
3. Start coding!
|
||||
- Refer to the
|
||||
[Lua API](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt),
|
||||
[Lua API](https://github.com/minetest/minetest/blob/master/doc/lua_api.md),
|
||||
[Developer Wiki](http://dev.minetest.net/Main_Page) and other
|
||||
[documentation](https://github.com/minetest/minetest/tree/master/doc).
|
||||
- Follow the [C/C++](http://dev.minetest.net/Code_style_guidelines) and
|
||||
|
||||
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,32 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: Unconfirmed bug
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
##### Minetest version
|
||||
<!--
|
||||
Paste Minetest version between quotes below.
|
||||
If you are on a devel version, please add git commit hash.
|
||||
You can use `minetest --version` to find it.
|
||||
-->
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
##### OS / Hardware
|
||||
<!-- General information about your hardware and operating system -->
|
||||
Operating system:
|
||||
CPU:
|
||||
|
||||
<!-- For graphical issues only -->
|
||||
GPU model:
|
||||
OpenGL version:
|
||||
|
||||
##### Summary
|
||||
<!-- Describe your problem here -->
|
||||
|
||||
##### Steps to reproduce
|
||||
<!-- Explain how the problem has happened, providing a minimal test (i.e. a code snippet reduced to the bone) where possible -->
|
||||
91
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
91
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
name: Bug report
|
||||
description: Create a report to help us improve
|
||||
labels: ["Unconfirmed bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please note the following:
|
||||
1. **Please update your Minetest Engine to the latest stable or dev version** before submitting bug reports. Make sure the bug is still reproducible on the latest version.
|
||||
2. This page is for reporting the bugs of **the engine itself**. For bugs in a particular game, please [search for the game in the ContentDB](https://content.minetest.net/packages/?type=game) and submit a bug report in their issue trackers.
|
||||
* For example, you can submit issues about the Minetest Game (the official game of Minetest) [in its own repository](https://github.com/minetest/minetest_game/issues).
|
||||
3. Please provide as many details as possible for us to spot the problem quicker.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Minetest version
|
||||
description: |
|
||||
Paste the Minetest version below.
|
||||
If you are on a devel version, please add a git commit hash.
|
||||
You can use `minetest --version` to find it.
|
||||
You can also refer to the "About" tab of the menu.
|
||||
placeholder: |
|
||||
Example:
|
||||
Minetest 5.7.0-dev-ca13c51 (Linux)
|
||||
Using Irrlicht 1.9.0mt9
|
||||
Using LuaJIT 2.1.0-beta3
|
||||
BUILD_TYPE=Release
|
||||
RUN_IN_PLACE=1
|
||||
USE_CURL=1
|
||||
USE_GETTEXT=1
|
||||
USE_SOUND=1
|
||||
STATIC_SHAREDIR="."
|
||||
STATIC_LOCALEDIR="locale"
|
||||
render: "true"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Active renderer
|
||||
description: For graphical and input-related issues. You can find these in the About tab in the mainmenu.
|
||||
placeholder: "Example: OpenGL 4.6.0"
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
attributes:
|
||||
label: Irrlicht device
|
||||
description:
|
||||
placeholder: "Example: X11"
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
attributes:
|
||||
label: Operating system and version
|
||||
description: It is recommended to upgrade your operating system to see if the problem still exists.
|
||||
placeholder: "Example: Ubuntu 22.04"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: CPU model
|
||||
description: Usually found in system settings.
|
||||
placeholder: "Example: Intel i5-2410M (4) @ 2.900GHz"
|
||||
validations:
|
||||
required: false
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: The GPU model and OpenGL version can be omitted if the bug is not a graphical issue.
|
||||
- type: input
|
||||
attributes:
|
||||
label: GPU model
|
||||
description: Usually found in system settings.
|
||||
placeholder: "Example: NVIDA GeForce RTX 4090"
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
attributes:
|
||||
label: OpenGL version
|
||||
placeholder: "Example: 4.6"
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: Describe your problem here.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Explain how the problem has happened, providing a minimal test (i.e. a code snippet reduced to the bone) where possible.
|
||||
validations:
|
||||
required: true
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Submit issues about Minetest Game
|
||||
url: https://github.com/minetest/minetest_game/issues/new/choose
|
||||
about: Only submit issues of the engine in this repository's issue tracker. Submit those of Minetest Game in its own issue tracker.
|
||||
- name: Search for issue trackers of third-party games
|
||||
url: https://content.minetest.net/packages/?type=game
|
||||
about: For issues of third-party games, search for the game in the ContentDB and then submit an issue in their issue tracker.
|
||||
25
.github/ISSUE_TEMPLATE/feature_request.md
vendored
25
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,25 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: Feature request
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
A clear and concise description of what the problem is.
|
||||
ie: Why is this needed?
|
||||
Ex. I'm always frustrated when [...]
|
||||
|
||||
## Solutions
|
||||
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
## Alternatives
|
||||
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
## Additional context
|
||||
|
||||
Add any other context or screenshots about the feature request here.
|
||||
39
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
39
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Feature request
|
||||
description: Suggest an idea for this project
|
||||
labels: ["Feature request"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please note the following:
|
||||
1. Only submit a feature request if the feature does not exist on the latest dev version.
|
||||
2. This page is for suggesting changes to **the engine itself**. To suggest changes to games, please [search for the game in the ContentDB](https://content.minetest.net/packages/?type=game) and submit a feature request in their issue trackers.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Problem
|
||||
description: |
|
||||
A clear and concise description of the problem, i.e. "Why is this needed?"
|
||||
Example: I'm always frustrated when [...]
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Solutions
|
||||
description: |
|
||||
A clear and concise description of what you want to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Alternatives
|
||||
description: |
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: |
|
||||
Add any other context or screenshots about the feature request here.
|
||||
validations:
|
||||
required: false
|
||||
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -3,7 +3,7 @@ Add compact, short information about your PR for easier understanding:
|
||||
- Goal of the PR
|
||||
- How does the PR work?
|
||||
- Does it resolve any reported issue?
|
||||
- Does this relate to a goal in [the roadmap](../doc/direction.md)?
|
||||
- Does this relate to a goal in [the roadmap](https://github.com/minetest/minetest/blob/master/doc/direction.md)?
|
||||
- If not a bug fix, why is this PR needed? What usecases does it solve?
|
||||
|
||||
## To do
|
||||
|
||||
11
.github/workflows/android.yml
vendored
11
.github/workflows/android.yml
vendored
@@ -28,8 +28,17 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends gettext openjdk-11-jdk-headless
|
||||
- name: Build with Gradle
|
||||
- name: Build AAB with Gradle
|
||||
# We build an AAB as well for uploading to the the Play Store.
|
||||
run: cd android; ./gradlew bundlerelease
|
||||
- name: Build APKs with Gradle
|
||||
# "assemblerelease" is very fast after "bundlerelease".
|
||||
run: cd android; ./gradlew assemblerelease
|
||||
- name: Save AAB artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Minetest-release.aab
|
||||
path: android/app/build/outputs/bundle/release/app-release.aab
|
||||
- name: Save armeabi artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
||||
35
.github/workflows/build.yml
vendored
35
.github/workflows/build.yml
vendored
@@ -34,22 +34,21 @@ env:
|
||||
|
||||
jobs:
|
||||
# Older gcc version (should be close to our minimum supported version)
|
||||
gcc_5:
|
||||
runs-on: ubuntu-18.04
|
||||
if: "false" # FIXME
|
||||
gcc_7:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps g++-5
|
||||
install_linux_deps g++-7
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./util/ci/build.sh
|
||||
env:
|
||||
CC: gcc-5
|
||||
CXX: g++-5
|
||||
CC: gcc-7
|
||||
CXX: g++-7
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
@@ -77,31 +76,27 @@ jobs:
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
# Older clang version (should be close to our minimum supported version)
|
||||
clang_3_9:
|
||||
runs-on: ubuntu-18.04
|
||||
if: "false" # FIXME
|
||||
clang_7:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps clang-3.9 valgrind
|
||||
install_linux_deps clang-7 llvm
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./util/ci/build.sh
|
||||
env:
|
||||
CC: clang-3.9
|
||||
CXX: clang++-3.9
|
||||
CC: clang-7
|
||||
CXX: clang++-7
|
||||
CMAKE_FLAGS: '-DCMAKE_C_FLAGS="-fsanitize=address" -DCMAKE_CXX_FLAGS="-fsanitize=address"'
|
||||
|
||||
- name: Unittest
|
||||
run: |
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
- name: Valgrind
|
||||
run: |
|
||||
valgrind --leak-check=full --leak-check-heuristics=all --undef-value-errors=no --error-exitcode=9 ./bin/minetest --run-unittests
|
||||
|
||||
# Current clang version
|
||||
clang_14:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -179,7 +174,6 @@ jobs:
|
||||
run: |
|
||||
EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh winbuild
|
||||
env:
|
||||
NO_MINETEST_GAME: 1
|
||||
NO_PACKAGE: 1
|
||||
|
||||
win64:
|
||||
@@ -197,15 +191,14 @@ jobs:
|
||||
run: |
|
||||
EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh winbuild
|
||||
env:
|
||||
NO_MINETEST_GAME: 1
|
||||
NO_PACKAGE: 1
|
||||
|
||||
msvc:
|
||||
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
|
||||
runs-on: windows-2019
|
||||
env:
|
||||
VCPKG_VERSION: 5cf60186a241e84e8232641ee973395d4fde90e1
|
||||
# 2022.02
|
||||
VCPKG_VERSION: 8eb57355a4ffb410a2e94c07b4dca2dffbee8e50
|
||||
# 2023.10.19
|
||||
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -249,6 +242,8 @@ jobs:
|
||||
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
|
||||
-DCMAKE_BUILD_TYPE=Release `
|
||||
-DENABLE_POSTGRESQL=OFF `
|
||||
-DENABLE_LUAJIT=TRUE `
|
||||
-DREQUIRE_LUAJIT=TRUE `
|
||||
-DRUN_IN_PLACE=${{ contains(matrix.type, 'portable') }} .
|
||||
|
||||
- name: Build Minetest
|
||||
|
||||
48
.github/workflows/lua_api_deploy.yml
vendored
Normal file
48
.github/workflows/lua_api_deploy.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: lua_api_deploy
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/lua_api_deploy.yml'
|
||||
- 'doc/lua_api.md'
|
||||
- 'doc/mkdocs/'
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.11
|
||||
|
||||
- name: Install mkdocs
|
||||
run: |
|
||||
pip install -U -r doc/mkdocs/requirements.txt
|
||||
|
||||
- name: Build documentation
|
||||
run: |
|
||||
cd doc/mkdocs/
|
||||
./build.sh
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v3
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v2
|
||||
with:
|
||||
path: 'public/'
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v2
|
||||
10
.github/workflows/macos.yml
vendored
10
.github/workflows/macos.yml
vendored
@@ -21,14 +21,9 @@ on:
|
||||
- 'cmake/Modules/**'
|
||||
- '.github/workflows/macos.yml'
|
||||
|
||||
env:
|
||||
MINETEST_GAME_REPO: https://github.com/minetest/minetest_game.git
|
||||
MINETEST_GAME_BRANCH: master
|
||||
MINETEST_GAME_NAME: minetest_game
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-11
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
@@ -38,7 +33,6 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
git clone -b $MINETEST_GAME_BRANCH $MINETEST_GAME_REPO games/$MINETEST_GAME_NAME
|
||||
git clone https://github.com/minetest/irrlicht lib/irrlichtmt --depth 1 -b $(cat misc/irrlichtmt_tag.txt)
|
||||
mkdir build
|
||||
cd build
|
||||
@@ -48,7 +42,7 @@ jobs:
|
||||
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
|
||||
-DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE \
|
||||
-DINSTALL_DEVTEST=TRUE
|
||||
make -j2
|
||||
cmake --build . -j$(sysctl -n hw.logicalcpu)
|
||||
make install
|
||||
|
||||
- name: Test
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -28,10 +28,17 @@ gtags.files
|
||||
# Visual Studio Code & plugins
|
||||
.vscode/
|
||||
build/.cmake/
|
||||
# Fleet
|
||||
.fleet
|
||||
# Gradle
|
||||
.gradle
|
||||
# Clang
|
||||
.cache
|
||||
# AppImage
|
||||
*.AppImage
|
||||
*.zsync
|
||||
appimage-build
|
||||
AppDir
|
||||
|
||||
## Files related to Minetest development cycle
|
||||
/*.patch
|
||||
|
||||
@@ -9,7 +9,6 @@ stages:
|
||||
- deploy
|
||||
|
||||
variables:
|
||||
MINETEST_GAME_REPO: "https://github.com/minetest/minetest_game.git"
|
||||
CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH
|
||||
|
||||
.build_template:
|
||||
@@ -120,8 +119,6 @@ package:appimage-client:
|
||||
- mkdir AppDir
|
||||
- cp -a artifact/minetest/usr/ AppDir/usr/
|
||||
- cp -a clientmods AppDir/usr/share/minetest
|
||||
- git clone $MINETEST_GAME_REPO AppDir/usr/share/minetest/games/minetest_game
|
||||
- rm -rf AppDir/usr/share/minetest/games/minetest_game/.git
|
||||
# Remove PrefersNonDefaultGPU property due to validation errors
|
||||
- sed -i '/PrefersNonDefaultGPU/d' AppDir/usr/share/applications/net.minetest.minetest.desktop
|
||||
script:
|
||||
|
||||
@@ -70,6 +70,7 @@ files["builtin/mainmenu"] = {
|
||||
|
||||
read_globals = {
|
||||
"PLATFORM",
|
||||
"TOUCHSCREEN_GUI",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -80,3 +81,9 @@ files["builtin/common/tests"] = {
|
||||
"assert",
|
||||
},
|
||||
}
|
||||
|
||||
files["builtin/fstk"] = {
|
||||
read_globals = {
|
||||
"TOUCHSCREEN_GUI",
|
||||
},
|
||||
}
|
||||
|
||||
120
CMakeLists.txt
120
CMakeLists.txt
@@ -1,25 +1,18 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# Set policies up to 3.9 since we want to enable the IPO option
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.9)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.9)
|
||||
endif()
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
# This can be read from ${PROJECT_NAME} after project() is called
|
||||
project(minetest)
|
||||
set(PROJECT_NAME_CAPITALIZED "Minetest")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
set(GCC_MINIMUM_VERSION "5.1")
|
||||
set(CLANG_MINIMUM_VERSION "3.5")
|
||||
set(GCC_MINIMUM_VERSION "7.5")
|
||||
set(CLANG_MINIMUM_VERSION "7.0.1")
|
||||
|
||||
# You should not need to edit these manually, use util/bump_version.sh
|
||||
set(VERSION_MAJOR 5)
|
||||
set(VERSION_MINOR 7)
|
||||
set(VERSION_PATCH 0)
|
||||
set(VERSION_MINOR 8)
|
||||
set(VERSION_PATCH 1)
|
||||
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
|
||||
|
||||
# Change to false for releases
|
||||
@@ -37,10 +30,20 @@ if (CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
set(VERSION_STRING "${VERSION_STRING}-debug")
|
||||
endif()
|
||||
|
||||
message(STATUS "*** Will build version ${VERSION_STRING} ***")
|
||||
|
||||
|
||||
# Configuration options
|
||||
set(BUILD_CLIENT TRUE CACHE BOOL "Build client")
|
||||
set(BUILD_SERVER FALSE CACHE BOOL "Build server")
|
||||
set(BUILD_UNITTESTS TRUE CACHE BOOL "Build unittests")
|
||||
set(BUILD_BENCHMARKS FALSE CACHE BOOL "Build benchmarks")
|
||||
set(BUILD_DOCUMENTATION TRUE CACHE BOOL "Build documentation")
|
||||
|
||||
set(DEFAULT_ENABLE_LTO TRUE)
|
||||
# by default don't enable on Debug builds to get faster builds
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(DEFAULT_ENABLE_LTO FALSE)
|
||||
endif()
|
||||
set(ENABLE_LTO ${DEFAULT_ENABLE_LTO} CACHE BOOL "Use Link Time Optimization")
|
||||
|
||||
set(DEFAULT_RUN_IN_PLACE FALSE)
|
||||
if(WIN32)
|
||||
set(DEFAULT_RUN_IN_PLACE TRUE)
|
||||
@@ -48,11 +51,13 @@ endif()
|
||||
set(RUN_IN_PLACE ${DEFAULT_RUN_IN_PLACE} CACHE BOOL
|
||||
"Run directly in source directory structure")
|
||||
|
||||
|
||||
set(BUILD_CLIENT TRUE CACHE BOOL "Build client")
|
||||
set(BUILD_SERVER FALSE CACHE BOOL "Build server")
|
||||
set(BUILD_UNITTESTS TRUE CACHE BOOL "Build unittests")
|
||||
set(BUILD_BENCHMARKS FALSE CACHE BOOL "Build benchmarks")
|
||||
message(STATUS "*** Will build version ${VERSION_STRING} ***")
|
||||
message(STATUS "BUILD_CLIENT: " ${BUILD_CLIENT})
|
||||
message(STATUS "BUILD_SERVER: " ${BUILD_SERVER})
|
||||
message(STATUS "BUILD_UNITTESTS: " ${BUILD_UNITTESTS})
|
||||
message(STATUS "BUILD_BENCHMARKS: " ${BUILD_BENCHMARKS})
|
||||
message(STATUS "BUILD_DOCUMENTATION: " ${BUILD_DOCUMENTATION})
|
||||
message(STATUS "RUN_IN_PLACE: " ${RUN_IN_PLACE})
|
||||
|
||||
set(WARN_ALL TRUE CACHE BOOL "Enable -Wall for Release build")
|
||||
|
||||
@@ -61,15 +66,37 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type: Debug or Release" FORCE)
|
||||
endif()
|
||||
|
||||
# FIXME: Windows build fails in multiple places to link, needs to be investigated.
|
||||
if (ENABLE_LTO AND NOT WIN32)
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT lto_supported OUTPUT lto_output)
|
||||
if(lto_supported)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
message(STATUS "LTO/IPO is enabled")
|
||||
else()
|
||||
message(STATUS "LTO/IPO requested but it is not supported by the compiler: ${lto_output}")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "LTO/IPO is not enabled")
|
||||
endif()
|
||||
|
||||
set(ENABLE_UPDATE_CHECKER (NOT ${DEVELOPMENT_BUILD}) CACHE BOOL
|
||||
"Whether to enable update checks by default")
|
||||
|
||||
# Included stuff
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
|
||||
|
||||
# Load default options for Android
|
||||
if(ANDROID)
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
include(MinetestAndroidLibs)
|
||||
endif()
|
||||
|
||||
|
||||
set(IRRLICHTMT_BUILD_DIR "" CACHE PATH "Path to IrrlichtMt build directory.")
|
||||
if(NOT "${IRRLICHTMT_BUILD_DIR}" STREQUAL "")
|
||||
if(ANDROID)
|
||||
# currently manually provided
|
||||
elseif(NOT "${IRRLICHTMT_BUILD_DIR}" STREQUAL "")
|
||||
find_package(IrrlichtMt QUIET
|
||||
PATHS "${IRRLICHTMT_BUILD_DIR}"
|
||||
NO_DEFAULT_PATH
|
||||
@@ -120,14 +147,16 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_CLIENT AND TARGET IrrlichtMt::IrrlichtMt)
|
||||
if(ANDROID)
|
||||
# skipped for now
|
||||
elseif(BUILD_CLIENT AND TARGET IrrlichtMt::IrrlichtMt)
|
||||
# retrieve version somehow
|
||||
if(NOT IrrlichtMt_VERSION)
|
||||
get_target_property(IrrlichtMt_VERSION IrrlichtMt VERSION)
|
||||
endif()
|
||||
message(STATUS "Found IrrlichtMt ${IrrlichtMt_VERSION}")
|
||||
|
||||
set(TARGET_VER_S 1.9.0mt10)
|
||||
set(TARGET_VER_S 1.9.0mt13)
|
||||
string(REPLACE "mt" "." TARGET_VER ${TARGET_VER_S})
|
||||
if(IrrlichtMt_VERSION VERSION_LESS ${TARGET_VER})
|
||||
message(FATAL_ERROR "At least IrrlichtMt ${TARGET_VER_S} is required to build")
|
||||
@@ -247,9 +276,6 @@ if(RUN_IN_PLACE)
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/textures/texture_packs_here.txt" DESTINATION "${SHAREDIR}/textures")
|
||||
endif()
|
||||
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/minetest_game" DESTINATION "${SHAREDIR}/games/"
|
||||
COMPONENT "SUBGAME_MINETEST_GAME" OPTIONAL PATTERN ".git*" EXCLUDE )
|
||||
|
||||
set(INSTALL_DEVTEST FALSE CACHE BOOL "Install Development Test")
|
||||
|
||||
if(INSTALL_DEVTEST)
|
||||
@@ -267,11 +293,11 @@ if(BUILD_CLIENT)
|
||||
endif()
|
||||
|
||||
install(FILES "README.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||
install(FILES "doc/lua_api.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||
install(FILES "doc/client_lua_api.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||
install(FILES "doc/menu_lua_api.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||
install(FILES "doc/texture_packs.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||
install(FILES "doc/world_format.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||
install(FILES "doc/lua_api.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||
install(FILES "doc/client_lua_api.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||
install(FILES "doc/menu_lua_api.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||
install(FILES "doc/texture_packs.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||
install(FILES "doc/world_format.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||
install(FILES "minetest.conf.example" DESTINATION "${EXAMPLE_CONF_DIR}")
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
@@ -321,16 +347,6 @@ cpack_add_component(Docs
|
||||
DESCRIPTION "Documentation about Minetest and Minetest modding"
|
||||
)
|
||||
|
||||
cpack_add_component(SUBGAME_MINETEST_GAME
|
||||
DISPLAY_NAME "Minetest Game"
|
||||
DESCRIPTION "The default game bundled in the Minetest engine. Mainly used as a modding base."
|
||||
GROUP "Games"
|
||||
)
|
||||
|
||||
cpack_add_component_group(Subgames
|
||||
DESCRIPTION "Games for the Minetest engine."
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
# Include all dynamically linked runtime libraries such as MSVCRxxx.dll
|
||||
include(InstallRequiredSystemLibraries)
|
||||
@@ -384,13 +400,15 @@ include(CPack)
|
||||
|
||||
|
||||
# Add a target to generate API documentation with Doxygen
|
||||
find_package(Doxygen)
|
||||
if(DOXYGEN_FOUND)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile @ONLY)
|
||||
add_custom_target(doc
|
||||
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc
|
||||
COMMENT "Generating API documentation with Doxygen" VERBATIM
|
||||
)
|
||||
if(BUILD_DOCUMENTATION)
|
||||
find_package(Doxygen)
|
||||
if(DOXYGEN_FOUND)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile @ONLY)
|
||||
add_custom_target(doc
|
||||
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc
|
||||
COMMENT "Generating API documentation with Doxygen" VERBATIM
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
27
Dockerfile
27
Dockerfile
@@ -1,7 +1,6 @@
|
||||
ARG DOCKER_IMAGE=alpine:3.16
|
||||
FROM $DOCKER_IMAGE AS builder
|
||||
FROM $DOCKER_IMAGE AS dev
|
||||
|
||||
ENV MINETEST_GAME_VERSION master
|
||||
ENV IRRLICHT_VERSION master
|
||||
ENV SPATIALINDEX_VERSION 1.9.3
|
||||
ENV LUAJIT_VERSION v2.1
|
||||
@@ -35,6 +34,8 @@ RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp/ && \
|
||||
git clone --depth=1 https://github.com/minetest/irrlicht/ -b ${IRRLICHT_VERSION} && \
|
||||
cp -r irrlicht/include /usr/include/irrlichtmt
|
||||
|
||||
FROM dev as builder
|
||||
|
||||
COPY .git /usr/src/minetest/.git
|
||||
COPY CMakeLists.txt /usr/src/minetest/CMakeLists.txt
|
||||
COPY README.md /usr/src/minetest/README.md
|
||||
@@ -50,18 +51,16 @@ COPY src /usr/src/minetest/src
|
||||
COPY textures /usr/src/minetest/textures
|
||||
|
||||
WORKDIR /usr/src/minetest
|
||||
RUN git clone --depth=1 -b ${MINETEST_GAME_VERSION} https://github.com/minetest/minetest_game.git ./games/minetest_game && \
|
||||
rm -fr ./games/minetest_game/.git && \
|
||||
cmake -B build \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SERVER=TRUE \
|
||||
-DENABLE_PROMETHEUS=TRUE \
|
||||
-DBUILD_UNITTESTS=FALSE \
|
||||
-DBUILD_CLIENT=FALSE \
|
||||
-GNinja && \
|
||||
cmake --build build && \
|
||||
cmake --install build
|
||||
RUN cmake -B build \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SERVER=TRUE \
|
||||
-DENABLE_PROMETHEUS=TRUE \
|
||||
-DBUILD_UNITTESTS=FALSE \
|
||||
-DBUILD_CLIENT=FALSE \
|
||||
-GNinja && \
|
||||
cmake --build build && \
|
||||
cmake --install build
|
||||
|
||||
ARG DOCKER_IMAGE=alpine:3.16
|
||||
FROM $DOCKER_IMAGE AS runtime
|
||||
|
||||
12
LICENSE.txt
12
LICENSE.txt
@@ -61,7 +61,11 @@ Zughy:
|
||||
textures/base/pack/cdb_downloading.png
|
||||
textures/base/pack/cdb_queued.png
|
||||
textures/base/pack/cdb_update.png
|
||||
textures/base/pack/cdb_update_cropped.png
|
||||
textures/base/pack/cdb_viewonline.png
|
||||
textures/base/pack/settings_btn.png
|
||||
textures/base/pack/settings_info.png
|
||||
textures/base/pack/settings_reset.png
|
||||
|
||||
appgurueu:
|
||||
textures/base/pack/server_incompatible.png
|
||||
@@ -71,15 +75,23 @@ erlehmann, Warr1024, rollerozxa:
|
||||
|
||||
kilbith:
|
||||
textures/base/pack/server_favorite.png
|
||||
textures/base/pack/progress_bar.png
|
||||
textures/base/pack/progress_bar_bg.png
|
||||
|
||||
SmallJoker:
|
||||
textures/base/pack/cdb_clear.png
|
||||
textures/base/pack/server_favorite_delete.png (based on server_favorite.png)
|
||||
|
||||
DS:
|
||||
games/devtest/mods/soundstuff/textures/soundstuff_bigfoot.png
|
||||
games/devtest/mods/soundstuff/textures/soundstuff_jukebox.png
|
||||
games/devtest/mods/soundstuff/textures/soundstuff_racecar.png
|
||||
games/devtest/mods/soundstuff/sounds/soundstuff_sinus.ogg
|
||||
games/devtest/mods/testtools/textures/testtools_branding_iron.png
|
||||
|
||||
grorp:
|
||||
textures/base/pack/exit_btn.png
|
||||
|
||||
License of Minetest source code
|
||||
-------------------------------
|
||||
|
||||
|
||||
334
README.md
334
README.md
@@ -10,12 +10,6 @@ Minetest is a free open-source voxel game engine with easy modding and game crea
|
||||
Copyright (C) 2010-2022 Perttu Ahola <celeron55@gmail.com>
|
||||
and contributors (see source file comments and the version control log)
|
||||
|
||||
In case you downloaded the source code
|
||||
--------------------------------------
|
||||
If you downloaded the Minetest Engine source code in which this file is
|
||||
contained, you probably want to download the [Minetest Game](https://github.com/minetest/minetest_game/)
|
||||
project too. See its README.txt for more information.
|
||||
|
||||
Table of Contents
|
||||
------------------
|
||||
|
||||
@@ -51,7 +45,7 @@ Some can be changed in the key config dialog in the settings tab.
|
||||
| Shift | Sneak/move down |
|
||||
| Q | Drop itemstack |
|
||||
| Shift + Q | Drop single item |
|
||||
| Left mouse button | Dig/punch/take item |
|
||||
| Left mouse button | Dig/punch/use |
|
||||
| Right mouse button | Place/use |
|
||||
| Shift + right mouse button | Build (without using) |
|
||||
| I | Inventory menu |
|
||||
@@ -124,330 +118,16 @@ Command-line options
|
||||
|
||||
Compiling
|
||||
---------
|
||||
### Compiling on GNU/Linux
|
||||
|
||||
#### Dependencies
|
||||
|
||||
| Dependency | Version | Commentary |
|
||||
|------------|---------|------------|
|
||||
| GCC | 5.1+ | or Clang 3.5+ |
|
||||
| CMake | 3.5+ | |
|
||||
| IrrlichtMt | - | Custom version of Irrlicht, see https://github.com/minetest/irrlicht |
|
||||
| Freetype | 2.0+ | |
|
||||
| SQLite3 | 3+ | |
|
||||
| Zstd | 1.0+ | |
|
||||
| LuaJIT | 2.0+ | Bundled Lua 5.1 is used if not present |
|
||||
| GMP | 5.0.0+ | Bundled mini-GMP is used if not present |
|
||||
| JsonCPP | 1.0.0+ | Bundled JsonCPP is used if not present |
|
||||
|
||||
For Debian/Ubuntu users:
|
||||
|
||||
sudo apt install g++ make libc6-dev cmake libpng-dev libjpeg-dev libxi-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev libzstd-dev libluajit-5.1-dev
|
||||
|
||||
For Fedora users:
|
||||
|
||||
sudo dnf install make automake gcc gcc-c++ kernel-devel cmake libcurl-devel openal-soft-devel libpng-devel libjpeg-devel libvorbis-devel libXi-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel spatialindex-devel libzstd-devel
|
||||
|
||||
For Arch users:
|
||||
|
||||
sudo pacman -S base-devel libcurl-gnutls cmake libxi libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses zstd
|
||||
|
||||
For Alpine users:
|
||||
|
||||
sudo apk add build-base cmake libpng-dev jpeg-dev libxi-dev mesa-dev sqlite-dev libogg-dev libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev gmp-dev jsoncpp-dev luajit-dev zstd-dev
|
||||
|
||||
#### Download
|
||||
|
||||
You can install Git for easily keeping your copy up to date.
|
||||
If you don’t want Git, read below on how to get the source without Git.
|
||||
This is an example for installing Git on Debian/Ubuntu:
|
||||
|
||||
sudo apt install git
|
||||
|
||||
For Fedora users:
|
||||
|
||||
sudo dnf install git
|
||||
|
||||
For Arch users:
|
||||
|
||||
sudo pacman -S git
|
||||
|
||||
For Alpine users:
|
||||
|
||||
sudo apk add git
|
||||
|
||||
Download source (this is the URL to the latest of source repository, which might not work at all times) using Git:
|
||||
|
||||
git clone --depth 1 https://github.com/minetest/minetest.git
|
||||
cd minetest
|
||||
|
||||
Download Minetest Game (otherwise only the "Development Test" game is available) using Git:
|
||||
|
||||
git clone --depth 1 https://github.com/minetest/minetest_game.git games/minetest_game
|
||||
|
||||
Download IrrlichtMt to `lib/irrlichtmt`, it will be used to satisfy the IrrlichtMt dependency that way:
|
||||
|
||||
git clone --depth 1 https://github.com/minetest/irrlicht.git lib/irrlichtmt
|
||||
|
||||
Download source, without using Git:
|
||||
|
||||
wget https://github.com/minetest/minetest/archive/master.tar.gz
|
||||
tar xf master.tar.gz
|
||||
cd minetest-master
|
||||
|
||||
Download Minetest Game, without using Git:
|
||||
|
||||
cd games/
|
||||
wget https://github.com/minetest/minetest_game/archive/master.tar.gz
|
||||
tar xf master.tar.gz
|
||||
mv minetest_game-master minetest_game
|
||||
cd ..
|
||||
|
||||
Download IrrlichtMt, without using Git:
|
||||
|
||||
cd lib/
|
||||
wget https://github.com/minetest/irrlicht/archive/master.tar.gz
|
||||
tar xf master.tar.gz
|
||||
mv irrlicht-master irrlichtmt
|
||||
cd ..
|
||||
|
||||
#### Build
|
||||
|
||||
Build a version that runs directly from the source directory:
|
||||
|
||||
cmake . -DRUN_IN_PLACE=TRUE
|
||||
make -j$(nproc)
|
||||
|
||||
Run it:
|
||||
|
||||
./bin/minetest
|
||||
|
||||
- Use `cmake . -LH` to see all CMake options and their current state.
|
||||
- If you want to install it system-wide (or are making a distribution package),
|
||||
you will want to use `-DRUN_IN_PLACE=FALSE`.
|
||||
- You can build a bare server by specifying `-DBUILD_SERVER=TRUE`.
|
||||
- You can disable the client build by specifying `-DBUILD_CLIENT=FALSE`.
|
||||
- You can select between Release and Debug build by `-DCMAKE_BUILD_TYPE=<Debug or Release>`.
|
||||
- Debug build is slower, but gives much more useful output in a debugger.
|
||||
- If you build a bare server you don't need to compile IrrlichtMt, just the headers suffice.
|
||||
- In that case use `-DIRRLICHT_INCLUDE_DIR=/some/where/irrlichtmt/include`.
|
||||
|
||||
- Minetest will use the IrrlichtMt package that is found first, given by the following order:
|
||||
1. Specified `IRRLICHTMT_BUILD_DIR` CMake variable
|
||||
2. `${PROJECT_SOURCE_DIR}/lib/irrlichtmt` (if existent)
|
||||
3. Installation of IrrlichtMt in the system-specific library paths
|
||||
4. For server builds with disabled `BUILD_CLIENT` variable, the headers from `IRRLICHT_INCLUDE_DIR` will be used.
|
||||
- NOTE: Changing the IrrlichtMt build directory (includes system installs) requires regenerating the CMake cache (`rm CMakeCache.txt`)
|
||||
|
||||
### CMake options
|
||||
|
||||
General options and their default values:
|
||||
|
||||
BUILD_CLIENT=TRUE - Build Minetest client
|
||||
BUILD_SERVER=FALSE - Build Minetest server
|
||||
BUILD_UNITTESTS=TRUE - Build unittest sources
|
||||
BUILD_BENCHMARKS=FALSE - Build benchmark sources
|
||||
CMAKE_BUILD_TYPE=Release - Type of build (Release vs. Debug)
|
||||
Release - Release build
|
||||
Debug - Debug build
|
||||
SemiDebug - Partially optimized debug build
|
||||
RelWithDebInfo - Release build with debug information
|
||||
MinSizeRel - Release build with -Os passed to compiler to make executable as small as possible
|
||||
ENABLE_CURL=ON - Build with cURL; Enables use of online mod repo, public serverlist and remote media fetching via http
|
||||
ENABLE_CURSES=ON - Build with (n)curses; Enables a server side terminal (command line option: --terminal)
|
||||
ENABLE_GETTEXT=ON - Build with Gettext; Allows using translations
|
||||
ENABLE_GLES=OFF - Enable extra support code for OpenGL ES (requires support by IrrlichtMt)
|
||||
ENABLE_LEVELDB=ON - Build with LevelDB; Enables use of LevelDB map backend
|
||||
ENABLE_POSTGRESQL=ON - Build with libpq; Enables use of PostgreSQL map backend (PostgreSQL 9.5 or greater recommended)
|
||||
ENABLE_REDIS=ON - Build with libhiredis; Enables use of Redis map backend
|
||||
ENABLE_SPATIAL=ON - Build with LibSpatial; Speeds up AreaStores
|
||||
ENABLE_SOUND=ON - Build with OpenAL, libogg & libvorbis; in-game sounds
|
||||
ENABLE_LUAJIT=ON - Build with LuaJIT (much faster than non-JIT Lua)
|
||||
ENABLE_PROMETHEUS=OFF - Build with Prometheus metrics exporter (listens on tcp/30000 by default)
|
||||
ENABLE_SYSTEM_GMP=ON - Use GMP from system (much faster than bundled mini-gmp)
|
||||
ENABLE_SYSTEM_JSONCPP=ON - Use JsonCPP from system
|
||||
RUN_IN_PLACE=FALSE - Create a portable install (worlds, settings etc. in current directory)
|
||||
ENABLE_UPDATE_CHECKER=TRUE - Whether to enable update checks by default
|
||||
INSTALL_DEVTEST=FALSE - Whether the Development Test game should be installed alongside Minetest
|
||||
USE_GPROF=FALSE - Enable profiling using GProf
|
||||
VERSION_EXTRA= - Text to append to version (e.g. VERSION_EXTRA=foobar -> Minetest 0.4.9-foobar)
|
||||
ENABLE_TOUCH=FALSE - Enable Touchscreen support (requires support by IrrlichtMt)
|
||||
|
||||
Library specific options:
|
||||
|
||||
CURL_DLL - Only if building with cURL on Windows; path to libcurl.dll
|
||||
CURL_INCLUDE_DIR - Only if building with cURL; directory where curl.h is located
|
||||
CURL_LIBRARY - Only if building with cURL; path to libcurl.a/libcurl.so/libcurl.lib
|
||||
EGL_INCLUDE_DIR - Only if building with GLES; directory that contains egl.h
|
||||
EGL_LIBRARY - Only if building with GLES; path to libEGL.a/libEGL.so
|
||||
EXTRA_DLL - Only on Windows; optional paths to additional DLLs that should be packaged
|
||||
FREETYPE_INCLUDE_DIR_freetype2 - Directory that contains files such as ftimage.h
|
||||
FREETYPE_INCLUDE_DIR_ft2build - Directory that contains ft2build.h
|
||||
FREETYPE_LIBRARY - Path to libfreetype.a/libfreetype.so/freetype.lib
|
||||
FREETYPE_DLL - Only on Windows; path to libfreetype-6.dll
|
||||
GETTEXT_DLL - Only when building with gettext on Windows; paths to libintl + libiconv DLLs
|
||||
GETTEXT_INCLUDE_DIR - Only when building with gettext; directory that contains libintl.h
|
||||
GETTEXT_LIBRARY - Optional/platform-dependent with gettext; path to libintl.so/libintl.dll.a
|
||||
GETTEXT_MSGFMT - Only when building with gettext; path to msgfmt/msgfmt.exe
|
||||
ICONV_LIBRARY - Optional/platform-dependent; path to libiconv.so/libiconv.dylib
|
||||
IRRLICHT_DLL - Only on Windows; path to IrrlichtMt.dll
|
||||
IRRLICHT_INCLUDE_DIR - Directory that contains IrrCompileConfig.h (usable for server build only)
|
||||
LEVELDB_INCLUDE_DIR - Only when building with LevelDB; directory that contains db.h
|
||||
LEVELDB_LIBRARY - Only when building with LevelDB; path to libleveldb.a/libleveldb.so/libleveldb.dll.a
|
||||
LEVELDB_DLL - Only when building with LevelDB on Windows; path to libleveldb.dll
|
||||
PostgreSQL_INCLUDE_DIR - Only when building with PostgreSQL; directory that contains libpq-fe.h
|
||||
PostgreSQL_LIBRARY - Only when building with PostgreSQL; path to libpq.a/libpq.so/libpq.lib
|
||||
REDIS_INCLUDE_DIR - Only when building with Redis; directory that contains hiredis.h
|
||||
REDIS_LIBRARY - Only when building with Redis; path to libhiredis.a/libhiredis.so
|
||||
SPATIAL_INCLUDE_DIR - Only when building with LibSpatial; directory that contains spatialindex/SpatialIndex.h
|
||||
SPATIAL_LIBRARY - Only when building with LibSpatial; path to libspatialindex.so/spatialindex-32.lib
|
||||
LUA_INCLUDE_DIR - Only if you want to use LuaJIT; directory where luajit.h is located
|
||||
LUA_LIBRARY - Only if you want to use LuaJIT; path to libluajit.a/libluajit.so
|
||||
OGG_DLL - Only if building with sound on Windows; path to libogg.dll
|
||||
OGG_INCLUDE_DIR - Only if building with sound; directory that contains an ogg directory which contains ogg.h
|
||||
OGG_LIBRARY - Only if building with sound; path to libogg.a/libogg.so/libogg.dll.a
|
||||
OPENAL_DLL - Only if building with sound on Windows; path to OpenAL32.dll
|
||||
OPENAL_INCLUDE_DIR - Only if building with sound; directory where al.h is located
|
||||
OPENAL_LIBRARY - Only if building with sound; path to libopenal.a/libopenal.so/OpenAL32.lib
|
||||
SQLITE3_INCLUDE_DIR - Directory that contains sqlite3.h
|
||||
SQLITE3_LIBRARY - Path to libsqlite3.a/libsqlite3.so/sqlite3.lib
|
||||
VORBISFILE_LIBRARY - Only if building with sound; path to libvorbisfile.a/libvorbisfile.so/libvorbisfile.dll.a
|
||||
VORBIS_DLL - Only if building with sound on Windows; paths to vorbis DLLs
|
||||
VORBIS_INCLUDE_DIR - Only if building with sound; directory that contains a directory vorbis with vorbisenc.h inside
|
||||
VORBIS_LIBRARY - Only if building with sound; path to libvorbis.a/libvorbis.so/libvorbis.dll.a
|
||||
ZLIB_DLL - Only on Windows; path to zlib1.dll
|
||||
ZLIB_INCLUDE_DIR - Directory that contains zlib.h
|
||||
ZLIB_LIBRARY - Path to libz.a/libz.so/zlib.lib
|
||||
ZSTD_DLL - Only on Windows; path to libzstd.dll
|
||||
ZSTD_INCLUDE_DIR - Directory that contains zstd.h
|
||||
ZSTD_LIBRARY - Path to libzstd.a/libzstd.so/ztd.lib
|
||||
|
||||
### Compiling on Windows using MSVC
|
||||
|
||||
### Requirements
|
||||
|
||||
- [Visual Studio 2015 or newer](https://visualstudio.microsoft.com)
|
||||
- [CMake](https://cmake.org/download/)
|
||||
- [vcpkg](https://github.com/Microsoft/vcpkg)
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
|
||||
### Compiling and installing the dependencies
|
||||
|
||||
It is highly recommended to use vcpkg as package manager.
|
||||
|
||||
After you successfully built vcpkg you can easily install the required libraries:
|
||||
```powershell
|
||||
vcpkg install zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry --triplet x64-windows
|
||||
```
|
||||
|
||||
- **Don't forget about IrrlichtMt.** The easiest way is to clone it to `lib/irrlichtmt` as described in the Linux section.
|
||||
- `curl` is optional, but required to read the serverlist, `curl[winssl]` is required to use the content store.
|
||||
- `openal-soft`, `libvorbis` and `libogg` are optional, but required to use sound.
|
||||
- `luajit` is optional, it replaces the integrated Lua interpreter with a faster just-in-time interpreter.
|
||||
- `gmp` and `jsoncpp` are optional, otherwise the bundled versions will be compiled
|
||||
|
||||
There are other optional libraries, but they are not tested if they can build and link correctly.
|
||||
|
||||
Use `--triplet` to specify the target triplet, e.g. `x64-windows` or `x86-windows`.
|
||||
|
||||
### Compile Minetest
|
||||
|
||||
#### a) Using the vcpkg toolchain and CMake GUI
|
||||
1. Start up the CMake GUI
|
||||
2. Select **Browse Source...** and select DIR/minetest
|
||||
3. Select **Browse Build...** and select DIR/minetest-build
|
||||
4. Select **Configure**
|
||||
5. Choose the right visual Studio version and target platform. It has to match the version of the installed dependencies
|
||||
6. Choose **Specify toolchain file for cross-compiling**
|
||||
7. Click **Next**
|
||||
8. Select the vcpkg toolchain file e.g. `D:/vcpkg/scripts/buildsystems/vcpkg.cmake`
|
||||
9. Click Finish
|
||||
10. Wait until cmake have generated the cash file
|
||||
11. If there are any errors, solve them and hit **Configure**
|
||||
12. Click **Generate**
|
||||
13. Click **Open Project**
|
||||
14. Compile Minetest inside Visual studio.
|
||||
|
||||
#### b) Using the vcpkg toolchain and the commandline
|
||||
|
||||
Run the following script in PowerShell:
|
||||
|
||||
```powershell
|
||||
cmake . -G"Visual Studio 15 2017 Win64" -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_GETTEXT=OFF -DENABLE_CURSES=OFF
|
||||
cmake --build . --config Release
|
||||
```
|
||||
Make sure that the right compiler is selected and the path to the vcpkg toolchain is correct.
|
||||
|
||||
### Windows Installer using WiX Toolset
|
||||
|
||||
Requirements:
|
||||
* [Visual Studio 2017](https://visualstudio.microsoft.com/)
|
||||
* [WiX Toolset](https://wixtoolset.org/)
|
||||
|
||||
In the Visual Studio 2017 Installer select **Optional Features -> WiX Toolset**.
|
||||
|
||||
Build the binaries as described above, but make sure you unselect `RUN_IN_PLACE`.
|
||||
|
||||
Open the generated project file with Visual Studio. Right-click **Package** and choose **Generate**.
|
||||
It may take some minutes to generate the installer.
|
||||
|
||||
### Compiling on MacOS
|
||||
|
||||
#### Requirements
|
||||
- [Homebrew](https://brew.sh/)
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
|
||||
Install dependencies with homebrew:
|
||||
|
||||
```
|
||||
brew install cmake freetype gettext gmp hiredis jpeg jsoncpp leveldb libogg libpng libvorbis luajit zstd
|
||||
```
|
||||
|
||||
#### Download
|
||||
|
||||
Download source (this is the URL to the latest of source repository, which might not work at all times) using Git:
|
||||
|
||||
```bash
|
||||
git clone --depth 1 https://github.com/minetest/minetest.git
|
||||
cd minetest
|
||||
```
|
||||
|
||||
Download Minetest Game (otherwise only the "Development Test" game is available) using Git:
|
||||
|
||||
```
|
||||
git clone --depth 1 https://github.com/minetest/minetest_game.git games/minetest_game
|
||||
```
|
||||
|
||||
Download Minetest's fork of Irrlicht:
|
||||
|
||||
```
|
||||
git clone --depth 1 https://github.com/minetest/irrlicht.git lib/irrlichtmt
|
||||
```
|
||||
|
||||
#### Build
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
cmake .. \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
|
||||
-DCMAKE_FIND_FRAMEWORK=LAST \
|
||||
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
|
||||
-DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE
|
||||
|
||||
make -j$(sysctl -n hw.logicalcpu)
|
||||
make install
|
||||
```
|
||||
|
||||
#### Run
|
||||
|
||||
```
|
||||
open ./build/macos/minetest.app
|
||||
```
|
||||
- [Compiling on GNU/Linux](doc/compiling/linux.md)
|
||||
- [Compiling on Windows](doc/compiling/windows.md)
|
||||
- [Compiling on MacOS](doc/compiling/macos.md)
|
||||
|
||||
Docker
|
||||
------
|
||||
|
||||
- [Developing minetestserver with Docker](doc/developing/docker.md)
|
||||
|
||||
We provide Minetest server Docker images using the GitLab mirror registry.
|
||||
|
||||
Images are built on each commit and available using the following tag scheme:
|
||||
|
||||
@@ -54,7 +54,9 @@ android {
|
||||
task prepareAssets() {
|
||||
def assetsFolder = "build/assets"
|
||||
def projRoot = rootDir.parent
|
||||
def gameToCopy = "minetest_game"
|
||||
|
||||
// See issue #4638
|
||||
def unsupportedLanguages = new File("${projRoot}/src/unsupported_language_list.txt").text.readLines()
|
||||
|
||||
doFirst {
|
||||
logger.lifecycle('Preparing assets at {}', assetsFolder)
|
||||
@@ -79,14 +81,13 @@ task prepareAssets() {
|
||||
from "${projRoot}/fonts" include "*.ttf" into "${assetsFolder}/fonts"
|
||||
}
|
||||
copy {
|
||||
from "${projRoot}/games/${gameToCopy}" into "${assetsFolder}/games/${gameToCopy}"
|
||||
}
|
||||
copy {
|
||||
from "${projRoot}/textures" into "${assetsFolder}/textures"
|
||||
from "${projRoot}/textures/base/pack" into "${assetsFolder}/textures/base/pack"
|
||||
}
|
||||
|
||||
// compile translations
|
||||
fileTree("${projRoot}/po").include("**/*.po").forEach { poFile ->
|
||||
fileTree("${projRoot}/po").include("**/*.po").grep {
|
||||
it.parentFile.name !in unsupportedLanguages
|
||||
}.forEach { poFile ->
|
||||
def moPath = "${assetsFolder}/locale/${poFile.parentFile.name}/LC_MESSAGES/"
|
||||
file(moPath).mkdirs()
|
||||
exec {
|
||||
|
||||
@@ -5,20 +5,13 @@
|
||||
android:installLocation="auto">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-feature android:glEsVersion="0x00020000" />
|
||||
|
||||
<!--
|
||||
`android:requestLegacyExternalStorage="true"` is workaround for using `/sdcard`
|
||||
instead of the `getFilesDir()` patch for assets. Check link below for more information:
|
||||
https://developer.android.com/training/data-storage/compatibility
|
||||
-->
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/label"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:resizeableActivity="false"
|
||||
tools:ignore="UnusedAttribute">
|
||||
|
||||
@@ -53,7 +46,7 @@
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
||||
android:value="Minetest" />
|
||||
android:value="minetest" />
|
||||
</activity>
|
||||
|
||||
<service
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
Minetest
|
||||
Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
|
||||
Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
|
||||
Copyright (C) 2023 srifqi, Muhammad Rifqi Priyo Susanto
|
||||
<muhammadrifqipriyosusanto@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -29,17 +31,52 @@ import androidx.appcompat.widget.AppCompatEditText;
|
||||
import java.util.Objects;
|
||||
|
||||
public class CustomEditText extends AppCompatEditText {
|
||||
private int editType = 2; // single line text input as default
|
||||
private boolean wantsToShowKeyboard = false;
|
||||
|
||||
public CustomEditText(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public CustomEditText(Context context, int _editType) {
|
||||
super(context);
|
||||
editType = _editType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
// For multi-line, do not close the dialog after pressing back button
|
||||
if (editType != 1 && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
InputMethodManager mgr = (InputMethodManager)
|
||||
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
Objects.requireNonNull(mgr).hideSoftInputFromWindow(this.getWindowToken(), 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasWindowFocus) {
|
||||
super.onWindowFocusChanged(hasWindowFocus);
|
||||
tryShowKeyboard();
|
||||
}
|
||||
|
||||
public void requestFocusTryShow() {
|
||||
requestFocus();
|
||||
wantsToShowKeyboard = true;
|
||||
tryShowKeyboard();
|
||||
}
|
||||
|
||||
private void tryShowKeyboard() {
|
||||
if (hasWindowFocus() && wantsToShowKeyboard) {
|
||||
if (isFocused()) {
|
||||
CustomEditText that = this;
|
||||
post(() -> {
|
||||
final InputMethodManager imm = (InputMethodManager)
|
||||
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.showSoftInput(that, 0);
|
||||
});
|
||||
}
|
||||
wantsToShowKeyboard = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ package net.minetest.minetest;
|
||||
import android.app.NativeActivity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
@@ -32,7 +31,6 @@ import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
@@ -40,6 +38,7 @@ import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
// Native code finds these methods by name (see porting_android.cpp).
|
||||
@@ -49,14 +48,12 @@ import java.util.Objects;
|
||||
public class GameActivity extends NativeActivity {
|
||||
static {
|
||||
System.loadLibrary("c++_shared");
|
||||
System.loadLibrary("Minetest");
|
||||
System.loadLibrary("minetest");
|
||||
}
|
||||
|
||||
private int messageReturnCode = -1;
|
||||
private String messageReturnValue = "";
|
||||
|
||||
public static native void putMessageBoxResult(String text);
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -64,11 +61,10 @@ public class GameActivity extends NativeActivity {
|
||||
}
|
||||
|
||||
private void makeFullScreen() {
|
||||
if (Build.VERSION.SDK_INT >= 19)
|
||||
this.getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
||||
this.getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,6 +80,17 @@ public class GameActivity extends NativeActivity {
|
||||
makeFullScreen();
|
||||
}
|
||||
|
||||
private native void saveSettings();
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
// Avoid losing setting changes in case the app is onDestroy()ed later.
|
||||
// Saving stuff in onStop() is recommended in the Android activity
|
||||
// lifecycle documentation.
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// Ignore the back press so Minetest can handle it
|
||||
@@ -99,21 +106,11 @@ public class GameActivity extends NativeActivity {
|
||||
container.setOrientation(LinearLayout.VERTICAL);
|
||||
builder.setView(container);
|
||||
AlertDialog alertDialog = builder.create();
|
||||
EditText editText;
|
||||
// For multi-line, do not close the dialog after pressing back button
|
||||
if (editType == 1) {
|
||||
editText = new EditText(this);
|
||||
} else {
|
||||
editText = new CustomEditText(this);
|
||||
}
|
||||
CustomEditText editText = new CustomEditText(this, editType);
|
||||
container.addView(editText);
|
||||
editText.setMaxLines(8);
|
||||
editText.requestFocus();
|
||||
editText.setHint(hint);
|
||||
editText.setText(current);
|
||||
final InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
Objects.requireNonNull(imm).toggleSoftInput(InputMethodManager.SHOW_FORCED,
|
||||
InputMethodManager.HIDE_IMPLICIT_ONLY);
|
||||
if (editType == 1)
|
||||
editText.setInputType(InputType.TYPE_CLASS_TEXT |
|
||||
InputType.TYPE_TEXT_FLAG_MULTI_LINE);
|
||||
@@ -122,7 +119,8 @@ public class GameActivity extends NativeActivity {
|
||||
InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
else
|
||||
editText.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
editText.setSelection(editText.getText().length());
|
||||
editText.setSelection(Objects.requireNonNull(editText.getText()).length());
|
||||
final InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
editText.setOnKeyListener((view, keyCode, event) -> {
|
||||
// For multi-line, do not submit the text after pressing Enter key
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER && editType != 1) {
|
||||
@@ -146,12 +144,13 @@ public class GameActivity extends NativeActivity {
|
||||
alertDialog.dismiss();
|
||||
}));
|
||||
}
|
||||
alertDialog.show();
|
||||
alertDialog.setOnCancelListener(dialog -> {
|
||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||
messageReturnValue = current;
|
||||
messageReturnCode = -1;
|
||||
});
|
||||
alertDialog.show();
|
||||
editText.requestFocusTryShow();
|
||||
}
|
||||
|
||||
public int getDialogState() {
|
||||
@@ -205,4 +204,28 @@ public class GameActivity extends NativeActivity {
|
||||
Intent shareIntent = Intent.createChooser(intent, null);
|
||||
startActivity(shareIntent);
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
String langCode = Locale.getDefault().getLanguage();
|
||||
|
||||
// getLanguage() still uses old language codes to preserve compatibility.
|
||||
// List of code changes in ISO 639-2:
|
||||
// https://www.loc.gov/standards/iso639-2/php/code_changes.php
|
||||
switch (langCode) {
|
||||
case "in":
|
||||
langCode = "id"; // Indonesian
|
||||
break;
|
||||
case "iw":
|
||||
langCode = "he"; // Hebrew
|
||||
break;
|
||||
case "ji":
|
||||
langCode = "yi"; // Yiddish
|
||||
break;
|
||||
case "jw":
|
||||
langCode = "jv"; // Javanese
|
||||
break;
|
||||
}
|
||||
|
||||
return langCode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,38 +20,30 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
package net.minetest.minetest;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static net.minetest.minetest.UnzipService.*;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
public static final String NOTIFICATION_CHANNEL_ID = "Minetest channel";
|
||||
|
||||
private final static int versionCode = BuildConfig.VERSION_CODE;
|
||||
private final static int PERMISSIONS = 1;
|
||||
private static final String[] REQUIRED_SDK_PERMISSIONS =
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
||||
private static final String SETTINGS = "MinetestSettings";
|
||||
private static final String TAG_VERSION_CODE = "versionCode";
|
||||
|
||||
@@ -95,63 +87,21 @@ public class MainActivity extends AppCompatActivity {
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
IntentFilter filter = new IntentFilter(ACTION_UPDATE);
|
||||
registerReceiver(myReceiver, filter);
|
||||
|
||||
mProgressBar = findViewById(R.id.progressBar);
|
||||
mTextView = findViewById(R.id.textView);
|
||||
sharedPreferences = getSharedPreferences(SETTINGS, Context.MODE_PRIVATE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
|
||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
|
||||
checkPermission();
|
||||
else
|
||||
checkAppVersion();
|
||||
}
|
||||
checkAppVersion();
|
||||
|
||||
private void checkPermission() {
|
||||
final List<String> missingPermissions = new ArrayList<>();
|
||||
for (final String permission : REQUIRED_SDK_PERMISSIONS) {
|
||||
final int result = ContextCompat.checkSelfPermission(this, permission);
|
||||
if (result != PackageManager.PERMISSION_GRANTED)
|
||||
missingPermissions.add(permission);
|
||||
}
|
||||
if (!missingPermissions.isEmpty()) {
|
||||
final String[] permissions = missingPermissions
|
||||
.toArray(new String[0]);
|
||||
ActivityCompat.requestPermissions(this, permissions, PERMISSIONS);
|
||||
} else {
|
||||
final int[] grantResults = new int[REQUIRED_SDK_PERMISSIONS.length];
|
||||
Arrays.fill(grantResults, PackageManager.PERMISSION_GRANTED);
|
||||
onRequestPermissionsResult(PERMISSIONS, REQUIRED_SDK_PERMISSIONS, grantResults);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(
|
||||
int requestCode,
|
||||
@NonNull String[] permissions,
|
||||
@NonNull int[] grantResults
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (requestCode == PERMISSIONS) {
|
||||
for (int grantResult : grantResults) {
|
||||
if (grantResult != PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(this, R.string.not_granted, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
checkAppVersion();
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
createNotificationChannel();
|
||||
}
|
||||
|
||||
private void checkAppVersion() {
|
||||
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
Toast.makeText(this, R.string.no_external_storage, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (UnzipService.getIsRunning()) {
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
mProgressBar.setIndeterminate(true);
|
||||
@@ -176,6 +126,28 @@ public class MainActivity extends AppCompatActivity {
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private void createNotificationChannel() {
|
||||
NotificationManager notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (notifyManager == null)
|
||||
return;
|
||||
|
||||
NotificationChannel notifyChannel = new NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID,
|
||||
getString(R.string.notification_channel_name),
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
);
|
||||
notifyChannel.setDescription(getString(R.string.notification_channel_description));
|
||||
// Configure the notification channel without sound set
|
||||
notifyChannel.setSound(null, null);
|
||||
notifyChannel.enableLights(false);
|
||||
notifyChannel.enableVibration(false);
|
||||
|
||||
// It is fine to always create the notification channel because creating a channel
|
||||
// with the same ID is the same as overriding it (only its name and description).
|
||||
notifyManager.createNotificationChannel(notifyChannel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// Prevent abrupt interruption when copy game files from assets
|
||||
|
||||
@@ -22,13 +22,11 @@ package net.minetest.minetest;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -59,9 +57,11 @@ public class UnzipService extends IntentService {
|
||||
private String failureMessage;
|
||||
|
||||
private static boolean isRunning = false;
|
||||
|
||||
public static synchronized boolean getIsRunning() {
|
||||
return isRunning;
|
||||
}
|
||||
|
||||
private static synchronized void setIsRunning(boolean v) {
|
||||
isRunning = v;
|
||||
}
|
||||
@@ -88,7 +88,6 @@ public class UnzipService extends IntentService {
|
||||
}
|
||||
}
|
||||
|
||||
migrate(notificationBuilder, userDataDirectory);
|
||||
unzip(notificationBuilder, zipFile, userDataDirectory);
|
||||
} catch (IOException e) {
|
||||
isSuccess = false;
|
||||
@@ -101,28 +100,13 @@ public class UnzipService extends IntentService {
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Notification.Builder createNotification() {
|
||||
String name = "net.minetest.minetest";
|
||||
String channelId = "Minetest channel";
|
||||
String description = "notifications from Minetest";
|
||||
Notification.Builder builder;
|
||||
if (mNotifyManager == null)
|
||||
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
int importance = NotificationManager.IMPORTANCE_LOW;
|
||||
NotificationChannel mChannel = null;
|
||||
if (mNotifyManager != null)
|
||||
mChannel = mNotifyManager.getNotificationChannel(channelId);
|
||||
if (mChannel == null) {
|
||||
mChannel = new NotificationChannel(channelId, name, importance);
|
||||
mChannel.setDescription(description);
|
||||
// Configure the notification channel, NO SOUND
|
||||
mChannel.setSound(null, null);
|
||||
mChannel.enableLights(false);
|
||||
mChannel.enableVibration(false);
|
||||
mNotifyManager.createNotificationChannel(mChannel);
|
||||
}
|
||||
builder = new Notification.Builder(this, channelId);
|
||||
builder = new Notification.Builder(this, MainActivity.NOTIFICATION_CHANNEL_ID);
|
||||
} else {
|
||||
builder = new Notification.Builder(this);
|
||||
}
|
||||
@@ -137,9 +121,9 @@ public class UnzipService extends IntentService {
|
||||
PendingIntent intent = PendingIntent.getActivity(this, 0,
|
||||
notificationIntent, pendingIntentFlag);
|
||||
|
||||
builder.setContentTitle(getString(R.string.notification_title))
|
||||
builder.setContentTitle(getString(R.string.unzip_notification_title))
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setContentText(getString(R.string.notification_description))
|
||||
.setContentText(getString(R.string.unzip_notification_description))
|
||||
.setContentIntent(intent)
|
||||
.setOngoing(true)
|
||||
.setProgress(0, 0, true);
|
||||
@@ -182,9 +166,9 @@ public class UnzipService extends IntentService {
|
||||
try {
|
||||
Process p = new ProcessBuilder("/system/bin/mv",
|
||||
src.getAbsolutePath(), dst.getAbsolutePath()).start();
|
||||
int exitcode = p.waitFor();
|
||||
if (exitcode != 0)
|
||||
throw new IOException("Move failed with exit code " + exitcode);
|
||||
int exitCode = p.waitFor();
|
||||
if (exitCode != 0)
|
||||
throw new IOException("Move failed with exit code " + exitCode);
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException("Move operation interrupted");
|
||||
}
|
||||
@@ -200,45 +184,7 @@ public class UnzipService extends IntentService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates user data from deprecated external storage to app scoped storage
|
||||
*/
|
||||
private void migrate(Notification.Builder notificationBuilder, File newLocation) throws IOException {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
return;
|
||||
}
|
||||
|
||||
File oldLocation = new File(Environment.getExternalStorageDirectory(), "Minetest");
|
||||
if (!oldLocation.isDirectory())
|
||||
return;
|
||||
|
||||
publishProgress(notificationBuilder, R.string.migrating, 0);
|
||||
if (!newLocation.mkdir()) {
|
||||
Log.e("UnzipService", "New installation folder cannot be made");
|
||||
}
|
||||
|
||||
String[] dirs = new String[] { "worlds", "games", "mods", "textures", "client" };
|
||||
for (int i = 0; i < dirs.length; i++) {
|
||||
publishProgress(notificationBuilder, R.string.migrating, 100 * i / dirs.length);
|
||||
File dir = new File(oldLocation, dirs[i]), dir2 = new File(newLocation, dirs[i]);
|
||||
if (dir.isDirectory() && !dir2.isDirectory()) {
|
||||
moveFileOrDir(dir, dir2);
|
||||
}
|
||||
}
|
||||
|
||||
for (String filename : new String[] { "minetest.conf" }) {
|
||||
File file = new File(oldLocation, filename), file2 = new File(newLocation, filename);
|
||||
if (file.isFile() && !file2.isFile()) {
|
||||
moveFileOrDir(file, file2);
|
||||
}
|
||||
}
|
||||
|
||||
if (!recursivelyDeleteDirectory(oldLocation)) {
|
||||
Log.w("UnzipService", "Old installation files cannot be deleted successfully");
|
||||
}
|
||||
}
|
||||
|
||||
private void publishProgress(@Nullable Notification.Builder notificationBuilder, @StringRes int message, int progress) {
|
||||
private void publishProgress(@Nullable Notification.Builder notificationBuilder, @StringRes int message, int progress) {
|
||||
Intent intentUpdate = new Intent(ACTION_UPDATE);
|
||||
intentUpdate.putExtra(ACTION_PROGRESS, progress);
|
||||
intentUpdate.putExtra(ACTION_PROGRESS_MESSAGE, message);
|
||||
|
||||
@@ -38,7 +38,6 @@ public class Utils {
|
||||
public static boolean isInstallValid(@NonNull Context context) {
|
||||
File userDataDirectory = getUserDataDirectory(context);
|
||||
return userDataDirectory.isDirectory() &&
|
||||
new File(userDataDirectory, "games").isDirectory() &&
|
||||
new File(userDataDirectory, "builtin").isDirectory() &&
|
||||
new File(userDataDirectory, "client").isDirectory() &&
|
||||
new File(userDataDirectory, "textures").isDirectory();
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="label">Minetest</string>
|
||||
<string name="loading">Loading…</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="notification_channel_name">General notification</string>
|
||||
<string name="notification_channel_description">Notifications from Minetest</string>
|
||||
<string name="unzip_notification_title">Loading Minetest</string>
|
||||
<string name="unzip_notification_description">Less than 1 minute…</string>
|
||||
<string name="ime_dialog_done">Done</string>
|
||||
<string name="no_external_storage">External storage isn\'t available. If you use an SDCard, please reinsert it. Otherwise, try restarting your phone or contacting the Minetest developers</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
project.ext.set("versionMajor", 5) // Version Major
|
||||
project.ext.set("versionMinor", 7) // Version Minor
|
||||
project.ext.set("versionPatch", 0) // Version Patch
|
||||
project.ext.set("versionExtra", "") // Version Extra
|
||||
project.ext.set("versionCode", 44) // Android Version Code
|
||||
project.ext.set("developmentBuild", 0) // Whether it is a development build, or a release
|
||||
project.ext.set("versionMinor", 8) // Version Minor
|
||||
project.ext.set("versionPatch", 1) // Version Patch
|
||||
// ^ keep in sync with cmake
|
||||
project.ext.set("versionCode", 48) // Android Version Code
|
||||
// NOTE: +2 after each release!
|
||||
// +1 for ARM and +1 for ARM64 APK's, because
|
||||
// each APK must have a larger `versionCode` than the previous
|
||||
|
||||
@@ -7,5 +7,5 @@ org.gradle.daemon=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.parallel.threads=8
|
||||
org.gradle.configureondemand=true
|
||||
android.enableJetifier=true
|
||||
android.enableJetifier=false
|
||||
android.useAndroidX=true
|
||||
|
||||
111
android/icons/exit_btn.svg
Normal file
111
android/icons/exit_btn.svg
Normal file
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 135.46666 135.46667"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||
sodipodi:docname="exit_btn.svg"
|
||||
inkscape:export-filename="../../textures/base/pack/exit_btn.png"
|
||||
inkscape:export-xdpi="24.000002"
|
||||
inkscape:export-ydpi="24.000002"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs10" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#404040"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.84958349"
|
||||
inkscape:cx="-94.752312"
|
||||
inkscape:cy="291.31922"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer2"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:pagecheckerboard="false"
|
||||
inkscape:snap-grids="true"
|
||||
inkscape:snap-page="true"
|
||||
showguides="false"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:deskcolor="#404040">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid16"
|
||||
spacingx="0.26458333"
|
||||
spacingy="0.26458333"
|
||||
empspacing="4"
|
||||
color="#40ff40"
|
||||
opacity="0.1254902"
|
||||
empcolor="#40ff40"
|
||||
empopacity="0.25098039" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Layer 2"
|
||||
style="display:inline">
|
||||
<path
|
||||
id="rect5028"
|
||||
style="display:inline;fill:none;stroke:#ffffff;stroke-width:5.99996;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
|
||||
d="m 78.052082,90.48746 v 17.4625 l -50.535415,4e-5 V 27.516667 l 50.535415,3.7e-5 v 17.462423"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
<path
|
||||
style="display:inline;fill:none;stroke:#ffffff;stroke-width:5.99996;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 101.49853,55.033202 12.69966,12.700052 -12.69966,12.699942"
|
||||
id="path4737"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
<path
|
||||
style="display:inline;fill:none;stroke:#ffffff;stroke-width:6;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 113.36416,67.733332 H 59.484405"
|
||||
id="path4729"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -9,20 +9,18 @@ android {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 33
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
arguments '-j' + Runtime.getRuntime().availableProcessors(),
|
||||
"versionMajor=${versionMajor}",
|
||||
"versionMinor=${versionMinor}",
|
||||
"versionPatch=${versionPatch}",
|
||||
"versionExtra=${versionExtra}",
|
||||
"developmentBuild=${developmentBuild}"
|
||||
cmake {
|
||||
arguments "-DANDROID_STL=c++_shared",
|
||||
"-DENABLE_CURL=1", "-DENABLE_SOUND=1",
|
||||
"-DENABLE_TOUCH=1", "-DENABLE_GETTEXT=1",
|
||||
"-DBUILD_UNITTESTS=0", "-DENABLE_UPDATE_CHECKER=0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
path file('jni/Android.mk')
|
||||
cmake {
|
||||
path file("../../CMakeLists.txt")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,12 +35,6 @@ android {
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
arguments 'NDEBUG=1'
|
||||
}
|
||||
}
|
||||
|
||||
ndk {
|
||||
debugSymbolLevel 'SYMBOL_TABLE'
|
||||
}
|
||||
@@ -51,19 +43,25 @@ android {
|
||||
}
|
||||
|
||||
// get precompiled deps
|
||||
task downloadDeps(type: Download) {
|
||||
def depsDir = new File(buildDir.parent, 'deps')
|
||||
def depsZip = new File(buildDir, 'deps.zip')
|
||||
def depsDir = new File(buildDir.parent, 'deps')
|
||||
if (new File(depsDir, 'armeabi-v7a').exists()) {
|
||||
task getDeps {
|
||||
doLast { logger.lifecycle('Using existing deps from {}', depsDir) }
|
||||
}
|
||||
} else {
|
||||
task downloadDeps(type: Download) {
|
||||
def depsZip = new File(buildDir, 'deps.zip')
|
||||
|
||||
src 'https://github.com/minetest/minetest_android_deps/releases/download/latest/deps.zip'
|
||||
dest depsZip
|
||||
overwrite false
|
||||
src 'https://github.com/minetest/minetest_android_deps/releases/download/5.8.0/deps.zip'
|
||||
dest depsZip
|
||||
overwrite false
|
||||
|
||||
task getDeps(dependsOn: downloadDeps, type: Copy) {
|
||||
depsDir.mkdir()
|
||||
from zipTree(depsZip)
|
||||
into depsDir
|
||||
doFirst { logger.lifecycle('Extracting to {}', depsDir) }
|
||||
task getDeps(dependsOn: downloadDeps, type: Copy) {
|
||||
depsDir.mkdir()
|
||||
from zipTree(depsZip)
|
||||
into depsDir
|
||||
doFirst { logger.lifecycle('Extracting to {}', depsDir) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,302 +0,0 @@
|
||||
LOCAL_PATH := $(call my-dir)/..
|
||||
|
||||
#LOCAL_ADDRESS_SANITIZER:=true
|
||||
#USE_BUILTIN_LUA:=true
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Curl
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Curl/libcurl.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libmbedcrypto
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Curl/libmbedcrypto.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libmbedtls
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Curl/libmbedtls.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libmbedx509
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Curl/libmbedx509.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Freetype
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Freetype/libfreetype.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Iconv
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Iconv/libiconv.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libcharset
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Iconv/libcharset.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Irrlicht
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Irrlicht/libIrrlichtMt.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Irrlicht-libpng
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Irrlicht/libpng.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Irrlicht-libjpeg
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Irrlicht/libjpeg.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
ifndef USE_BUILTIN_LUA
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := LuaJIT
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/LuaJIT/libluajit.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
endif
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := OpenAL
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/OpenAL-Soft/libopenal.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Gettext
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Gettext/libintl.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := SQLite3
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/SQLite/libsqlite3.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Vorbis
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Vorbis/libvorbis.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libvorbisfile
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Vorbis/libvorbisfile.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libogg
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Vorbis/libogg.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Zstd
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Zstd/libzstd.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Minetest
|
||||
|
||||
LOCAL_CFLAGS += \
|
||||
-DJSONCPP_NO_LOCALE_SUPPORT \
|
||||
-DHAVE_TOUCHSCREENGUI \
|
||||
-DENABLE_GLES=1 \
|
||||
-DUSE_CURL=1 \
|
||||
-DUSE_SOUND=1 \
|
||||
-DUSE_LEVELDB=0 \
|
||||
-DUSE_GETTEXT=1 \
|
||||
-DVERSION_MAJOR=${versionMajor} \
|
||||
-DVERSION_MINOR=${versionMinor} \
|
||||
-DVERSION_PATCH=${versionPatch} \
|
||||
-DVERSION_EXTRA=${versionExtra} \
|
||||
-DDEVELOPMENT_BUILD=${developmentBuild} \
|
||||
$(GPROF_DEF)
|
||||
|
||||
ifdef USE_BUILTIN_LUA
|
||||
LOCAL_CFLAGS += -DUSE_LUAJIT=0
|
||||
else
|
||||
LOCAL_CFLAGS += -DUSE_LUAJIT=1
|
||||
endif
|
||||
|
||||
ifdef NDEBUG
|
||||
LOCAL_CFLAGS += -DNDEBUG=1
|
||||
endif
|
||||
|
||||
ifdef GPROF
|
||||
GPROF_DEF := -DGPROF
|
||||
PROFILER_LIBS := android-ndk-profiler
|
||||
LOCAL_CFLAGS += -pg
|
||||
endif
|
||||
|
||||
LOCAL_C_INCLUDES := \
|
||||
../../src \
|
||||
../../src/script \
|
||||
../../lib/gmp \
|
||||
../../lib/jsoncpp \
|
||||
deps/$(APP_ABI)/Curl/include \
|
||||
deps/$(APP_ABI)/Freetype/include/freetype2 \
|
||||
deps/$(APP_ABI)/Irrlicht/include \
|
||||
deps/$(APP_ABI)/Gettext/include \
|
||||
deps/$(APP_ABI)/Iconv/include \
|
||||
deps/$(APP_ABI)/OpenAL-Soft/include \
|
||||
deps/$(APP_ABI)/SQLite/include \
|
||||
deps/$(APP_ABI)/Vorbis/include \
|
||||
deps/$(APP_ABI)/Zstd/include
|
||||
|
||||
ifdef USE_BUILTIN_LUA
|
||||
LOCAL_C_INCLUDES += \
|
||||
../../lib/lua/src \
|
||||
../../lib/bitop
|
||||
else
|
||||
LOCAL_C_INCLUDES += deps/$(APP_ABI)/LuaJIT/include
|
||||
endif
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
$(wildcard ../../src/client/*.cpp) \
|
||||
$(wildcard ../../src/client/*/*.cpp) \
|
||||
$(wildcard ../../src/content/*.cpp) \
|
||||
../../src/database/database.cpp \
|
||||
../../src/database/database-dummy.cpp \
|
||||
../../src/database/database-files.cpp \
|
||||
../../src/database/database-sqlite3.cpp \
|
||||
$(wildcard ../../src/gui/*.cpp) \
|
||||
$(wildcard ../../src/irrlicht_changes/*.cpp) \
|
||||
$(wildcard ../../src/mapgen/*.cpp) \
|
||||
$(wildcard ../../src/network/*.cpp) \
|
||||
$(wildcard ../../src/script/*.cpp) \
|
||||
$(wildcard ../../src/script/*/*.cpp) \
|
||||
$(wildcard ../../src/server/*.cpp) \
|
||||
$(wildcard ../../src/threading/*.cpp) \
|
||||
$(wildcard ../../src/util/*.c) \
|
||||
$(wildcard ../../src/util/*.cpp) \
|
||||
../../src/ban.cpp \
|
||||
../../src/chat.cpp \
|
||||
../../src/clientiface.cpp \
|
||||
../../src/collision.cpp \
|
||||
../../src/content_mapnode.cpp \
|
||||
../../src/content_nodemeta.cpp \
|
||||
../../src/convert_json.cpp \
|
||||
../../src/craftdef.cpp \
|
||||
../../src/debug.cpp \
|
||||
../../src/defaultsettings.cpp \
|
||||
../../src/emerge.cpp \
|
||||
../../src/environment.cpp \
|
||||
../../src/face_position_cache.cpp \
|
||||
../../src/filesys.cpp \
|
||||
../../src/gettext.cpp \
|
||||
../../src/httpfetch.cpp \
|
||||
../../src/hud.cpp \
|
||||
../../src/inventory.cpp \
|
||||
../../src/inventorymanager.cpp \
|
||||
../../src/itemdef.cpp \
|
||||
../../src/itemstackmetadata.cpp \
|
||||
../../src/light.cpp \
|
||||
../../src/lighting.cpp \
|
||||
../../src/log.cpp \
|
||||
../../src/main.cpp \
|
||||
../../src/map.cpp \
|
||||
../../src/map_settings_manager.cpp \
|
||||
../../src/mapblock.cpp \
|
||||
../../src/mapnode.cpp \
|
||||
../../src/mapsector.cpp \
|
||||
../../src/metadata.cpp \
|
||||
../../src/modchannels.cpp \
|
||||
../../src/nameidmapping.cpp \
|
||||
../../src/nodedef.cpp \
|
||||
../../src/nodemetadata.cpp \
|
||||
../../src/nodetimer.cpp \
|
||||
../../src/noise.cpp \
|
||||
../../src/objdef.cpp \
|
||||
../../src/object_properties.cpp \
|
||||
../../src/particles.cpp \
|
||||
../../src/pathfinder.cpp \
|
||||
../../src/player.cpp \
|
||||
../../src/porting.cpp \
|
||||
../../src/porting_android.cpp \
|
||||
../../src/profiler.cpp \
|
||||
../../src/raycast.cpp \
|
||||
../../src/reflowscan.cpp \
|
||||
../../src/remoteplayer.cpp \
|
||||
../../src/rollback.cpp \
|
||||
../../src/rollback_interface.cpp \
|
||||
../../src/serialization.cpp \
|
||||
../../src/server.cpp \
|
||||
../../src/serverenvironment.cpp \
|
||||
../../src/serverlist.cpp \
|
||||
../../src/settings.cpp \
|
||||
../../src/staticobject.cpp \
|
||||
../../src/texture_override.cpp \
|
||||
../../src/tileanimation.cpp \
|
||||
../../src/tool.cpp \
|
||||
../../src/translation.cpp \
|
||||
../../src/version.cpp \
|
||||
../../src/voxel.cpp \
|
||||
../../src/voxelalgorithms.cpp
|
||||
|
||||
# Built-in Lua
|
||||
ifdef USE_BUILTIN_LUA
|
||||
LOCAL_SRC_FILES += \
|
||||
../../lib/lua/src/lapi.c \
|
||||
../../lib/lua/src/lauxlib.c \
|
||||
../../lib/lua/src/lbaselib.c \
|
||||
../../lib/lua/src/lcode.c \
|
||||
../../lib/lua/src/ldblib.c \
|
||||
../../lib/lua/src/ldebug.c \
|
||||
../../lib/lua/src/ldo.c \
|
||||
../../lib/lua/src/ldump.c \
|
||||
../../lib/lua/src/lfunc.c \
|
||||
../../lib/lua/src/lgc.c \
|
||||
../../lib/lua/src/linit.c \
|
||||
../../lib/lua/src/liolib.c \
|
||||
../../lib/lua/src/llex.c \
|
||||
../../lib/lua/src/lmathlib.c \
|
||||
../../lib/lua/src/lmem.c \
|
||||
../../lib/lua/src/loadlib.c \
|
||||
../../lib/lua/src/lobject.c \
|
||||
../../lib/lua/src/lopcodes.c \
|
||||
../../lib/lua/src/loslib.c \
|
||||
../../lib/lua/src/lparser.c \
|
||||
../../lib/lua/src/lstate.c \
|
||||
../../lib/lua/src/lstring.c \
|
||||
../../lib/lua/src/lstrlib.c \
|
||||
../../lib/lua/src/ltable.c \
|
||||
../../lib/lua/src/ltablib.c \
|
||||
../../lib/lua/src/ltm.c \
|
||||
../../lib/lua/src/lundump.c \
|
||||
../../lib/lua/src/lvm.c \
|
||||
../../lib/lua/src/lzio.c \
|
||||
../../lib/bitop/bit.c
|
||||
endif
|
||||
|
||||
# GMP
|
||||
LOCAL_SRC_FILES += ../../lib/gmp/mini-gmp.c
|
||||
|
||||
# JSONCPP
|
||||
LOCAL_SRC_FILES += ../../lib/jsoncpp/jsoncpp.cpp
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += \
|
||||
Curl libmbedcrypto libmbedtls libmbedx509 \
|
||||
Freetype \
|
||||
Iconv libcharset \
|
||||
Irrlicht Irrlicht-libpng Irrlicht-libjpeg \
|
||||
OpenAL \
|
||||
Gettext \
|
||||
SQLite3 \
|
||||
Vorbis libvorbisfile libogg \
|
||||
Zstd
|
||||
ifndef USE_BUILTIN_LUA
|
||||
LOCAL_STATIC_LIBRARIES += LuaJIT
|
||||
endif
|
||||
LOCAL_STATIC_LIBRARIES += android_native_app_glue $(PROFILER_LIBS)
|
||||
|
||||
LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES -lz
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
ifdef GPROF
|
||||
$(call import-module,android-ndk-profiler)
|
||||
endif
|
||||
$(call import-module,android/native_app_glue)
|
||||
@@ -1,32 +0,0 @@
|
||||
APP_PLATFORM := ${APP_PLATFORM}
|
||||
APP_ABI := ${TARGET_ABI}
|
||||
APP_STL := c++_shared
|
||||
NDK_TOOLCHAIN_VERSION := clang
|
||||
APP_SHORT_COMMANDS := true
|
||||
APP_MODULES := Minetest
|
||||
|
||||
APP_CPPFLAGS := -O2 -fvisibility=hidden
|
||||
|
||||
ifeq ($(APP_ABI),armeabi-v7a)
|
||||
APP_CPPFLAGS += -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb
|
||||
endif
|
||||
|
||||
ifeq ($(APP_ABI),x86)
|
||||
APP_CPPFLAGS += -mssse3 -mfpmath=sse -funroll-loops
|
||||
endif
|
||||
|
||||
ifndef NDEBUG
|
||||
APP_CPPFLAGS := -g -Og -fno-omit-frame-pointer
|
||||
endif
|
||||
|
||||
APP_CFLAGS := $(APP_CPPFLAGS) -Wno-inconsistent-missing-override -Wno-parentheses-equality
|
||||
APP_CXXFLAGS := $(APP_CPPFLAGS) -fexceptions -frtti -std=gnu++14
|
||||
APP_LDFLAGS := -Wl,--no-warn-mismatch,--gc-sections,--icf=safe
|
||||
|
||||
ifeq ($(APP_ABI),arm64-v8a)
|
||||
APP_LDFLAGS := -Wl,--no-warn-mismatch,--gc-sections
|
||||
endif
|
||||
|
||||
ifndef NDEBUG
|
||||
APP_LDFLAGS :=
|
||||
endif
|
||||
@@ -36,11 +36,16 @@ do
|
||||
setmetatable(v, {__newindex = {}})
|
||||
-- Reassemble the other tables
|
||||
if v.type == "node" then
|
||||
getmetatable(v).__index = all.nodedef_default
|
||||
all.registered_nodes[k] = v
|
||||
elseif v.type == "craftitem" then
|
||||
elseif v.type == "craft" then
|
||||
getmetatable(v).__index = all.craftitemdef_default
|
||||
all.registered_craftitems[k] = v
|
||||
elseif v.type == "tool" then
|
||||
getmetatable(v).__index = all.tooldef_default
|
||||
all.registered_tools[k] = v
|
||||
else
|
||||
getmetatable(v).__index = all.noneitemdef_default
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -5,3 +5,14 @@ function core.setting_get_pos(name)
|
||||
end
|
||||
return core.string_to_pos(value)
|
||||
end
|
||||
|
||||
|
||||
-- old non-method sound functions
|
||||
|
||||
function core.sound_stop(handle, ...)
|
||||
return handle:stop(...)
|
||||
end
|
||||
|
||||
function core.sound_fade(handle, ...)
|
||||
return handle:fade(...)
|
||||
end
|
||||
|
||||
74
builtin/common/register.lua
Normal file
74
builtin/common/register.lua
Normal file
@@ -0,0 +1,74 @@
|
||||
local builtin_shared = ...
|
||||
|
||||
do
|
||||
local default = {mod = "??", name = "??"}
|
||||
core.callback_origins = setmetatable({}, {
|
||||
__index = function()
|
||||
return default
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
function core.run_callbacks(callbacks, mode, ...)
|
||||
assert(type(callbacks) == "table")
|
||||
local cb_len = #callbacks
|
||||
if cb_len == 0 then
|
||||
if mode == 2 or mode == 3 then
|
||||
return true
|
||||
elseif mode == 4 or mode == 5 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
local ret = nil
|
||||
for i = 1, cb_len do
|
||||
local origin = core.callback_origins[callbacks[i]]
|
||||
core.set_last_run_mod(origin.mod)
|
||||
local cb_ret = callbacks[i](...)
|
||||
|
||||
if mode == 0 and i == 1 then
|
||||
ret = cb_ret
|
||||
elseif mode == 1 and i == cb_len then
|
||||
ret = cb_ret
|
||||
elseif mode == 2 then
|
||||
if not cb_ret or i == 1 then
|
||||
ret = cb_ret
|
||||
end
|
||||
elseif mode == 3 then
|
||||
if cb_ret then
|
||||
return cb_ret
|
||||
end
|
||||
ret = cb_ret
|
||||
elseif mode == 4 then
|
||||
if (cb_ret and not ret) or i == 1 then
|
||||
ret = cb_ret
|
||||
end
|
||||
elseif mode == 5 and cb_ret then
|
||||
return cb_ret
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function builtin_shared.make_registration()
|
||||
local t = {}
|
||||
local registerfunc = function(func)
|
||||
t[#t + 1] = func
|
||||
core.callback_origins[func] = {
|
||||
mod = core.get_current_modname() or "??",
|
||||
name = debug.getinfo(1, "n").name or "??"
|
||||
}
|
||||
end
|
||||
return t, registerfunc
|
||||
end
|
||||
|
||||
function builtin_shared.make_registration_reverse()
|
||||
local t = {}
|
||||
local registerfunc = function(func)
|
||||
table.insert(t, 1, func)
|
||||
core.callback_origins[func] = {
|
||||
mod = core.get_current_modname() or "??",
|
||||
name = debug.getinfo(1, "n").name or "??"
|
||||
}
|
||||
end
|
||||
return t, registerfunc
|
||||
end
|
||||
@@ -462,4 +462,11 @@ describe("vector", function()
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
it("in_area()", function()
|
||||
assert.True(vector.in_area(vector.zero(), vector.new(-10, -10, -10), vector.new(10, 10, 10)))
|
||||
assert.True(vector.in_area(vector.new(-2, 5, -8), vector.new(-10, -10, -10), vector.new(10, 10, 10)))
|
||||
assert.True(vector.in_area(vector.new(-10, -10, -10), vector.new(-10, -10, -10), vector.new(10, 10, 10)))
|
||||
assert.False(vector.in_area(vector.new(-10, -10, -10), vector.new(10, 10, 10), vector.new(-11, -10, -10)))
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -369,6 +369,12 @@ function vector.dir_to_rotation(forward, up)
|
||||
return rot
|
||||
end
|
||||
|
||||
function vector.in_area(pos, min, max)
|
||||
return (pos.x >= min.x) and (pos.x <= max.x) and
|
||||
(pos.y >= min.y) and (pos.y <= max.y) and
|
||||
(pos.z >= min.z) and (pos.z <= max.z)
|
||||
end
|
||||
|
||||
if rawget(_G, "core") and core.set_read_vector and core.set_push_vector then
|
||||
local function read_vector(v)
|
||||
return v.x, v.y, v.z
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
--Minetest
|
||||
--Copyright (C) 2014 sapier
|
||||
--Copyright (C) 2023 Gregor Parzefall
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -16,115 +17,101 @@
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
|
||||
local function buttonbar_formspec(self)
|
||||
local BASE_SPACING = 0.1
|
||||
local SCROLL_BTN_WIDTH = TOUCHSCREEN_GUI and 0.8 or 0.5
|
||||
|
||||
local function buttonbar_formspec(self)
|
||||
if self.hidden then
|
||||
return ""
|
||||
end
|
||||
|
||||
local formspec = string.format("box[%f,%f;%f,%f;%s]",
|
||||
self.pos.x,self.pos.y ,self.size.x,self.size.y,self.bgcolor)
|
||||
local formspec = {
|
||||
"style_type[box;noclip=true]",
|
||||
string.format("box[%f,%f;%f,%f;%s]", self.pos.x, self.pos.y, self.size.x,
|
||||
self.size.y, self.bgcolor),
|
||||
"style_type[box;noclip=false]",
|
||||
}
|
||||
|
||||
for i=self.startbutton,#self.buttons,1 do
|
||||
local btn_name = self.buttons[i].name
|
||||
local btn_pos = {}
|
||||
local btn_size = self.size.y - 2*BASE_SPACING
|
||||
|
||||
if self.orientation == "horizontal" then
|
||||
btn_pos.x = self.pos.x + --base pos
|
||||
(i - self.startbutton) * self.btn_size + --button offset
|
||||
self.btn_initial_offset
|
||||
else
|
||||
btn_pos.x = self.pos.x + (self.btn_size * 0.05)
|
||||
end
|
||||
-- Spacing works like CSS Flexbox with `justify-content: space-evenly;`.
|
||||
-- `BASE_SPACING` is used as the minimum spacing, like `gap` in CSS Flexbox.
|
||||
|
||||
if self.orientation == "vertical" then
|
||||
btn_pos.y = self.pos.y + --base pos
|
||||
(i - self.startbutton) * self.btn_size + --button offset
|
||||
self.btn_initial_offset
|
||||
else
|
||||
btn_pos.y = self.pos.y + (self.btn_size * 0.05)
|
||||
end
|
||||
-- The number of buttons per page is always calculated as if the scroll
|
||||
-- buttons were visible.
|
||||
local avail_space = self.size.x - 2*BASE_SPACING - 2*SCROLL_BTN_WIDTH
|
||||
local btns_per_page = math.floor((avail_space - BASE_SPACING) / (btn_size + BASE_SPACING))
|
||||
|
||||
if (self.orientation == "vertical" and
|
||||
(btn_pos.y + self.btn_size <= self.pos.y + self.size.y)) or
|
||||
(self.orientation == "horizontal" and
|
||||
(btn_pos.x + self.btn_size <= self.pos.x + self.size.x)) then
|
||||
self.num_pages = math.ceil(#self.buttons / btns_per_page)
|
||||
self.cur_page = math.min(self.cur_page, self.num_pages)
|
||||
local first_btn = (self.cur_page - 1) * btns_per_page + 1
|
||||
|
||||
local borders="true"
|
||||
local show_scroll_btns = self.num_pages > 1
|
||||
|
||||
if self.buttons[i].image ~= nil then
|
||||
borders="false"
|
||||
end
|
||||
-- In contrast, the button spacing calculation takes hidden scroll buttons
|
||||
-- into account.
|
||||
local real_avail_space = show_scroll_btns and avail_space or self.size.x
|
||||
local btn_spacing = (real_avail_space - btns_per_page * btn_size) / (btns_per_page + 1)
|
||||
|
||||
formspec = formspec ..
|
||||
string.format("image_button[%f,%f;%f,%f;%s;%s;%s;true;%s]tooltip[%s;%s]",
|
||||
btn_pos.x, btn_pos.y, self.btn_size, self.btn_size,
|
||||
self.buttons[i].image, btn_name, self.buttons[i].caption,
|
||||
borders, btn_name, self.buttons[i].tooltip)
|
||||
else
|
||||
--print("end of displayable buttons: orientation: " .. self.orientation)
|
||||
--print( "button_end: " .. (btn_pos.y + self.btn_size - (self.btn_size * 0.05)))
|
||||
--print( "bar_end: " .. (self.pos.x + self.size.x))
|
||||
local btn_start_x = self.pos.x + btn_spacing
|
||||
if show_scroll_btns then
|
||||
btn_start_x = btn_start_x + BASE_SPACING + SCROLL_BTN_WIDTH
|
||||
end
|
||||
|
||||
for i = first_btn, first_btn + btns_per_page - 1 do
|
||||
local btn = self.buttons[i]
|
||||
if btn == nil then
|
||||
break
|
||||
end
|
||||
|
||||
local btn_pos = {
|
||||
x = btn_start_x + (i - first_btn) * (btn_size + btn_spacing),
|
||||
y = self.pos.y + BASE_SPACING,
|
||||
}
|
||||
|
||||
table.insert(formspec, string.format("image_button[%f,%f;%f,%f;%s;%s;%s;true;false]tooltip[%s;%s]",
|
||||
btn_pos.x, btn_pos.y, btn_size, btn_size, btn.image, btn.name,
|
||||
btn.caption, btn.name, btn.tooltip))
|
||||
end
|
||||
|
||||
if (self.have_move_buttons) then
|
||||
local btn_dec_pos = {}
|
||||
btn_dec_pos.x = self.pos.x + (self.btn_size * 0.05)
|
||||
btn_dec_pos.y = self.pos.y + (self.btn_size * 0.05)
|
||||
local btn_inc_pos = {}
|
||||
local btn_size = {}
|
||||
if show_scroll_btns then
|
||||
local btn_prev_pos = {
|
||||
x = self.pos.x + BASE_SPACING,
|
||||
y = self.pos.y + BASE_SPACING,
|
||||
}
|
||||
local btn_next_pos = {
|
||||
x = self.pos.x + self.size.x - BASE_SPACING - SCROLL_BTN_WIDTH,
|
||||
y = self.pos.y + BASE_SPACING,
|
||||
}
|
||||
|
||||
if self.orientation == "horizontal" then
|
||||
btn_size.x = 0.5
|
||||
btn_size.y = self.btn_size
|
||||
btn_inc_pos.x = self.pos.x + self.size.x - 0.5
|
||||
btn_inc_pos.y = self.pos.y + (self.btn_size * 0.05)
|
||||
else
|
||||
btn_size.x = self.btn_size
|
||||
btn_size.y = 0.5
|
||||
btn_inc_pos.x = self.pos.x + (self.btn_size * 0.05)
|
||||
btn_inc_pos.y = self.pos.y + self.size.y - 0.5
|
||||
end
|
||||
table.insert(formspec, string.format("style[%s,%s;noclip=true]",
|
||||
self.btn_prev_name, self.btn_next_name))
|
||||
|
||||
local text_dec = "<"
|
||||
local text_inc = ">"
|
||||
if self.orientation == "vertical" then
|
||||
text_dec = "^"
|
||||
text_inc = "v"
|
||||
end
|
||||
table.insert(formspec, string.format("button[%f,%f;%f,%f;%s;<]",
|
||||
btn_prev_pos.x, btn_prev_pos.y, SCROLL_BTN_WIDTH, btn_size,
|
||||
self.btn_prev_name))
|
||||
|
||||
formspec = formspec ..
|
||||
string.format("image_button[%f,%f;%f,%f;;btnbar_dec_%s;%s;true;true]",
|
||||
btn_dec_pos.x, btn_dec_pos.y, btn_size.x, btn_size.y,
|
||||
self.name, text_dec)
|
||||
|
||||
formspec = formspec ..
|
||||
string.format("image_button[%f,%f;%f,%f;;btnbar_inc_%s;%s;true;true]",
|
||||
btn_inc_pos.x, btn_inc_pos.y, btn_size.x, btn_size.y,
|
||||
self.name, text_inc)
|
||||
table.insert(formspec, string.format("button[%f,%f;%f,%f;%s;>]",
|
||||
btn_next_pos.x, btn_next_pos.y, SCROLL_BTN_WIDTH, btn_size,
|
||||
self.btn_next_name))
|
||||
end
|
||||
|
||||
return formspec
|
||||
return table.concat(formspec)
|
||||
end
|
||||
|
||||
local function buttonbar_buttonhandler(self, fields)
|
||||
|
||||
if fields["btnbar_inc_" .. self.name] ~= nil and
|
||||
self.startbutton < #self.buttons then
|
||||
|
||||
self.startbutton = self.startbutton + 1
|
||||
if fields[self.btn_prev_name] and self.cur_page > 1 then
|
||||
self.cur_page = self.cur_page - 1
|
||||
return true
|
||||
end
|
||||
|
||||
if fields["btnbar_dec_" .. self.name] ~= nil and self.startbutton > 1 then
|
||||
self.startbutton = self.startbutton - 1
|
||||
if fields[self.btn_next_name] and self.cur_page < self.num_pages then
|
||||
self.cur_page = self.cur_page + 1
|
||||
return true
|
||||
end
|
||||
|
||||
for i=1,#self.buttons,1 do
|
||||
if fields[self.buttons[i].name] ~= nil then
|
||||
for _, btn in ipairs(self.buttons) do
|
||||
if fields[btn.name] then
|
||||
return self.userbuttonhandler(fields)
|
||||
end
|
||||
end
|
||||
@@ -141,74 +128,45 @@ local buttonbar_metatable = {
|
||||
delete = function(self) ui.delete(self) end,
|
||||
|
||||
add_button = function(self, name, caption, image, tooltip)
|
||||
if caption == nil then caption = "" end
|
||||
if image == nil then image = "" end
|
||||
if tooltip == nil then tooltip = "" end
|
||||
if caption == nil then caption = "" end
|
||||
if image == nil then image = "" end
|
||||
if tooltip == nil then tooltip = "" end
|
||||
|
||||
self.buttons[#self.buttons + 1] = {
|
||||
name = name,
|
||||
caption = caption,
|
||||
image = image,
|
||||
tooltip = tooltip
|
||||
}
|
||||
if self.orientation == "horizontal" then
|
||||
if ( (self.btn_size * #self.buttons) + (self.btn_size * 0.05 *2)
|
||||
> self.size.x ) then
|
||||
|
||||
self.btn_initial_offset = self.btn_size * 0.05 + 0.5
|
||||
self.have_move_buttons = true
|
||||
end
|
||||
else
|
||||
if ((self.btn_size * #self.buttons) + (self.btn_size * 0.05 *2)
|
||||
> self.size.y ) then
|
||||
|
||||
self.btn_initial_offset = self.btn_size * 0.05 + 0.5
|
||||
self.have_move_buttons = true
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
set_bgparams = function(self, bgcolor)
|
||||
if (type(bgcolor) == "string") then
|
||||
self.bgcolor = bgcolor
|
||||
end
|
||||
end,
|
||||
table.insert(self.buttons, {
|
||||
name = name,
|
||||
caption = caption,
|
||||
image = image,
|
||||
tooltip = tooltip,
|
||||
})
|
||||
end,
|
||||
}
|
||||
|
||||
buttonbar_metatable.__index = buttonbar_metatable
|
||||
|
||||
function buttonbar_create(name, cbf_buttonhandler, pos, orientation, size)
|
||||
assert(name ~= nil)
|
||||
assert(cbf_buttonhandler ~= nil)
|
||||
assert(orientation == "vertical" or orientation == "horizontal")
|
||||
assert(pos ~= nil and type(pos) == "table")
|
||||
assert(size ~= nil and type(size) == "table")
|
||||
function buttonbar_create(name, pos, size, bgcolor, cbf_buttonhandler)
|
||||
assert(type(name) == "string" )
|
||||
assert(type(pos) == "table" )
|
||||
assert(type(size) == "table" )
|
||||
assert(type(bgcolor) == "string" )
|
||||
assert(type(cbf_buttonhandler) == "function")
|
||||
|
||||
local self = {}
|
||||
self.name = name
|
||||
self.type = "addon"
|
||||
self.bgcolor = "#000000"
|
||||
self.name = name
|
||||
self.pos = pos
|
||||
self.size = size
|
||||
self.orientation = orientation
|
||||
self.startbutton = 1
|
||||
self.have_move_buttons = false
|
||||
self.hidden = false
|
||||
|
||||
if self.orientation == "horizontal" then
|
||||
self.btn_size = self.size.y
|
||||
else
|
||||
self.btn_size = self.size.x
|
||||
end
|
||||
|
||||
if (self.btn_initial_offset == nil) then
|
||||
self.btn_initial_offset = self.btn_size * 0.05
|
||||
end
|
||||
|
||||
self.bgcolor = bgcolor
|
||||
self.userbuttonhandler = cbf_buttonhandler
|
||||
self.buttons = {}
|
||||
|
||||
setmetatable(self,buttonbar_metatable)
|
||||
self.hidden = false
|
||||
self.buttons = {}
|
||||
self.num_pages = 1
|
||||
self.cur_page = 1
|
||||
|
||||
self.btn_prev_name = "btnbar_prev_" .. self.name
|
||||
self.btn_next_name = "btnbar_next_" .. self.name
|
||||
|
||||
setmetatable(self, buttonbar_metatable)
|
||||
|
||||
ui.add(self)
|
||||
return self
|
||||
|
||||
@@ -38,8 +38,18 @@ local dialog_metatable = {
|
||||
handle_events = function(self,event)
|
||||
if not self.hidden then return self.eventhandler(self,event) end
|
||||
end,
|
||||
hide = function(self) self.hidden = true end,
|
||||
show = function(self) self.hidden = false end,
|
||||
hide = function(self)
|
||||
if not self.hidden then
|
||||
self.hidden = true
|
||||
self.eventhandler(self, "DialogHide")
|
||||
end
|
||||
end,
|
||||
show = function(self)
|
||||
if self.hidden then
|
||||
self.hidden = false
|
||||
self.eventhandler(self, "DialogShow")
|
||||
end
|
||||
end,
|
||||
delete = function(self)
|
||||
if self.parent ~= nil then
|
||||
self.parent:show()
|
||||
|
||||
@@ -42,6 +42,7 @@ local function add_tab(self,tab)
|
||||
event_handler = tab.cbf_events,
|
||||
get_formspec = tab.cbf_formspec,
|
||||
tabsize = tab.tabsize,
|
||||
formspec_version = tab.formspec_version or 6,
|
||||
on_change = tab.on_change,
|
||||
tabdata = {},
|
||||
}
|
||||
@@ -65,13 +66,38 @@ local function get_formspec(self)
|
||||
|
||||
local content, prepend = tab.get_formspec(self, tab.name, tab.tabdata, tab.tabsize)
|
||||
|
||||
local tsize = tab.tabsize or { width = self.width, height = self.height }
|
||||
if self.parent == nil and not prepend then
|
||||
local tsize = tab.tabsize or {width=self.width, height=self.height}
|
||||
prepend = string.format("size[%f,%f,%s]", tsize.width, tsize.height,
|
||||
dump(self.fixed_size))
|
||||
|
||||
if tab.formspec_version then
|
||||
prepend = ("formspec_version[%d]"):format(tab.formspec_version) .. prepend
|
||||
end
|
||||
end
|
||||
|
||||
local end_button_size = 0.75
|
||||
|
||||
local tab_header_size = { width = tsize.width, height = 0.85 }
|
||||
if self.end_button then
|
||||
tab_header_size.width = tab_header_size.width - end_button_size - 0.1
|
||||
end
|
||||
|
||||
local formspec = (prepend or "") .. self:tab_header(tab_header_size) .. content
|
||||
|
||||
if self.end_button then
|
||||
formspec = formspec ..
|
||||
("style[%s;noclip=true;border=false]"):format(self.end_button.name) ..
|
||||
("tooltip[%s;%s]"):format(self.end_button.name, self.end_button.label) ..
|
||||
("image_button[%f,%f;%f,%f;%s;%s;]"):format(
|
||||
self.width - end_button_size,
|
||||
(-tab_header_size.height - end_button_size) / 2,
|
||||
end_button_size,
|
||||
end_button_size,
|
||||
core.formspec_escape(self.end_button.icon),
|
||||
self.end_button.name)
|
||||
end
|
||||
|
||||
local formspec = (prepend or "") .. self:tab_header() .. content
|
||||
return formspec
|
||||
end
|
||||
|
||||
@@ -86,8 +112,12 @@ local function handle_buttons(self,fields)
|
||||
return true
|
||||
end
|
||||
|
||||
if self.end_button and fields[self.end_button.name] then
|
||||
return self.end_button.on_click(self)
|
||||
end
|
||||
|
||||
if self.glb_btn_handler ~= nil and
|
||||
self.glb_btn_handler(self,fields) then
|
||||
self.glb_btn_handler(self, fields) then
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -121,20 +151,23 @@ end
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
local function tab_header(self)
|
||||
|
||||
local function tab_header(self, size)
|
||||
local toadd = ""
|
||||
|
||||
for i=1,#self.tablist,1 do
|
||||
|
||||
for i = 1, #self.tablist do
|
||||
if toadd ~= "" then
|
||||
toadd = toadd .. ","
|
||||
end
|
||||
|
||||
toadd = toadd .. self.tablist[i].caption
|
||||
local caption = self.tablist[i].caption
|
||||
if type(caption) == "function" then
|
||||
caption = caption(self)
|
||||
end
|
||||
|
||||
toadd = toadd .. caption
|
||||
end
|
||||
return string.format("tabheader[%f,%f;%s;%s;%i;true;false]",
|
||||
self.header_x, self.header_y, self.name, toadd, self.last_tab_index);
|
||||
return string.format("tabheader[%f,%f;%f,%f;%s;%s;%i;true;false]",
|
||||
self.header_x, self.header_y, size.width, size.height, self.name, toadd, self.last_tab_index)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@@ -225,6 +258,8 @@ local tabview_metatable = {
|
||||
function(self,handler) self.glb_evt_handler = handler end,
|
||||
set_fixed_size =
|
||||
function(self,state) self.fixed_size = state end,
|
||||
set_end_button =
|
||||
function(self, v) self.end_button = v end,
|
||||
tab_header = tab_header,
|
||||
handle_tab_buttons = handle_tab_buttons
|
||||
}
|
||||
|
||||
@@ -62,8 +62,8 @@ function ui.update()
|
||||
|
||||
-- handle errors
|
||||
if gamedata ~= nil and gamedata.reconnect_requested then
|
||||
local error_message = core.formspec_escape(
|
||||
gamedata.errormessage or fgettext("<none available>"))
|
||||
local error_message = core.formspec_escape(gamedata.errormessage)
|
||||
or fgettext("<none available>")
|
||||
formspec = {
|
||||
"size[14,8]",
|
||||
"real_coordinates[true]",
|
||||
|
||||
@@ -529,16 +529,16 @@ function core.check_single_for_falling(p)
|
||||
if same and d_bottom.paramtype2 == "leveled" and
|
||||
core.get_node_level(p_bottom) <
|
||||
core.get_node_max_level(p_bottom) then
|
||||
convert_to_falling_node(p, n)
|
||||
return true
|
||||
local success, _ = convert_to_falling_node(p, n)
|
||||
return success
|
||||
end
|
||||
-- Otherwise only if the bottom node is considered "fall through"
|
||||
if not same and
|
||||
(not d_bottom.walkable or d_bottom.buildable_to) and
|
||||
(core.get_item_group(n.name, "float") == 0 or
|
||||
d_bottom.liquidtype == "none") then
|
||||
convert_to_falling_node(p, n)
|
||||
return true
|
||||
local success, _ = convert_to_falling_node(p, n)
|
||||
return success
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -27,6 +27,8 @@ core.features = {
|
||||
get_light_data_buffer = true,
|
||||
mod_storage_on_disk = true,
|
||||
compress_zstd = true,
|
||||
sound_params_start_time = true,
|
||||
physics_overrides_v2 = true,
|
||||
}
|
||||
|
||||
function core.has_feature(arg)
|
||||
|
||||
@@ -10,7 +10,8 @@ local builtin_shared = {}
|
||||
dofile(gamepath .. "constants.lua")
|
||||
assert(loadfile(commonpath .. "item_s.lua"))(builtin_shared)
|
||||
assert(loadfile(gamepath .. "item.lua"))(builtin_shared)
|
||||
dofile(gamepath .. "register.lua")
|
||||
assert(loadfile(commonpath .. "register.lua"))(builtin_shared)
|
||||
assert(loadfile(gamepath .. "register.lua"))(builtin_shared)
|
||||
|
||||
if core.settings:get_bool("profiler.load") then
|
||||
profiler = dofile(scriptpath .. "profiler" .. DIR_DELIM .. "init.lua")
|
||||
|
||||
@@ -69,6 +69,7 @@ core.register_entity(":__builtin:item", {
|
||||
automatic_rotate = math.pi * 0.5 * 0.2 / size,
|
||||
wield_item = self.itemstring,
|
||||
glow = glow,
|
||||
infotext = stack:get_description(),
|
||||
})
|
||||
|
||||
-- cache for usage in on_step
|
||||
@@ -178,6 +179,11 @@ core.register_entity(":__builtin:item", {
|
||||
return
|
||||
end
|
||||
|
||||
-- Prevent assert when item_entity is attached
|
||||
if moveresult == nil and self.object:get_attach() then
|
||||
return
|
||||
end
|
||||
|
||||
if self.force_out then
|
||||
-- This code runs after the entity got a push from the is_stuck code.
|
||||
-- It makes sure the entity is entirely outside the solid node
|
||||
|
||||
@@ -261,6 +261,11 @@ function core.get_globals_to_transfer()
|
||||
local all = {
|
||||
registered_items = copy_filtering(core.registered_items),
|
||||
registered_aliases = core.registered_aliases,
|
||||
|
||||
nodedef_default = copy_filtering(core.nodedef_default),
|
||||
craftitemdef_default = copy_filtering(core.craftitemdef_default),
|
||||
tooldef_default = copy_filtering(core.tooldef_default),
|
||||
noneitemdef_default = copy_filtering(core.noneitemdef_default),
|
||||
}
|
||||
return all
|
||||
end
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
-- Minetest: builtin/register.lua
|
||||
|
||||
local builtin_shared = ...
|
||||
local S = core.get_translator("__builtin")
|
||||
|
||||
--
|
||||
@@ -420,55 +421,6 @@ function core.override_item(name, redefinition)
|
||||
register_item_raw(item)
|
||||
end
|
||||
|
||||
do
|
||||
local default = {mod = "??", name = "??"}
|
||||
core.callback_origins = setmetatable({}, {
|
||||
__index = function()
|
||||
return default
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
function core.run_callbacks(callbacks, mode, ...)
|
||||
assert(type(callbacks) == "table")
|
||||
local cb_len = #callbacks
|
||||
if cb_len == 0 then
|
||||
if mode == 2 or mode == 3 then
|
||||
return true
|
||||
elseif mode == 4 or mode == 5 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
local ret = nil
|
||||
for i = 1, cb_len do
|
||||
local origin = core.callback_origins[callbacks[i]]
|
||||
core.set_last_run_mod(origin.mod)
|
||||
local cb_ret = callbacks[i](...)
|
||||
|
||||
if mode == 0 and i == 1 then
|
||||
ret = cb_ret
|
||||
elseif mode == 1 and i == cb_len then
|
||||
ret = cb_ret
|
||||
elseif mode == 2 then
|
||||
if not cb_ret or i == 1 then
|
||||
ret = cb_ret
|
||||
end
|
||||
elseif mode == 3 then
|
||||
if cb_ret then
|
||||
return cb_ret
|
||||
end
|
||||
ret = cb_ret
|
||||
elseif mode == 4 then
|
||||
if (cb_ret and not ret) or i == 1 then
|
||||
ret = cb_ret
|
||||
end
|
||||
elseif mode == 5 and cb_ret then
|
||||
return cb_ret
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function core.run_priv_callbacks(name, priv, caller, method)
|
||||
local def = core.registered_privileges[priv]
|
||||
if not def or not def["on_" .. method] or
|
||||
@@ -485,34 +437,6 @@ end
|
||||
-- Callback registration
|
||||
--
|
||||
|
||||
local function make_registration()
|
||||
local t = {}
|
||||
local registerfunc = function(func)
|
||||
t[#t + 1] = func
|
||||
core.callback_origins[func] = {
|
||||
mod = core.get_current_modname() or "??",
|
||||
name = debug.getinfo(1, "n").name or "??"
|
||||
}
|
||||
--local origin = core.callback_origins[func]
|
||||
--print(origin.name .. ": " .. origin.mod .. " registering cbk " .. tostring(func))
|
||||
end
|
||||
return t, registerfunc
|
||||
end
|
||||
|
||||
local function make_registration_reverse()
|
||||
local t = {}
|
||||
local registerfunc = function(func)
|
||||
table.insert(t, 1, func)
|
||||
core.callback_origins[func] = {
|
||||
mod = core.get_current_modname() or "??",
|
||||
name = debug.getinfo(1, "n").name or "??"
|
||||
}
|
||||
--local origin = core.callback_origins[func]
|
||||
--print(origin.name .. ": " .. origin.mod .. " registering cbk " .. tostring(func))
|
||||
end
|
||||
return t, registerfunc
|
||||
end
|
||||
|
||||
local function make_registration_wrap(reg_fn_name, clear_fn_name)
|
||||
local list = {}
|
||||
|
||||
@@ -600,6 +524,9 @@ core.registered_decorations = make_registration_wrap("register_decoration", "cle
|
||||
core.unregister_biome = make_wrap_deregistration(core.register_biome,
|
||||
core.clear_registered_biomes, core.registered_biomes)
|
||||
|
||||
local make_registration = builtin_shared.make_registration
|
||||
local make_registration_reverse = builtin_shared.make_registration_reverse
|
||||
|
||||
core.registered_on_chat_messages, core.register_on_chat_message = make_registration()
|
||||
core.registered_on_chatcommands, core.register_on_chatcommand = make_registration()
|
||||
core.registered_globalsteps, core.register_globalstep = make_registration()
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
# textdomain: __builtin
|
||||
Invalid parameters (see /help @1).=Ungültige Parameter (siehe „/help @1“).
|
||||
Too many arguments, try using just /help <command>=Zu viele Argumente. Probieren Sie es mit „/help <Befehl>“
|
||||
Available commands: @1=Verfügbare Befehle: @1
|
||||
Use '/help <cmd>' to get more information, or '/help all' to list everything.=„/help <Befehl>“ benutzen, um mehr Informationen zu erhalten, oder „/help all“, um alles aufzulisten.
|
||||
Available commands:=Verfügbare Befehle:
|
||||
Command not available: @1=Befehl nicht verfügbar: @1
|
||||
[all | privs | <cmd>] [-t]=[all | privs | <Befehl>] [-t]
|
||||
Get help for commands or list privileges (-t: output in chat)=Hilfe für Befehle erhalten oder Privilegien auflisten (-t: Ausgabe im Chat)
|
||||
Available privileges:=Verfügbare Privilegien:
|
||||
Command=Befehl
|
||||
Parameters=Parameter
|
||||
For more information, click on any entry in the list.=Für mehr Informationen klicken Sie auf einen beliebigen Eintrag in der Liste.
|
||||
Double-click to copy the entry to the chat history.=Doppelklicken, um den Eintrag in die Chathistorie einzufügen.
|
||||
Command: @1 @2=Befehl: @1 @2
|
||||
Available commands: (see also: /help <cmd>)=Verfügbare Befehle: (siehe auch: /help <Befehl>)
|
||||
Close=Schließen
|
||||
Privilege=Privileg
|
||||
Description=Beschreibung
|
||||
Empty command.=Leerer Befehl.
|
||||
Invalid command: @1=Ungültiger Befehl: @1
|
||||
Invalid command usage.=Ungültige Befehlsverwendung.
|
||||
@@ -189,30 +207,6 @@ You are already dead.=Sie sind schon tot.
|
||||
@1 is already dead.=@1 ist bereits tot.
|
||||
@1 has been killed.=@1 wurde getötet.
|
||||
Kill player or yourself=Einen Spieler oder Sie selbst töten
|
||||
Invalid parameters (see /help @1).=Ungültige Parameter (siehe „/help @1“).
|
||||
Too many arguments, try using just /help <command>=Zu viele Argumente. Probieren Sie es mit „/help <Befehl>“
|
||||
Available commands: @1=Verfügbare Befehle: @1
|
||||
Use '/help <cmd>' to get more information, or '/help all' to list everything.=„/help <Befehl>“ benutzen, um mehr Informationen zu erhalten, oder „/help all“, um alles aufzulisten.
|
||||
Available commands:=Verfügbare Befehle:
|
||||
Command not available: @1=Befehl nicht verfügbar: @1
|
||||
[all | privs | <cmd>] [-t]=[all | privs | <Befehl>] [-t]
|
||||
Get help for commands or list privileges (-t: output in chat)=Hilfe für Befehle erhalten oder Privilegien auflisten (-t: Ausgabe im Chat)
|
||||
Available privileges:=Verfügbare Privilegien:
|
||||
Command=Befehl
|
||||
Parameters=Parameter
|
||||
For more information, click on any entry in the list.=Für mehr Informationen klicken Sie auf einen beliebigen Eintrag in der Liste.
|
||||
Double-click to copy the entry to the chat history.=Doppelklicken, um den Eintrag in die Chathistorie einzufügen.
|
||||
Command: @1 @2=Befehl: @1 @2
|
||||
Available commands: (see also: /help <cmd>)=Verfügbare Befehle: (siehe auch: /help <Befehl>)
|
||||
Close=Schließen
|
||||
Privilege=Privileg
|
||||
Description=Beschreibung
|
||||
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<Filter>] | dump [<Filter>] | save [<Format> [<Filter>]]
|
||||
Handle the profiler and profiling data=Den Profiler und Profilingdaten verwalten
|
||||
Statistics written to action log.=Statistiken zum Aktionsprotokoll geschrieben.
|
||||
Statistics were reset.=Statistiken wurden zurückgesetzt.
|
||||
Usage: @1=Verwendung: @1
|
||||
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=Format kann entweder „txt“, „csv“, „lua“, „json“ oder „json_pretty“ sein (die Struktur kann sich in Zukunft ändern).
|
||||
@1 joined the game.=@1 ist dem Spiel beigetreten.
|
||||
@1 left the game.=@1 hat das Spiel verlassen.
|
||||
@1 left the game (timed out).=@1 hat das Spiel verlassen (Netzwerkzeitüberschreitung).
|
||||
@@ -239,6 +233,12 @@ Unknown Item=Unbekannter Gegenstand
|
||||
Air=Luft
|
||||
Ignore=Ignorieren
|
||||
You can't place 'ignore' nodes!=Sie können keine „ignore“-Blöcke platzieren!
|
||||
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<Filter>] | dump [<Filter>] | save [<Format> [<Filter>]] | reset
|
||||
Handle the profiler and profiling data=Den Profiler und Profilingdaten verwalten
|
||||
Statistics written to action log.=Statistiken zum Aktionsprotokoll geschrieben.
|
||||
Statistics were reset.=Statistiken wurden zurückgesetzt.
|
||||
Usage: @1=Verwendung: @1
|
||||
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=Format kann entweder „txt“, „csv“, „lua“, „json“ oder „json_pretty“ sein (die Struktur kann sich in Zukunft ändern).
|
||||
Values below show absolute/relative times spend per server step by the instrumented function.=Die unten angegebenen Werte zeigen absolute/relative Zeitspannen, die je Server-Step von der instrumentierten Funktion in Anspruch genommen wurden.
|
||||
A total of @1 sample(s) were taken.=Es wurden insgesamt @1 Datenpunkt(e) aufgezeichnet.
|
||||
The output is limited to '@1'.=Die Ausgabe ist beschränkt auf „@1“.
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
# textdomain: __builtin
|
||||
Invalid parameters (see /help @1).=Parameter tidak sah (lihat /help @1).
|
||||
Too many arguments, try using just /help <command>=Terlalu banyak argumen. Coba hanya gunakan /help <perintah>
|
||||
Available commands: @1=Perintah yang tersedia: @1
|
||||
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Gunakan '/help <perintah>' untuk informasi lebih lanjut atau '/help all' untuk melihat daftar semuanya.
|
||||
Available commands:=Perintah yang tersedia:
|
||||
Command not available: @1=Perintah tidak tersedia: @1
|
||||
[all | privs | <cmd>] [-t]=[all | privs | <perintah>] [-t]
|
||||
Get help for commands or list privileges (-t: output in chat)=Ambil bantuan untuk perintah atau daftar hak (-t: keluaran di obrolan)
|
||||
Available privileges:=Hak yang ada:
|
||||
Command=Perintah
|
||||
Parameters=Parameter
|
||||
For more information, click on any entry in the list.=Untuk informasi lebih lanjut, klik pada entri apa pun dalam daftar.
|
||||
Double-click to copy the entry to the chat history.=Klik ganda untuk menyalin entri ke riwayat obrolan.
|
||||
Command: @1 @2=Perintah: @1 @2
|
||||
Available commands: (see also: /help <cmd>)=Perintah yang tersedia: (lihat juga: /help <perintah>)
|
||||
Close=Tutup
|
||||
Privilege=Hak
|
||||
Description=Deskripsi
|
||||
Empty command.=Perintah kosong.
|
||||
Invalid command: @1=Perintah tidak sah: @1
|
||||
Invalid command usage.=Penggunaan perintah tidak sah.
|
||||
(@1 s)= (@1 detik)
|
||||
Command execution took @1 s=Pelaksanaan perintah memerlukan @1 detik
|
||||
(@1 s)= (@1 detik)
|
||||
Command execution took @1 s=Pelaksanaan perintah memerlukan @1 detik
|
||||
You don't have permission to run this command (missing privileges: @1).=Anda tidak memiliki izin untuk menjalankan perintah ini (hak tidak ada: @1).
|
||||
Unable to get position of player @1.=Tidak dapat mengambil letak pemain @1.
|
||||
Incorrect area format. Expected: (x1,y1,z1) (x2,y2,z2)=Format daerah tidak sah. Diharapkan: (x1,y1,z1) (x2,y2,z2)
|
||||
@@ -189,30 +207,6 @@ You are already dead.=Anda telah mati.
|
||||
@1 is already dead.=@1 telah mati.
|
||||
@1 has been killed.=@1 telah dibunuh.
|
||||
Kill player or yourself=Bunuh pemain atau diri Anda
|
||||
Invalid parameters (see /help @1).=Parameter tidak sah (lihat /help @1).
|
||||
Too many arguments, try using just /help <command>=Terlalu banyak argumen. Coba hanya gunakan /help <perintah>
|
||||
Available commands: @1=Perintah yang tersedia: @1
|
||||
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Gunakan '/help <perintah>' untuk informasi lebih lanjut atau '/help all' untuk melihat daftar semuanya.
|
||||
Available commands:=Perintah yang tersedia:
|
||||
Command not available: @1=Perintah tidak tersedia: @1
|
||||
[all | privs | <cmd>] [-t]=[all | privs | <perintah>] [-t]
|
||||
Get help for commands or list privileges (-t: output in chat)=Ambil bantuan untuk perintah atau daftar hak (-t: keluaran di obrolan)
|
||||
Available privileges:=Hak yang ada:
|
||||
Command=Perintah
|
||||
Parameters=Parameter
|
||||
For more information, click on any entry in the list.=Untuk informasi lebih lanjut, klik pada entri apa pun dalam daftar.
|
||||
Double-click to copy the entry to the chat history.=Klik ganda untuk menyalin entri ke riwayat obrolan.
|
||||
Command: @1 @2=Perintah: @1 @2
|
||||
Available commands: (see also: /help <cmd>)=Perintah yang tersedia: (lihat juga: /help <perintah>)
|
||||
Close=Tutup
|
||||
Privilege=Hak
|
||||
Description=Deskripsi
|
||||
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset
|
||||
Handle the profiler and profiling data=Menangani profiler dan data profiling
|
||||
Statistics written to action log.=Statistik ditulis ke log action.
|
||||
Statistics were reset.=Statistik diatur ulang.
|
||||
Usage: @1=Penggunaan: @1
|
||||
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=Format berupa salah satu dari txt, csv, lua, json, json_pretty (struktur mungkin berubah).
|
||||
@1 joined the game.=@1 bergabung dalam permainan.
|
||||
@1 left the game.=@1 keluar permainan.
|
||||
@1 left the game (timed out).=@1 keluar permainan (kehabisan waktu).
|
||||
@@ -239,6 +233,12 @@ Unknown Item=Barang Tak Diketahui
|
||||
Air=Udara
|
||||
Ignore=Ignore
|
||||
You can't place 'ignore' nodes!=Anda tidak dapat menaruh nodus 'ignore'!
|
||||
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset
|
||||
Handle the profiler and profiling data=Menangani profiler dan data profiling
|
||||
Statistics written to action log.=Statistik ditulis ke log action.
|
||||
Statistics were reset.=Statistik diatur ulang.
|
||||
Usage: @1=Penggunaan: @1
|
||||
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=Format berupa salah satu dari txt, csv, lua, json, json_pretty (struktur mungkin berubah).
|
||||
Values below show absolute/relative times spend per server step by the instrumented function.=Nilai berikut menampilkan waktu mutlak/relatif yang dihabiskan tiap langkah server oleh fungsi instrumen.
|
||||
A total of @1 sample(s) were taken.=Total @1 sampel yang diambil.
|
||||
The output is limited to '@1'.=Keluaran dibatasi ke '@1'.
|
||||
|
||||
@@ -1,78 +1,97 @@
|
||||
# textdomain: __builtin
|
||||
# note for transaltors: sono state seguite le norme di https://italianoinclusivo.it/ a parte per il suffisso -tore -trice, con l'intento di trasformarlo in un epiceno
|
||||
Invalid parameters (see /help @1).=Parametri non validi (vedi /help @1)
|
||||
Too many arguments, try using just /help <command>=Troppi argomenti, prova a usare /help <comando>
|
||||
Available commands: @1=Comandi disponibili: @1
|
||||
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Usa '/help <comando>' per ottenere più informazioni, o '/help all' per elencare tutti i comandi.
|
||||
Available commands:=Comandi disponibili:
|
||||
Command not available: @1=Comando non disponibile: @1
|
||||
[all | privs | <cmd>] [-t]=[all | privs | <comando>] [-t]
|
||||
Get help for commands or list privileges (-t: output in chat)=Richiama la finestra d'aiuto dei comandi o dei privilegi (-t: mostra in chat)
|
||||
Available privileges:=Privilegi disponibili:
|
||||
Command=Comando
|
||||
Parameters=Parametri
|
||||
For more information, click on any entry in the list.=Per più informazioni, clicca su una qualsiasi voce dell'elenco.
|
||||
Double-click to copy the entry to the chat history.=Doppio clic per copiare la voce nella cronologia della chat.
|
||||
Command: @1 @2=Comando: @1 @2
|
||||
Available commands: (see also: /help <cmd>)=Comandi disponibili: (vedi anche /help <comando>)
|
||||
Close=Chiudi
|
||||
Privilege=Privilegio
|
||||
Description=Descrizione
|
||||
Empty command.=Comando vuoto.
|
||||
Invalid command: @1=Comando non valido: @1
|
||||
Invalid command usage.=Utilizzo del comando non valido.
|
||||
(@1 s)=
|
||||
Command execution took @1 s=
|
||||
(@1 s)= (@1 s)
|
||||
Command execution took @1 s=L'esecuzione del comando ha richiesto @1 s
|
||||
You don't have permission to run this command (missing privileges: @1).=Non hai il permesso di eseguire questo comando (privilegi mancanti: @1).
|
||||
Unable to get position of player @1.=Impossibile ottenere la posizione del giocatore @1.
|
||||
Unable to get position of player @1.=Impossibile ottenere la posizione dellə giocatore @1.
|
||||
Incorrect area format. Expected: (x1,y1,z1) (x2,y2,z2)=Formato dell'area non corretto. Richiesto: (x1,y1,z1) (x2,y2,z2)
|
||||
<action>=<azione>
|
||||
Show chat action (e.g., '/me orders a pizza' displays '<player name> orders a pizza')=Mostra un'azione in chat (es. `/me ordina una pizza` mostra `<nome giocatore> ordina una pizza`)
|
||||
Show the name of the server owner=Mostra il nome del proprietario del server
|
||||
Show the name of the server owner=Mostra il nome di chi possiede il server
|
||||
The administrator of this server is @1.=L'amministratore di questo server è @1.
|
||||
There's no administrator named in the config file.=Non c'è nessun amministratore nel file di configurazione.
|
||||
@1 does not have any privileges.=
|
||||
There's no administrator named in the config file.=Non c'è nessun*amministratore nel file di configurazione.
|
||||
@1 does not have any privileges.=@1 non ha nessun privilegio
|
||||
Privileges of @1: @2=Privilegi di @1: @2
|
||||
[<name>]=[<nome>]
|
||||
Show privileges of yourself or another player=Mostra i privilegi propri o di un altro giocatore
|
||||
Player @1 does not exist.=Il giocatore @1 non esiste.
|
||||
Show privileges of yourself or another player=Mostra i privilegi propri o di un*altrə giocatore
|
||||
Player @1 does not exist.=Lə giocatore @1 non esiste.
|
||||
<privilege>=<privilegio>
|
||||
Return list of all online players with privilege=Ritorna una lista di tutti i giocatori connessi col tale privilegio
|
||||
Return list of all online players with privilege=Ritorna una lista di tuttɜ lɜ giocatori connessɜ col tale privilegio
|
||||
Invalid parameters (see /help haspriv).=Parametri non validi (vedi /help haspriv).
|
||||
Unknown privilege!=Privilegio sconosciuto!
|
||||
No online player has the "@1" privilege.=
|
||||
Players online with the "@1" privilege: @2=Giocatori connessi con il privilegio "@1": @2
|
||||
No online player has the "@1" privilege.=Nessunə giocatore ha il privilegio "@1".
|
||||
Players online with the "@1" privilege: @2=Giocatori connessɜ con il privilegio "@1": @2
|
||||
Your privileges are insufficient.=I tuoi privilegi sono insufficienti.
|
||||
Your privileges are insufficient. '@1' only allows you to grant: @2=
|
||||
Your privileges are insufficient. '@1' only allows you to grant: @2=I tuoi privilegi sono insufficienti. '@1' ti permette soltanto di elargire: @2
|
||||
Unknown privilege: @1=Privilegio sconosciuto: @1
|
||||
@1 granted you privileges: @2=@1 ti ha assegnato i seguenti privilegi: @2
|
||||
<name> (<privilege> [, <privilege2> [<...>]] | all)=
|
||||
Give privileges to player=Dà privilegi al giocatore
|
||||
<name> (<privilege> [, <privilege2> [<...>]] | all)=<nome> (<privilegio> [, <privilegio2> [<...>]] | all)
|
||||
Give privileges to player=Dà privilegi allə giocatore
|
||||
Invalid parameters (see /help grant).=Parametri non validi (vedi /help grant).
|
||||
<privilege> [, <privilege2> [<...>]] | all=
|
||||
<privilege> [, <privilege2> [<...>]] | all=<privilegio> [, <privilegio2> [<...>]] | all
|
||||
Grant privileges to yourself=Assegna dei privilegi a te stessǝ
|
||||
Invalid parameters (see /help grantme).=Parametri non validi (vedi /help grantme).
|
||||
Your privileges are insufficient. '@1' only allows you to revoke: @2=
|
||||
Note: Cannot revoke in singleplayer: @1=
|
||||
Note: Cannot revoke from admin: @1=
|
||||
No privileges were revoked.=
|
||||
Your privileges are insufficient. '@1' only allows you to revoke: @2=I tuoi privilegi sono insufficienti. '@1' ti permette soltanto di revocare: @2
|
||||
Note: Cannot revoke in singleplayer: @1=N.B.: Non si può revocare in locale: @1
|
||||
Note: Cannot revoke from admin: @1=N.B.: Non si può revocare a un*amministratore: @1
|
||||
No privileges were revoked.=Nessun privilegio è stato revocato.
|
||||
@1 revoked privileges from you: @2=@1 ti ha revocato i seguenti privilegi: @2
|
||||
Remove privileges from player=Rimuove privilegi dal giocatore
|
||||
Remove privileges from player=Rimuove privilegi dallə giocatore
|
||||
Invalid parameters (see /help revoke).=Parametri non validi (vedi /help revoke).
|
||||
Revoke privileges from yourself=Revoca privilegi a te stessǝ
|
||||
Invalid parameters (see /help revokeme).=Parametri non validi (vedi /help revokeme).
|
||||
<name> <password>=<nome> <password>
|
||||
Set player's password (sent unencrypted, thus insecure)=
|
||||
Set player's password (sent unencrypted, thus insecure)=Imposta la password dellə giocatore (non crittografata, ergo insicura)
|
||||
Name field required.=Campo "nome" richiesto.
|
||||
Your password was cleared by @1.=La tua password è stata resettata da @1.
|
||||
Password of player "@1" cleared.=Password del giocatore "@1" resettata.
|
||||
Password of player "@1" cleared.=Password dellə giocatore "@1" resettata.
|
||||
Your password was set by @1.=La tua password è stata impostata da @1.
|
||||
Password of player "@1" set.=Password del giocatore "@1" impostata.
|
||||
Password of player "@1" set.=Password dellə giocatore "@1" impostata.
|
||||
<name>=<nome>
|
||||
Set empty password for a player=Imposta una password vuota a un giocatore
|
||||
Reload authentication data=Ricarica i dati d'autenticazione
|
||||
Done.=Fatto.
|
||||
Failed.=Errore.
|
||||
Remove a player's data=Rimuove i dati di un giocatore
|
||||
Remove a player's data=Rimuove i dati di unə giocatore
|
||||
Player "@1" removed.=Giocatore "@1" rimosso.
|
||||
No such player "@1" to remove.=Non è presente nessun giocatore "@1" da rimuovere.
|
||||
Player "@1" is connected, cannot remove.=Il giocatore "@1" è connesso, non può essere rimosso.
|
||||
No such player "@1" to remove.=Non è presente nessunə giocatore "@1" da rimuovere.
|
||||
Player "@1" is connected, cannot remove.=Lə giocatore "@1" è connessə, non può essere rimossə.
|
||||
Unhandled remove_player return code @1.=Codice ritornato da remove_player non gestito (@1).
|
||||
Cannot teleport out of map bounds!=Non ci si può teletrasportare fuori dai limiti della mappa!
|
||||
Cannot get player with name @1.=Impossibile trovare il giocatore chiamato @1.
|
||||
Cannot teleport, @1 is attached to an object!=Impossibile teletrasportare, @1 è attaccato a un oggetto!
|
||||
Cannot get player with name @1.=Impossibile trovare lə giocatore chiamato @1.
|
||||
Cannot teleport, @1 is attached to an object!=Impossibile teletrasportare, @1 è attaccatə a un oggetto!
|
||||
Teleporting @1 to @2.=Teletrasportando @1 da @2.
|
||||
One does not teleport to oneself.=Non ci si può teletrasportare su se stessi.
|
||||
Cannot get teleportee with name @1.=Impossibile trovare il giocatore chiamato @1 per il teletrasporto
|
||||
Cannot get target player with name @1.=Impossibile trovare il giocatore chiamato @1 per il teletrasporto
|
||||
One does not teleport to oneself.=Non ci si può teletrasportare su se stessɜ.
|
||||
Cannot get teleportee with name @1.=Impossibile trovare lə giocatore chiamatə @1 per il teletrasporto
|
||||
Cannot get target player with name @1.=Impossibile trovare lə giocatore chiamato @1 per il teletrasporto
|
||||
Teleporting @1 to @2 at @3.=Teletrasportando @1 da @2 a @3
|
||||
<X>,<Y>,<Z> | <to_name> | <name> <X>,<Y>,<Z> | <name> <to_name>=<X>,<Y>,<Z> | <da_nome> | <nome> <X>,<Y>,<Z> | <nome> <da_nome>
|
||||
Teleport to position or player=Teletrasporta a una posizione o da un giocatore
|
||||
You don't have permission to teleport other players (missing privilege: @1).=Non hai il permesso di teletrasportare altri giocatori (privilegio mancante: @1).
|
||||
Teleport to position or player=Teletrasporta a una posizione o da unə giocatore
|
||||
You don't have permission to teleport other players (missing privilege: @1).=Non hai il permesso di teletrasportare altrɜ giocatori (privilegio mancante: @1).
|
||||
([-n] <name> <value>) | <name>=([-n] <nome> <valore>) | <nome>
|
||||
Set or read server configuration setting=Imposta o ottieni le configurazioni del server
|
||||
Failed. Cannot modify secure settings. Edit the settings file manually.=
|
||||
Failed. Cannot modify secure settings. Edit the settings file manually.=Errore. Non è possibile modificare le impostazioni di sicurezza. Modifica manualmente il file d'impostazioni.
|
||||
Failed. Use '/set -n <name> <value>' to create a new setting.=Errore. Usa 'set -n <nome> <valore>' per creare una nuova impostazione
|
||||
@1 @= @2=@1 @= @2
|
||||
<not set>=<non impostato>
|
||||
@@ -89,11 +108,11 @@ Resets lighting in the area between pos1 and pos2 (<pos1> and <pos2> must be in
|
||||
Successfully reset light in the area ranging from @1 to @2.=Luce nell'area tra @1 e @2 reimpostata con successo.
|
||||
Failed to load one or more blocks in area.=Errore nel caricare uno o più blocchi mappa nell'area.
|
||||
List mods installed on the server=Elenca le mod installate nel server
|
||||
No mods installed.=
|
||||
No mods installed.=Nessuna mod installata.
|
||||
Cannot give an empty item.=Impossibile dare un oggetto vuoto.
|
||||
Cannot give an unknown item.=Impossibile dare un oggetto sconosciuto.
|
||||
Giving 'ignore' is not allowed.=Non è permesso dare 'ignore'.
|
||||
@1 is not a known player.=@1 non è un giocatore conosciuto.
|
||||
@1 is not a known player.=@1 non è unə giocatore conosciutə.
|
||||
@1 partially added to inventory.=@1 parzialmente aggiunto all'inventario.
|
||||
@1 could not be added to inventory.=@1 non può essere aggiunto all'inventario.
|
||||
@1 added to inventory.=@1 aggiunto all'inventario.
|
||||
@@ -101,7 +120,7 @@ Giving 'ignore' is not allowed.=Non è permesso dare 'ignore'.
|
||||
@1 could not be added to inventory of @2.=Non è stato possibile aggiungere @1 all'inventario di @2.
|
||||
@1 added to inventory of @2.=@1 aggiunto all'inventario di @2.
|
||||
<name> <ItemString> [<count> [<wear>]]=<nome> <NomeOggetto> [<quantità> [<usura>]]
|
||||
Give item to player=Dà oggetti ai giocatori
|
||||
Give item to player=Dà oggetti allɜ giocatori
|
||||
Name and ItemString required.=Richiesti nome e NomeOggetto.
|
||||
<ItemString> [<count> [<wear>]]=<NomeOggetto> [<quantità> [<usura>]]
|
||||
Give item to yourself=Dà oggetti a te stessǝ
|
||||
@@ -109,29 +128,29 @@ ItemString required.=Richiesto NomeOggetto.
|
||||
<EntityName> [<X>,<Y>,<Z>]=<NomeEntità> [<X>,<Y>,<Z>]
|
||||
Spawn entity at given (or your) position=Genera un'entità alla data coordinata (o la tua)
|
||||
EntityName required.=Richiesto NomeEntità
|
||||
Unable to spawn entity, player is nil.=Impossibile generare l'entità, il giocatore è nil.
|
||||
Unable to spawn entity, player is nil.=Impossibile generare l'entità, lə giocatore è nil.
|
||||
Cannot spawn an unknown entity.=Impossibile generare un'entità sconosciuta.
|
||||
Invalid parameters (@1).=Parametri non validi (@1).
|
||||
@1 spawned.=Generata entità @1.
|
||||
@1 failed to spawn.=Errore nel generare @1
|
||||
Destroy item in hand=Distrugge l'oggetto in mano
|
||||
Unable to pulverize, no player.=Impossibile polverizzare, nessun giocatore.
|
||||
Unable to pulverize, no player.=Impossibile polverizzare, nessunə giocatore.
|
||||
Unable to pulverize, no item in hand.=Impossibile polverizzare, nessun oggetto in mano.
|
||||
An item was pulverized.=Un oggetto è stato polverizzato.
|
||||
[<range>] [<seconds>] [<limit>]=[<raggio>] [<secondi>] [<limite>]
|
||||
Check who last touched a node or a node near it within the time specified by <seconds>. Default: range @= 0, seconds @= 86400 @= 24h, limit @= 5. Set <seconds> to inf for no time limit=Controlla chi è l'ultimo giocatore che ha toccato un nodo o un nodo nelle sue vicinanze, negli ultimi secondi indicati. Di base: raggio @= 0, secondi @= 86400 @= 24h, limite @= 5.
|
||||
Check who last touched a node or a node near it within the time specified by <seconds>. Default: range @= 0, seconds @= 86400 @= 24h, limit @= 5. Set <seconds> to inf for no time limit=Controlla chi è l'ultimə giocatore che ha toccato un nodo o un nodo nelle sue vicinanze, negli ultimi secondi indicati. Di base: raggio @= 0, secondi @= 86400 @= 24h, limite @= 5.
|
||||
Rollback functions are disabled.=Le funzioni di rollback sono disabilitate.
|
||||
That limit is too high!=Il limite è troppo alto!
|
||||
Checking @1 ...=Controllando @1 ...
|
||||
Nobody has touched the specified location in @1 seconds.=Nessuno ha toccato il punto specificato negli ultimi @1 secondi.
|
||||
Nobody has touched the specified location in @1 seconds.=Nessunə ha toccato il punto specificato negli ultimi @1 secondi.
|
||||
@1 @2 @3 -> @4 @5 seconds ago.=@1 @2 @3 -> @4 @5 secondi fa.
|
||||
Punch a node (range@=@1, seconds@=@2, limit@=@3).=Colpisce un nodo (raggio@=@1, secondi@=@2, limite@=@3)
|
||||
(<name> [<seconds>]) | (:<actor> [<seconds>])=(<nome> [<secondi>]) | (:<attore> [<secondi>])
|
||||
Revert actions of a player. Default for <seconds> is 60. Set <seconds> to inf for no time limit=Riavvolge le azioni di un giocatore. Di base, <secondi> è 60. Imposta <secondi> a inf per nessun limite di tempo
|
||||
Revert actions of a player. Default for <seconds> is 60. Set <seconds> to inf for no time limit=Riavvolge le azioni di unə giocatore. Di base, <secondi> è 60. Imposta <secondi> a inf per nessun limite di tempo
|
||||
Invalid parameters. See /help rollback and /help rollback_check.=Parametri non validi. Vedi /help rollback e /help rollback_check.
|
||||
Reverting actions of player '@1' since @2 seconds.=Riavvolge le azioni del giocatore '@1' avvenute negli ultimi @2 secondi.
|
||||
Reverting actions of player '@1' since @2 seconds.=Riavvolge le azioni dellə giocatore '@1' avvenute negli ultimi @2 secondi.
|
||||
Reverting actions of @1 since @2 seconds.=Riavvolge le azioni di @1 avvenute negli ultimi @2 secondi.
|
||||
(log is too long to show)=(il log è troppo lungo per essere mostrato)
|
||||
(log is too long to show)=(lo storico è troppo lungo per essere mostrato)
|
||||
Reverting actions succeeded.=Riavvolgimento azioni avvenuto con successo.
|
||||
Reverting actions FAILED.=Errore nel riavvolgere le azioni.
|
||||
Show server status=Mostra lo stato del server
|
||||
@@ -140,121 +159,89 @@ This command was disabled by a mod or game.=Questo comando è stato disabilitato
|
||||
Show or set time of day=Mostra o imposta l'orario della giornata
|
||||
Current time is @1:@2.=Orario corrente: @1:@2.
|
||||
You don't have permission to run this command (missing privilege: @1).=Non hai il permesso di eseguire questo comando (privilegio mancante: @1)
|
||||
Invalid time (must be between 0 and 24000).=
|
||||
Invalid time (must be between 0 and 24000).=Orario non valido (deve essere tra 0 e 24000).
|
||||
Time of day changed.=Orario della giornata cambiato.
|
||||
Invalid hour (must be between 0 and 23 inclusive).=Ora non valida (deve essere tra 0 e 23 inclusi)
|
||||
Invalid minute (must be between 0 and 59 inclusive).=Minuto non valido (deve essere tra 0 e 59 inclusi)
|
||||
Show day count since world creation=Mostra il conteggio dei giorni da quando il mondo è stato creato
|
||||
Current day is @1.=Giorno attuale: @1.
|
||||
[<delay_in_seconds> | -1] [-r] [<message>]=
|
||||
Shutdown server (-1 cancels a delayed shutdown, -r allows players to reconnect)=
|
||||
[<delay_in_seconds> | -1] [-r] [<message>]=[<ritardo_in_secondi> | -1] [-r] [<messaggio>]
|
||||
Shutdown server (-1 cancels a delayed shutdown, -r allows players to reconnect)=Arresta il server (-1 cancella un arresto programmato, -r permette allɜ giocatori di riconnettersi)
|
||||
Server shutting down (operator request).=Arresto del server in corso (per richiesta dell'operatore)
|
||||
Ban the IP of a player or show the ban list=Bandisce l'IP del giocatore o mostra la lista di quelli banditi
|
||||
The ban list is empty.=La lista banditi è vuota.
|
||||
Ban list: @1=Lista banditi: @1
|
||||
You cannot ban players in singleplayer!=
|
||||
Player is not online.=Il giocatore non è connesso.
|
||||
Failed to ban player.=Errore nel bandire il giocatore.
|
||||
Ban the IP of a player or show the ban list=Bandisce l'IP dellə giocatore o mostra la lista di quellɜ banditɜ
|
||||
The ban list is empty.=La lista banditɜ è vuota.
|
||||
Ban list: @1=Lista banditɜ: @1
|
||||
You cannot ban players in singleplayer!=Non puoi bandire giocatori in locale!
|
||||
Player is not online.=Lə giocatore non è connessə.
|
||||
Failed to ban player.=Errore nel bandire lə giocatore.
|
||||
Banned @1.=@1 banditǝ.
|
||||
<name> | <IP_address>=<nome> | <indirizzo_IP>
|
||||
Remove IP ban belonging to a player/IP=Perdona l'IP appartenente a un giocatore/IP
|
||||
Failed to unban player/IP.=Errore nel perdonare il giocatore/IP
|
||||
Remove IP ban belonging to a player/IP=Perdona l'IP appartenente a unə giocatore/IP
|
||||
Failed to unban player/IP.=Errore nel perdonare lə giocatore/IP
|
||||
Unbanned @1.=@1 perdonatǝ
|
||||
<name> [<reason>]=<nome> [<ragione>]
|
||||
Kick a player=Caccia un giocatore
|
||||
Failed to kick player @1.=Errore nel cacciare il giocatore @1.
|
||||
Kick a player=Caccia unə giocatore
|
||||
Failed to kick player @1.=Errore nel cacciare lə giocatore @1.
|
||||
Kicked @1.=@1 cacciatǝ.
|
||||
[full | quick]=[full | quick]
|
||||
Clear all objects in world=Elimina tutti gli oggetti/entità nel mondo
|
||||
Invalid usage, see /help clearobjects.=Uso incorretto, vedi /help clearobjects.
|
||||
Clearing all objects. This may take a long time. You may experience a timeout. (by @1)=Eliminando tutti gli oggetti/entità. Questo potrebbe richiedere molto tempo e farti eventualmente crashare. (di @1)
|
||||
Clearing all objects. This may take a long time. You may experience a timeout. (by @1)=Eliminando tutti gli oggetti/entità. Questo potrebbe richiedere molto tempo e farti disconnettere. (di @1)
|
||||
Cleared all objects.=Tutti gli oggetti sono stati eliminati.
|
||||
<name> <message>=<nome> <messaggio>
|
||||
Send a direct message to a player=Invia un messaggio privato al giocatore
|
||||
Send a direct message to a player=Invia un messaggio privato a unə giocatore
|
||||
Invalid usage, see /help msg.=Uso incorretto, vedi /help msg
|
||||
The player @1 is not online.=Il giocatore @1 non è connesso.
|
||||
The player @1 is not online.=Lə giocatore @1 non è connessə.
|
||||
DM from @1: @2=Messaggio privato da @1: @2
|
||||
Message sent.=Messaggio inviato.
|
||||
Get the last login time of a player or yourself=Ritorna l'ultimo accesso di un giocatore o di te stessǝ
|
||||
@1's last login time was @2.=L'ultimo accesso di @1 è avvenuto il @2
|
||||
@1's last login time is unknown.=L'ultimo accesso di @1 non è conosciuto
|
||||
Clear the inventory of yourself or another player=Svuota l'inventario tuo o di un altro giocatore
|
||||
You don't have permission to clear another player's inventory (missing privilege: @1).=Non hai il permesso di svuotare l'inventario di un altro giocatore (privilegio mancante: @1).
|
||||
Get the last login time of a player or yourself=Ritorna l'ultimo accesso di unə giocatore o di te stessǝ
|
||||
@1's last login time was @2.=L'ultimo accesso di @1 è avvenuto il @2.
|
||||
@1's last login time is unknown.=L'ultimo accesso di @1 è ignoto.
|
||||
Clear the inventory of yourself or another player=Svuota l'inventario tuo o di un*altrə giocatore
|
||||
You don't have permission to clear another player's inventory (missing privilege: @1).=Non hai il permesso di svuotare l'inventario di un*altrə giocatore (privilegio mancante: @1).
|
||||
@1 cleared your inventory.=@1 ha svuotato il tuo inventario.
|
||||
Cleared @1's inventory.=L'inventario di @1 è stato svuotato.
|
||||
Player must be online to clear inventory!=Il giocatore deve essere connesso per svuotarne l'inventario!
|
||||
Players can't be killed, damage has been disabled.=I giocatori non possono essere uccisi, il danno è disabilitato.
|
||||
Player @1 is not online.=Il giocatore @1 non è connesso.
|
||||
Player must be online to clear inventory!=Lə giocatore deve essere connessə per svuotarne l'inventario!
|
||||
Players can't be killed, damage has been disabled.=Lɜ giocatori non possono essere uccisɜ, il danno è disabilitato.
|
||||
Player @1 is not online.=Lə giocatore @1 non è connesso.
|
||||
You are already dead.=Sei già mortǝ.
|
||||
@1 is already dead.=@1 è già mortǝ.
|
||||
@1 has been killed.=@1 è stato uccisǝ.
|
||||
Kill player or yourself=Uccide un giocatore o te stessǝ
|
||||
Invalid parameters (see /help @1).=
|
||||
Too many arguments, try using just /help <command>=
|
||||
Available commands: @1=Comandi disponibili: @1
|
||||
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Usa '/help <comando>' per ottenere più informazioni, o '/help all' per elencare tutti i comandi.
|
||||
Available commands:=Comandi disponibili:
|
||||
Command not available: @1=Comando non disponibile: @1
|
||||
[all | privs | <cmd>] [-t]=
|
||||
Get help for commands or list privileges (-t: output in chat)=
|
||||
Available privileges:=Privilegi disponibili:
|
||||
Command=Comando
|
||||
Parameters=Parametri
|
||||
For more information, click on any entry in the list.=Per più informazioni, clicca su una qualsiasi voce dell'elenco.
|
||||
Double-click to copy the entry to the chat history.=Doppio click per copiare la voce nella cronologia della chat.
|
||||
Command: @1 @2=Comando: @1 @2
|
||||
Available commands: (see also: /help <cmd>)=Comandi disponibili: (vedi anche /help <comando>)
|
||||
Close=Chiudi
|
||||
Privilege=Privilegio
|
||||
Description=Descrizione
|
||||
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<filtro>] | dump [<filtro>] | save [<formato> [<filtro>]] | reset
|
||||
Handle the profiler and profiling data=Gestisce il profiler e i dati da esso elaborati
|
||||
Statistics written to action log.=Statistiche scritte nel log delle azioni.
|
||||
Statistics were reset.=Le statistiche sono state resettate.
|
||||
Usage: @1=Utilizzo: @1
|
||||
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=I formati supportati sono txt, csv, lua, json e json_pretty (le strutture potrebbero essere soggetti a cambiamenti).
|
||||
@1 joined the game.=
|
||||
@1 left the game.=
|
||||
@1 left the game (timed out).=
|
||||
Kill player or yourself=Uccide unə giocatore o te stessǝ
|
||||
@1 joined the game.=@1 si è connessə.
|
||||
@1 left the game.=@1 si è disconnessə.
|
||||
@1 left the game (timed out).=@1 si è disconnessə (connessione scaduta).
|
||||
(no description)=(nessuna descrizione)
|
||||
Can interact with things and modify the world=Si può interagire con le cose e modificare il mondo
|
||||
Can speak in chat=Si può parlare in chat
|
||||
Can modify basic privileges (@1)=
|
||||
Can modify basic privileges (@1)=Si possono modificare i privilegi di base (@1)
|
||||
Can modify privileges=Si possono modificare i privilegi
|
||||
Can teleport self=Si può teletrasportare se stessз
|
||||
Can teleport other players=Si possono teletrasportare gli altri giocatori
|
||||
Can teleport other players=Si possono teletrasportare lɜ altrɜ giocatori
|
||||
Can set the time of day using /time=Si può impostate l'orario della giornata tramite /time
|
||||
Can do server maintenance stuff=Si possono eseguire operazioni di manutenzione del server
|
||||
Can bypass node protection in the world=Si può aggirare la protezione dei nodi nel mondo
|
||||
Can ban and unban players=Si possono bandire e perdonare i giocatori
|
||||
Can kick players=Si possono cacciare i giocatori
|
||||
Can ban and unban players=Si possono bandire e perdonare lɜ giocatori
|
||||
Can kick players=Si possono cacciare lɜ giocatori
|
||||
Can use /give and /giveme=Si possono usare /give e /give me
|
||||
Can use /setpassword and /clearpassword=Si possono usare /setpassword e /clearpassword
|
||||
Can use fly mode=Si può usare la modalità volo
|
||||
Can use fast mode=Si può usare la modalità rapida
|
||||
Can fly through solid nodes using noclip mode=Si può volare attraverso i nodi solidi con la modalità incorporea
|
||||
Can use the rollback functionality=Si può usare la funzione di rollback
|
||||
Can enable wireframe=
|
||||
Can enable wireframe=Si può abilitare la vista reticolata
|
||||
Unknown Item=Oggetto sconosciuto
|
||||
Air=Aria
|
||||
Ignore=Ignora
|
||||
You can't place 'ignore' nodes!=Non puoi piazzare nodi 'ignore'!
|
||||
Values below show absolute/relative times spend per server step by the instrumented function.=
|
||||
A total of @1 sample(s) were taken.=
|
||||
The output is limited to '@1'.=
|
||||
Saving of profile failed: @1=
|
||||
Profile saved to @1=
|
||||
|
||||
|
||||
##### not used anymore #####
|
||||
|
||||
Set player's password=Imposta la password del giocatore
|
||||
Invalid time.=Orario non valido.
|
||||
[all | privs | <cmd>]=[all | privs | <comando>]
|
||||
Get help for commands or list privileges=Richiama la finestra d'aiuto dei comandi o dei privilegi
|
||||
Allows enabling various debug options that may affect gameplay=Permette di abilitare varie opzioni di debug che potrebbero influenzare l'esperienza di gioco
|
||||
[<delay_in_seconds> | -1] [reconnect] [<message>]=[<ritardo_in_secondi> | -1] [reconnect] [<messaggio>]
|
||||
Shutdown server (-1 cancels a delayed shutdown)=Arresta il server (-1 annulla un arresto programmato)
|
||||
<name> (<privilege> | all)=<nome> (<privilegio> | all)
|
||||
<privilege> | all=<privilegio> | all
|
||||
Can modify 'shout' and 'interact' privileges=Si possono modificare i privilegi 'shout' e 'interact'
|
||||
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<filtro>] | dump [<filtro>] | save [<formato> [<filtro>]] | reset
|
||||
Handle the profiler and profiling data=Gestisce il profiler e i dati da esso elaborati
|
||||
Statistics written to action log.=Statistiche scritte nel registro delle azioni.
|
||||
Statistics were reset.=Le statistiche sono state resettate.
|
||||
Usage: @1=Utilizzo: @1
|
||||
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=I formati supportati sono txt, csv, lua, json e json_pretty (le strutture potrebbero essere soggetti a cambiamenti).
|
||||
Values below show absolute/relative times spend per server step by the instrumented function.=I valori sottostanti mostrano i tempi assoluti/relativi impiegati su ogni singolo step dalla funzione analizzata
|
||||
A total of @1 sample(s) were taken.=Son stati ottenuti campioni per un totale di @1.
|
||||
The output is limited to '@1'.=L'output è limitato a '@1'.
|
||||
Saving of profile failed: @1=Errore nel salvare il profilo: @1
|
||||
Profile saved to @1=Profilo salvato in @1
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
# textdomain: __builtin
|
||||
Invalid parameters (see /help @1).=Parameter tidak sah (sila lihat /help @1).
|
||||
Too many arguments, try using just /help <command>=Terlalu banyak argumen, cuba guna /help <perintah> sahaja
|
||||
Available commands: @1=Perintah yang tersedia: @1
|
||||
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Gunakan '/help <perintah>' untuk maklumat lanjut, atau '/help all' untuk senaraikan kesemuanya.
|
||||
Available commands:=Perintah yang tersedia:
|
||||
Command not available: @1=Perintah tidak tersedia: @1
|
||||
[all | privs | <cmd>] [-t]=[all | privs | <perintah>] [-t]
|
||||
Get help for commands or list privileges (-t: output in chat)=Dapatkan bantuan untuk perintah atau senaraikan keistimewaan (-t: output dalam sembang)
|
||||
Available privileges:=Keistimewaan yang tersedia:
|
||||
Command=Perintah
|
||||
Parameters=Parameter
|
||||
For more information, click on any entry in the list.=Untuk maklumat lanjut, klik pada mana-mana entri dalam senarai.
|
||||
Double-click to copy the entry to the chat history.=Klik dua kali untuk salin entri ke sejarah sembang.
|
||||
Command: @1 @2=Perintah: @1 @2
|
||||
Available commands: (see also: /help <cmd>)=Perintah yang tersedua: (sila lihat juga: /help <perintah>)
|
||||
Close=Tutup
|
||||
Privilege=Keistimewaan
|
||||
Description=Keterangan
|
||||
Empty command.=Perintah kosong.
|
||||
Invalid command: @1=Perintah tidak sah: @1
|
||||
Invalid command usage.=Penggunaan perintah tidak sah.
|
||||
@@ -189,30 +207,6 @@ You are already dead.=Anda sudah pun mati.
|
||||
@1 is already dead.=@1 sudah pun mati.
|
||||
@1 has been killed.=@1 telah berjaya dibunuh.
|
||||
Kill player or yourself=Bunuh pemain atau diri sendiri
|
||||
Invalid parameters (see /help @1).=Parameter tidak sah (sila lihat /help @1).
|
||||
Too many arguments, try using just /help <command>=Terlalu banyak argumen, cuba guna /help <perintah> sahaja
|
||||
Available commands: @1=Perintah yang tersedia: @1
|
||||
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Gunakan '/help <perintah>' untuk maklumat lanjut, atau '/help all' untuk senaraikan kesemuanya.
|
||||
Available commands:=Perintah yang tersedia:
|
||||
Command not available: @1=Perintah tidak tersedia: @1
|
||||
[all | privs | <cmd>] [-t]=[all | privs | <perintah>] [-t]
|
||||
Get help for commands or list privileges (-t: output in chat)=Dapatkan bantuan untuk perintah atau senaraikan keistimewaan (-t: output dalam sembang)
|
||||
Available privileges:=Keistimewaan yang tersedia:
|
||||
Command=Perintah
|
||||
Parameters=Parameter
|
||||
For more information, click on any entry in the list.=Untuk maklumat lanjut, klik pada mana-mana entri dalam senarai.
|
||||
Double-click to copy the entry to the chat history.=Klik dua kali untuk salin entri ke sejarah sembang.
|
||||
Command: @1 @2=Perintah: @1 @2
|
||||
Available commands: (see also: /help <cmd>)=Perintah yang tersedua: (sila lihat juga: /help <perintah>)
|
||||
Close=Tutup
|
||||
Privilege=Keistimewaan
|
||||
Description=Keterangan
|
||||
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<tapisan>] | dump [<tapisan>] | save [<format> [<tapisan>]] | reset
|
||||
Handle the profiler and profiling data=Uruskan pembukah dan data pembukahan
|
||||
Statistics written to action log.=Statistik telah ditulis ke log perlakuan.
|
||||
Statistics were reset.=Statistik telah ditetapkan semula.
|
||||
Usage: @1=Kegunaan: @1
|
||||
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=Format boleh jadi salah satu daripada txt, csv, lua, json, json_pretty (struktur boleh berubah kemudian).
|
||||
@1 joined the game.=@1 telah menyertai permainan.
|
||||
@1 left the game.=@1 telah meninggalkan permainan.
|
||||
@1 left the game (timed out).=@1 telah meninggalkan permainan (tamat tempoh masa).
|
||||
@@ -239,6 +233,12 @@ Unknown Item=Item Tidak Diketahui
|
||||
Air=Udara
|
||||
Ignore=Abai
|
||||
You can't place 'ignore' nodes!=Anda tidak boleh meletakkan nod 'abai'!
|
||||
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<tapisan>] | dump [<tapisan>] | save [<format> [<tapisan>]] | reset
|
||||
Handle the profiler and profiling data=Uruskan pembukah dan data pembukahan
|
||||
Statistics written to action log.=Statistik telah ditulis ke log perlakuan.
|
||||
Statistics were reset.=Statistik telah ditetapkan semula.
|
||||
Usage: @1=Kegunaan: @1
|
||||
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=Format boleh jadi salah satu daripada txt, csv, lua, json, json_pretty (struktur boleh berubah kemudian).
|
||||
Values below show absolute/relative times spend per server step by the instrumented function.=Nilai di bawah menunjukkan masa mutlak/relatif yang digunakan oleh fungsi yang dipasangkan pada setiap langkah pelayan
|
||||
A total of @1 sample(s) were taken.=Sebanyak @1 sampel telah diambil secara keseluruhan.
|
||||
The output is limited to '@1'.=Output dihadkan kepada '@1'.
|
||||
|
||||
246
builtin/locale/__builtin.ru.tr
Normal file
246
builtin/locale/__builtin.ru.tr
Normal file
@@ -0,0 +1,246 @@
|
||||
# textdomain: __builtin
|
||||
Invalid parameters (see /help @1).=Недопустимые параметры (см. /help @1).
|
||||
Too many arguments, try using just /help <command>=Слишком много аргументов, попробуйте использовать просто /help <команда>
|
||||
Available commands: @1=Доступные команды: @1
|
||||
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Используйте '/help <команда>', чтобы получить дополнительную информацию, или '/help all', чтобы перечислить все.
|
||||
Available commands:=Доступные команды:
|
||||
Command not available: @1=Команда недоступна: @1
|
||||
[all | privs | <cmd>] [-t]=[all | privs | <команда>] [-t]
|
||||
Get help for commands or list privileges (-t: output in chat)=Получить справку по командам или списку привилегий (-t: вывод в чате)
|
||||
Available privileges:=Доступные привилегии:
|
||||
Command=Команда
|
||||
Parameters=Параметры
|
||||
For more information, click on any entry in the list.=Для получения дополнительной информации нажмите на любую запись в списке.
|
||||
Double-click to copy the entry to the chat history.=Дважды щелкните, чтобы скопировать запись в историю чата.
|
||||
Command: @1 @2=Команда: @1 @2
|
||||
Available commands: (see also: /help <cmd>)=Доступные команды: (смотрите также: /help <команда>)
|
||||
Close=Закрыть
|
||||
Privilege=Привилегия
|
||||
Description=Описание
|
||||
Empty command.=Пустая команда.
|
||||
Invalid command: @1=Недопустимая команда: @1
|
||||
Invalid command usage.=Недопустимое использование команды.
|
||||
(@1 s)= (@1 с)
|
||||
Command execution took @1 s=Выполнение команды заняло @1 с
|
||||
You don't have permission to run this command (missing privileges: @1).=У вас нет разрешения на запуск этой команды (отсутствуют привилегии: @1).
|
||||
Unable to get position of player @1.=Не удалось получить позицию игрока @1.
|
||||
Incorrect area format. Expected: (x1,y1,z1) (x2,y2,z2)=Неправильный формат области. Ожидаемое: (x1,y1,z1) (x2,y2,z2)
|
||||
<action>=<действие>
|
||||
Show chat action (e.g., '/me orders a pizza' displays '<player name> orders a pizza')=Показать действие в чате (например, '/me заказывает пиццу' отображает '<имя игрока> заказывает пиццу')
|
||||
Show the name of the server owner=Показать имя владельца сервера
|
||||
The administrator of this server is @1.=Администратором этого сервера является @1.
|
||||
There's no administrator named in the config file.=В конфигурационном файле нет имени администратора.
|
||||
@1 does not have any privileges.=@1 не имеет никаких привилегий.
|
||||
Privileges of @1: @2=Привилегии @1: @2
|
||||
[<name>]=[<имя>]
|
||||
Show privileges of yourself or another player=Показать ваши привилегии или привилегии другого игрока
|
||||
Player @1 does not exist.=Игрок @1 не существует.
|
||||
<privilege>=<привилегия>
|
||||
Return list of all online players with privilege=Возвращает список всех онлайн-игроков, имеющих указанную привилегию
|
||||
Invalid parameters (see /help haspriv).=Недопустимые параметры (см. /help haspriv).
|
||||
Unknown privilege!=Неизвестная привилегия!
|
||||
No online player has the "@1" privilege.=Ни один онлайн-игрок не имеет привилегии "@1".
|
||||
Players online with the "@1" privilege: @2=Игроки онлайн с привилегией "@1": @2
|
||||
Your privileges are insufficient.=Ваших привилегий недостаточно.
|
||||
Your privileges are insufficient. '@1' only allows you to grant: @2=Ваших привилегий недостаточно. '@1' позволяет вам предоставлять только: @2
|
||||
Unknown privilege: @1=Неизвестная привилегия: @1
|
||||
@1 granted you privileges: @2=@1 предоставил(а) вам привилегии: @2
|
||||
<name> (<privilege> [, <privilege2> [<...>]] | all)=<имя> (<привилегия> [, <привилегия2> [<...>]] | all)
|
||||
Give privileges to player=Предоставить привилегии игроку
|
||||
Invalid parameters (see /help grant).=Недопустимые параметры (см. /help grant).
|
||||
<privilege> [, <privilege2> [<...>]] | all=<привилегия> [, <привилегия2> [<...>]] | all
|
||||
Grant privileges to yourself=Предоставить привилегии себе
|
||||
Invalid parameters (see /help grantme).=Недопустимые параметры (см. /help grantme).
|
||||
Your privileges are insufficient. '@1' only allows you to revoke: @2=Ваших привилегий недостаточно. '@1' позволяет вам отозвать только: @2
|
||||
Note: Cannot revoke in singleplayer: @1=Примечание: Невозможно отозвать в одиночной игре: @1
|
||||
Note: Cannot revoke from admin: @1=Примечание: Невозможно отозвать у администратора: @1
|
||||
No privileges were revoked.=Никакие привилегии не были отозваны.
|
||||
@1 revoked privileges from you: @2=@1 отозвал у вас привилегии: @2
|
||||
Remove privileges from player=Отозвать привилегии у игрока
|
||||
Invalid parameters (see /help revoke).=Недопустимые параметры (см. /help revoke).
|
||||
Revoke privileges from yourself=Отозвать привилегии у себя
|
||||
Invalid parameters (see /help revokeme).=Недопустимые параметры (см. /help revokeme).
|
||||
<name> <password>=<имя> <пароль>
|
||||
Set player's password (sent unencrypted, thus insecure)=Установить пароль игрока (отправка в незашифрованном виде, следовательно, небезопасно)
|
||||
Name field required.=Обязательное поле "Имя".
|
||||
Your password was cleared by @1.=@1 сбросил ваш пароль.
|
||||
Password of player "@1" cleared.=Пароль игрока "@1" сброшен.
|
||||
Your password was set by @1.=@1 установил вам пароль.
|
||||
Password of player "@1" set.=Установлен пароль игрока "@1".
|
||||
<name>=<имя>
|
||||
Set empty password for a player=Установить пустой пароль для игрока
|
||||
Reload authentication data=Перезагрузить аутентификационные данные
|
||||
Done.=Готово.
|
||||
Failed.=Не удалось.
|
||||
Remove a player's data=Удалить данные игрока
|
||||
Player "@1" removed.=Игрок "@1" удален.
|
||||
No such player "@1" to remove.=Нет такого игрока "@1", которого можно было бы удалить.
|
||||
Player "@1" is connected, cannot remove.=Игрок "@1" сейчас в игре, удалить его невозможно.
|
||||
Unhandled remove_player return code @1.=Необработанный код возврата remove_player @1.
|
||||
Cannot teleport out of map bounds!=Невозможно телепортироваться за пределы карты!
|
||||
Cannot get player with name @1.=Не удается получить игрока с именем @1.
|
||||
Cannot teleport, @1 is attached to an object!=Невозможно телепортироваться, @1 привязан к объекту!
|
||||
Teleporting @1 to @2.=Телепортация @1 в @2.
|
||||
One does not teleport to oneself.=Игрок не может телепортироваться к самому себе.
|
||||
Cannot get teleportee with name @1.=Не удается найти телепортирующегося с именем @1.
|
||||
Cannot get target player with name @1.=Не удается найти целевого игрока с именем @1.
|
||||
Teleporting @1 to @2 at @3.=Телепортация @1 к @2 в @3.
|
||||
<X>,<Y>,<Z> | <to_name> | <name> <X>,<Y>,<Z> | <name> <to_name>=<X>,<Y>,<Z> | <имя_к_кому> | <имя> <X>,<Y>,<Z> | <имя> <имя_к_кому>
|
||||
Teleport to position or player=Телепортироваться на позицию или к игроку
|
||||
You don't have permission to teleport other players (missing privilege: @1).=У вас нет разрешения на телепортацию других игроков (отсутствует привилегия: @1).
|
||||
([-n] <name> <value>) | <name>=([-n] <имя> <значение>) | <имя>
|
||||
Set or read server configuration setting=Установить или прочитать настройки конфигурации сервера
|
||||
Failed. Cannot modify secure settings. Edit the settings file manually.=Ошибка. Не удается изменить настройки безопасности. Отредактируйте файл настроек вручную.
|
||||
Failed. Use '/set -n <name> <value>' to create a new setting.=Ошибка. Используйте '/set -n <имя> <значение>', чтобы создать новую настройку.
|
||||
@1 @= @2=@1 @= @2
|
||||
<not set>=<не задано>
|
||||
Invalid parameters (see /help set).=Недопустимые параметры (см. /help set).
|
||||
Finished emerging @1 blocks in @2ms.=Загрузка/генерация @1 map-блоков завершена за @2мс.
|
||||
emergeblocks update: @1/@2 blocks emerged (@3%)=обновление emergeblocks: загружено/сгенерировано: @1 из @2 блоков (@3%)
|
||||
(here [<radius>]) | (<pos1> <pos2>)=(here [<радиус>]) | (<позиция1> <позиция2>)
|
||||
Load (or, if nonexistent, generate) map blocks contained in area pos1 to pos2 (<pos1> and <pos2> must be in parentheses)=Загрузить (или сгенерировать, если не существуют) map-блоки, содержащиеся в области с позиции1 по позицию2 (<позиция1> и <позиция2> должны быть в круглых скобках)
|
||||
Started emerge of area ranging from @1 to @2.=Загрузка/генерация области в диапазоне от @1 до @2 начата.
|
||||
Delete map blocks contained in area pos1 to pos2 (<pos1> and <pos2> must be in parentheses)=Удалить map-блоки, содержащиеся в области с позиции1 по позицию2 (<позиция1> и <позиция2> должны быть заключены в круглые скобки)
|
||||
Successfully cleared area ranging from @1 to @2.=Успешно очищена область в диапазоне от @1 до @2.
|
||||
Failed to clear one or more blocks in area.=Не удалось очистить один или несколько блоков в области.
|
||||
Resets lighting in the area between pos1 and pos2 (<pos1> and <pos2> must be in parentheses)=Сбрасывает освещение в области с позиции1 по позицию2 (<позиция1> и <позиция2> должны быть заключены в круглые скобки)
|
||||
Successfully reset light in the area ranging from @1 to @2.=Успешно сброшено освещение в диапазоне от @1 до @2.
|
||||
Failed to load one or more blocks in area.=Не удалось загрузить один или несколько блоков в области.
|
||||
List mods installed on the server=Список модов, установленных на сервере
|
||||
No mods installed.=Моды не установлены.
|
||||
Cannot give an empty item.=Вы должны указать предмет.
|
||||
Cannot give an unknown item.=Неизвестный предмет.
|
||||
Giving 'ignore' is not allowed.=Выдача 'ignore' не допускается.
|
||||
@1 is not a known player.=Неизвестный игрок: @1.
|
||||
@1 partially added to inventory.=@1 частично добавлен в инвентарь.
|
||||
@1 could not be added to inventory.=@1 не удалось добавить в инвентарь.
|
||||
@1 added to inventory.=@1 добавлено в инвентарь.
|
||||
@1 partially added to inventory of @2.=@1 частично добавлен в инвентарь @2.
|
||||
@1 could not be added to inventory of @2.=@1 не удалось добавить в инвентарь @2.
|
||||
@1 added to inventory of @2.=@1 добавлен в инвентарь @2.
|
||||
<name> <ItemString> [<count> [<wear>]]=<имя> <ItemString> [<количество> [<износ>]]
|
||||
Give item to player=Добавить предмет в инвентарь игрока
|
||||
Name and ItemString required.=Требуется <имя> игрока и название предмета <ItemString>.
|
||||
<ItemString> [<count> [<wear>]]=<ItemString> [<количество> [<износ>]]
|
||||
Give item to yourself=Добавить предмет в свой инвентарь
|
||||
ItemString required.=Требуется название предмета <ItemString>.
|
||||
<EntityName> [<X>,<Y>,<Z>]=<EntityName> [<X>,<Y>,<Z>]
|
||||
Spawn entity at given (or your) position=Спаун сущности (моб/стрела/...) в заданной (или вашей) позиции
|
||||
EntityName required.=Требуется имя сущности <EntityName>.
|
||||
Unable to spawn entity, player is nil.=Не удается создать сущность, игрок не найден.
|
||||
Cannot spawn an unknown entity.=Не удается создать неизвестную сущность.
|
||||
Invalid parameters (@1).=Недопустимые параметры (@1).
|
||||
@1 spawned.=Сущность @1 создана.
|
||||
@1 failed to spawn.=Сущность @1 не удалось создать.
|
||||
Destroy item in hand=Уничтожить предмет, который у вас в руках
|
||||
Unable to pulverize, no player.=Невозможно уничтожить предмет, нет игрока.
|
||||
Unable to pulverize, no item in hand.=Невозможно уничтожить предмет, в руках нет предмета.
|
||||
An item was pulverized.=Предмет был уничтожен.
|
||||
[<range>] [<seconds>] [<limit>]=[<диапазон>] [<секунды>] [<ограничение>]
|
||||
Check who last touched a node or a node near it within the time specified by <seconds>. Default: range @= 0, seconds @= 86400 @= 24h, limit @= 5. Set <seconds> to inf for no time limit=Проверить кто в последний раз прикасался к ноде или нодам рядом в течение времени, указанного в <секундах>. По умолчанию: диапазон @= 0, секунды @= 86400 @= 24 часа, ограничение @= 5. Установите <секунды> в значение inf для отключения ограничения по времени
|
||||
Rollback functions are disabled.=Функции отката отключены.
|
||||
That limit is too high!=Это <ограничение> слишком велико!
|
||||
Checking @1 ...=Проверка @1 ...
|
||||
Nobody has touched the specified location in @1 seconds.=Никто не прикасался к указанному местоположению в течение @1 секунд(ы).
|
||||
@1 @2 @3 -> @4 @5 seconds ago.=@1 @2 @3 -> @4 @5 секунд назад.
|
||||
Punch a node (range@=@1, seconds@=@2, limit@=@3).=Ударьте по ноде (диапазон@=@1, секунды@=@2, предел @=@3).
|
||||
(<name> [<seconds>]) | (:<actor> [<seconds>])=(<имя> [<секунды>]) | (:<игрок> [<секунды>])
|
||||
Revert actions of a player. Default for <seconds> is 60. Set <seconds> to inf for no time limit=Отменяет действия игрока. Значение по умолчанию для <секунд> равно 60. Установите <секунды> в значение inf для отключения ограничения по времени
|
||||
Invalid parameters. See /help rollback and /help rollback_check.=Недопустимые параметры. Смотрите /help rollback и /help rollback_check.
|
||||
Reverting actions of player '@1' since @2 seconds.=Отмена действия игрока "@1" за последние @2 секунд(у/ы).
|
||||
Reverting actions of @1 since @2 seconds.=Отмена действий @1 за последние @2 секунд(у/ы).
|
||||
(log is too long to show)=(журнал слишком длинный для отображения)
|
||||
Reverting actions succeeded.=Отмена действий завершилась успешно.
|
||||
Reverting actions FAILED.=Отменить действия НЕ УДАЛОСЬ.
|
||||
Show server status=Показать статус сервера
|
||||
This command was disabled by a mod or game.=Эта команда была отключена модом или игрой.
|
||||
[<0..23>:<0..59> | <0..24000>]=[<0..23>:<0..59> | <0..24000>]
|
||||
Show or set time of day=Показать или установить время суток
|
||||
Current time is @1:@2.=Текущее время @1:@2.
|
||||
You don't have permission to run this command (missing privilege: @1).=У вас нет разрешения на выполнение этой команды (отсутствует привилегия: @1).
|
||||
Invalid time (must be between 0 and 24000).=Недопустимое время (должно быть в диапазоне от 0 до 24000).
|
||||
Time of day changed.=Время суток изменено.
|
||||
Invalid hour (must be between 0 and 23 inclusive).=Недопустимый час (должно быть от 0 до 23 включительно).
|
||||
Invalid minute (must be between 0 and 59 inclusive).=Недопустимая минута (должно быть от 0 до 59 включительно).
|
||||
Show day count since world creation=Показать количество дней с момента сотворения мира
|
||||
Current day is @1.=Текущий день: @1.
|
||||
[<delay_in_seconds> | -1] [-r] [<message>]=[<задержка_в_секундах> | -1] [-r] [<сообщение>]
|
||||
Shutdown server (-1 cancels a delayed shutdown, -r allows players to reconnect)=Завершение работы сервера (-1 отменяет отложенное завершение работы, -r позволяет игрокам повторно подключиться)
|
||||
Server shutting down (operator request).=Завершение работы сервера (по запросу администрации).
|
||||
Ban the IP of a player or show the ban list=Заблокировать IP игрока или показать список заблокированных
|
||||
The ban list is empty.=Список заблокированных пуст.
|
||||
Ban list: @1=Список заблокированных: @1
|
||||
You cannot ban players in singleplayer!=Вы не можете забанить игроков в одиночной игре!
|
||||
Player is not online.=Игрок не подключен к Сети.
|
||||
Failed to ban player.=Не удалось забанить игрока.
|
||||
Banned @1.=@1 забанен.
|
||||
<name> | <IP_address>=<имя> | <IP_адрес>
|
||||
Remove IP ban belonging to a player/IP=Удалить IP-бан, принадлежащий игроку/IP-адресу
|
||||
Failed to unban player/IP.=Не удалось разбанить игрока/IP.
|
||||
Unbanned @1.=@1 разбанен.
|
||||
<name> [<reason>]=<имя> [<причина>]
|
||||
Kick a player=Кикнуть игрока
|
||||
Failed to kick player @1.=Не удалось кикнуть @1.
|
||||
Kicked @1.=@1 кикнут.
|
||||
[full | quick]=[full | quick]
|
||||
Clear all objects in world=Удалить ВСЕ объекты/сущности во ВСЁМ мире
|
||||
Invalid usage, see /help clearobjects.=Недопустимое использование, см. /help clearobjects.
|
||||
Clearing all objects. This may take a long time. You may experience a timeout. (by @1)=Удаление всех объектов. Это может занять много времени. Ваш клиент может отключиться из-за тайм-аута. (запущено: @1)
|
||||
Cleared all objects.=Все объекты удалены.
|
||||
<name> <message>=<имя> <сообщение>
|
||||
Send a direct message to a player=Отправить прямое сообщение игроку
|
||||
Invalid usage, see /help msg.=Недопустимое использование, см. /help msg.
|
||||
The player @1 is not online.=Игрок @1 не находится в игре.
|
||||
DM from @1: @2=DM от @1: @2
|
||||
Message sent.=Сообщение отправлено.
|
||||
Get the last login time of a player or yourself=Вывести время последнего входа игрока или своё
|
||||
@1's last login time was @2.=Время последнего входа @1: @2.
|
||||
@1's last login time is unknown.=Время последнего входа @1 неизвестно.
|
||||
Clear the inventory of yourself or another player=Очистить свой инвентарь или инвентарь другого игрока
|
||||
You don't have permission to clear another player's inventory (missing privilege: @1).=У вас нет разрешения на очистку инвентаря другого игрока (отсутствует привилегия: @1).
|
||||
@1 cleared your inventory.=@1 очистил ваш инвентарь.
|
||||
Cleared @1's inventory.=Инвентарь @1 очищен.
|
||||
Player must be online to clear inventory!=Игрок должен быть онлайн, чтобы очистить его инвентарь!
|
||||
Players can't be killed, damage has been disabled.=Игроки не могут быть убиты, урон отключен.
|
||||
Player @1 is not online.=Игрок @1 не находится в игре.
|
||||
You are already dead.=Ты уже мертв.
|
||||
@1 is already dead.=@1 уже мертв.
|
||||
@1 has been killed.=@1 был убит.
|
||||
Kill player or yourself=Убить игрока или себя
|
||||
@1 joined the game.=@1 присоединился к игре.
|
||||
@1 left the game.=@1 вышел из игры.
|
||||
@1 left the game (timed out).=@1 вышел из игры (тайм-аут).
|
||||
(no description)=(без описания)
|
||||
Can interact with things and modify the world=Может взаимодействовать (с объектами/игроками/...) и изменять мир
|
||||
Can speak in chat=Может общаться в чате
|
||||
Can modify basic privileges (@1)=Может изменять базовые привилегии (@1)
|
||||
Can modify privileges=Может изменять привилегии
|
||||
Can teleport self=Может самостоятельно телепортироваться
|
||||
Can teleport other players=Может телепортировать других игроков
|
||||
Can set the time of day using /time=Может устанавливать время суток с помощью /time
|
||||
Can do server maintenance stuff=Может заниматься обслуживанием сервера
|
||||
Can bypass node protection in the world=Может обходить защиту нод во всем мире
|
||||
Can ban and unban players=Может банить и разбанивать игроков
|
||||
Can kick players=Может кикать игроков
|
||||
Can use /give and /giveme=Может использовать /give и /giveme
|
||||
Can use /setpassword and /clearpassword=Может использовать /setpassword и /clearpassword
|
||||
Can use fly mode=Может использовать режим полета
|
||||
Can use fast mode=Может использовать быстрый режим (ускорение)
|
||||
Can fly through solid nodes using noclip mode=Может пролетать сквозь ноды, используя режим noclip
|
||||
Can use the rollback functionality=Может использовать функцию отката
|
||||
Can enable wireframe=Может использовать режим каркаса в отладке
|
||||
Unknown Item=Неизвестный предмет
|
||||
Air=Воздух
|
||||
Ignore=Игнорируемая встроенная нода (":ignore")
|
||||
You can't place 'ignore' nodes!=Вы не можете установить ноду 'ignore'!
|
||||
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset
|
||||
Handle the profiler and profiling data=Работа с профайлером и данными профилирования
|
||||
Statistics written to action log.=Статистика записывается в журнал действий.
|
||||
Statistics were reset.=Статистика была сброшена.
|
||||
Usage: @1=Использование: @1
|
||||
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=Формат может быть одним из txt, csv, lua, json, json_pretty (структуры могут быть изменены).
|
||||
Values below show absolute/relative times spend per server step by the instrumented function.=Приведенные ниже значения показывают абсолютное/относительное время, затрачиваемое функцией на каждый шаг сервера.
|
||||
A total of @1 sample(s) were taken.=Всего было взято @1 образец(ов).
|
||||
The output is limited to '@1'.=Вывод ограничен значением '@1'.
|
||||
Saving of profile failed: @1=Не удалось сохранить данные профилирования: @1
|
||||
Profile saved to @1=Данные профилирования сохранены в @1
|
||||
@@ -1,4 +1,22 @@
|
||||
# textdomain: __builtin
|
||||
Invalid parameters (see /help @1).=
|
||||
Too many arguments, try using just /help <command>=
|
||||
Available commands: @1=
|
||||
Use '/help <cmd>' to get more information, or '/help all' to list everything.=
|
||||
Available commands:=
|
||||
Command not available: @1=
|
||||
[all | privs | <cmd>] [-t]=
|
||||
Get help for commands or list privileges (-t: output in chat)=
|
||||
Available privileges:=
|
||||
Command=
|
||||
Parameters=
|
||||
For more information, click on any entry in the list.=
|
||||
Double-click to copy the entry to the chat history.=
|
||||
Command: @1 @2=
|
||||
Available commands: (see also: /help <cmd>)=
|
||||
Close=
|
||||
Privilege=
|
||||
Description=
|
||||
Empty command.=
|
||||
Invalid command: @1=
|
||||
Invalid command usage.=
|
||||
@@ -189,30 +207,6 @@ You are already dead.=
|
||||
@1 is already dead.=
|
||||
@1 has been killed.=
|
||||
Kill player or yourself=
|
||||
Invalid parameters (see /help @1).=
|
||||
Too many arguments, try using just /help <command>=
|
||||
Available commands: @1=
|
||||
Use '/help <cmd>' to get more information, or '/help all' to list everything.=
|
||||
Available commands:=
|
||||
Command not available: @1=
|
||||
[all | privs | <cmd>] [-t]=
|
||||
Get help for commands or list privileges (-t: output in chat)=
|
||||
Available privileges:=
|
||||
Command=
|
||||
Parameters=
|
||||
For more information, click on any entry in the list.=
|
||||
Double-click to copy the entry to the chat history.=
|
||||
Command: @1 @2=
|
||||
Available commands: (see also: /help <cmd>)=
|
||||
Close=
|
||||
Privilege=
|
||||
Description=
|
||||
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=
|
||||
Handle the profiler and profiling data=
|
||||
Statistics written to action log.=
|
||||
Statistics were reset.=
|
||||
Usage: @1=
|
||||
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=
|
||||
@1 joined the game.=
|
||||
@1 left the game.=
|
||||
@1 left the game (timed out).=
|
||||
@@ -239,6 +233,12 @@ Unknown Item=
|
||||
Air=
|
||||
Ignore=
|
||||
You can't place 'ignore' nodes!=
|
||||
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=
|
||||
Handle the profiler and profiling data=
|
||||
Statistics written to action log.=
|
||||
Statistics were reset.=
|
||||
Usage: @1=
|
||||
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=
|
||||
Values below show absolute/relative times spend per server step by the instrumented function.=
|
||||
A total of @1 sample(s) were taken.=
|
||||
The output is limited to '@1'.=
|
||||
|
||||
@@ -23,9 +23,18 @@ if not core.get_http_api then
|
||||
return
|
||||
end
|
||||
|
||||
-- Unordered preserves the original order of the ContentDB API,
|
||||
-- before the package list is ordered based on installed state.
|
||||
local store = { packages = {}, packages_full = {}, packages_full_unordered = {}, aliases = {} }
|
||||
local store = {
|
||||
loading = false,
|
||||
load_ok = false,
|
||||
load_error = false,
|
||||
|
||||
-- Unordered preserves the original order of the ContentDB API,
|
||||
-- before the package list is ordered based on installed state.
|
||||
packages = {},
|
||||
packages_full = {},
|
||||
packages_full_unordered = {},
|
||||
aliases = {},
|
||||
}
|
||||
|
||||
local http = core.get_http_api()
|
||||
|
||||
@@ -47,6 +56,9 @@ local filter_types_titles = {
|
||||
fgettext("Texture packs"),
|
||||
}
|
||||
|
||||
-- Automatic package installation
|
||||
local auto_install_spec = nil
|
||||
|
||||
local number_downloading = 0
|
||||
local download_queue = {}
|
||||
|
||||
@@ -62,15 +74,6 @@ local REASON_UPDATE = "update"
|
||||
local REASON_DEPENDENCY = "dependency"
|
||||
|
||||
|
||||
-- encodes for use as URL parameter or path component
|
||||
local function urlencode(str)
|
||||
return str:gsub("[^%a%d()._~-]", function(char)
|
||||
return string.format("%%%02X", string.byte(char))
|
||||
end)
|
||||
end
|
||||
assert(urlencode("sample text?") == "sample%20text%3F")
|
||||
|
||||
|
||||
local function get_download_url(package, reason)
|
||||
local base_url = core.settings:get("contentdb_url")
|
||||
local ret = base_url .. ("/packages/%s/releases/%d/download/"):format(
|
||||
@@ -89,7 +92,7 @@ local function download_and_extract(param)
|
||||
if filename == "" or not core.download_file(param.url, filename) then
|
||||
core.log("error", "Downloading " .. dump(param.url) .. " failed")
|
||||
return {
|
||||
msg = fgettext("Failed to download \"$1\"", package.title)
|
||||
msg = fgettext_ne("Failed to download \"$1\"", package.title)
|
||||
}
|
||||
end
|
||||
|
||||
@@ -105,7 +108,7 @@ local function download_and_extract(param)
|
||||
os.remove(filename)
|
||||
if not tempfolder then
|
||||
return {
|
||||
msg = fgettext("Failed to extract \"$1\" (unsupported file type or broken archive)", package.title),
|
||||
msg = fgettext_ne("Failed to extract \"$1\" (unsupported file type or broken archive)", package.title),
|
||||
}
|
||||
end
|
||||
|
||||
@@ -129,7 +132,7 @@ local function start_install(package, reason)
|
||||
local path, msg = pkgmgr.install_dir(package.type, result.path, package.name, package.path)
|
||||
core.delete_dir(result.path)
|
||||
if not path then
|
||||
gamedata.errormessage = fgettext("Error installing \"$1\": $2", package.title, msg)
|
||||
gamedata.errormessage = fgettext_ne("Error installing \"$1\": $2", package.title, msg)
|
||||
else
|
||||
core.log("action", "Installed package to " .. path)
|
||||
|
||||
@@ -184,12 +187,16 @@ local function start_install(package, reason)
|
||||
|
||||
if not core.handle_async(download_and_extract, params, callback) then
|
||||
core.log("error", "ERROR: async event failed")
|
||||
gamedata.errormessage = fgettext("Failed to download $1", package.name)
|
||||
gamedata.errormessage = fgettext_ne("Failed to download $1", package.name)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local function queue_download(package, reason)
|
||||
if package.queued or package.downloading then
|
||||
return
|
||||
end
|
||||
|
||||
local max_concurrent_downloads = tonumber(core.settings:get("contentdb_max_concurrent_downloads"))
|
||||
if number_downloading < math.max(max_concurrent_downloads, 1) then
|
||||
start_install(package, reason)
|
||||
@@ -200,6 +207,9 @@ local function queue_download(package, reason)
|
||||
end
|
||||
|
||||
local function get_raw_dependencies(package)
|
||||
if package.type ~= "mod" then
|
||||
return {}
|
||||
end
|
||||
if package.raw_deps then
|
||||
return package.raw_deps
|
||||
end
|
||||
@@ -207,7 +217,7 @@ local function get_raw_dependencies(package)
|
||||
local url_fmt = "/api/packages/%s/dependencies/?only_hard=1&protocol_version=%s&engine_version=%s"
|
||||
local version = core.get_version()
|
||||
local base_url = core.settings:get("contentdb_url")
|
||||
local url = base_url .. url_fmt:format(package.url_part, core.get_max_supp_proto(), urlencode(version.string))
|
||||
local url = base_url .. url_fmt:format(package.url_part, core.get_max_supp_proto(), core.urlencode(version.string))
|
||||
|
||||
local response = http.fetch_sync({ url = url })
|
||||
if not response.succeeded then
|
||||
@@ -429,7 +439,7 @@ function install_dialog.get_formspec()
|
||||
"container_end[]",
|
||||
}
|
||||
|
||||
return table.concat(formspec, "")
|
||||
return table.concat(formspec)
|
||||
end
|
||||
|
||||
function install_dialog.handle_submit(this, fields)
|
||||
@@ -520,6 +530,50 @@ function confirm_overwrite.create(package, callback)
|
||||
nil)
|
||||
end
|
||||
|
||||
local function install_or_update_package(this, package)
|
||||
local install_parent
|
||||
if package.type == "mod" then
|
||||
install_parent = core.get_modpath()
|
||||
elseif package.type == "game" then
|
||||
install_parent = core.get_gamepath()
|
||||
elseif package.type == "txp" then
|
||||
install_parent = core.get_texturepath()
|
||||
else
|
||||
error("Unknown package type: " .. package.type)
|
||||
end
|
||||
|
||||
if package.queued or package.downloading then
|
||||
return
|
||||
end
|
||||
|
||||
local function on_confirm()
|
||||
local deps = get_raw_dependencies(package)
|
||||
if deps and has_hard_deps(deps) then
|
||||
local dlg = install_dialog.create(package, deps)
|
||||
dlg:set_parent(this)
|
||||
this:hide()
|
||||
dlg:show()
|
||||
else
|
||||
queue_download(package, package.path and REASON_UPDATE or REASON_NEW)
|
||||
end
|
||||
end
|
||||
|
||||
if package.type == "mod" and #pkgmgr.games == 0 then
|
||||
local dlg = messagebox("install_game",
|
||||
fgettext("You need to install a game before you can install a mod"))
|
||||
dlg:set_parent(this)
|
||||
this:hide()
|
||||
dlg:show()
|
||||
elseif not package.path and core.is_dir(install_parent .. DIR_DELIM .. package.name) then
|
||||
local dlg = confirm_overwrite.create(package, on_confirm)
|
||||
dlg:set_parent(this)
|
||||
this:hide()
|
||||
dlg:show()
|
||||
else
|
||||
on_confirm()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function get_file_extension(path)
|
||||
local parts = path:split(".")
|
||||
@@ -574,29 +628,33 @@ local function get_screenshot(package)
|
||||
return defaulttexturedir .. "loading_screenshot.png"
|
||||
end
|
||||
|
||||
function store.load()
|
||||
local function fetch_pkgs()
|
||||
local version = core.get_version()
|
||||
local base_url = core.settings:get("contentdb_url")
|
||||
local url = base_url ..
|
||||
"/api/packages/?type=mod&type=game&type=txp&protocol_version=" ..
|
||||
core.get_max_supp_proto() .. "&engine_version=" .. urlencode(version.string)
|
||||
core.get_max_supp_proto() .. "&engine_version=" .. core.urlencode(version.string)
|
||||
|
||||
for _, item in pairs(core.settings:get("contentdb_flag_blacklist"):split(",")) do
|
||||
item = item:trim()
|
||||
if item ~= "" then
|
||||
url = url .. "&hide=" .. urlencode(item)
|
||||
url = url .. "&hide=" .. core.urlencode(item)
|
||||
end
|
||||
end
|
||||
|
||||
local http = core.get_http_api()
|
||||
local response = http.fetch_sync({ url = url })
|
||||
if not response.succeeded then
|
||||
return
|
||||
end
|
||||
|
||||
store.packages_full = core.parse_json(response.data) or {}
|
||||
store.aliases = {}
|
||||
local packages = core.parse_json(response.data)
|
||||
if not packages or #packages == 0 then
|
||||
return
|
||||
end
|
||||
local aliases = {}
|
||||
|
||||
for _, package in pairs(store.packages_full) do
|
||||
for _, package in pairs(packages) do
|
||||
local name_len = #package.name
|
||||
-- This must match what store.update_paths() does!
|
||||
package.id = package.author:lower() .. "/"
|
||||
@@ -606,48 +664,137 @@ function store.load()
|
||||
package.id = package.id .. package.name
|
||||
end
|
||||
|
||||
package.url_part = urlencode(package.author) .. "/" .. urlencode(package.name)
|
||||
package.url_part = core.urlencode(package.author) .. "/" .. core.urlencode(package.name)
|
||||
|
||||
if package.aliases then
|
||||
for _, alias in ipairs(package.aliases) do
|
||||
-- We currently don't support name changing
|
||||
local suffix = "/" .. package.name
|
||||
if alias:sub(-#suffix) == suffix then
|
||||
store.aliases[alias:lower()] = package.id
|
||||
aliases[alias:lower()] = package.id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
store.packages_full_unordered = store.packages_full
|
||||
store.packages = store.packages_full
|
||||
store.loaded = true
|
||||
return { packages = packages, aliases = aliases }
|
||||
end
|
||||
|
||||
local function sort_and_filter_pkgs()
|
||||
store.update_paths()
|
||||
store.sort_packages()
|
||||
store.filter_packages(search_string)
|
||||
end
|
||||
|
||||
-- Resolves the package specification stored in auto_install_spec into an actual package.
|
||||
-- May only be called after the package list has been loaded successfully.
|
||||
local function resolve_auto_install_spec()
|
||||
assert(store.load_ok)
|
||||
|
||||
if not auto_install_spec then
|
||||
return nil
|
||||
end
|
||||
|
||||
local spec = store.aliases[auto_install_spec] or auto_install_spec
|
||||
local resolved = nil
|
||||
|
||||
for _, pkg in ipairs(store.packages_full_unordered) do
|
||||
if pkg.id == spec then
|
||||
resolved = pkg
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not resolved then
|
||||
gamedata.errormessage = fgettext("The package $1 was not found.", auto_install_spec)
|
||||
ui.update()
|
||||
|
||||
auto_install_spec = nil
|
||||
end
|
||||
|
||||
return resolved
|
||||
end
|
||||
|
||||
-- Installs the package specified by auto_install_spec.
|
||||
-- Only does something if:
|
||||
-- a. The package list has been loaded successfully.
|
||||
-- b. The store dialog is currently visible.
|
||||
local function do_auto_install()
|
||||
if not store.load_ok then
|
||||
return
|
||||
end
|
||||
|
||||
local pkg = resolve_auto_install_spec()
|
||||
if not pkg then
|
||||
return
|
||||
end
|
||||
|
||||
local store_dlg = ui.find_by_name("store")
|
||||
if not store_dlg or store_dlg.hidden then
|
||||
return
|
||||
end
|
||||
|
||||
install_or_update_package(store_dlg, pkg)
|
||||
auto_install_spec = nil
|
||||
end
|
||||
|
||||
function store.load()
|
||||
if store.load_ok then
|
||||
sort_and_filter_pkgs()
|
||||
return
|
||||
end
|
||||
if store.loading then
|
||||
return
|
||||
end
|
||||
store.loading = true
|
||||
core.handle_async(
|
||||
fetch_pkgs,
|
||||
nil,
|
||||
function(result)
|
||||
if result then
|
||||
store.load_ok = true
|
||||
store.load_error = false
|
||||
store.packages = result.packages
|
||||
store.packages_full = result.packages
|
||||
store.packages_full_unordered = result.packages
|
||||
store.aliases = result.aliases
|
||||
|
||||
sort_and_filter_pkgs()
|
||||
do_auto_install()
|
||||
else
|
||||
store.load_error = true
|
||||
end
|
||||
|
||||
store.loading = false
|
||||
ui.update()
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function store.update_paths()
|
||||
local mod_hash = {}
|
||||
pkgmgr.refresh_globals()
|
||||
for _, mod in pairs(pkgmgr.global_mods:get_list()) do
|
||||
if mod.author and mod.release > 0 then
|
||||
local id = mod.author:lower() .. "/" .. mod.name
|
||||
mod_hash[store.aliases[id] or id] = mod
|
||||
local cdb_id = pkgmgr.get_contentdb_id(mod)
|
||||
if cdb_id then
|
||||
mod_hash[store.aliases[cdb_id] or cdb_id] = mod
|
||||
end
|
||||
end
|
||||
|
||||
local game_hash = {}
|
||||
pkgmgr.update_gamelist()
|
||||
for _, game in pairs(pkgmgr.games) do
|
||||
if game.author ~= "" and game.release > 0 then
|
||||
local id = game.author:lower() .. "/" .. game.id
|
||||
game_hash[store.aliases[id] or id] = game
|
||||
local cdb_id = pkgmgr.get_contentdb_id(game)
|
||||
if cdb_id then
|
||||
game_hash[store.aliases[cdb_id] or cdb_id] = game
|
||||
end
|
||||
end
|
||||
|
||||
local txp_hash = {}
|
||||
for _, txp in pairs(pkgmgr.get_texture_packs()) do
|
||||
if txp.author and txp.release > 0 then
|
||||
local id = txp.author:lower() .. "/" .. txp.name
|
||||
txp_hash[store.aliases[id] or id] = txp
|
||||
local cdb_id = pkgmgr.get_contentdb_id(txp)
|
||||
if cdb_id then
|
||||
txp_hash[store.aliases[cdb_id] or cdb_id] = txp
|
||||
end
|
||||
end
|
||||
|
||||
@@ -666,6 +813,7 @@ function store.update_paths()
|
||||
package.installed_release = content.release or 0
|
||||
else
|
||||
package.path = nil
|
||||
package.installed_release = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -673,27 +821,40 @@ end
|
||||
function store.sort_packages()
|
||||
local ret = {}
|
||||
|
||||
local auto_install_pkg = resolve_auto_install_spec() -- can be nil
|
||||
|
||||
-- Add installed content
|
||||
for i=1, #store.packages_full_unordered do
|
||||
local package = store.packages_full_unordered[i]
|
||||
if package.path then
|
||||
ret[#ret + 1] = package
|
||||
for _, pkg in ipairs(store.packages_full_unordered) do
|
||||
if pkg.path and pkg ~= auto_install_pkg then
|
||||
ret[#ret + 1] = pkg
|
||||
end
|
||||
end
|
||||
|
||||
-- Sort installed content by title
|
||||
-- Sort installed content first by "is there an update available?", then by title
|
||||
table.sort(ret, function(a, b)
|
||||
local a_updatable = a.installed_release < a.release
|
||||
local b_updatable = b.installed_release < b.release
|
||||
if a_updatable and not b_updatable then
|
||||
return true
|
||||
elseif b_updatable and not a_updatable then
|
||||
return false
|
||||
end
|
||||
|
||||
return a.title < b.title
|
||||
end)
|
||||
|
||||
-- Add uninstalled content
|
||||
for i=1, #store.packages_full_unordered do
|
||||
local package = store.packages_full_unordered[i]
|
||||
if not package.path then
|
||||
ret[#ret + 1] = package
|
||||
for _, pkg in ipairs(store.packages_full_unordered) do
|
||||
if not pkg.path and pkg ~= auto_install_pkg then
|
||||
ret[#ret + 1] = pkg
|
||||
end
|
||||
end
|
||||
|
||||
-- Put the package that will be auto-installed at the very top
|
||||
if auto_install_pkg then
|
||||
table.insert(ret, 1, auto_install_pkg)
|
||||
end
|
||||
|
||||
store.packages_full = ret
|
||||
end
|
||||
|
||||
@@ -732,7 +893,29 @@ function store.filter_packages(query)
|
||||
end
|
||||
end
|
||||
|
||||
local function get_info_formspec(text)
|
||||
local H = 9.5
|
||||
return table.concat({
|
||||
"formspec_version[6]",
|
||||
"size[15.75,9.5]",
|
||||
TOUCHSCREEN_GUI and "padding[0.01,0.01]" or "position[0.5,0.55]",
|
||||
|
||||
"label[4,4.35;", text, "]",
|
||||
"container[0,", H - 0.8 - 0.375, "]",
|
||||
"button[0.375,0;5,0.8;back;", fgettext("Back to Main Menu"), "]",
|
||||
"container_end[]",
|
||||
})
|
||||
end
|
||||
|
||||
function store.get_formspec(dlgdata)
|
||||
if store.loading then
|
||||
return get_info_formspec(fgettext("Loading..."))
|
||||
end
|
||||
if store.load_error then
|
||||
return get_info_formspec(fgettext("No packages could be retrieved"))
|
||||
end
|
||||
assert(store.load_ok)
|
||||
|
||||
store.update_paths()
|
||||
|
||||
dlgdata.pagemax = math.max(math.ceil(#store.packages / num_per_page), 1)
|
||||
@@ -742,82 +925,70 @@ function store.get_formspec(dlgdata)
|
||||
|
||||
local W = 15.75
|
||||
local H = 9.5
|
||||
local formspec
|
||||
if #store.packages_full > 0 then
|
||||
formspec = {
|
||||
"formspec_version[3]",
|
||||
"size[15.75,9.5]",
|
||||
"position[0.5,0.55]",
|
||||
local formspec = {
|
||||
"formspec_version[6]",
|
||||
"size[15.75,9.5]",
|
||||
TOUCHSCREEN_GUI and "padding[0.01,0.01]" or "position[0.5,0.55]",
|
||||
|
||||
"style[status,downloading,queued;border=false]",
|
||||
"style[status,downloading,queued;border=false]",
|
||||
|
||||
"container[0.375,0.375]",
|
||||
"field[0,0;7.225,0.8;search_string;;", core.formspec_escape(search_string), "]",
|
||||
"field_close_on_enter[search_string;false]",
|
||||
"image_button[7.3,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]",
|
||||
"image_button[8.125,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";clear;]",
|
||||
"dropdown[9.6,0;2.4,0.8;type;", table.concat(filter_types_titles, ","), ";", filter_type, "]",
|
||||
"container_end[]",
|
||||
"container[0.375,0.375]",
|
||||
"field[0,0;7.225,0.8;search_string;;", core.formspec_escape(search_string), "]",
|
||||
"field_enter_after_edit[search_string;true]",
|
||||
"image_button[7.3,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]",
|
||||
"image_button[8.125,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";clear;]",
|
||||
"dropdown[9.175,0;2.7875,0.8;type;", table.concat(filter_types_titles, ","), ";", filter_type, "]",
|
||||
"container_end[]",
|
||||
|
||||
-- Page nav buttons
|
||||
"container[0,", H - 0.8 - 0.375, "]",
|
||||
"button[0.375,0;4,0.8;back;", fgettext("Back to Main Menu"), "]",
|
||||
-- Page nav buttons
|
||||
"container[0,", H - 0.8 - 0.375, "]",
|
||||
"button[0.375,0;5,0.8;back;", fgettext("Back to Main Menu"), "]",
|
||||
|
||||
"container[", W - 0.375 - 0.8*4 - 2, ",0]",
|
||||
"image_button[0,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "start_icon.png;pstart;]",
|
||||
"image_button[0.8,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "prev_icon.png;pback;]",
|
||||
"style[pagenum;border=false]",
|
||||
"button[1.6,0;2,0.8;pagenum;", tonumber(cur_page), " / ", tonumber(dlgdata.pagemax), "]",
|
||||
"image_button[3.6,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "next_icon.png;pnext;]",
|
||||
"image_button[4.4,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "end_icon.png;pend;]",
|
||||
"container_end[]",
|
||||
"container[", W - 0.375 - 0.8*4 - 2, ",0]",
|
||||
"image_button[0,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "start_icon.png;pstart;]",
|
||||
"image_button[0.8,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "prev_icon.png;pback;]",
|
||||
"style[pagenum;border=false]",
|
||||
"button[1.6,0;2,0.8;pagenum;", tonumber(cur_page), " / ", tonumber(dlgdata.pagemax), "]",
|
||||
"image_button[3.6,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "next_icon.png;pnext;]",
|
||||
"image_button[4.4,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "end_icon.png;pend;]",
|
||||
"container_end[]",
|
||||
|
||||
"container_end[]",
|
||||
}
|
||||
"container_end[]",
|
||||
}
|
||||
|
||||
if number_downloading > 0 then
|
||||
formspec[#formspec + 1] = "button[12.75,0.375;2.625,0.8;downloading;"
|
||||
if #download_queue > 0 then
|
||||
formspec[#formspec + 1] = fgettext("$1 downloading,\n$2 queued", number_downloading, #download_queue)
|
||||
else
|
||||
formspec[#formspec + 1] = fgettext("$1 downloading...", number_downloading)
|
||||
if number_downloading > 0 then
|
||||
formspec[#formspec + 1] = "button[12.5875,0.375;2.7875,0.8;downloading;"
|
||||
if #download_queue > 0 then
|
||||
formspec[#formspec + 1] = fgettext("$1 downloading,\n$2 queued", number_downloading, #download_queue)
|
||||
else
|
||||
formspec[#formspec + 1] = fgettext("$1 downloading...", number_downloading)
|
||||
end
|
||||
formspec[#formspec + 1] = "]"
|
||||
else
|
||||
local num_avail_updates = 0
|
||||
for i=1, #store.packages_full do
|
||||
local package = store.packages_full[i]
|
||||
if package.path and package.installed_release < package.release and
|
||||
not (package.downloading or package.queued) then
|
||||
num_avail_updates = num_avail_updates + 1
|
||||
end
|
||||
end
|
||||
|
||||
if num_avail_updates == 0 then
|
||||
formspec[#formspec + 1] = "button[12.5875,0.375;2.7875,0.8;status;"
|
||||
formspec[#formspec + 1] = fgettext("No updates")
|
||||
formspec[#formspec + 1] = "]"
|
||||
else
|
||||
local num_avail_updates = 0
|
||||
for i=1, #store.packages_full do
|
||||
local package = store.packages_full[i]
|
||||
if package.path and package.installed_release < package.release and
|
||||
not (package.downloading or package.queued) then
|
||||
num_avail_updates = num_avail_updates + 1
|
||||
end
|
||||
end
|
||||
|
||||
if num_avail_updates == 0 then
|
||||
formspec[#formspec + 1] = "button[12.75,0.375;2.625,0.8;status;"
|
||||
formspec[#formspec + 1] = fgettext("No updates")
|
||||
formspec[#formspec + 1] = "]"
|
||||
else
|
||||
formspec[#formspec + 1] = "button[12.75,0.375;2.625,0.8;update_all;"
|
||||
formspec[#formspec + 1] = fgettext("Update All [$1]", num_avail_updates)
|
||||
formspec[#formspec + 1] = "]"
|
||||
end
|
||||
end
|
||||
|
||||
if #store.packages == 0 then
|
||||
formspec[#formspec + 1] = "label[4,3;"
|
||||
formspec[#formspec + 1] = fgettext("No results")
|
||||
formspec[#formspec + 1] = "button[12.5875,0.375;2.7875,0.8;update_all;"
|
||||
formspec[#formspec + 1] = fgettext("Update All [$1]", num_avail_updates)
|
||||
formspec[#formspec + 1] = "]"
|
||||
end
|
||||
else
|
||||
formspec = {
|
||||
"size[12,7]",
|
||||
"position[0.5,0.55]",
|
||||
"label[4,3;", fgettext("No packages could be retrieved"), "]",
|
||||
"container[0,", H - 0.8 - 0.375, "]",
|
||||
"button[0,0;4,0.8;back;", fgettext("Back to Main Menu"), "]",
|
||||
"container_end[]",
|
||||
}
|
||||
end
|
||||
|
||||
if #store.packages == 0 then
|
||||
formspec[#formspec + 1] = "label[4,4.75;"
|
||||
formspec[#formspec + 1] = fgettext("No results")
|
||||
formspec[#formspec + 1] = "]"
|
||||
end
|
||||
|
||||
-- download/queued tooltips always have the same message
|
||||
@@ -846,7 +1017,10 @@ function store.get_formspec(dlgdata)
|
||||
formspec[#formspec + 1] = "]"
|
||||
|
||||
-- buttons
|
||||
local left_base = "image_button[-1.55,0;0.7,0.7;" .. core.formspec_escape(defaulttexturedir)
|
||||
local description_width = W - 2.625 - 2 * 0.7 - 2 * 0.15
|
||||
|
||||
local second_base = "image_button[-1.55,0;0.7,0.7;" .. core.formspec_escape(defaulttexturedir)
|
||||
local third_base = "image_button[-2.4,0;0.7,0.7;" .. core.formspec_escape(defaulttexturedir)
|
||||
formspec[#formspec + 1] = "container["
|
||||
formspec[#formspec + 1] = W - 0.375*2
|
||||
formspec[#formspec + 1] = ",0.1]"
|
||||
@@ -856,28 +1030,28 @@ function store.get_formspec(dlgdata)
|
||||
formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
|
||||
formspec[#formspec + 1] = "cdb_downloading.png;3;400;]"
|
||||
elseif package.queued then
|
||||
formspec[#formspec + 1] = left_base
|
||||
formspec[#formspec + 1] = second_base
|
||||
formspec[#formspec + 1] = "cdb_queued.png;queued;]"
|
||||
elseif not package.path then
|
||||
local elem_name = "install_" .. i .. ";"
|
||||
formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#71aa34]"
|
||||
formspec[#formspec + 1] = left_base .. "cdb_add.png;" .. elem_name .. "]"
|
||||
formspec[#formspec + 1] = second_base .. "cdb_add.png;" .. elem_name .. "]"
|
||||
formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Install") .. tooltip_colors
|
||||
else
|
||||
if package.installed_release < package.release then
|
||||
|
||||
-- The install_ action also handles updating
|
||||
local elem_name = "install_" .. i .. ";"
|
||||
formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#28ccdf]"
|
||||
formspec[#formspec + 1] = left_base .. "cdb_update.png;" .. elem_name .. "]"
|
||||
formspec[#formspec + 1] = third_base .. "cdb_update.png;" .. elem_name .. "]"
|
||||
formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Update") .. tooltip_colors
|
||||
else
|
||||
|
||||
local elem_name = "uninstall_" .. i .. ";"
|
||||
formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#a93b3b]"
|
||||
formspec[#formspec + 1] = left_base .. "cdb_clear.png;" .. elem_name .. "]"
|
||||
formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Uninstall") .. tooltip_colors
|
||||
description_width = description_width - 0.7 - 0.15
|
||||
end
|
||||
|
||||
local elem_name = "uninstall_" .. i .. ";"
|
||||
formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#a93b3b]"
|
||||
formspec[#formspec + 1] = second_base .. "cdb_clear.png;" .. elem_name .. "]"
|
||||
formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Uninstall") .. tooltip_colors
|
||||
end
|
||||
|
||||
local web_elem_name = "view_" .. i .. ";"
|
||||
@@ -888,7 +1062,6 @@ function store.get_formspec(dlgdata)
|
||||
formspec[#formspec + 1] = "container_end[]"
|
||||
|
||||
-- description
|
||||
local description_width = W - 0.375*5 - 0.85 - 2*0.7
|
||||
formspec[#formspec + 1] = "textarea[1.855,0.3;"
|
||||
formspec[#formspec + 1] = tostring(description_width)
|
||||
formspec[#formspec + 1] = ",0.8;;;"
|
||||
@@ -898,7 +1071,7 @@ function store.get_formspec(dlgdata)
|
||||
formspec[#formspec + 1] = "container_end[]"
|
||||
end
|
||||
|
||||
return table.concat(formspec, "")
|
||||
return table.concat(formspec)
|
||||
end
|
||||
|
||||
function store.handle_submit(this, fields)
|
||||
@@ -952,6 +1125,7 @@ function store.handle_submit(this, fields)
|
||||
local new_type = table.indexof(filter_types_titles, fields.type)
|
||||
if new_type ~= filter_type then
|
||||
filter_type = new_type
|
||||
cur_page = 1
|
||||
store.filter_packages(search_string)
|
||||
return true
|
||||
end
|
||||
@@ -975,39 +1149,7 @@ function store.handle_submit(this, fields)
|
||||
assert(package)
|
||||
|
||||
if fields["install_" .. i] then
|
||||
local install_parent
|
||||
if package.type == "mod" then
|
||||
install_parent = core.get_modpath()
|
||||
elseif package.type == "game" then
|
||||
install_parent = core.get_gamepath()
|
||||
elseif package.type == "txp" then
|
||||
install_parent = core.get_texturepath()
|
||||
else
|
||||
error("Unknown package type: " .. package.type)
|
||||
end
|
||||
|
||||
|
||||
local function on_confirm()
|
||||
local deps = get_raw_dependencies(package)
|
||||
if deps and has_hard_deps(deps) then
|
||||
local dlg = install_dialog.create(package, deps)
|
||||
dlg:set_parent(this)
|
||||
this:hide()
|
||||
dlg:show()
|
||||
else
|
||||
queue_download(package, package.path and REASON_UPDATE or REASON_NEW)
|
||||
end
|
||||
end
|
||||
|
||||
if not package.path and core.is_dir(install_parent .. DIR_DELIM .. package.name) then
|
||||
local dlg = confirm_overwrite.create(package, on_confirm)
|
||||
dlg:set_parent(this)
|
||||
this:hide()
|
||||
dlg:show()
|
||||
else
|
||||
on_confirm()
|
||||
end
|
||||
|
||||
install_or_update_package(this, package)
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -1031,17 +1173,30 @@ function store.handle_submit(this, fields)
|
||||
return false
|
||||
end
|
||||
|
||||
function create_store_dlg(type)
|
||||
if not store.loaded or #store.packages_full == 0 then
|
||||
store.load()
|
||||
function store.handle_events(event)
|
||||
if event == "DialogShow" then
|
||||
-- On mobile, don't show the "MINETEST" header behind the dialog.
|
||||
mm_game_theme.set_engine(TOUCHSCREEN_GUI)
|
||||
|
||||
-- If the store is already loaded, auto-install packages here.
|
||||
do_auto_install()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
store.update_paths()
|
||||
store.sort_packages()
|
||||
return false
|
||||
end
|
||||
|
||||
--- Creates a ContentDB dialog.
|
||||
---
|
||||
--- @param type string | nil
|
||||
--- Sets initial package filter. "game", "mod", "txp" or nil (no filter).
|
||||
--- @param install_spec table | nil
|
||||
--- ContentDB ID of package as returned by pkgmgr.get_contentdb_id().
|
||||
--- Sets package to install or update automatically.
|
||||
function create_store_dlg(type, install_spec)
|
||||
search_string = ""
|
||||
cur_page = 1
|
||||
|
||||
if type then
|
||||
-- table.indexof does not work on tables that contain `nil`
|
||||
for i, v in pairs(filter_types_type) do
|
||||
@@ -1050,12 +1205,19 @@ function create_store_dlg(type)
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
filter_type = 1
|
||||
end
|
||||
|
||||
store.filter_packages(search_string)
|
||||
-- Keep the old auto_install_spec if the caller doesn't specify one.
|
||||
if install_spec then
|
||||
auto_install_spec = install_spec
|
||||
end
|
||||
|
||||
store.load()
|
||||
|
||||
return dialog_create("store",
|
||||
store.get_formspec,
|
||||
store.handle_submit,
|
||||
nil)
|
||||
store.handle_events)
|
||||
end
|
||||
22
builtin/mainmenu/content/init.lua
Normal file
22
builtin/mainmenu/content/init.lua
Normal file
@@ -0,0 +1,22 @@
|
||||
--Minetest
|
||||
--Copyright (C) 2023 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
local path = core.get_mainmenu_path() .. DIR_DELIM .. "content"
|
||||
|
||||
dofile(path .. DIR_DELIM .. "pkgmgr.lua")
|
||||
dofile(path .. DIR_DELIM .. "update_detector.lua")
|
||||
dofile(path .. DIR_DELIM .. "dlg_contentstore.lua")
|
||||
@@ -177,6 +177,7 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack)
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function pkgmgr.get_texture_packs()
|
||||
local txtpath = core.get_texturepath()
|
||||
local txtpath_system = core.get_texturepath_share()
|
||||
@@ -195,6 +196,23 @@ function pkgmgr.get_texture_packs()
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function pkgmgr.get_all()
|
||||
local result = {}
|
||||
|
||||
for _, mod in pairs(pkgmgr.global_mods:get_list()) do
|
||||
result[#result + 1] = mod
|
||||
end
|
||||
for _, game in pairs(pkgmgr.games) do
|
||||
result[#result + 1] = game
|
||||
end
|
||||
for _, txp in pairs(pkgmgr.get_texture_packs()) do
|
||||
result[#result + 1] = txp
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function pkgmgr.get_folder_type(path)
|
||||
local testfile = io.open(path .. DIR_DELIM .. "init.lua","r")
|
||||
@@ -260,7 +278,10 @@ function pkgmgr.is_valid_modname(modpath)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
||||
--- @param render_list filterlist
|
||||
--- @param use_technical_names boolean to show technical names instead of human-readable titles
|
||||
--- @param with_icon table or nil, from virtual path to icon object
|
||||
function pkgmgr.render_packagelist(render_list, use_technical_names, with_icon)
|
||||
if not render_list then
|
||||
if not pkgmgr.global_mods then
|
||||
pkgmgr.refresh_globals()
|
||||
@@ -273,10 +294,10 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
||||
for i, v in ipairs(list) do
|
||||
local color = ""
|
||||
local icon = 0
|
||||
local error = with_error and with_error[v.virtual_path]
|
||||
local function update_error(val)
|
||||
if val and (not error or (error.type == "warning" and val.type == "error")) then
|
||||
error = val
|
||||
local icon_info = with_icon and with_icon[v.virtual_path or v.path]
|
||||
local function update_icon_info(val)
|
||||
if val and (not icon_info or (icon_info.type == "warning" and val.type == "error")) then
|
||||
icon_info = val
|
||||
end
|
||||
end
|
||||
|
||||
@@ -286,8 +307,8 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
||||
|
||||
for j = 1, #rawlist do
|
||||
if rawlist[j].modpack == list[i].name then
|
||||
if with_error then
|
||||
update_error(with_error[rawlist[j].virtual_path])
|
||||
if with_icon then
|
||||
update_icon_info(with_icon[rawlist[j].virtual_path or rawlist[j].path])
|
||||
end
|
||||
|
||||
if rawlist[j].enabled then
|
||||
@@ -303,10 +324,10 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
||||
color = mt_color_blue
|
||||
|
||||
local rawlist = render_list:get_raw_list()
|
||||
if v.type == "game" and with_error then
|
||||
if v.type == "game" and with_icon then
|
||||
for j = 1, #rawlist do
|
||||
if rawlist[j].is_game_content then
|
||||
update_error(with_error[rawlist[j].virtual_path])
|
||||
update_icon_info(with_icon[rawlist[j].virtual_path or rawlist[j].path])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -315,13 +336,17 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
||||
color = mt_color_green
|
||||
end
|
||||
|
||||
if error then
|
||||
if error.type == "warning" then
|
||||
if icon_info then
|
||||
if icon_info.type == "warning" then
|
||||
color = mt_color_orange
|
||||
icon = 2
|
||||
else
|
||||
elseif icon_info.type == "error" then
|
||||
color = mt_color_red
|
||||
icon = 3
|
||||
elseif icon_info.type == "update" then
|
||||
icon = 4
|
||||
else
|
||||
error("Unknown icon type " .. icon_info.type)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -332,7 +357,7 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
||||
retval[#retval + 1] = "0"
|
||||
end
|
||||
|
||||
if with_error then
|
||||
if with_icon then
|
||||
retval[#retval + 1] = icon
|
||||
end
|
||||
|
||||
@@ -534,7 +559,7 @@ function pkgmgr.install_dir(expected_type, path, basename, targetpath)
|
||||
-- There's no good way to detect a texture pack, so let's just assume
|
||||
-- it's correct for now.
|
||||
if basefolder and basefolder.type ~= "invalid" and basefolder.type ~= "txp" then
|
||||
return nil, fgettext("Unable to install a $1 as a texture pack", basefolder.type)
|
||||
return nil, fgettext_ne("Unable to install a $1 as a texture pack", basefolder.type)
|
||||
end
|
||||
|
||||
local from = basefolder and basefolder.path or path
|
||||
@@ -544,17 +569,17 @@ function pkgmgr.install_dir(expected_type, path, basename, targetpath)
|
||||
core.delete_dir(targetpath)
|
||||
if not core.copy_dir(from, targetpath, false) then
|
||||
return nil,
|
||||
fgettext("Failed to install $1 to $2", basename, targetpath)
|
||||
fgettext_ne("Failed to install $1 to $2", basename, targetpath)
|
||||
end
|
||||
return targetpath, nil
|
||||
|
||||
elseif not basefolder then
|
||||
return nil, fgettext("Unable to find a valid mod, modpack, or game")
|
||||
return nil, fgettext_ne("Unable to find a valid mod, modpack, or game")
|
||||
end
|
||||
|
||||
-- Check type
|
||||
if basefolder.type ~= expected_type and (basefolder.type ~= "modpack" or expected_type ~= "mod") then
|
||||
return nil, fgettext("Unable to install a $1 as a $2", basefolder.type, expected_type)
|
||||
return nil, fgettext_ne("Unable to install a $1 as a $2", basefolder.type, expected_type)
|
||||
end
|
||||
|
||||
-- Set targetpath if not predetermined
|
||||
@@ -575,7 +600,7 @@ function pkgmgr.install_dir(expected_type, path, basename, targetpath)
|
||||
targetpath = content_path .. DIR_DELIM .. basename
|
||||
else
|
||||
return nil,
|
||||
fgettext("Install: Unable to find suitable folder name for $1", path)
|
||||
fgettext_ne("Install: Unable to find suitable folder name for $1", path)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -583,7 +608,7 @@ function pkgmgr.install_dir(expected_type, path, basename, targetpath)
|
||||
core.delete_dir(targetpath)
|
||||
if not core.copy_dir(basefolder.path, targetpath, false) then
|
||||
return nil,
|
||||
fgettext("Failed to install $1 to $2", basename, targetpath)
|
||||
fgettext_ne("Failed to install $1 to $2", basename, targetpath)
|
||||
end
|
||||
|
||||
if basefolder.type == "game" then
|
||||
@@ -752,6 +777,30 @@ function pkgmgr.update_gamelist()
|
||||
end)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Returns the ContentDB ID for an installed piece of content.
|
||||
function pkgmgr.get_contentdb_id(content)
|
||||
-- core.get_games() will return "" instead of nil if there is no "author" field.
|
||||
if content.author and content.author ~= "" and content.release > 0 then
|
||||
if content.type == "game" then
|
||||
return content.author:lower() .. "/" .. content.id
|
||||
end
|
||||
return content.author:lower() .. "/" .. content.name
|
||||
end
|
||||
|
||||
-- Until Minetest 5.8.0, Minetest Game was bundled with Minetest.
|
||||
-- Unfortunately, the bundled MTG was not versioned (missing "release"
|
||||
-- field in game.conf).
|
||||
-- Therefore, we consider any installation of MTG that is not versioned,
|
||||
-- has not been cloned from Git, and is not system-wide to be updatable.
|
||||
if content.type == "game" and content.id == "minetest" and content.release == 0 and
|
||||
not core.is_dir(content.path .. "/.git") and core.may_modify_path(content.path) then
|
||||
return "minetest/minetest"
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- read initial data
|
||||
--------------------------------------------------------------------------------
|
||||
@@ -52,12 +52,12 @@ local function reset()
|
||||
function core.get_gamepath()
|
||||
return games_dir
|
||||
end
|
||||
function env.fgettext(fmt, ...)
|
||||
function env.fgettext_ne(fmt, ...)
|
||||
return fmt
|
||||
end
|
||||
|
||||
setfenv(loadfile("builtin/common/misc_helpers.lua"), env)()
|
||||
setfenv(loadfile("builtin/mainmenu/pkgmgr.lua"), env)()
|
||||
setfenv(loadfile("builtin/mainmenu/content/pkgmgr.lua"), env)()
|
||||
|
||||
function env.pkgmgr.update_gamelist()
|
||||
table.insert(calls, { "update_gamelist" })
|
||||
139
builtin/mainmenu/content/update_detector.lua
Normal file
139
builtin/mainmenu/content/update_detector.lua
Normal file
@@ -0,0 +1,139 @@
|
||||
--Minetest
|
||||
--Copyright (C) 2023 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
|
||||
update_detector = {}
|
||||
|
||||
|
||||
if not core.get_http_api then
|
||||
update_detector.get_all = function() return {} end
|
||||
update_detector.get_count = function() return 0 end
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
local has_fetched = false
|
||||
local latest_releases
|
||||
|
||||
|
||||
local function fetch_latest_releases()
|
||||
local version = core.get_version()
|
||||
local base_url = core.settings:get("contentdb_url")
|
||||
local url = base_url ..
|
||||
"/api/updates/?type=mod&type=game&type=txp&protocol_version=" ..
|
||||
core.get_max_supp_proto() .. "&engine_version=" .. core.urlencode(version.string)
|
||||
local http = core.get_http_api()
|
||||
local response = http.fetch_sync({ url = url })
|
||||
if not response.succeeded then
|
||||
return
|
||||
end
|
||||
|
||||
return core.parse_json(response.data)
|
||||
end
|
||||
|
||||
|
||||
--- Get a table from package key (author/name) to latest release id
|
||||
---
|
||||
--- @param callback function that takes a single argument, table or nil
|
||||
local function get_latest_releases(callback)
|
||||
core.handle_async(fetch_latest_releases, nil, callback)
|
||||
end
|
||||
|
||||
|
||||
local function has_packages_from_cdb()
|
||||
pkgmgr.refresh_globals()
|
||||
pkgmgr.update_gamelist()
|
||||
|
||||
for _, content in pairs(pkgmgr.get_all()) do
|
||||
if pkgmgr.get_contentdb_id(content) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--- @returns a new table with all keys lowercase
|
||||
local function lowercase_keys(tab)
|
||||
local ret = {}
|
||||
for key, value in pairs(tab) do
|
||||
ret[key:lower()] = value
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
|
||||
local function fetch()
|
||||
if has_fetched or not has_packages_from_cdb() then
|
||||
return
|
||||
end
|
||||
|
||||
has_fetched = true
|
||||
|
||||
get_latest_releases(function(releases)
|
||||
if not releases then
|
||||
has_fetched = false
|
||||
return
|
||||
end
|
||||
|
||||
latest_releases = lowercase_keys(releases)
|
||||
if update_detector.get_count() > 0 then
|
||||
local maintab = ui.find_by_name("maintab")
|
||||
if not maintab.hidden then
|
||||
ui.update()
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
--- @returns a list of content with an update available
|
||||
function update_detector.get_all()
|
||||
if latest_releases == nil then
|
||||
fetch()
|
||||
return {}
|
||||
end
|
||||
|
||||
pkgmgr.refresh_globals()
|
||||
pkgmgr.update_gamelist()
|
||||
|
||||
local ret = {}
|
||||
local all_content = pkgmgr.get_all()
|
||||
for _, content in ipairs(all_content) do
|
||||
local cdb_id = pkgmgr.get_contentdb_id(content)
|
||||
|
||||
if cdb_id then
|
||||
-- The backend will account for aliases in `latest_releases`
|
||||
local latest_release = latest_releases[cdb_id]
|
||||
if not latest_release and content.type == "game" then
|
||||
latest_release = latest_releases[cdb_id .. "_game"]
|
||||
end
|
||||
|
||||
if latest_release and latest_release > content.release then
|
||||
ret[#ret + 1] = content
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
|
||||
--- @return number of packages with updates available
|
||||
function update_detector.get_count()
|
||||
return #update_detector.get_all()
|
||||
end
|
||||
@@ -245,7 +245,7 @@ local function get_formspec(data)
|
||||
|
||||
return retval ..
|
||||
"tablecolumns[color;tree;image,align=inline,width=1.5,0=" .. core.formspec_escape(defaulttexturedir .. "blank.png") ..
|
||||
",1=" .. core.formspec_escape(defaulttexturedir .. "checkbox_16_white.png") ..
|
||||
",1=" .. core.formspec_escape(defaulttexturedir .. "checkbox_16.png") ..
|
||||
",2=" .. core.formspec_escape(defaulttexturedir .. "error_icon_orange.png") ..
|
||||
",3=" .. core.formspec_escape(defaulttexturedir .. "error_icon_red.png") .. ";text]" ..
|
||||
"table[5.5,0.75;5.75,6;world_config_modlist;" ..
|
||||
|
||||
@@ -91,16 +91,6 @@ local mgv6_biomes = {
|
||||
|
||||
local function create_world_formspec(dialogdata)
|
||||
|
||||
-- Point the player to ContentDB when no games are found
|
||||
if #pkgmgr.games == 0 then
|
||||
return "size[8,2.5,true]" ..
|
||||
"style[label_button;border=false]" ..
|
||||
"button[0.5,0.5;7,0.5;label_button;" ..
|
||||
fgettext("You have no games installed.") .. "]" ..
|
||||
"button[0.5,1.5;2.5,0.5;world_create_open_cdb;" .. fgettext("Install a game") .. "]" ..
|
||||
"button[5.0,1.5;2.5,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]"
|
||||
end
|
||||
|
||||
local current_mg = dialogdata.mg
|
||||
local mapgens = core.get_mapgen_names()
|
||||
|
||||
@@ -310,8 +300,8 @@ local function create_world_formspec(dialogdata)
|
||||
"label[0,2;" .. fgettext("Mapgen") .. "]"..
|
||||
"dropdown[0,2.5;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]"
|
||||
|
||||
-- Warning if only devtest is installed
|
||||
if #pkgmgr.games == 1 and pkgmgr.games[1].id == "devtest" then
|
||||
-- Warning when making a devtest world
|
||||
if game.id == "devtest" then
|
||||
retval = retval ..
|
||||
"container[0,3.5]" ..
|
||||
"box[0,0;5.8,1.7;#ff8800]" ..
|
||||
@@ -363,7 +353,7 @@ local function create_world_buttonhandler(this, fields)
|
||||
|
||||
local message
|
||||
if game == nil then
|
||||
message = fgettext("No game selected")
|
||||
message = fgettext_ne("No game selected")
|
||||
end
|
||||
|
||||
if message == nil then
|
||||
@@ -382,7 +372,7 @@ local function create_world_buttonhandler(this, fields)
|
||||
end
|
||||
|
||||
if menudata.worldlist:uid_exists_raw(worldname) then
|
||||
message = fgettext("A world named \"$1\" already exists", worldname)
|
||||
message = fgettext_ne("A world named \"$1\" already exists", worldname)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ local function delete_content_buttonhandler(this, fields)
|
||||
this.data.content.path ~= core.get_gamepath() and
|
||||
this.data.content.path ~= core.get_texturepath() then
|
||||
if not core.delete_dir(this.data.content.path) then
|
||||
gamedata.errormessage = fgettext("pkgmgr: failed to delete \"$1\"", this.data.content.path)
|
||||
gamedata.errormessage = fgettext_ne("pkgmgr: failed to delete \"$1\"", this.data.content.path)
|
||||
end
|
||||
|
||||
if this.data.content.type == "game" then
|
||||
@@ -43,7 +43,7 @@ local function delete_content_buttonhandler(this, fields)
|
||||
pkgmgr.refresh_globals()
|
||||
end
|
||||
else
|
||||
gamedata.errormessage = fgettext("pkgmgr: invalid path \"$1\"", this.data.content.path)
|
||||
gamedata.errormessage = fgettext_ne("pkgmgr: invalid path \"$1\"", this.data.content.path)
|
||||
end
|
||||
this:delete()
|
||||
return true
|
||||
|
||||
115
builtin/mainmenu/dlg_reinstall_mtg.lua
Normal file
115
builtin/mainmenu/dlg_reinstall_mtg.lua
Normal file
@@ -0,0 +1,115 @@
|
||||
--Minetest
|
||||
--Copyright (C) 2023 Gregor Parzefall
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
function check_reinstall_mtg()
|
||||
if core.settings:get_bool("no_mtg_notification") then
|
||||
return
|
||||
end
|
||||
|
||||
local games = core.get_games()
|
||||
for _, game in ipairs(games) do
|
||||
if game.id == "minetest" then
|
||||
core.settings:set_bool("no_mtg_notification", true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local mtg_world_found = false
|
||||
local worlds = core.get_worlds()
|
||||
for _, world in ipairs(worlds) do
|
||||
if world.gameid == "minetest" then
|
||||
mtg_world_found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not mtg_world_found then
|
||||
core.settings:set_bool("no_mtg_notification", true)
|
||||
return
|
||||
end
|
||||
|
||||
local maintab = ui.find_by_name("maintab")
|
||||
|
||||
local dlg = create_reinstall_mtg_dlg()
|
||||
dlg:set_parent(maintab)
|
||||
maintab:hide()
|
||||
dlg:show()
|
||||
ui.update()
|
||||
end
|
||||
|
||||
local function get_formspec(dialogdata)
|
||||
local markup = table.concat({
|
||||
"<big>", fgettext("Minetest Game is no longer installed by default"), "</big>\n",
|
||||
fgettext("For a long time, the Minetest engine shipped with a default game called \"Minetest Game\". " ..
|
||||
"Since Minetest 5.8.0, Minetest ships without a default game."), "\n",
|
||||
fgettext("If you want to continue playing in your Minetest Game worlds, you need to reinstall Minetest Game."),
|
||||
})
|
||||
|
||||
return table.concat({
|
||||
"formspec_version[6]",
|
||||
"size[12.8,7]",
|
||||
"hypertext[0.375,0.375;12.05,5.2;text;", minetest.formspec_escape(markup), "]",
|
||||
"container[0.375,5.825]",
|
||||
"style[dismiss;bgcolor=red]",
|
||||
"button[0,0;4,0.8;dismiss;", fgettext("Dismiss"), "]",
|
||||
"button[4.25,0;8,0.8;reinstall;", fgettext("Reinstall Minetest Game"), "]",
|
||||
"container_end[]",
|
||||
})
|
||||
end
|
||||
|
||||
local function buttonhandler(this, fields)
|
||||
if fields.reinstall then
|
||||
-- Don't set "no_mtg_notification" here so that the dialog will be shown
|
||||
-- again if downloading MTG fails for whatever reason.
|
||||
this:delete()
|
||||
|
||||
local maintab = ui.find_by_name("maintab")
|
||||
|
||||
local dlg = create_store_dlg(nil, "minetest/minetest")
|
||||
dlg:set_parent(maintab)
|
||||
maintab:hide()
|
||||
dlg:show()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.dismiss then
|
||||
core.settings:set_bool("no_mtg_notification", true)
|
||||
this:delete()
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function eventhandler(event)
|
||||
if event == "DialogShow" then
|
||||
mm_game_theme.set_engine()
|
||||
return true
|
||||
elseif event == "MenuQuit" then
|
||||
-- Don't allow closing the dialog with ESC, but still allow exiting
|
||||
-- Minetest.
|
||||
core.close()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function create_reinstall_mtg_dlg()
|
||||
local dlg = dialog_create("dlg_reinstall_mtg", get_formspec,
|
||||
buttonhandler, eventhandler)
|
||||
return dlg
|
||||
end
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -71,6 +71,15 @@ local function version_info_buttonhandler(this, fields)
|
||||
return false
|
||||
end
|
||||
|
||||
local function version_info_eventhandler(event)
|
||||
if event == "DialogShow" then
|
||||
mm_game_theme.set_engine()
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function create_version_info_dlg(new_version, url)
|
||||
assert(type(new_version) == "string")
|
||||
assert(type(url) == "string")
|
||||
@@ -78,7 +87,7 @@ local function create_version_info_dlg(new_version, url)
|
||||
local retval = dialog_create("version_info",
|
||||
version_info_formspec,
|
||||
version_info_buttonhandler,
|
||||
nil)
|
||||
version_info_eventhandler)
|
||||
|
||||
retval.data.new_version = new_version
|
||||
retval.data.url = url
|
||||
|
||||
@@ -20,10 +20,6 @@ mm_game_theme = {}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_game_theme.init()
|
||||
mm_game_theme.defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" ..
|
||||
DIR_DELIM .. "pack" .. DIR_DELIM
|
||||
mm_game_theme.basetexturedir = mm_game_theme.defaulttexturedir
|
||||
|
||||
mm_game_theme.texturepack = core.settings:get("texture_path")
|
||||
|
||||
mm_game_theme.gameid = nil
|
||||
@@ -32,35 +28,27 @@ function mm_game_theme.init()
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_game_theme.update(tab,gamedetails)
|
||||
if tab ~= "singleplayer" then
|
||||
mm_game_theme.reset()
|
||||
return
|
||||
end
|
||||
|
||||
if gamedetails == nil then
|
||||
return
|
||||
end
|
||||
|
||||
mm_game_theme.update_game(gamedetails)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_game_theme.reset()
|
||||
function mm_game_theme.set_engine(hide_decorations)
|
||||
mm_game_theme.gameid = nil
|
||||
mm_game_theme.stop_music()
|
||||
|
||||
core.set_topleft_text("")
|
||||
|
||||
local have_bg = false
|
||||
local have_overlay = mm_game_theme.set_generic("overlay")
|
||||
local have_overlay = mm_game_theme.set_engine_single("overlay")
|
||||
|
||||
if not have_overlay then
|
||||
have_bg = mm_game_theme.set_generic("background")
|
||||
have_bg = mm_game_theme.set_engine_single("background")
|
||||
end
|
||||
|
||||
mm_game_theme.clear("header")
|
||||
mm_game_theme.clear("footer")
|
||||
mm_game_theme.clear_single("header")
|
||||
mm_game_theme.clear_single("footer")
|
||||
core.set_clouds(false)
|
||||
|
||||
mm_game_theme.set_generic("footer")
|
||||
mm_game_theme.set_generic("header")
|
||||
if not hide_decorations then
|
||||
mm_game_theme.set_engine_single("header")
|
||||
mm_game_theme.set_engine_single("footer")
|
||||
end
|
||||
|
||||
if not have_bg then
|
||||
if core.settings:get_bool("menu_clouds") then
|
||||
@@ -69,51 +57,50 @@ function mm_game_theme.reset()
|
||||
mm_game_theme.set_dirt_bg()
|
||||
end
|
||||
end
|
||||
|
||||
if mm_game_theme.music_handle ~= nil then
|
||||
core.sound_stop(mm_game_theme.music_handle)
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_game_theme.update_game(gamedetails)
|
||||
function mm_game_theme.set_game(gamedetails)
|
||||
assert(gamedetails ~= nil)
|
||||
|
||||
if mm_game_theme.gameid == gamedetails.id then
|
||||
return
|
||||
end
|
||||
mm_game_theme.gameid = gamedetails.id
|
||||
mm_game_theme.set_music(gamedetails)
|
||||
|
||||
core.set_topleft_text(gamedetails.name)
|
||||
|
||||
local have_bg = false
|
||||
local have_overlay = mm_game_theme.set_game("overlay",gamedetails)
|
||||
local have_overlay = mm_game_theme.set_game_single("overlay", gamedetails)
|
||||
|
||||
if not have_overlay then
|
||||
have_bg = mm_game_theme.set_game("background",gamedetails)
|
||||
have_bg = mm_game_theme.set_game_single("background", gamedetails)
|
||||
end
|
||||
|
||||
mm_game_theme.clear("header")
|
||||
mm_game_theme.clear("footer")
|
||||
mm_game_theme.clear_single("header")
|
||||
mm_game_theme.clear_single("footer")
|
||||
core.set_clouds(false)
|
||||
|
||||
if not have_bg then
|
||||
mm_game_theme.set_game_single("header", gamedetails)
|
||||
mm_game_theme.set_game_single("footer", gamedetails)
|
||||
|
||||
if not have_bg then
|
||||
if core.settings:get_bool("menu_clouds") then
|
||||
core.set_clouds(true)
|
||||
else
|
||||
mm_game_theme.set_dirt_bg()
|
||||
end
|
||||
end
|
||||
|
||||
mm_game_theme.set_game("footer",gamedetails)
|
||||
mm_game_theme.set_game("header",gamedetails)
|
||||
|
||||
mm_game_theme.gameid = gamedetails.id
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_game_theme.clear(identifier)
|
||||
function mm_game_theme.clear_single(identifier)
|
||||
core.set_background(identifier,"")
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_game_theme.set_generic(identifier)
|
||||
function mm_game_theme.set_engine_single(identifier)
|
||||
--try texture pack first
|
||||
if mm_game_theme.texturepack ~= nil then
|
||||
local path = mm_game_theme.texturepack .. DIR_DELIM .."menu_" ..
|
||||
@@ -123,25 +110,17 @@ function mm_game_theme.set_generic(identifier)
|
||||
end
|
||||
end
|
||||
|
||||
if mm_game_theme.defaulttexturedir ~= nil then
|
||||
local path = mm_game_theme.defaulttexturedir .. DIR_DELIM .."menu_" ..
|
||||
identifier .. ".png"
|
||||
if core.set_background(identifier,path) then
|
||||
return true
|
||||
end
|
||||
local path = defaulttexturedir .. DIR_DELIM .. "menu_" .. identifier .. ".png"
|
||||
if core.set_background(identifier, path) then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_game_theme.set_game(identifier, gamedetails)
|
||||
|
||||
if gamedetails == nil then
|
||||
return false
|
||||
end
|
||||
|
||||
mm_game_theme.set_music(gamedetails)
|
||||
function mm_game_theme.set_game_single(identifier, gamedetails)
|
||||
assert(gamedetails ~= nil)
|
||||
|
||||
if mm_game_theme.texturepack ~= nil then
|
||||
local path = mm_game_theme.texturepack .. DIR_DELIM ..
|
||||
@@ -194,10 +173,18 @@ function mm_game_theme.set_dirt_bg()
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_game_theme.set_music(gamedetails)
|
||||
function mm_game_theme.stop_music()
|
||||
if mm_game_theme.music_handle ~= nil then
|
||||
core.sound_stop(mm_game_theme.music_handle)
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_game_theme.set_music(gamedetails)
|
||||
mm_game_theme.stop_music()
|
||||
|
||||
assert(gamedetails ~= nil)
|
||||
|
||||
local music_path = gamedetails.path .. DIR_DELIM .. "menu" .. DIR_DELIM .. "theme"
|
||||
mm_game_theme.music_handle = core.sound_play(music_path, true)
|
||||
end
|
||||
|
||||
@@ -28,6 +28,8 @@ local basepath = core.get_builtin_path()
|
||||
defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" ..
|
||||
DIR_DELIM .. "pack" .. DIR_DELIM
|
||||
|
||||
dofile(menupath .. DIR_DELIM .. "misc.lua")
|
||||
|
||||
dofile(basepath .. "common" .. DIR_DELIM .. "filterlist.lua")
|
||||
dofile(basepath .. "fstk" .. DIR_DELIM .. "buttonbar.lua")
|
||||
dofile(basepath .. "fstk" .. DIR_DELIM .. "dialog.lua")
|
||||
@@ -35,27 +37,26 @@ dofile(basepath .. "fstk" .. DIR_DELIM .. "tabview.lua")
|
||||
dofile(basepath .. "fstk" .. DIR_DELIM .. "ui.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "async_event.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "common.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "pkgmgr.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "serverlistmgr.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "game_theme.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "content" .. DIR_DELIM .. "init.lua")
|
||||
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_settings_advanced.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_contentstore.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "settings" .. DIR_DELIM .. "init.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_register.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_version_info.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_reinstall_mtg.lua")
|
||||
|
||||
local tabs = {}
|
||||
|
||||
tabs.settings = dofile(menupath .. DIR_DELIM .. "tab_settings.lua")
|
||||
tabs.content = dofile(menupath .. DIR_DELIM .. "tab_content.lua")
|
||||
tabs.about = dofile(menupath .. DIR_DELIM .. "tab_about.lua")
|
||||
tabs.local_game = dofile(menupath .. DIR_DELIM .. "tab_local.lua")
|
||||
tabs.play_online = dofile(menupath .. DIR_DELIM .. "tab_online.lua")
|
||||
local tabs = {
|
||||
content = dofile(menupath .. DIR_DELIM .. "tab_content.lua"),
|
||||
about = dofile(menupath .. DIR_DELIM .. "tab_about.lua"),
|
||||
local_game = dofile(menupath .. DIR_DELIM .. "tab_local.lua"),
|
||||
play_online = dofile(menupath .. DIR_DELIM .. "tab_online.lua")
|
||||
}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
local function main_event_handler(tabview, event)
|
||||
@@ -86,26 +87,16 @@ local function init_globals()
|
||||
menudata.worldlist:add_sort_mechanism("alphabetic", sort_worlds_alphabetic)
|
||||
menudata.worldlist:set_sortmode("alphabetic")
|
||||
|
||||
local gameid = core.settings:get("menu_last_game")
|
||||
local game = gameid and pkgmgr.find_by_gameid(gameid)
|
||||
if not game then
|
||||
gameid = core.settings:get("default_game") or "minetest"
|
||||
game = pkgmgr.find_by_gameid(gameid)
|
||||
core.settings:set("menu_last_game", gameid)
|
||||
end
|
||||
|
||||
mm_game_theme.init()
|
||||
mm_game_theme.set_engine() -- This is just a fallback.
|
||||
|
||||
-- Create main tabview
|
||||
local tv_main = tabview_create("maintab", {x = 12, y = 5.4}, {x = 0, y = 0})
|
||||
-- note: size would be 15.5,7.1 in real coordinates mode
|
||||
local tv_main = tabview_create("maintab", {x = 15.5, y = 7.1}, {x = 0, y = 0})
|
||||
|
||||
tv_main:set_autosave_tab(true)
|
||||
tv_main:add(tabs.local_game)
|
||||
tv_main:add(tabs.play_online)
|
||||
|
||||
tv_main:add(tabs.content)
|
||||
tv_main:add(tabs.settings)
|
||||
tv_main:add(tabs.about)
|
||||
|
||||
tv_main:set_global_event_handler(main_event_handler)
|
||||
@@ -116,16 +107,25 @@ local function init_globals()
|
||||
tv_main:set_tab(last_tab)
|
||||
end
|
||||
|
||||
-- In case the folder of the last selected game has been deleted,
|
||||
-- display "Minetest" as a header
|
||||
if tv_main.current_tab == "local" and not game then
|
||||
mm_game_theme.reset()
|
||||
end
|
||||
tv_main:set_end_button({
|
||||
icon = defaulttexturedir .. "settings_btn.png",
|
||||
label = fgettext("Settings"),
|
||||
name = "open_settings",
|
||||
on_click = function(tabview)
|
||||
local dlg = create_settings_dlg()
|
||||
dlg:set_parent(tabview)
|
||||
tabview:hide()
|
||||
dlg:show()
|
||||
return true
|
||||
end,
|
||||
})
|
||||
|
||||
ui.set_default("maintab")
|
||||
check_new_version()
|
||||
tv_main:show()
|
||||
ui.update()
|
||||
|
||||
check_reinstall_mtg()
|
||||
check_new_version()
|
||||
end
|
||||
|
||||
init_globals()
|
||||
|
||||
6
builtin/mainmenu/misc.lua
Normal file
6
builtin/mainmenu/misc.lua
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
-- old non-method sound function
|
||||
|
||||
function core.sound_stop(handle, ...)
|
||||
return handle:stop(...)
|
||||
end
|
||||
407
builtin/mainmenu/settings/components.lua
Normal file
407
builtin/mainmenu/settings/components.lua
Normal file
@@ -0,0 +1,407 @@
|
||||
--Minetest
|
||||
--Copyright (C) 2022 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
local make = {}
|
||||
|
||||
|
||||
-- This file defines various component constructors, of the form:
|
||||
--
|
||||
-- make.component(setting)
|
||||
--
|
||||
-- `setting` is a table representing the settingtype.
|
||||
--
|
||||
-- A component is a table with the following:
|
||||
--
|
||||
-- * `full_width`: (Optional) true if the component shouldn't reserve space for info / reset.
|
||||
-- * `info_text`: (Optional) string, informational text shown in an info icon.
|
||||
-- * `setting`: (Optional) the setting.
|
||||
-- * `max_w`: (Optional) maximum width, `avail_w` will never exceed this.
|
||||
-- * `resettable`: (Optional) if this is true, a reset button is shown.
|
||||
-- * `get_formspec = function(self, avail_w)`:
|
||||
-- * `avail_w` is the available width for the component.
|
||||
-- * Returns `fs, used_height`.
|
||||
-- * `fs` is a string for the formspec.
|
||||
-- Components should be relative to `0,0`, and not exceed `avail_w` or the returned `used_height`.
|
||||
-- * `used_height` is the space used by components in `fs`.
|
||||
-- * `on_submit = function(self, fields, parent)`:
|
||||
-- * `fields`: submitted formspec fields
|
||||
-- * `parent`: the fstk element for the settings UI, use to show dialogs
|
||||
-- * Return true if the event was handled, to prevent future components receiving it.
|
||||
|
||||
|
||||
local function get_label(setting)
|
||||
local show_technical_names = core.settings:get_bool("show_technical_names")
|
||||
if not show_technical_names and setting.readable_name then
|
||||
return fgettext(setting.readable_name)
|
||||
end
|
||||
return setting.name
|
||||
end
|
||||
|
||||
|
||||
local function is_valid_number(value)
|
||||
return type(value) == "number" and not (value ~= value or value >= math.huge or value <= -math.huge)
|
||||
end
|
||||
|
||||
|
||||
function make.heading(text)
|
||||
return {
|
||||
full_width = true,
|
||||
get_formspec = function(self, avail_w)
|
||||
return ("label[0,0.6;%s]box[0,0.9;%f,0.05;#ccc6]"):format(core.formspec_escape(text), avail_w), 1.2
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
--- Used for string and numeric style fields
|
||||
---
|
||||
--- @param converter Function to coerce values from strings.
|
||||
--- @param validator Validator function, optional. Returns true when valid.
|
||||
--- @param stringifier Function to convert values to strings, optional.
|
||||
local function make_field(converter, validator, stringifier)
|
||||
return function(setting)
|
||||
return {
|
||||
info_text = setting.comment,
|
||||
setting = setting,
|
||||
|
||||
get_formspec = function(self, avail_w)
|
||||
local value = core.settings:get(setting.name) or setting.default
|
||||
self.resettable = core.settings:has(setting.name)
|
||||
|
||||
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
|
||||
avail_w - 1.5, setting.name, get_label(setting), core.formspec_escape(value))
|
||||
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name)
|
||||
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))
|
||||
|
||||
return fs, 1.1
|
||||
end,
|
||||
|
||||
on_submit = function(self, fields)
|
||||
if fields["set_" .. setting.name] or fields.key_enter_field == setting.name then
|
||||
local value = converter(fields[setting.name])
|
||||
if value == nil or (validator and not validator(value)) then
|
||||
return true
|
||||
end
|
||||
|
||||
if setting.min then
|
||||
value = math.max(value, setting.min)
|
||||
end
|
||||
if setting.max then
|
||||
value = math.min(value, setting.max)
|
||||
end
|
||||
core.settings:set(setting.name, (stringifier or tostring)(value))
|
||||
return true
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
make.float = make_field(tonumber, is_valid_number, function(x)
|
||||
local str = tostring(x)
|
||||
if str:match("^[+-]?%d+$") then
|
||||
str = str .. ".0"
|
||||
end
|
||||
return str
|
||||
end)
|
||||
make.int = make_field(function(x)
|
||||
local value = tonumber(x)
|
||||
return value and math.floor(value)
|
||||
end, is_valid_number)
|
||||
make.string = make_field(tostring, nil)
|
||||
|
||||
|
||||
function make.bool(setting)
|
||||
return {
|
||||
info_text = setting.comment,
|
||||
setting = setting,
|
||||
|
||||
get_formspec = function(self, avail_w)
|
||||
local value = core.settings:get_bool(setting.name, core.is_yes(setting.default))
|
||||
self.resettable = core.settings:has(setting.name)
|
||||
|
||||
local fs = ("checkbox[0,0.25;%s;%s;%s]"):format(
|
||||
setting.name, get_label(setting), tostring(value))
|
||||
return fs, 0.5
|
||||
end,
|
||||
|
||||
on_submit = function(self, fields)
|
||||
if fields[setting.name] == nil then
|
||||
return false
|
||||
end
|
||||
|
||||
core.settings:set_bool(setting.name, core.is_yes(fields[setting.name]))
|
||||
return true
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
function make.enum(setting)
|
||||
return {
|
||||
info_text = setting.comment,
|
||||
setting = setting,
|
||||
max_w = 4.5,
|
||||
|
||||
get_formspec = function(self, avail_w)
|
||||
local value = core.settings:get(setting.name) or setting.default
|
||||
self.resettable = core.settings:has(setting.name)
|
||||
|
||||
local labels = setting.option_labels or {}
|
||||
|
||||
local items = {}
|
||||
for i, option in ipairs(setting.values) do
|
||||
items[i] = core.formspec_escape(labels[option] or option)
|
||||
end
|
||||
|
||||
local selected_idx = table.indexof(setting.values, value)
|
||||
local fs = "label[0,0.1;" .. get_label(setting) .. "]"
|
||||
|
||||
fs = fs .. ("dropdown[0,0.3;%f,0.8;%s;%s;%d;true]"):format(
|
||||
avail_w, setting.name, table.concat(items, ","), selected_idx, value)
|
||||
|
||||
return fs, 1.1
|
||||
end,
|
||||
|
||||
on_submit = function(self, fields)
|
||||
local old_value = core.settings:get(setting.name) or setting.default
|
||||
local idx = tonumber(fields[setting.name]) or 0
|
||||
local value = setting.values[idx]
|
||||
if value == nil or value == old_value then
|
||||
return false
|
||||
end
|
||||
|
||||
core.settings:set(setting.name, value)
|
||||
return true
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
local function make_path(setting)
|
||||
return {
|
||||
info_text = setting.comment,
|
||||
setting = setting,
|
||||
|
||||
get_formspec = function(self, avail_w)
|
||||
local value = core.settings:get(setting.name) or setting.default
|
||||
self.resettable = core.settings:has(setting.name)
|
||||
|
||||
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
|
||||
avail_w - 3, setting.name, get_label(setting), core.formspec_escape(value))
|
||||
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 3, "pick_" .. setting.name, fgettext("Browse"))
|
||||
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))
|
||||
|
||||
return fs, 1.1
|
||||
end,
|
||||
|
||||
on_submit = function(self, fields)
|
||||
local dialog_name = "dlg_path_" .. setting.name
|
||||
if fields["pick_" .. setting.name] then
|
||||
local is_file = setting.type ~= "path"
|
||||
core.show_path_select_dialog(dialog_name,
|
||||
is_file and fgettext_ne("Select file") or fgettext_ne("Select directory"), is_file)
|
||||
return true
|
||||
end
|
||||
if fields[dialog_name .. "_accepted"] then
|
||||
local value = fields[dialog_name .. "_accepted"]
|
||||
if value ~= nil then
|
||||
core.settings:set(setting.name, value)
|
||||
end
|
||||
return true
|
||||
end
|
||||
if fields["set_" .. setting.name] or fields.key_enter_field == setting.name then
|
||||
local value = fields[setting.name]
|
||||
if value ~= nil then
|
||||
core.settings:set(setting.name, value)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
if PLATFORM == "Android" then
|
||||
-- The Irrlicht file picker doesn't work on Android.
|
||||
make.path = make.string
|
||||
make.filepath = make.string
|
||||
else
|
||||
make.path = make_path
|
||||
make.filepath = make_path
|
||||
end
|
||||
|
||||
|
||||
function make.v3f(setting)
|
||||
return {
|
||||
info_text = setting.comment,
|
||||
setting = setting,
|
||||
|
||||
get_formspec = function(self, avail_w)
|
||||
local value = vector.from_string(core.settings:get(setting.name) or setting.default)
|
||||
self.resettable = core.settings:has(setting.name)
|
||||
|
||||
-- Allocate space for "Set" button
|
||||
avail_w = avail_w - 1
|
||||
|
||||
local fs = "label[0,0.1;" .. get_label(setting) .. "]"
|
||||
|
||||
local field_width = (avail_w - 3*0.25) / 3
|
||||
|
||||
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
|
||||
0, field_width, setting.name .. "_x", "X", value.x)
|
||||
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
|
||||
field_width + 0.25, field_width, setting.name .. "_y", "Y", value.y)
|
||||
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
|
||||
2 * (field_width + 0.25), field_width, setting.name .. "_z", "Z", value.z)
|
||||
|
||||
fs = fs .. ("button[%f,0.6;1,0.8;%s;%s]"):format(avail_w, "set_" .. setting.name, fgettext("Set"))
|
||||
|
||||
return fs, 1.4
|
||||
end,
|
||||
|
||||
on_submit = function(self, fields)
|
||||
if fields["set_" .. setting.name] or
|
||||
fields.key_enter_field == setting.name .. "_x" or
|
||||
fields.key_enter_field == setting.name .. "_y" or
|
||||
fields.key_enter_field == setting.name .. "_z" then
|
||||
local x = tonumber(fields[setting.name .. "_x"])
|
||||
local y = tonumber(fields[setting.name .. "_y"])
|
||||
local z = tonumber(fields[setting.name .. "_z"])
|
||||
if is_valid_number(x) and is_valid_number(y) and is_valid_number(z) then
|
||||
core.settings:set(setting.name, vector.new(x, y, z):to_string())
|
||||
else
|
||||
core.log("error", "Invalid vector: " .. dump({x, y, z}))
|
||||
end
|
||||
return true
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
function make.flags(setting)
|
||||
local checkboxes = {}
|
||||
|
||||
return {
|
||||
info_text = setting.comment,
|
||||
setting = setting,
|
||||
|
||||
get_formspec = function(self, avail_w)
|
||||
local fs = {
|
||||
"label[0,0.1;" .. get_label(setting) .. "]",
|
||||
}
|
||||
|
||||
local value = core.settings:get(setting.name) or setting.default
|
||||
self.resettable = core.settings:has(setting.name)
|
||||
|
||||
checkboxes = {}
|
||||
for _, name in ipairs(value:split(",")) do
|
||||
name = name:trim()
|
||||
if name:sub(1, 2) == "no" then
|
||||
checkboxes[name:sub(3)] = false
|
||||
elseif name ~= "" then
|
||||
checkboxes[name] = true
|
||||
end
|
||||
end
|
||||
|
||||
local columns = math.max(math.floor(avail_w / 2.5), 1)
|
||||
local column_width = avail_w / columns
|
||||
local x = 0
|
||||
local y = 0.55
|
||||
|
||||
for _, possible in ipairs(setting.possible) do
|
||||
if possible:sub(1, 2) ~= "no" then
|
||||
if x >= avail_w then
|
||||
x = 0
|
||||
y = y + 0.5
|
||||
end
|
||||
|
||||
local is_checked = checkboxes[possible]
|
||||
fs[#fs + 1] = ("checkbox[%f,%f;%s;%s;%s]"):format(
|
||||
x, y, setting.name .. "_" .. possible,
|
||||
core.formspec_escape(possible), tostring(is_checked))
|
||||
x = x + column_width
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat(fs, ""), y + 0.25
|
||||
end,
|
||||
|
||||
on_submit = function(self, fields)
|
||||
local changed = false
|
||||
for name, _ in pairs(checkboxes) do
|
||||
local value = fields[setting.name .. "_" .. name]
|
||||
if value ~= nil then
|
||||
checkboxes[name] = core.is_yes(value)
|
||||
changed = true
|
||||
end
|
||||
end
|
||||
|
||||
if changed then
|
||||
local values = {}
|
||||
for _, name in ipairs(setting.possible) do
|
||||
if name:sub(1, 2) ~= "no" then
|
||||
if checkboxes[name] then
|
||||
table.insert(values, name)
|
||||
else
|
||||
table.insert(values, "no" .. name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
core.settings:set(setting.name, table.concat(values, ","))
|
||||
end
|
||||
return changed
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
local function make_noise_params(setting)
|
||||
return {
|
||||
info_text = setting.comment,
|
||||
setting = setting,
|
||||
|
||||
get_formspec = function(self, avail_w)
|
||||
-- The "defaults" noise parameter flag doesn't reset a noise
|
||||
-- setting to its default value, so we offer a regular reset button.
|
||||
self.resettable = core.settings:has(setting.name)
|
||||
|
||||
local fs = "label[0,0.4;" .. get_label(setting) .. "]" ..
|
||||
("button[%f,0;2.5,0.8;%s;%s]"):format(avail_w - 2.5, "edit_" .. setting.name, fgettext("Edit"))
|
||||
return fs, 0.8
|
||||
end,
|
||||
|
||||
on_submit = function(self, fields, tabview)
|
||||
if fields["edit_" .. setting.name] then
|
||||
local dlg = create_change_mapgen_flags_dlg(setting)
|
||||
dlg:set_parent(tabview)
|
||||
tabview:hide()
|
||||
dlg:show()
|
||||
|
||||
return true
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
make.noise_params_2d = make_noise_params
|
||||
make.noise_params_3d = make_noise_params
|
||||
|
||||
|
||||
return make
|
||||
252
builtin/mainmenu/settings/dlg_change_mapgen_flags.lua
Normal file
252
builtin/mainmenu/settings/dlg_change_mapgen_flags.lua
Normal file
@@ -0,0 +1,252 @@
|
||||
--Minetest
|
||||
--Copyright (C) 2015 PilzAdam
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
|
||||
local checkboxes = {}
|
||||
|
||||
local function flags_to_table(flags)
|
||||
return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split
|
||||
end
|
||||
|
||||
local function get_current_np_group(setting)
|
||||
local value = core.settings:get_np_group(setting.name)
|
||||
if value == nil then
|
||||
return setting.values
|
||||
end
|
||||
local p = "%g"
|
||||
return {
|
||||
p:format(value.offset),
|
||||
p:format(value.scale),
|
||||
p:format(value.spread.x),
|
||||
p:format(value.spread.y),
|
||||
p:format(value.spread.z),
|
||||
p:format(value.seed),
|
||||
p:format(value.octaves),
|
||||
p:format(value.persistence),
|
||||
p:format(value.lacunarity),
|
||||
value.flags
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
local function get_formspec(dialogdata)
|
||||
local setting = dialogdata.setting
|
||||
|
||||
-- Final formspec will be created at the end of this function
|
||||
-- Default values below, may be changed depending on setting type
|
||||
local width = 10
|
||||
local height = 2
|
||||
local description_height = 1.5
|
||||
|
||||
local t = get_current_np_group(setting)
|
||||
local dimension = 3
|
||||
if setting.type == "noise_params_2d" then
|
||||
dimension = 2
|
||||
end
|
||||
|
||||
local fields = {}
|
||||
local function add_field(x, name, label, value)
|
||||
fields[#fields + 1] = ("field[%f,%f;3.3,1;%s;%s;%s]"):format(
|
||||
x, height, name, label, core.formspec_escape(value or "")
|
||||
)
|
||||
end
|
||||
-- First row
|
||||
height = height + 0.3
|
||||
add_field(0.3, "te_offset", fgettext("Offset"), t[1])
|
||||
add_field(3.6, "te_scale", fgettext("Scale"), t[2])
|
||||
add_field(6.9, "te_seed", fgettext("Seed"), t[6])
|
||||
height = height + 1.1
|
||||
|
||||
-- Second row
|
||||
add_field(0.3, "te_spreadx", fgettext("X spread"), t[3])
|
||||
if dimension == 3 then
|
||||
add_field(3.6, "te_spready", fgettext("Y spread"), t[4])
|
||||
else
|
||||
fields[#fields + 1] = "label[4," .. height - 0.2 .. ";" ..
|
||||
fgettext("2D Noise") .. "]"
|
||||
end
|
||||
add_field(6.9, "te_spreadz", fgettext("Z spread"), t[5])
|
||||
height = height + 1.1
|
||||
|
||||
-- Third row
|
||||
add_field(0.3, "te_octaves", fgettext("Octaves"), t[7])
|
||||
add_field(3.6, "te_persist", fgettext("Persistence"), t[8])
|
||||
add_field(6.9, "te_lacun", fgettext("Lacunarity"), t[9])
|
||||
height = height + 1.1
|
||||
|
||||
|
||||
local enabled_flags = flags_to_table(t[10])
|
||||
local flags = {}
|
||||
for _, name in ipairs(enabled_flags) do
|
||||
-- Index by name, to avoid iterating over all enabled_flags for every possible flag.
|
||||
flags[name] = true
|
||||
end
|
||||
for _, name in ipairs(setting.flags) do
|
||||
local checkbox_name = "cb_" .. name
|
||||
local is_enabled = flags[name] == true -- to get false if nil
|
||||
checkboxes[checkbox_name] = is_enabled
|
||||
end
|
||||
|
||||
local formspec = table.concat(fields)
|
||||
.. "checkbox[0.5," .. height - 0.6 .. ";cb_defaults;"
|
||||
--[[~ "defaults" is a noise parameter flag.
|
||||
It describes the default processing options
|
||||
for noise settings in the settings menu. ]]
|
||||
.. fgettext("defaults") .. ";" -- defaults
|
||||
.. tostring(flags["defaults"] == true) .. "]" -- to get false if nil
|
||||
.. "checkbox[5," .. height - 0.6 .. ";cb_eased;"
|
||||
--[[~ "eased" is a noise parameter flag.
|
||||
It is used to make the map smoother and
|
||||
can be enabled in noise settings in
|
||||
the settings menu. ]]
|
||||
.. fgettext("eased") .. ";" -- eased
|
||||
.. tostring(flags["eased"] == true) .. "]"
|
||||
.. "checkbox[5," .. height - 0.15 .. ";cb_absvalue;"
|
||||
--[[~ "absvalue" is a noise parameter flag.
|
||||
It is short for "absolute value".
|
||||
It can be enabled in noise settings in
|
||||
the settings menu. ]]
|
||||
.. fgettext("absvalue") .. ";" -- absvalue
|
||||
.. tostring(flags["absvalue"] == true) .. "]"
|
||||
|
||||
height = height + 1
|
||||
|
||||
-- Box good, textarea bad. Calculate textarea size from box.
|
||||
local function create_textfield(size, label, text, bg_color)
|
||||
local textarea = {
|
||||
x = size.x + 0.3,
|
||||
y = size.y,
|
||||
w = size.w + 0.25,
|
||||
h = size.h * 1.16 + 0.12
|
||||
}
|
||||
return ("box[%f,%f;%f,%f;%s]textarea[%f,%f;%f,%f;;%s;%s]"):format(
|
||||
size.x, size.y, size.w, size.h, bg_color or "#000",
|
||||
textarea.x, textarea.y, textarea.w, textarea.h,
|
||||
core.formspec_escape(label), core.formspec_escape(text)
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
-- When there's an error: Shrink description textarea and add error below
|
||||
if dialogdata.error_message then
|
||||
local error_box = {
|
||||
x = 0,
|
||||
y = description_height - 0.4,
|
||||
w = width - 0.25,
|
||||
h = 0.5
|
||||
}
|
||||
formspec = formspec ..
|
||||
create_textfield(error_box, "", dialogdata.error_message, "#600")
|
||||
description_height = description_height - 0.75
|
||||
end
|
||||
|
||||
-- Get description field
|
||||
local description_box = {
|
||||
x = 0,
|
||||
y = 0.2,
|
||||
w = width - 0.25,
|
||||
h = description_height
|
||||
}
|
||||
|
||||
local setting_name = setting.name
|
||||
if setting.readable_name then
|
||||
setting_name = fgettext_ne(setting.readable_name) ..
|
||||
" (" .. setting.name .. ")"
|
||||
end
|
||||
|
||||
local comment_text
|
||||
if setting.comment == "" then
|
||||
comment_text = fgettext_ne("(No description of setting given)")
|
||||
else
|
||||
comment_text = fgettext_ne(setting.comment)
|
||||
end
|
||||
|
||||
return (
|
||||
"size[" .. width .. "," .. height + 0.25 .. ",true]" ..
|
||||
create_textfield(description_box, setting_name, comment_text) ..
|
||||
formspec ..
|
||||
"button[" .. width / 2 - 2.5 .. "," .. height - 0.4 .. ";2.5,1;btn_done;" ..
|
||||
fgettext("Save") .. "]" ..
|
||||
"button[" .. width / 2 .. "," .. height - 0.4 .. ";2.5,1;btn_cancel;" ..
|
||||
fgettext("Cancel") .. "]"
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
local function buttonhandler(this, fields)
|
||||
local setting = this.data.setting
|
||||
if fields["btn_done"] or fields["key_enter"] then
|
||||
local np_flags = {}
|
||||
for _, name in ipairs(setting.flags) do
|
||||
if checkboxes["cb_" .. name] then
|
||||
table.insert(np_flags, name)
|
||||
end
|
||||
end
|
||||
|
||||
checkboxes = {}
|
||||
|
||||
if setting.type == "noise_params_2d" then
|
||||
fields["te_spready"] = fields["te_spreadz"]
|
||||
end
|
||||
local new_value = {
|
||||
offset = fields["te_offset"],
|
||||
scale = fields["te_scale"],
|
||||
spread = {
|
||||
x = fields["te_spreadx"],
|
||||
y = fields["te_spready"],
|
||||
z = fields["te_spreadz"]
|
||||
},
|
||||
seed = fields["te_seed"],
|
||||
octaves = fields["te_octaves"],
|
||||
persistence = fields["te_persist"],
|
||||
lacunarity = fields["te_lacun"],
|
||||
flags = table.concat(np_flags, ", ")
|
||||
}
|
||||
core.settings:set_np_group(setting.name, new_value)
|
||||
|
||||
core.settings:write()
|
||||
this:delete()
|
||||
return true
|
||||
end
|
||||
|
||||
if fields["btn_cancel"] then
|
||||
this:delete()
|
||||
return true
|
||||
end
|
||||
|
||||
for name, value in pairs(fields) do
|
||||
if name:sub(1, 3) == "cb_" then
|
||||
checkboxes[name] = core.is_yes(value)
|
||||
return false -- Don't update the formspec!
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
function create_change_mapgen_flags_dlg(setting)
|
||||
assert(type(setting) == "table")
|
||||
|
||||
local retval = dialog_create("dlg_change_mapgen_flags",
|
||||
get_formspec,
|
||||
buttonhandler,
|
||||
nil)
|
||||
|
||||
retval.data.setting = setting
|
||||
return retval
|
||||
end
|
||||
725
builtin/mainmenu/settings/dlg_settings.lua
Normal file
725
builtin/mainmenu/settings/dlg_settings.lua
Normal file
@@ -0,0 +1,725 @@
|
||||
--Minetest
|
||||
--Copyright (C) 2022 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
|
||||
local component_funcs = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
|
||||
"settings" .. DIR_DELIM .. "components.lua")
|
||||
|
||||
local shadows_component = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
|
||||
"settings" .. DIR_DELIM .. "shadows_component.lua")
|
||||
|
||||
|
||||
local full_settings = settingtypes.parse_config_file(false, true)
|
||||
local info_icon_path = core.formspec_escape(defaulttexturedir .. "settings_info.png")
|
||||
local reset_icon_path = core.formspec_escape(defaulttexturedir .. "settings_reset.png")
|
||||
|
||||
local all_pages = {}
|
||||
local page_by_id = {}
|
||||
local filtered_pages = all_pages
|
||||
local filtered_page_by_id = page_by_id
|
||||
|
||||
local function add_page(page)
|
||||
assert(type(page.id) == "string")
|
||||
assert(type(page.title) == "string")
|
||||
assert(page.section == nil or type(page.section) == "string")
|
||||
assert(type(page.content) == "table")
|
||||
|
||||
assert(not page_by_id[page.id], "Page " .. page.id .. " already registered")
|
||||
|
||||
all_pages[#all_pages + 1] = page
|
||||
page_by_id[page.id] = page
|
||||
return page
|
||||
end
|
||||
|
||||
|
||||
local change_keys = {
|
||||
query_text = "Change Keys",
|
||||
requires = {
|
||||
keyboard_mouse = true,
|
||||
},
|
||||
get_formspec = function(self, avail_w)
|
||||
local btn_w = math.min(avail_w, 3)
|
||||
return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Change Keys")), 0.8
|
||||
end,
|
||||
on_submit = function(self, fields)
|
||||
if fields.btn_change_keys then
|
||||
core.show_keys_menu()
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
add_page({
|
||||
id = "accessibility",
|
||||
title = fgettext_ne("Accessibility"),
|
||||
content = {
|
||||
"language",
|
||||
{ heading = fgettext_ne("General") },
|
||||
"font_size",
|
||||
"chat_font_size",
|
||||
"gui_scaling",
|
||||
"hud_scaling",
|
||||
"show_nametag_backgrounds",
|
||||
{ heading = fgettext_ne("Chat") },
|
||||
"console_height",
|
||||
"console_alpha",
|
||||
"console_color",
|
||||
{ heading = fgettext_ne("Controls") },
|
||||
"autojump",
|
||||
"safe_dig_and_place",
|
||||
{ heading = fgettext_ne("Movement") },
|
||||
"arm_inertia",
|
||||
"view_bobbing_amount",
|
||||
"fall_bobbing_amount",
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
local function load_settingtypes()
|
||||
local page = nil
|
||||
local section = nil
|
||||
local function ensure_page_started()
|
||||
if not page then
|
||||
page = add_page({
|
||||
id = (section or "general"):lower():gsub(" ", "_"),
|
||||
title = section or fgettext_ne("General"),
|
||||
section = section,
|
||||
content = {},
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
for _, entry in ipairs(full_settings) do
|
||||
if entry.type == "category" then
|
||||
if entry.level == 0 then
|
||||
section = entry.name
|
||||
page = nil
|
||||
elseif entry.level == 1 then
|
||||
page = {
|
||||
id = ((section and section .. "_" or "") .. entry.name):lower():gsub(" ", "_"),
|
||||
title = entry.readable_name or entry.name,
|
||||
section = section,
|
||||
content = {},
|
||||
}
|
||||
|
||||
page = add_page(page)
|
||||
elseif entry.level == 2 then
|
||||
ensure_page_started()
|
||||
page.content[#page.content + 1] = {
|
||||
heading = fgettext_ne(entry.readable_name or entry.name),
|
||||
}
|
||||
end
|
||||
else
|
||||
ensure_page_started()
|
||||
page.content[#page.content + 1] = entry.name
|
||||
end
|
||||
end
|
||||
end
|
||||
load_settingtypes()
|
||||
|
||||
table.insert(page_by_id.controls_keyboard_and_mouse.content, 1, change_keys)
|
||||
do
|
||||
local content = page_by_id.graphics_and_audio_shaders.content
|
||||
local idx = table.indexof(content, "enable_dynamic_shadows")
|
||||
table.insert(content, idx, shadows_component)
|
||||
end
|
||||
|
||||
|
||||
local function get_setting_info(name)
|
||||
for _, entry in ipairs(full_settings) do
|
||||
if entry.type ~= "category" and entry.name == name then
|
||||
return entry
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
-- These must not be translated, as they need to show in the local
|
||||
-- language no matter the user's current language.
|
||||
-- This list must be kept in sync with src/unsupported_language_list.txt.
|
||||
get_setting_info("language").option_labels = {
|
||||
[""] = fgettext_ne("(Use system language)"),
|
||||
--ar = " [ar]", blacklisted
|
||||
be = "Беларуская [be]",
|
||||
bg = "Български [bg]",
|
||||
ca = "Català [ca]",
|
||||
cs = "Česky [cs]",
|
||||
cy = "Cymraeg [cy]",
|
||||
da = "Dansk [da]",
|
||||
de = "Deutsch [de]",
|
||||
--dv = " [dv]", blacklisted
|
||||
el = "Ελληνικά [el]",
|
||||
en = "English [en]",
|
||||
eo = "Esperanto [eo]",
|
||||
es = "Español [es]",
|
||||
et = "Eesti [et]",
|
||||
eu = "Euskara [eu]",
|
||||
fi = "Suomi [fi]",
|
||||
fil = "Wikang Filipino [fil]",
|
||||
fr = "Français [fr]",
|
||||
gd = "Gàidhlig [gd]",
|
||||
gl = "Galego [gl]",
|
||||
--he = " [he]", blacklisted
|
||||
--hi = " [hi]", blacklisted
|
||||
hu = "Magyar [hu]",
|
||||
id = "Bahasa Indonesia [id]",
|
||||
it = "Italiano [it]",
|
||||
ja = "日本語 [ja]",
|
||||
jbo = "Lojban [jbo]",
|
||||
kk = "Қазақша [kk]",
|
||||
--kn = " [kn]", blacklisted
|
||||
ko = "한국어 [ko]",
|
||||
ky = "Kırgızca / Кыргызча [ky]",
|
||||
lt = "Lietuvių [lt]",
|
||||
lv = "Latviešu [lv]",
|
||||
mn = "Монгол [mn]",
|
||||
mr = "मराठी [mr]",
|
||||
ms = "Bahasa Melayu [ms]",
|
||||
--ms_Arab = " [ms_Arab]", blacklisted
|
||||
nb = "Norsk Bokmål [nb]",
|
||||
nl = "Nederlands [nl]",
|
||||
nn = "Norsk Nynorsk [nn]",
|
||||
oc = "Occitan [oc]",
|
||||
pl = "Polski [pl]",
|
||||
pt = "Português [pt]",
|
||||
pt_BR = "Português do Brasil [pt_BR]",
|
||||
ro = "Română [ro]",
|
||||
ru = "Русский [ru]",
|
||||
sk = "Slovenčina [sk]",
|
||||
sl = "Slovenščina [sl]",
|
||||
sr_Cyrl = "Српски [sr_Cyrl]",
|
||||
sr_Latn = "Srpski (Latinica) [sr_Latn]",
|
||||
sv = "Svenska [sv]",
|
||||
sw = "Kiswahili [sw]",
|
||||
--th = " [th]", blacklisted
|
||||
tr = "Türkçe [tr]",
|
||||
tt = "Tatarça [tt]",
|
||||
uk = "Українська [uk]",
|
||||
vi = "Tiếng Việt [vi]",
|
||||
zh_CN = "中文 (简体) [zh_CN]",
|
||||
zh_TW = "正體中文 (繁體) [zh_TW]",
|
||||
}
|
||||
|
||||
|
||||
-- See if setting matches keywords
|
||||
local function get_setting_match_weight(entry, query_keywords)
|
||||
local setting_score = 0
|
||||
for _, keyword in ipairs(query_keywords) do
|
||||
if string.find(entry.name:lower(), keyword, 1, true) then
|
||||
setting_score = setting_score + 1
|
||||
end
|
||||
|
||||
if entry.readable_name and
|
||||
string.find(fgettext(entry.readable_name):lower(), keyword, 1, true) then
|
||||
setting_score = setting_score + 1
|
||||
end
|
||||
|
||||
if entry.comment and
|
||||
string.find(fgettext_ne(entry.comment):lower(), keyword, 1, true) then
|
||||
setting_score = setting_score + 1
|
||||
end
|
||||
end
|
||||
|
||||
return setting_score
|
||||
end
|
||||
|
||||
|
||||
local function filter_page_content(page, query_keywords)
|
||||
if #query_keywords == 0 then
|
||||
return page.content, 0
|
||||
end
|
||||
|
||||
local retval = {}
|
||||
local i = 1
|
||||
local max_weight = 0
|
||||
for _, content in ipairs(page.content) do
|
||||
if type(content) == "string" then
|
||||
local setting = get_setting_info(content)
|
||||
assert(setting, "Unknown setting: " .. content)
|
||||
|
||||
local weight = get_setting_match_weight(setting, query_keywords)
|
||||
if weight > 0 then
|
||||
max_weight = math.max(max_weight, weight)
|
||||
retval[i] = content
|
||||
i = i + 1
|
||||
end
|
||||
elseif type(content) == "table" and content.query_text then
|
||||
for _, keyword in ipairs(query_keywords) do
|
||||
if string.find(fgettext(content.query_text), keyword, 1, true) then
|
||||
max_weight = math.max(max_weight, 1)
|
||||
retval[i] = content
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return retval, max_weight
|
||||
end
|
||||
|
||||
|
||||
local function update_filtered_pages(query)
|
||||
filtered_pages = {}
|
||||
filtered_page_by_id = {}
|
||||
|
||||
local query_keywords = {}
|
||||
for word in query:lower():gmatch("%S+") do
|
||||
table.insert(query_keywords, word)
|
||||
end
|
||||
|
||||
local best_page = nil
|
||||
local best_page_weight = -1
|
||||
|
||||
for _, page in ipairs(all_pages) do
|
||||
local content, page_weight = filter_page_content(page, query_keywords)
|
||||
if page_has_contents(page, content) then
|
||||
local new_page = table.copy(page)
|
||||
new_page.content = content
|
||||
|
||||
filtered_pages[#filtered_pages + 1] = new_page
|
||||
filtered_page_by_id[new_page.id] = new_page
|
||||
|
||||
if page_weight > best_page_weight then
|
||||
best_page = new_page
|
||||
best_page_weight = page_weight
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return best_page and best_page.id or nil
|
||||
end
|
||||
|
||||
|
||||
local function check_requirements(name, requires)
|
||||
if requires == nil then
|
||||
return true
|
||||
end
|
||||
|
||||
local video_driver = core.get_active_driver()
|
||||
local shaders_support = video_driver == "opengl" or video_driver == "ogles2"
|
||||
local special = {
|
||||
android = PLATFORM == "Android",
|
||||
desktop = PLATFORM ~= "Android",
|
||||
touchscreen_gui = TOUCHSCREEN_GUI,
|
||||
keyboard_mouse = not TOUCHSCREEN_GUI,
|
||||
shaders_support = shaders_support,
|
||||
shaders = core.settings:get_bool("enable_shaders") and shaders_support,
|
||||
opengl = video_driver == "opengl",
|
||||
gles = video_driver:sub(1, 5) == "ogles",
|
||||
}
|
||||
|
||||
for req_key, req_value in pairs(requires) do
|
||||
if special[req_key] == nil then
|
||||
local required_setting = get_setting_info(req_key)
|
||||
if required_setting == nil then
|
||||
core.log("warning", "Unknown setting " .. req_key .. " required by " .. name)
|
||||
end
|
||||
local actual_value = core.settings:get_bool(req_key,
|
||||
required_setting and core.is_yes(required_setting.default))
|
||||
if actual_value ~= req_value then
|
||||
return false
|
||||
end
|
||||
elseif special[req_key] ~= req_value then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function page_has_contents(page, actual_content)
|
||||
local is_advanced =
|
||||
page.id:sub(1, #"client_and_server") == "client_and_server" or
|
||||
page.id:sub(1, #"mapgen") == "mapgen" or
|
||||
page.id:sub(1, #"advanced") == "advanced"
|
||||
local show_advanced = core.settings:get_bool("show_advanced")
|
||||
if is_advanced and not show_advanced then
|
||||
return false
|
||||
end
|
||||
|
||||
for _, item in ipairs(actual_content) do
|
||||
if item == false or item.heading then --luacheck: ignore
|
||||
-- skip
|
||||
elseif type(item) == "string" then
|
||||
local setting = get_setting_info(item)
|
||||
assert(setting, "Unknown setting: " .. item)
|
||||
if check_requirements(setting.name, setting.requires) then
|
||||
return true
|
||||
end
|
||||
elseif item.get_formspec then
|
||||
if check_requirements(item.id, item.requires) then
|
||||
return true
|
||||
end
|
||||
else
|
||||
error("Unknown content in page: " .. dump(item))
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
local function build_page_components(page)
|
||||
-- Filter settings based on requirements
|
||||
local content = {}
|
||||
local last_heading
|
||||
for _, item in ipairs(page.content) do
|
||||
if item == false then --luacheck: ignore
|
||||
-- skip
|
||||
elseif item.heading then
|
||||
last_heading = item
|
||||
else
|
||||
local name, requires
|
||||
if type(item) == "string" then
|
||||
local setting = get_setting_info(item)
|
||||
assert(setting, "Unknown setting: " .. item)
|
||||
name = setting.name
|
||||
requires = setting.requires
|
||||
elseif item.get_formspec then
|
||||
name = item.id
|
||||
requires = item.requires
|
||||
else
|
||||
error("Unknown content in page: " .. dump(item))
|
||||
end
|
||||
|
||||
if check_requirements(name, requires) then
|
||||
if last_heading then
|
||||
content[#content + 1] = last_heading
|
||||
last_heading = nil
|
||||
end
|
||||
content[#content + 1] = item
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Create components
|
||||
local retval = {}
|
||||
for i, item in ipairs(content) do
|
||||
if type(item) == "string" then
|
||||
local setting = get_setting_info(item)
|
||||
local component_func = component_funcs[setting.type]
|
||||
assert(component_func, "Unknown setting type: " .. setting.type)
|
||||
retval[i] = component_func(setting)
|
||||
elseif item.get_formspec then
|
||||
retval[i] = item
|
||||
elseif item.heading then
|
||||
retval[i] = component_funcs.heading(item.heading)
|
||||
end
|
||||
end
|
||||
return retval
|
||||
end
|
||||
|
||||
|
||||
--- Creates a scrollbaroptions for a scroll_container
|
||||
--
|
||||
-- @param visible_l the length of the scroll_container and scrollbar
|
||||
-- @param total_l length of the scrollable area
|
||||
-- @param scroll_factor as passed to scroll_container
|
||||
local function make_scrollbaroptions_for_scroll_container(visible_l, total_l, scroll_factor)
|
||||
assert(total_l >= visible_l)
|
||||
local max = total_l - visible_l
|
||||
local thumb_size = (visible_l / total_l) * max
|
||||
return ("scrollbaroptions[min=0;max=%f;thumbsize=%f]"):format(max / scroll_factor, thumb_size / scroll_factor)
|
||||
end
|
||||
|
||||
|
||||
local formspec_show_hack = false
|
||||
|
||||
|
||||
local function get_formspec(dialogdata)
|
||||
local page_id = dialogdata.page_id or "accessibility"
|
||||
local page = filtered_page_by_id[page_id]
|
||||
|
||||
local extra_h = 1 -- not included in tabsize.height
|
||||
local tabsize = {
|
||||
width = TOUCHSCREEN_GUI and 16.5 or 15.5,
|
||||
height = TOUCHSCREEN_GUI and (10 - extra_h) or 12,
|
||||
}
|
||||
|
||||
local scrollbar_w = TOUCHSCREEN_GUI and 0.6 or 0.4
|
||||
|
||||
local left_pane_width = TOUCHSCREEN_GUI and 4.5 or 4.25
|
||||
local search_width = left_pane_width + scrollbar_w - (0.75 * 2)
|
||||
|
||||
local back_w = 3
|
||||
local checkbox_w = (tabsize.width - back_w - 2*0.2) / 2
|
||||
local show_technical_names = core.settings:get_bool("show_technical_names")
|
||||
local show_advanced = core.settings:get_bool("show_advanced")
|
||||
|
||||
formspec_show_hack = not formspec_show_hack
|
||||
|
||||
local fs = {
|
||||
"formspec_version[6]",
|
||||
"size[", tostring(tabsize.width), ",", tostring(tabsize.height + extra_h), "]",
|
||||
TOUCHSCREEN_GUI and "padding[0.01,0.01]" or "",
|
||||
"bgcolor[#0000]",
|
||||
|
||||
-- HACK: this is needed to allow resubmitting the same formspec
|
||||
formspec_show_hack and " " or "",
|
||||
|
||||
"box[0,0;", tostring(tabsize.width), ",", tostring(tabsize.height), ";#0000008C]",
|
||||
|
||||
("button[0,%f;%f,0.8;back;%s]"):format(
|
||||
tabsize.height + 0.2, back_w, fgettext("Back")),
|
||||
|
||||
("box[%f,%f;%f,0.8;#0000008C]"):format(
|
||||
back_w + 0.2, tabsize.height + 0.2, checkbox_w),
|
||||
("checkbox[%f,%f;show_technical_names;%s;%s]"):format(
|
||||
back_w + 2*0.2, tabsize.height + 0.6,
|
||||
fgettext("Show technical names"), tostring(show_technical_names)),
|
||||
|
||||
("box[%f,%f;%f,0.8;#0000008C]"):format(
|
||||
back_w + 2*0.2 + checkbox_w, tabsize.height + 0.2, checkbox_w),
|
||||
("checkbox[%f,%f;show_advanced;%s;%s]"):format(
|
||||
back_w + 3*0.2 + checkbox_w, tabsize.height + 0.6,
|
||||
fgettext("Show advanced settings"), tostring(show_advanced)),
|
||||
|
||||
"field[0.25,0.25;", tostring(search_width), ",0.75;search_query;;",
|
||||
core.formspec_escape(dialogdata.query or ""), "]",
|
||||
"field_enter_after_edit[search_query;true]",
|
||||
"container[", tostring(search_width + 0.25), ", 0.25]",
|
||||
"image_button[0,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]",
|
||||
"image_button[0.75,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";search_clear;]",
|
||||
"tooltip[search;", fgettext("Search"), "]",
|
||||
"tooltip[search_clear;", fgettext("Clear"), "]",
|
||||
"container_end[]",
|
||||
"scroll_container[0.25,1.25;", tostring(left_pane_width), ",",
|
||||
tostring(tabsize.height - 1.5), ";leftscroll;vertical;0.1]",
|
||||
"style_type[button;border=false;bgcolor=#3333]",
|
||||
"style_type[button:hover;border=false;bgcolor=#6663]",
|
||||
}
|
||||
|
||||
local y = 0
|
||||
local last_section = nil
|
||||
for _, other_page in ipairs(filtered_pages) do
|
||||
if other_page.section ~= last_section then
|
||||
fs[#fs + 1] = ("label[0.1,%f;%s]"):format(
|
||||
y + 0.41, core.colorize("#ff0", fgettext(other_page.section)))
|
||||
last_section = other_page.section
|
||||
y = y + 0.82
|
||||
end
|
||||
fs[#fs + 1] = ("box[0,%f;%f,0.8;%s]"):format(
|
||||
y, left_pane_width, other_page.id == page_id and "#467832FF" or "#3339")
|
||||
fs[#fs + 1] = ("button[0,%f;%f,0.8;page_%s;%s]")
|
||||
:format(y, left_pane_width, other_page.id, fgettext(other_page.title))
|
||||
y = y + 0.82
|
||||
end
|
||||
|
||||
if #filtered_pages == 0 then
|
||||
fs[#fs + 1] = "label[0.1,0.41;"
|
||||
fs[#fs + 1] = fgettext("No results")
|
||||
fs[#fs + 1] = "]"
|
||||
end
|
||||
|
||||
fs[#fs + 1] = "scroll_container_end[]"
|
||||
|
||||
if y >= tabsize.height - 1.25 then
|
||||
fs[#fs + 1] = make_scrollbaroptions_for_scroll_container(tabsize.height - 1.5, y, 0.1)
|
||||
fs[#fs + 1] = ("scrollbar[%f,1.25;%f,%f;vertical;leftscroll;%f]"):format(
|
||||
left_pane_width + 0.25, scrollbar_w, tabsize.height - 1.5, dialogdata.leftscroll or 0)
|
||||
end
|
||||
|
||||
fs[#fs + 1] = "style_type[button;border=;bgcolor=]"
|
||||
|
||||
if not dialogdata.components then
|
||||
dialogdata.components = page and build_page_components(page) or {}
|
||||
end
|
||||
|
||||
local right_pane_width = tabsize.width - left_pane_width - 0.375 - 2*scrollbar_w - 0.25
|
||||
fs[#fs + 1] = ("scroll_container[%f,0;%f,%f;rightscroll;vertical;0.1]"):format(
|
||||
tabsize.width - right_pane_width - scrollbar_w, right_pane_width, tabsize.height)
|
||||
|
||||
y = 0.25
|
||||
for i, comp in ipairs(dialogdata.components) do
|
||||
fs[#fs + 1] = ("container[0,%f]"):format(y)
|
||||
|
||||
local avail_w = right_pane_width - 0.25
|
||||
if not comp.full_width then
|
||||
avail_w = avail_w - 1.4
|
||||
end
|
||||
if comp.max_w then
|
||||
avail_w = math.min(avail_w, comp.max_w)
|
||||
end
|
||||
|
||||
local comp_fs, used_h = comp:get_formspec(avail_w)
|
||||
fs[#fs + 1] = comp_fs
|
||||
|
||||
fs[#fs + 1] = "style_type[image_button;border=false;padding=]"
|
||||
|
||||
local show_reset = comp.resettable and comp.setting
|
||||
local show_info = comp.info_text and comp.info_text ~= ""
|
||||
if show_reset or show_info then
|
||||
-- ensure there's enough space for reset/info
|
||||
used_h = math.max(used_h, 0.5)
|
||||
end
|
||||
local info_reset_y = used_h / 2 - 0.25
|
||||
|
||||
if show_reset then
|
||||
local default = comp.setting.default
|
||||
local reset_tooltip = default and
|
||||
fgettext("Reset setting to default ($1)", tostring(default)) or
|
||||
fgettext("Reset setting to default")
|
||||
fs[#fs + 1] = ("image_button[%f,%f;0.5,0.5;%s;%s;]"):format(
|
||||
right_pane_width - 1.4, info_reset_y, reset_icon_path, "reset_" .. i)
|
||||
fs[#fs + 1] = ("tooltip[%s;%s]"):format("reset_" .. i, reset_tooltip)
|
||||
end
|
||||
|
||||
if show_info then
|
||||
local info_x = right_pane_width - 0.75
|
||||
fs[#fs + 1] = ("image[%f,%f;0.5,0.5;%s]"):format(info_x, info_reset_y, info_icon_path)
|
||||
fs[#fs + 1] = ("tooltip[%f,%f;0.5,0.5;%s]"):format(info_x, info_reset_y, fgettext(comp.info_text))
|
||||
end
|
||||
|
||||
fs[#fs + 1] = "style_type[image_button;border=;padding=]"
|
||||
|
||||
fs[#fs + 1] = "container_end[]"
|
||||
|
||||
if used_h > 0 then
|
||||
y = y + used_h + 0.25
|
||||
end
|
||||
end
|
||||
|
||||
fs[#fs + 1] = "scroll_container_end[]"
|
||||
|
||||
if y >= tabsize.height then
|
||||
fs[#fs + 1] = make_scrollbaroptions_for_scroll_container(tabsize.height, y + 0.375, 0.1)
|
||||
fs[#fs + 1] = ("scrollbar[%f,0;%f,%f;vertical;rightscroll;%f]"):format(
|
||||
tabsize.width - scrollbar_w, scrollbar_w, tabsize.height, dialogdata.rightscroll or 0)
|
||||
end
|
||||
|
||||
return table.concat(fs, "")
|
||||
end
|
||||
|
||||
|
||||
-- On Android, closing the app via the "Recents screen" won't result in a clean
|
||||
-- exit, discarding any setting changes made by the user.
|
||||
-- To avoid that, we write the settings file in more cases on Android.
|
||||
function write_settings_early()
|
||||
if PLATFORM == "Android" then
|
||||
core.settings:write()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function buttonhandler(this, fields)
|
||||
local dialogdata = this.data
|
||||
dialogdata.leftscroll = core.explode_scrollbar_event(fields.leftscroll).value or dialogdata.leftscroll
|
||||
dialogdata.rightscroll = core.explode_scrollbar_event(fields.rightscroll).value or dialogdata.rightscroll
|
||||
dialogdata.query = fields.search_query
|
||||
|
||||
if fields.back then
|
||||
this:delete()
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.show_technical_names ~= nil then
|
||||
local value = core.is_yes(fields.show_technical_names)
|
||||
core.settings:set_bool("show_technical_names", value)
|
||||
write_settings_early()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.show_advanced ~= nil then
|
||||
local value = core.is_yes(fields.show_advanced)
|
||||
core.settings:set_bool("show_advanced", value)
|
||||
write_settings_early()
|
||||
|
||||
local suggested_page_id = update_filtered_pages(dialogdata.query)
|
||||
|
||||
if not filtered_page_by_id[dialogdata.page_id] then
|
||||
dialogdata.components = nil
|
||||
dialogdata.leftscroll = 0
|
||||
dialogdata.rightscroll = 0
|
||||
|
||||
dialogdata.page_id = suggested_page_id
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.search or fields.key_enter_field == "search_query" then
|
||||
dialogdata.components = nil
|
||||
dialogdata.leftscroll = 0
|
||||
dialogdata.rightscroll = 0
|
||||
|
||||
dialogdata.page_id = update_filtered_pages(dialogdata.query)
|
||||
|
||||
return true
|
||||
end
|
||||
if fields.search_clear then
|
||||
dialogdata.query = ""
|
||||
dialogdata.components = nil
|
||||
dialogdata.leftscroll = 0
|
||||
dialogdata.rightscroll = 0
|
||||
|
||||
dialogdata.page_id = update_filtered_pages("")
|
||||
return true
|
||||
end
|
||||
|
||||
for _, page in ipairs(all_pages) do
|
||||
if fields["page_" .. page.id] then
|
||||
dialogdata.page_id = page.id
|
||||
dialogdata.components = nil
|
||||
dialogdata.rightscroll = 0
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
for i, comp in ipairs(dialogdata.components) do
|
||||
if comp.on_submit and comp:on_submit(fields, this) then
|
||||
write_settings_early()
|
||||
|
||||
-- Clear components so they regenerate
|
||||
dialogdata.components = nil
|
||||
return true
|
||||
end
|
||||
if comp.setting and fields["reset_" .. i] then
|
||||
core.settings:remove(comp.setting.name)
|
||||
write_settings_early()
|
||||
|
||||
-- Clear components so they regenerate
|
||||
dialogdata.components = nil
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
local function eventhandler(event)
|
||||
if event == "DialogShow" then
|
||||
-- Don't show the "MINETEST" header behind the dialog.
|
||||
mm_game_theme.set_engine(true)
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
function create_settings_dlg()
|
||||
local dlg = dialog_create("dlg_settings", get_formspec, buttonhandler, eventhandler)
|
||||
|
||||
dlg.data.page_id = update_filtered_pages("")
|
||||
|
||||
return dlg
|
||||
end
|
||||
@@ -1,5 +1,3 @@
|
||||
local settings = ...
|
||||
|
||||
local concat = table.concat
|
||||
local insert = table.insert
|
||||
local sprintf = string.format
|
||||
@@ -36,7 +34,7 @@ local group_format_template = [[
|
||||
|
||||
]]
|
||||
|
||||
local function create_minetest_conf_example()
|
||||
local function create_minetest_conf_example(settings)
|
||||
local result = { minetest_example_header }
|
||||
for _, entry in ipairs(settings) do
|
||||
if entry.type == "category" then
|
||||
@@ -108,14 +106,11 @@ local translation_file_header = [[
|
||||
|
||||
fake_function() {]]
|
||||
|
||||
local function create_translation_file()
|
||||
local function create_translation_file(settings)
|
||||
local result = { translation_file_header }
|
||||
for _, entry in ipairs(settings) do
|
||||
if entry.type == "category" then
|
||||
insert(result, sprintf("\tgettext(%q);", entry.name))
|
||||
elseif entry.type == "key" then --luacheck: ignore
|
||||
-- Neither names nor descriptions of keys are used since we have a
|
||||
-- dedicated menu for them.
|
||||
else
|
||||
if entry.readable_name then
|
||||
insert(result, sprintf("\tgettext(%q);", entry.readable_name))
|
||||
@@ -132,12 +127,13 @@ local function create_translation_file()
|
||||
end
|
||||
|
||||
local file = assert(io.open("minetest.conf.example", "w"))
|
||||
file:write(create_minetest_conf_example())
|
||||
file:write(create_minetest_conf_example(settingtypes.parse_config_file(true, false)))
|
||||
file:close()
|
||||
|
||||
file = assert(io.open("src/settings_translation_file.cpp", "w"))
|
||||
-- If 'minetest.conf.example' appears in the 'bin' folder, the line below may have to be
|
||||
-- used instead. The file will also appear in the 'bin' folder.
|
||||
--file = assert(io.open("settings_translation_file.cpp", "w"))
|
||||
file:write(create_translation_file())
|
||||
-- We don't want hidden settings to be translated, so we set read_all to false.
|
||||
file:write(create_translation_file(settingtypes.parse_config_file(false, false)))
|
||||
file:close()
|
||||
28
builtin/mainmenu/settings/init.lua
Normal file
28
builtin/mainmenu/settings/init.lua
Normal file
@@ -0,0 +1,28 @@
|
||||
--Minetest
|
||||
--Copyright (C) 2022 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
local path = core.get_mainmenu_path() .. DIR_DELIM .. "settings"
|
||||
|
||||
dofile(path .. DIR_DELIM .. "settingtypes.lua")
|
||||
dofile(path .. DIR_DELIM .. "dlg_change_mapgen_flags.lua")
|
||||
dofile(path .. DIR_DELIM .. "dlg_settings.lua")
|
||||
|
||||
-- Uncomment to generate 'minetest.conf.example' and 'settings_translation_file.cpp'.
|
||||
-- For RUN_IN_PLACE the generated files may appear in the 'bin' folder.
|
||||
-- See comment and alternative line at the end of 'generate_from_settingtypes.lua'.
|
||||
|
||||
-- dofile(path .. DIR_DELIM .. "generate_from_settingtypes.lua")
|
||||
507
builtin/mainmenu/settings/settingtypes.lua
Normal file
507
builtin/mainmenu/settings/settingtypes.lua
Normal file
@@ -0,0 +1,507 @@
|
||||
--Minetest
|
||||
--Copyright (C) 2015 PilzAdam
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
settingtypes = {}
|
||||
|
||||
-- A Setting type is a table with the following keys:
|
||||
--
|
||||
-- name: Identifier
|
||||
-- readable_name: Readable title
|
||||
-- type: Category
|
||||
--
|
||||
-- name = mod.name,
|
||||
-- readable_name = mod.title,
|
||||
-- level = 1,
|
||||
-- type = "category", "int", "string", ""
|
||||
-- }
|
||||
|
||||
|
||||
local FILENAME = "settingtypes.txt"
|
||||
|
||||
local CHAR_CLASSES = {
|
||||
SPACE = "[%s]",
|
||||
VARIABLE = "[%w_%-%.]",
|
||||
INTEGER = "[+-]?[%d]",
|
||||
FLOAT = "[+-]?[%d%.]",
|
||||
FLAGS = "[%w_%-%.,]",
|
||||
}
|
||||
|
||||
local function flags_to_table(flags)
|
||||
return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split
|
||||
end
|
||||
|
||||
-- returns error message, or nil
|
||||
local function parse_setting_line(settings, line, read_all, base_level, allow_secure)
|
||||
|
||||
-- strip carriage returns (CR, /r)
|
||||
line = line:gsub("\r", "")
|
||||
|
||||
-- comment
|
||||
local comment_match = line:match("^#" .. CHAR_CLASSES.SPACE .. "*(.*)$")
|
||||
if comment_match then
|
||||
settings.current_comment[#settings.current_comment + 1] = comment_match
|
||||
return
|
||||
end
|
||||
|
||||
-- clear current_comment so only comments directly above a setting are bound to it
|
||||
-- but keep a local reference to it for variables in the current line
|
||||
local current_comment = settings.current_comment
|
||||
settings.current_comment = {}
|
||||
|
||||
-- empty lines
|
||||
if line:match("^" .. CHAR_CLASSES.SPACE .. "*$") then
|
||||
return
|
||||
end
|
||||
|
||||
-- category
|
||||
local stars, category = line:match("^%[([%*]*)([^%]]+)%]$")
|
||||
if category then
|
||||
local category_level = stars:len() + base_level
|
||||
|
||||
if settings.current_hide_level then
|
||||
if settings.current_hide_level < category_level then
|
||||
-- Skip this category, it's inside a hidden category.
|
||||
return
|
||||
else
|
||||
-- The start of this category marks the end of a hidden category.
|
||||
settings.current_hide_level = nil
|
||||
end
|
||||
end
|
||||
|
||||
if not read_all and category:sub(1, 5) == "Hide:" then
|
||||
-- This category is hidden.
|
||||
settings.current_hide_level = category_level
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = category,
|
||||
level = category_level,
|
||||
type = "category",
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if settings.current_hide_level then
|
||||
-- Ignore this line, we're inside a hidden category.
|
||||
return
|
||||
end
|
||||
|
||||
-- settings
|
||||
local first_part, name, readable_name, setting_type = line:match("^"
|
||||
-- this first capture group matches the whole first part,
|
||||
-- so we can later strip it from the rest of the line
|
||||
.. "("
|
||||
.. "([" .. CHAR_CLASSES.VARIABLE .. "+)" -- variable name
|
||||
.. CHAR_CLASSES.SPACE .. "*"
|
||||
.. "%(([^%)]*)%)" -- readable name
|
||||
.. CHAR_CLASSES.SPACE .. "*"
|
||||
.. "(" .. CHAR_CLASSES.VARIABLE .. "+)" -- type
|
||||
.. CHAR_CLASSES.SPACE .. "*"
|
||||
.. ")")
|
||||
|
||||
if not first_part then
|
||||
return "Invalid line"
|
||||
end
|
||||
|
||||
if name:match("secure%.[.]*") and not allow_secure then
|
||||
return "Tried to add \"secure.\" setting"
|
||||
end
|
||||
|
||||
local requires = {}
|
||||
local last_line = #current_comment > 0 and current_comment[#current_comment]:trim()
|
||||
if last_line and last_line:lower():sub(1, 9) == "requires:" then
|
||||
local parts = last_line:sub(10):split(",")
|
||||
current_comment[#current_comment] = nil
|
||||
|
||||
for _, part in ipairs(parts) do
|
||||
part = part:trim()
|
||||
|
||||
local value = true
|
||||
if part:sub(1, 1) == "!" then
|
||||
value = false
|
||||
part = part:sub(2):trim()
|
||||
end
|
||||
|
||||
requires[part] = value
|
||||
end
|
||||
end
|
||||
|
||||
if readable_name == "" then
|
||||
readable_name = nil
|
||||
end
|
||||
local remaining_line = line:sub(first_part:len() + 1)
|
||||
|
||||
local comment = table.concat(current_comment, "\n"):trim()
|
||||
|
||||
if setting_type == "int" then
|
||||
local default, min, max = remaining_line:match("^"
|
||||
-- first int is required, the last 2 are optional
|
||||
.. "(" .. CHAR_CLASSES.INTEGER .. "+)" .. CHAR_CLASSES.SPACE .. "*"
|
||||
.. "(" .. CHAR_CLASSES.INTEGER .. "*)" .. CHAR_CLASSES.SPACE .. "*"
|
||||
.. "(" .. CHAR_CLASSES.INTEGER .. "*)"
|
||||
.. "$")
|
||||
|
||||
if not default or not tonumber(default) then
|
||||
return "Invalid integer setting"
|
||||
end
|
||||
|
||||
min = tonumber(min)
|
||||
max = tonumber(max)
|
||||
table.insert(settings, {
|
||||
name = name,
|
||||
readable_name = readable_name,
|
||||
type = "int",
|
||||
default = default,
|
||||
min = min,
|
||||
max = max,
|
||||
requires = requires,
|
||||
comment = comment,
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if setting_type == "string"
|
||||
or setting_type == "key" or setting_type == "v3f" then
|
||||
local default = remaining_line:match("^(.*)$")
|
||||
|
||||
if not default then
|
||||
return "Invalid string setting"
|
||||
end
|
||||
if setting_type == "key" and not read_all then
|
||||
-- ignore key type if read_all is false
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = name,
|
||||
readable_name = readable_name,
|
||||
type = setting_type,
|
||||
default = default,
|
||||
requires = requires,
|
||||
comment = comment,
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if setting_type == "noise_params_2d"
|
||||
or setting_type == "noise_params_3d" then
|
||||
local default = remaining_line:match("^(.*)$")
|
||||
|
||||
if not default then
|
||||
return "Invalid string setting"
|
||||
end
|
||||
|
||||
local values = {}
|
||||
local ti = 1
|
||||
local index = 1
|
||||
for match in default:gmatch("[+-]?[%d.-e]+") do -- All numeric characters
|
||||
index = default:find("[+-]?[%d.-e]+", index) + match:len()
|
||||
table.insert(values, match)
|
||||
ti = ti + 1
|
||||
if ti > 9 then
|
||||
break
|
||||
end
|
||||
end
|
||||
index = default:find("[^, ]", index)
|
||||
local flags = ""
|
||||
if index then
|
||||
flags = default:sub(index)
|
||||
end
|
||||
table.insert(values, flags)
|
||||
|
||||
table.insert(settings, {
|
||||
name = name,
|
||||
readable_name = readable_name,
|
||||
type = setting_type,
|
||||
default = default,
|
||||
default_table = {
|
||||
offset = values[1],
|
||||
scale = values[2],
|
||||
spread = {
|
||||
x = values[3],
|
||||
y = values[4],
|
||||
z = values[5]
|
||||
},
|
||||
seed = values[6],
|
||||
octaves = values[7],
|
||||
persistence = values[8],
|
||||
lacunarity = values[9],
|
||||
flags = values[10]
|
||||
},
|
||||
values = values,
|
||||
requires = requires,
|
||||
comment = comment,
|
||||
noise_params = true,
|
||||
flags = flags_to_table("defaults,eased,absvalue")
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if setting_type == "bool" then
|
||||
if remaining_line ~= "false" and remaining_line ~= "true" then
|
||||
return "Invalid boolean setting"
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = name,
|
||||
readable_name = readable_name,
|
||||
type = "bool",
|
||||
default = remaining_line,
|
||||
requires = requires,
|
||||
comment = comment,
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if setting_type == "float" then
|
||||
local default, min, max = remaining_line:match("^"
|
||||
-- first float is required, the last 2 are optional
|
||||
.. "(" .. CHAR_CLASSES.FLOAT .. "+)" .. CHAR_CLASSES.SPACE .. "*"
|
||||
.. "(" .. CHAR_CLASSES.FLOAT .. "*)" .. CHAR_CLASSES.SPACE .. "*"
|
||||
.. "(" .. CHAR_CLASSES.FLOAT .. "*)"
|
||||
.."$")
|
||||
|
||||
if not default or not tonumber(default) then
|
||||
return "Invalid float setting"
|
||||
end
|
||||
|
||||
min = tonumber(min)
|
||||
max = tonumber(max)
|
||||
table.insert(settings, {
|
||||
name = name,
|
||||
readable_name = readable_name,
|
||||
type = "float",
|
||||
default = default,
|
||||
min = min,
|
||||
max = max,
|
||||
requires = requires,
|
||||
comment = comment,
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if setting_type == "enum" then
|
||||
local default, values = remaining_line:match("^"
|
||||
-- first value (default) may be empty (i.e. is optional)
|
||||
.. "(" .. CHAR_CLASSES.VARIABLE .. "*)" .. CHAR_CLASSES.SPACE .. "*"
|
||||
.. "(" .. CHAR_CLASSES.FLAGS .. "+)"
|
||||
.. "$")
|
||||
|
||||
if not default or values == "" then
|
||||
return "Invalid enum setting"
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = name,
|
||||
readable_name = readable_name,
|
||||
type = "enum",
|
||||
default = default,
|
||||
values = values:split(",", true),
|
||||
requires = requires,
|
||||
comment = comment,
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if setting_type == "path" or setting_type == "filepath" then
|
||||
local default = remaining_line:match("^(.*)$")
|
||||
|
||||
if not default then
|
||||
return "Invalid path setting"
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = name,
|
||||
readable_name = readable_name,
|
||||
type = setting_type,
|
||||
default = default,
|
||||
requires = requires,
|
||||
comment = comment,
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if setting_type == "flags" then
|
||||
local default, possible = remaining_line:match("^"
|
||||
-- first value (default) may be empty (i.e. is optional)
|
||||
-- this is implemented by making the last value optional, and
|
||||
-- swapping them around if it turns out empty.
|
||||
.. "(" .. CHAR_CLASSES.FLAGS .. "+)" .. CHAR_CLASSES.SPACE .. "*"
|
||||
.. "(" .. CHAR_CLASSES.FLAGS .. "*)"
|
||||
.. "$")
|
||||
|
||||
if not default or not possible then
|
||||
return "Invalid flags setting"
|
||||
end
|
||||
|
||||
if possible == "" then
|
||||
possible = default
|
||||
default = ""
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = name,
|
||||
readable_name = readable_name,
|
||||
type = "flags",
|
||||
default = default,
|
||||
possible = flags_to_table(possible),
|
||||
requires = requires,
|
||||
comment = comment,
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
return "Invalid setting type \"" .. setting_type .. "\""
|
||||
end
|
||||
|
||||
local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure)
|
||||
-- store this helper variable in the table so it's easier to pass to parse_setting_line()
|
||||
result.current_comment = {}
|
||||
result.current_hide_level = nil
|
||||
|
||||
local line = file:read("*line")
|
||||
while line do
|
||||
local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure)
|
||||
if error_msg then
|
||||
core.log("error", error_msg .. " in " .. filepath .. " \"" .. line .. "\"")
|
||||
end
|
||||
line = file:read("*line")
|
||||
end
|
||||
|
||||
result.current_comment = nil
|
||||
result.current_hide_level = nil
|
||||
end
|
||||
|
||||
|
||||
--- Returns table of setting types
|
||||
--
|
||||
-- @param read_all Whether to ignore certain setting types for GUI or not
|
||||
-- @parse_mods Whether to parse settingtypes.txt in mods and games
|
||||
function settingtypes.parse_config_file(read_all, parse_mods)
|
||||
local settings = {}
|
||||
|
||||
do
|
||||
local builtin_path = core.get_builtin_path() .. FILENAME
|
||||
local file = io.open(builtin_path, "r")
|
||||
if not file then
|
||||
core.log("error", "Can't load " .. FILENAME)
|
||||
return settings
|
||||
end
|
||||
|
||||
parse_single_file(file, builtin_path, read_all, settings, 0, true)
|
||||
|
||||
file:close()
|
||||
end
|
||||
|
||||
if parse_mods then
|
||||
-- Parse games
|
||||
local games_category_initialized = false
|
||||
for _, game in ipairs(pkgmgr.games) do
|
||||
local path = game.path .. DIR_DELIM .. FILENAME
|
||||
local file = io.open(path, "r")
|
||||
if file then
|
||||
if not games_category_initialized then
|
||||
fgettext_ne("Content: Games") -- not used, but needed for xgettext
|
||||
table.insert(settings, {
|
||||
name = "Content: Games",
|
||||
level = 0,
|
||||
type = "category",
|
||||
})
|
||||
games_category_initialized = true
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = game.path,
|
||||
readable_name = game.title,
|
||||
level = 1,
|
||||
type = "category",
|
||||
})
|
||||
|
||||
parse_single_file(file, path, read_all, settings, 2, false)
|
||||
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
|
||||
-- Parse mods
|
||||
local mods_category_initialized = false
|
||||
local mods = {}
|
||||
pkgmgr.get_mods(core.get_modpath(), "mods", mods)
|
||||
table.sort(mods, function(a, b) return a.name < b.name end)
|
||||
|
||||
for _, mod in ipairs(mods) do
|
||||
local path = mod.path .. DIR_DELIM .. FILENAME
|
||||
local file = io.open(path, "r")
|
||||
if file then
|
||||
if not mods_category_initialized then
|
||||
fgettext_ne("Content: Mods") -- not used, but needed for xgettext
|
||||
table.insert(settings, {
|
||||
name = "Content: Mods",
|
||||
level = 0,
|
||||
type = "category",
|
||||
})
|
||||
mods_category_initialized = true
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = mod.path,
|
||||
readable_name = mod.title or mod.name,
|
||||
level = 1,
|
||||
type = "category",
|
||||
})
|
||||
|
||||
parse_single_file(file, path, read_all, settings, 2, false)
|
||||
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
|
||||
-- Parse client mods
|
||||
local clientmods_category_initialized = false
|
||||
local clientmods = {}
|
||||
pkgmgr.get_mods(core.get_clientmodpath(), "clientmods", clientmods)
|
||||
for _, mod in ipairs(clientmods) do
|
||||
local path = mod.path .. DIR_DELIM .. FILENAME
|
||||
local file = io.open(path, "r")
|
||||
if file then
|
||||
if not clientmods_category_initialized then
|
||||
fgettext_ne("Client Mods") -- not used, but needed for xgettext
|
||||
table.insert(settings, {
|
||||
name = "Client Mods",
|
||||
level = 0,
|
||||
type = "category",
|
||||
})
|
||||
clientmods_category_initialized = true
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = mod.path,
|
||||
readable_name = mod.title or mod.name,
|
||||
level = 1,
|
||||
type = "category",
|
||||
})
|
||||
|
||||
parse_single_file(file, path, read_all, settings, 2, false)
|
||||
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return settings
|
||||
end
|
||||
121
builtin/mainmenu/settings/shadows_component.lua
Normal file
121
builtin/mainmenu/settings/shadows_component.lua
Normal file
@@ -0,0 +1,121 @@
|
||||
--Minetest
|
||||
--Copyright (C) 2021-2 x2048
|
||||
--Copyright (C) 2022-3 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
|
||||
local shadow_levels_labels = {
|
||||
fgettext("Disabled"),
|
||||
fgettext("Very Low"),
|
||||
fgettext("Low"),
|
||||
fgettext("Medium"),
|
||||
fgettext("High"),
|
||||
fgettext("Very High"),
|
||||
fgettext("Custom"),
|
||||
}
|
||||
local PRESET_DISABLED = 1
|
||||
local PRESET_CUSTOM = #shadow_levels_labels
|
||||
|
||||
|
||||
-- max distance, texture size, texture_32bit, filters, map color
|
||||
local shadow_presets = {
|
||||
[2] = { 62, 512, true, 0, false },
|
||||
[3] = { 93, 1024, true, 0, false },
|
||||
[4] = { 140, 2048, true, 1, false },
|
||||
[5] = { 210, 4096, true, 2, true },
|
||||
[6] = { 300, 8192, true, 2, true },
|
||||
}
|
||||
|
||||
|
||||
local function detect_mapping_idx()
|
||||
if not core.settings:get_bool("enable_dynamic_shadows", false) then
|
||||
return PRESET_DISABLED
|
||||
end
|
||||
|
||||
local shadow_map_max_distance = tonumber(core.settings:get("shadow_map_max_distance"))
|
||||
local shadow_map_texture_size = tonumber(core.settings:get("shadow_map_texture_size"))
|
||||
local shadow_map_texture_32bit = core.settings:get_bool("shadow_map_texture_32bit", false)
|
||||
local shadow_filters = tonumber(core.settings:get("shadow_filters"))
|
||||
local shadow_map_color = core.settings:get_bool("shadow_map_color", false)
|
||||
|
||||
for i = 2, 6 do
|
||||
local preset = shadow_presets[i]
|
||||
if preset[1] == shadow_map_max_distance and
|
||||
preset[2] == shadow_map_texture_size and
|
||||
preset[3] == shadow_map_texture_32bit and
|
||||
preset[4] == shadow_filters and
|
||||
preset[5] == shadow_map_color then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return PRESET_CUSTOM
|
||||
end
|
||||
|
||||
|
||||
local function apply_preset(preset)
|
||||
if preset then
|
||||
core.settings:set_bool("enable_dynamic_shadows", true)
|
||||
core.settings:set("shadow_map_max_distance", preset[1])
|
||||
core.settings:set("shadow_map_texture_size", preset[2])
|
||||
core.settings:set_bool("shadow_map_texture_32bit", preset[3])
|
||||
core.settings:set("shadow_filters", preset[4])
|
||||
core.settings:set_bool("shadow_map_color", preset[5])
|
||||
else
|
||||
core.settings:set_bool("enable_dynamic_shadows", false)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return {
|
||||
query_text = "Shadows",
|
||||
requires = {
|
||||
shaders = true,
|
||||
opengl = true,
|
||||
},
|
||||
get_formspec = function(self, avail_w)
|
||||
local labels = table.copy(shadow_levels_labels)
|
||||
local idx = detect_mapping_idx()
|
||||
|
||||
-- Remove "custom" if not already selected
|
||||
if idx ~= PRESET_CUSTOM then
|
||||
table.remove(labels, PRESET_CUSTOM)
|
||||
end
|
||||
|
||||
local fs =
|
||||
"label[0,0.2;" .. fgettext("Dynamic shadows") .. "]" ..
|
||||
"dropdown[0,0.4;3,0.8;dd_shadows;" .. table.concat(labels, ",") .. ";" .. idx .. ";true]" ..
|
||||
"label[0,1.5;" .. core.colorize("#bbb", fgettext("(The game will need to enable shadows as well)")) .. "]"
|
||||
return fs, 1.8
|
||||
end,
|
||||
on_submit = function(self, fields)
|
||||
if fields.dd_shadows then
|
||||
local old_shadow_level_idx = detect_mapping_idx()
|
||||
local shadow_level_idx = tonumber(fields.dd_shadows)
|
||||
if shadow_level_idx == nil or shadow_level_idx == old_shadow_level_idx then
|
||||
return false
|
||||
end
|
||||
|
||||
if shadow_level_idx == PRESET_CUSTOM then
|
||||
core.settings:set_bool("enable_dynamic_shadows", true)
|
||||
return true
|
||||
end
|
||||
|
||||
local preset = shadow_presets[shadow_level_idx]
|
||||
apply_preset(preset)
|
||||
return true
|
||||
end
|
||||
end,
|
||||
}
|
||||
@@ -27,9 +27,9 @@ local core_developers = {
|
||||
"Krock/SmallJoker <mk939@ymail.com>",
|
||||
"Lars Hofhansl <larsh@apache.org>",
|
||||
"v-rob <robinsonvincent89@gmail.com>",
|
||||
"Hugues Ross <hugues.ross@gmail.com>",
|
||||
"Dmitry Kostenko (x2048) <codeforsmile@gmail.com>",
|
||||
"Desour",
|
||||
"Desour/DS",
|
||||
"srifqi",
|
||||
"Gregor Parzefall (grorp)",
|
||||
}
|
||||
|
||||
-- currently only https://github.com/orgs/minetest/teams/triagers/members
|
||||
@@ -43,18 +43,19 @@ local core_team = {
|
||||
-- For updating active/previous contributors, see the script in ./util/gather_git_credits.py
|
||||
|
||||
local active_contributors = {
|
||||
"Wuzzy [Features, translations, devtest]",
|
||||
"Lars Müller [Bugfixes and entity features]",
|
||||
"paradust7 [Bugfixes]",
|
||||
"ROllerozxa [Bugfixes, Android]",
|
||||
"srifqi [Android, translations]",
|
||||
"Lexi Hale [Particlespawner animation]",
|
||||
"Wuzzy [Features, translations, documentation]",
|
||||
"numzero [Optimizations, work on OpenGL driver]",
|
||||
"ROllerozxa [Bugfixes, Mainmenu]",
|
||||
"Lars Müller [Bugfixes]",
|
||||
"AFCMS [Documentation]",
|
||||
"savilli [Bugfixes]",
|
||||
"fluxionary [Bugfixes]",
|
||||
"Gregor Parzefall [Bugfixes]",
|
||||
"Bradley Pierce (Thresher) [Documentation]",
|
||||
"Stvk imension [Android]",
|
||||
"JosiahWI [Code cleanups]",
|
||||
"OgelGames [UI, Bugfixes]",
|
||||
"ndren [Bugfixes]",
|
||||
"Abdou-31 [Documentation]",
|
||||
"pecksin [Bouncy physics]",
|
||||
"Daroc Alden [Fixes]",
|
||||
}
|
||||
|
||||
local previous_core_developers = {
|
||||
@@ -75,13 +76,14 @@ local previous_core_developers = {
|
||||
"Pierre-Yves Rollo <dev@pyrollo.com>",
|
||||
"hecks",
|
||||
"Jude Melton-Houghton (TurkeyMcMac) [RIP]",
|
||||
"Hugues Ross <hugues.ross@gmail.com>",
|
||||
"Dmitry Kostenko (x2048) <codeforsmile@gmail.com>",
|
||||
}
|
||||
|
||||
local previous_contributors = {
|
||||
"Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net> [Minetest logo]",
|
||||
"red-001 <red-001@outlook.ie>",
|
||||
"Giuseppe Bilotta",
|
||||
"numzero",
|
||||
"HybridDog",
|
||||
"ClobberXD",
|
||||
"Dániel Juhász (juhdanad) <juhdanad@gmail.com>",
|
||||
@@ -99,83 +101,79 @@ local previous_contributors = {
|
||||
}
|
||||
|
||||
local function prepare_credits(dest, source)
|
||||
for _, s in ipairs(source) do
|
||||
-- if there's text inside brackets make it gray-ish
|
||||
s = s:gsub("%[.-%]", core.colorize("#aaa", "%1"))
|
||||
dest[#dest+1] = s
|
||||
end
|
||||
end
|
||||
local string = table.concat(source, "\n") .. "\n"
|
||||
|
||||
local function build_hacky_list(items, spacing)
|
||||
spacing = spacing or 0.5
|
||||
local y = spacing / 2
|
||||
local ret = {}
|
||||
for _, item in ipairs(items) do
|
||||
if item ~= "" then
|
||||
ret[#ret+1] = ("label[0,%f;%s]"):format(y, core.formspec_escape(item))
|
||||
end
|
||||
y = y + spacing
|
||||
end
|
||||
return table.concat(ret, ""), y
|
||||
local hypertext_escapes = {
|
||||
["\\"] = "\\\\",
|
||||
["<"] = "\\<",
|
||||
[">"] = "\\>",
|
||||
}
|
||||
string = string:gsub("[\\<>]", hypertext_escapes)
|
||||
string = string:gsub("%[.-%]", "<gray>%1</gray>")
|
||||
|
||||
table.insert(dest, string)
|
||||
end
|
||||
|
||||
return {
|
||||
name = "about",
|
||||
caption = fgettext("About"),
|
||||
|
||||
cbf_formspec = function(tabview, name, tabdata)
|
||||
local logofile = defaulttexturedir .. "logo.png"
|
||||
local version = core.get_version()
|
||||
|
||||
local credit_list = {}
|
||||
table.insert_all(credit_list, {
|
||||
core.colorize("#000", "Dedication of the current release"),
|
||||
"The 5.7.0 release is dedicated to the memory of",
|
||||
"Minetest developer Jude Melton-Houghton (TurkeyMcMac)",
|
||||
"who died on February 1, 2023.",
|
||||
"Our thoughts are with his family and friends.",
|
||||
"",
|
||||
core.colorize("#ff0", fgettext("Core Developers"))
|
||||
local hypertext = {
|
||||
"<tag name=heading color=#ff0>",
|
||||
"<tag name=gray color=#aaa>",
|
||||
}
|
||||
|
||||
table.insert_all(hypertext, {
|
||||
"<heading>", fgettext_ne("Core Developers"), "</heading>\n",
|
||||
})
|
||||
prepare_credits(credit_list, core_developers)
|
||||
table.insert_all(credit_list, {
|
||||
"",
|
||||
core.colorize("#ff0", fgettext("Core Team"))
|
||||
prepare_credits(hypertext, core_developers)
|
||||
table.insert_all(hypertext, {
|
||||
"\n",
|
||||
"<heading>", fgettext_ne("Core Team"), "</heading>\n",
|
||||
})
|
||||
prepare_credits(credit_list, core_team)
|
||||
table.insert_all(credit_list, {
|
||||
"",
|
||||
core.colorize("#ff0", fgettext("Active Contributors"))
|
||||
prepare_credits(hypertext, core_team)
|
||||
table.insert_all(hypertext, {
|
||||
"\n",
|
||||
"<heading>", fgettext_ne("Active Contributors"), "</heading>\n",
|
||||
})
|
||||
prepare_credits(credit_list, active_contributors)
|
||||
table.insert_all(credit_list, {
|
||||
"",
|
||||
core.colorize("#ff0", fgettext("Previous Core Developers"))
|
||||
prepare_credits(hypertext, active_contributors)
|
||||
table.insert_all(hypertext, {
|
||||
"\n",
|
||||
"<heading>", fgettext_ne("Previous Core Developers"), "</heading>\n",
|
||||
})
|
||||
prepare_credits(credit_list, previous_core_developers)
|
||||
table.insert_all(credit_list, {
|
||||
"",
|
||||
core.colorize("#ff0", fgettext("Previous Contributors"))
|
||||
prepare_credits(hypertext, previous_core_developers)
|
||||
table.insert_all(hypertext, {
|
||||
"\n",
|
||||
"<heading>", fgettext_ne("Previous Contributors"), "</heading>\n",
|
||||
})
|
||||
prepare_credits(credit_list, previous_contributors)
|
||||
local credit_fs, scroll_height = build_hacky_list(credit_list)
|
||||
-- account for the visible portion
|
||||
scroll_height = math.max(0, scroll_height - 6.9)
|
||||
prepare_credits(hypertext, previous_contributors)
|
||||
|
||||
hypertext = table.concat(hypertext):sub(1, -2)
|
||||
|
||||
local fs = "image[1.5,0.6;2.5,2.5;" .. core.formspec_escape(logofile) .. "]" ..
|
||||
"style[label_button;border=false]" ..
|
||||
"button[0.1,3.4;5.3,0.5;label_button;" ..
|
||||
core.formspec_escape(version.project .. " " .. version.string) .. "]" ..
|
||||
"button[1.5,4.1;2.5,0.8;homepage;minetest.net]" ..
|
||||
"scroll_container[5.5,0.1;9.5,6.9;scroll_credits;vertical;" ..
|
||||
tostring(scroll_height / 1000) .. "]" .. credit_fs ..
|
||||
"scroll_container_end[]"..
|
||||
"scrollbar[15,0.1;0.4,6.9;vertical;scroll_credits;0]"
|
||||
"hypertext[5.5,0.25;9.75,6.6;credits;" .. minetest.formspec_escape(hypertext) .. "]"
|
||||
|
||||
-- Render information
|
||||
local active_renderer_info = fgettext("Active renderer:") .. " " ..
|
||||
core.formspec_escape(core.get_active_renderer())
|
||||
fs = fs .. "style[label_button2;border=false]" ..
|
||||
"button[0.1,6;5.3,1;label_button2;" ..
|
||||
fgettext("Active renderer:") .. "\n" ..
|
||||
core.formspec_escape(core.get_active_renderer()) .. "]"
|
||||
"button[0.1,6;5.3,0.5;label_button2;" .. active_renderer_info .. "]"..
|
||||
"tooltip[label_button2;" .. active_renderer_info .. "]"
|
||||
|
||||
-- Irrlicht device information
|
||||
local irrlicht_device_info = fgettext("Irrlicht device:") .. " " ..
|
||||
core.formspec_escape(core.get_active_irrlicht_device())
|
||||
fs = fs .. "style[label_button3;border=false]" ..
|
||||
"button[0.1,6.5;5.3,0.5;label_button3;" .. irrlicht_device_info .. "]"..
|
||||
"tooltip[label_button3;" .. irrlicht_device_info .. "]"
|
||||
|
||||
if PLATFORM == "Android" then
|
||||
fs = fs .. "button[0.5,5.1;4.5,0.8;share_debug;" .. fgettext("Share debug log") .. "]"
|
||||
@@ -186,8 +184,9 @@ return {
|
||||
fs = fs .. "button[0.5,5.1;4.5,0.8;userdata;" .. fgettext("Open User Data Directory") .. "]"
|
||||
end
|
||||
|
||||
return fs, "size[15.5,7.1,false]real_coordinates[true]"
|
||||
return fs
|
||||
end,
|
||||
|
||||
cbf_button_handler = function(this, fields, name, tabdata)
|
||||
if fields.homepage then
|
||||
core.open_url("https://www.minetest.net")
|
||||
@@ -202,4 +201,10 @@ return {
|
||||
core.open_dir(core.get_user_path())
|
||||
end
|
||||
end,
|
||||
|
||||
on_change = function(type)
|
||||
if type == "ENTER" then
|
||||
mm_game_theme.set_engine()
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
@@ -16,77 +16,108 @@
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
local packages_raw
|
||||
local packages
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
local function get_formspec(tabview, name, tabdata)
|
||||
if pkgmgr.global_mods == nil then
|
||||
local function get_content_icons(packages_with_updates)
|
||||
local ret = {}
|
||||
for _, content in ipairs(packages_with_updates) do
|
||||
ret[content.virtual_path or content.path] = { type = "update" }
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
|
||||
local packages_raw, packages
|
||||
|
||||
local function update_packages()
|
||||
if not pkgmgr.global_mods then
|
||||
pkgmgr.refresh_globals()
|
||||
end
|
||||
if pkgmgr.games == nil then
|
||||
if not pkgmgr.games then
|
||||
pkgmgr.update_gamelist()
|
||||
end
|
||||
|
||||
if packages == nil then
|
||||
packages_raw = {}
|
||||
table.insert_all(packages_raw, pkgmgr.games)
|
||||
table.insert_all(packages_raw, pkgmgr.get_texture_packs())
|
||||
table.insert_all(packages_raw, pkgmgr.global_mods:get_list())
|
||||
packages_raw = {}
|
||||
table.insert_all(packages_raw, pkgmgr.games)
|
||||
table.insert_all(packages_raw, pkgmgr.get_texture_packs())
|
||||
table.insert_all(packages_raw, pkgmgr.global_mods:get_list())
|
||||
|
||||
local function get_data()
|
||||
return packages_raw
|
||||
end
|
||||
|
||||
local function is_equal(element, uid) --uid match
|
||||
return (element.type == "game" and element.id == uid) or
|
||||
element.name == uid
|
||||
end
|
||||
|
||||
packages = filterlist.create(get_data, pkgmgr.compare_package,
|
||||
is_equal, nil, {})
|
||||
local function get_data()
|
||||
return packages_raw
|
||||
end
|
||||
|
||||
if tabdata.selected_pkg == nil then
|
||||
local function is_equal(element, uid) --uid match
|
||||
return (element.type == "game" and element.id == uid) or
|
||||
element.name == uid
|
||||
end
|
||||
|
||||
packages = filterlist.create(get_data, pkgmgr.compare_package,
|
||||
is_equal, nil, {})
|
||||
end
|
||||
|
||||
local function on_change(type)
|
||||
if type == "ENTER" then
|
||||
mm_game_theme.set_engine()
|
||||
update_packages()
|
||||
end
|
||||
end
|
||||
|
||||
local function get_formspec(tabview, name, tabdata)
|
||||
if not packages then
|
||||
update_packages()
|
||||
end
|
||||
|
||||
if not tabdata.selected_pkg then
|
||||
tabdata.selected_pkg = 1
|
||||
end
|
||||
|
||||
local use_technical_names = core.settings:get_bool("show_technical_names")
|
||||
|
||||
local packages_with_updates = update_detector.get_all()
|
||||
local update_icons = get_content_icons(packages_with_updates)
|
||||
local update_count = #packages_with_updates
|
||||
local contentdb_label
|
||||
if update_count == 0 then
|
||||
contentdb_label = fgettext("Browse online content")
|
||||
else
|
||||
contentdb_label = fgettext("Browse online content [$1]", update_count)
|
||||
end
|
||||
|
||||
local retval =
|
||||
"label[0.05,-0.25;".. fgettext("Installed Packages:") .. "]" ..
|
||||
"tablecolumns[color;tree;text]" ..
|
||||
"table[0,0.25;5.1,4.3;pkglist;" ..
|
||||
pkgmgr.render_packagelist(packages, use_technical_names) ..
|
||||
";" .. tabdata.selected_pkg .. "]" ..
|
||||
"button[0,4.85;5.25,0.5;btn_contentdb;".. fgettext("Browse online content") .. "]"
|
||||
local retval = {
|
||||
"label[0.4,0.4;", fgettext("Installed Packages:"), "]",
|
||||
"tablecolumns[color;tree;image,align=inline,width=1.5",
|
||||
",tooltip=", fgettext("Update available?"),
|
||||
",0=", core.formspec_escape(defaulttexturedir .. "blank.png"),
|
||||
",4=", core.formspec_escape(defaulttexturedir .. "cdb_update_cropped.png"),
|
||||
";text]",
|
||||
"table[0.4,0.8;6.3,4.8;pkglist;",
|
||||
pkgmgr.render_packagelist(packages, use_technical_names, update_icons),
|
||||
";", tabdata.selected_pkg, "]",
|
||||
|
||||
"button[0.4,5.8;6.3,0.9;btn_contentdb;", contentdb_label, "]"
|
||||
}
|
||||
|
||||
local selected_pkg
|
||||
if filterlist.size(packages) >= tabdata.selected_pkg then
|
||||
selected_pkg = packages:get_list()[tabdata.selected_pkg]
|
||||
end
|
||||
|
||||
if selected_pkg ~= nil then
|
||||
--check for screenshot beeing available
|
||||
if selected_pkg then
|
||||
-- Check for screenshot being available
|
||||
local screenshotfilename = selected_pkg.path .. DIR_DELIM .. "screenshot.png"
|
||||
local screenshotfile, error = io.open(screenshotfilename, "r")
|
||||
|
||||
local modscreenshot
|
||||
if error == nil then
|
||||
if not error then
|
||||
screenshotfile:close()
|
||||
modscreenshot = screenshotfilename
|
||||
end
|
||||
|
||||
if modscreenshot == nil then
|
||||
modscreenshot = defaulttexturedir .. "no_screenshot.png"
|
||||
else
|
||||
modscreenshot = defaulttexturedir .. "no_screenshot.png"
|
||||
end
|
||||
|
||||
local info = core.get_content_info(selected_pkg.path)
|
||||
local desc = fgettext("No package description available")
|
||||
if info.description and info.description:trim() ~= "" then
|
||||
desc = info.description
|
||||
desc = core.formspec_escape(info.description)
|
||||
end
|
||||
|
||||
local title_and_name
|
||||
@@ -97,65 +128,76 @@ local function get_formspec(tabview, name, tabdata)
|
||||
core.colorize("#BFBFBF", selected_pkg.name)
|
||||
end
|
||||
|
||||
retval = retval ..
|
||||
"image[5.5,0;3,2;" .. core.formspec_escape(modscreenshot) .. "]" ..
|
||||
"label[8.25,0.6;" .. core.formspec_escape(title_and_name) .. "]" ..
|
||||
"box[5.5,2.2;6.15,2.35;#000]"
|
||||
local desc_height = 3.2
|
||||
|
||||
if selected_pkg.type == "mod" then
|
||||
if selected_pkg.is_modpack then
|
||||
retval = retval ..
|
||||
"button[8.65,4.65;3.25,1;btn_mod_mgr_rename_modpack;" ..
|
||||
fgettext("Rename") .. "]"
|
||||
if selected_pkg.is_modpack then
|
||||
desc_height = 2.1
|
||||
|
||||
table.insert_all(retval, {
|
||||
"button[7.1,4.7;8,0.9;btn_mod_mgr_rename_modpack;",
|
||||
fgettext("Rename"), "]"
|
||||
})
|
||||
elseif selected_pkg.type == "mod" then
|
||||
-- Show dependencies for mods
|
||||
desc = desc .. "\n\n"
|
||||
local toadd_hard = table.concat(info.depends or {}, "\n")
|
||||
local toadd_soft = table.concat(info.optional_depends or {}, "\n")
|
||||
if toadd_hard == "" and toadd_soft == "" then
|
||||
desc = desc .. fgettext("No dependencies.")
|
||||
else
|
||||
--show dependencies
|
||||
desc = desc .. "\n\n"
|
||||
local toadd_hard = table.concat(info.depends or {}, "\n")
|
||||
local toadd_soft = table.concat(info.optional_depends or {}, "\n")
|
||||
if toadd_hard == "" and toadd_soft == "" then
|
||||
desc = desc .. fgettext("No dependencies.")
|
||||
else
|
||||
if toadd_hard ~= "" then
|
||||
desc = desc ..fgettext("Dependencies:") ..
|
||||
"\n" .. toadd_hard
|
||||
end
|
||||
if toadd_soft ~= "" then
|
||||
if toadd_hard ~= "" then
|
||||
desc = desc ..fgettext("Dependencies:") ..
|
||||
"\n" .. toadd_hard
|
||||
end
|
||||
if toadd_soft ~= "" then
|
||||
if toadd_hard ~= "" then
|
||||
desc = desc .. "\n\n"
|
||||
end
|
||||
desc = desc .. fgettext("Optional dependencies:") ..
|
||||
"\n" .. toadd_soft
|
||||
desc = desc .. "\n\n"
|
||||
end
|
||||
desc = desc .. fgettext("Optional dependencies:") ..
|
||||
"\n" .. toadd_soft
|
||||
end
|
||||
end
|
||||
elseif selected_pkg.type == "txp" then
|
||||
desc_height = 2.1
|
||||
|
||||
else
|
||||
if selected_pkg.type == "txp" then
|
||||
if selected_pkg.enabled then
|
||||
retval = retval ..
|
||||
"button[8.65,4.65;3.25,1;btn_mod_mgr_disable_txp;" ..
|
||||
fgettext("Disable Texture Pack") .. "]"
|
||||
else
|
||||
retval = retval ..
|
||||
"button[8.65,4.65;3.25,1;btn_mod_mgr_use_txp;" ..
|
||||
fgettext("Use Texture Pack") .. "]"
|
||||
end
|
||||
if selected_pkg.enabled then
|
||||
table.insert_all(retval, {
|
||||
"button[7.1,4.7;8,0.9;btn_mod_mgr_disable_txp;",
|
||||
fgettext("Disable Texture Pack"), "]"
|
||||
})
|
||||
else
|
||||
table.insert_all(retval, {
|
||||
"button[7.1,4.7;8,0.9;btn_mod_mgr_use_txp;",
|
||||
fgettext("Use Texture Pack"), "]"
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
retval = retval .. "textarea[5.85,2.2;6.35,2.9;;" ..
|
||||
fgettext("Information:") .. ";" .. desc .. "]"
|
||||
table.insert_all(retval, {
|
||||
"image[7.1,0.2;3,2;", core.formspec_escape(modscreenshot), "]",
|
||||
"label[10.5,1;", core.formspec_escape(title_and_name), "]",
|
||||
"box[7.1,2.4;8,", tostring(desc_height), ";#000]",
|
||||
"textarea[7.1,2.4;8,", tostring(desc_height), ";;;", desc, "]",
|
||||
})
|
||||
|
||||
if core.may_modify_path(selected_pkg.path) then
|
||||
retval = retval ..
|
||||
"button[5.5,4.65;3.25,1;btn_mod_mgr_delete_mod;" ..
|
||||
fgettext("Uninstall Package") .. "]"
|
||||
table.insert_all(retval, {
|
||||
"button[7.1,5.8;4,0.9;btn_mod_mgr_delete_mod;",
|
||||
fgettext("Uninstall"), "]"
|
||||
})
|
||||
end
|
||||
|
||||
if update_icons[selected_pkg.virtual_path or selected_pkg.path] then
|
||||
table.insert_all(retval, {
|
||||
"button[11.1,5.8;4,0.9;btn_mod_mgr_update;",
|
||||
fgettext("Update"), "]"
|
||||
})
|
||||
end
|
||||
end
|
||||
return retval
|
||||
|
||||
return table.concat(retval)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
local function handle_doubleclick(pkg)
|
||||
if pkg.type == "txp" then
|
||||
if core.settings:get("texture_path") == pkg.path then
|
||||
@@ -166,14 +208,14 @@ local function handle_doubleclick(pkg)
|
||||
packages = nil
|
||||
|
||||
mm_game_theme.init()
|
||||
mm_game_theme.reset()
|
||||
mm_game_theme.set_engine()
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
local function handle_buttons(tabview, fields, tabname, tabdata)
|
||||
if fields["pkglist"] ~= nil then
|
||||
local event = core.explode_table_event(fields["pkglist"])
|
||||
|
||||
if fields.pkglist then
|
||||
local event = core.explode_table_event(fields.pkglist)
|
||||
tabdata.selected_pkg = event.row
|
||||
if event.type == "DCL" then
|
||||
handle_doubleclick(packages:get_list()[tabdata.selected_pkg])
|
||||
@@ -181,7 +223,7 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
|
||||
return true
|
||||
end
|
||||
|
||||
if fields["btn_contentdb"] ~= nil then
|
||||
if fields.btn_contentdb then
|
||||
local dlg = create_store_dlg()
|
||||
dlg:set_parent(tabview)
|
||||
tabview:hide()
|
||||
@@ -190,7 +232,7 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
|
||||
return true
|
||||
end
|
||||
|
||||
if fields["btn_mod_mgr_rename_modpack"] ~= nil then
|
||||
if fields.btn_mod_mgr_rename_modpack then
|
||||
local mod = packages:get_list()[tabdata.selected_pkg]
|
||||
local dlg_renamemp = create_rename_modpack_dlg(mod)
|
||||
dlg_renamemp:set_parent(tabview)
|
||||
@@ -200,7 +242,7 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
|
||||
return true
|
||||
end
|
||||
|
||||
if fields["btn_mod_mgr_delete_mod"] ~= nil then
|
||||
if fields.btn_mod_mgr_delete_mod then
|
||||
local mod = packages:get_list()[tabdata.selected_pkg]
|
||||
local dlg_delmod = create_delete_content_dlg(mod)
|
||||
dlg_delmod:set_parent(tabview)
|
||||
@@ -210,6 +252,16 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.btn_mod_mgr_update then
|
||||
local pkg = packages:get_list()[tabdata.selected_pkg]
|
||||
local dlg = create_store_dlg(nil, pkgmgr.get_contentdb_id(pkg))
|
||||
dlg:set_parent(tabview)
|
||||
tabview:hide()
|
||||
dlg:show()
|
||||
packages = nil
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.btn_mod_mgr_use_txp or fields.btn_mod_mgr_disable_txp then
|
||||
local txp_path = ""
|
||||
if fields.btn_mod_mgr_use_txp then
|
||||
@@ -220,18 +272,24 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
|
||||
packages = nil
|
||||
|
||||
mm_game_theme.init()
|
||||
mm_game_theme.reset()
|
||||
mm_game_theme.set_engine()
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
return {
|
||||
name = "content",
|
||||
caption = fgettext("Content"),
|
||||
caption = function()
|
||||
local update_count = update_detector.get_count()
|
||||
if update_count == 0 then
|
||||
return fgettext("Content")
|
||||
else
|
||||
return fgettext("Content [$1]", update_count)
|
||||
end
|
||||
end,
|
||||
cbf_formspec = get_formspec,
|
||||
cbf_button_handler = handle_buttons,
|
||||
on_change = pkgmgr.update_gamelist
|
||||
on_change = on_change
|
||||
}
|
||||
|
||||
@@ -23,21 +23,40 @@ local valid_disabled_settings = {
|
||||
["enable_server"]=true,
|
||||
}
|
||||
|
||||
-- Name and port stored to persist when updating the formspec
|
||||
local current_name = core.settings:get("name")
|
||||
local current_port = core.settings:get("port")
|
||||
|
||||
-- Currently chosen game in gamebar for theming and filtering
|
||||
function current_game()
|
||||
local last_game_id = core.settings:get("menu_last_game")
|
||||
local game = pkgmgr.find_by_gameid(last_game_id)
|
||||
local gameid = core.settings:get("menu_last_game")
|
||||
local game = gameid and pkgmgr.find_by_gameid(gameid)
|
||||
-- Fall back to first game installed if one exists.
|
||||
if not game and #pkgmgr.games > 0 then
|
||||
|
||||
-- If devtest is the first game in the list and there is another
|
||||
-- game available, pick the other game instead.
|
||||
local picked_game
|
||||
if pkgmgr.games[1].id == "devtest" and #pkgmgr.games > 1 then
|
||||
picked_game = 2
|
||||
else
|
||||
picked_game = 1
|
||||
end
|
||||
|
||||
game = pkgmgr.games[picked_game]
|
||||
gameid = game.id
|
||||
core.settings:set("menu_last_game", gameid)
|
||||
end
|
||||
|
||||
return game
|
||||
end
|
||||
|
||||
-- Apply menu changes from given game
|
||||
function apply_game(game)
|
||||
core.set_topleft_text(game.name)
|
||||
core.settings:set("menu_last_game", game.id)
|
||||
menudata.worldlist:set_filtercriteria(game.id)
|
||||
|
||||
mm_game_theme.update("singleplayer", game) -- this refreshes the formspec
|
||||
mm_game_theme.set_game(game)
|
||||
|
||||
local index = filterlist.get_current_index(menudata.worldlist,
|
||||
tonumber(core.settings:get("mainmenu_last_selected_world")))
|
||||
@@ -59,16 +78,12 @@ function singleplayer_refresh_gamebar()
|
||||
old_bar:delete()
|
||||
end
|
||||
|
||||
local function game_buttonbar_button_handler(fields)
|
||||
if fields.game_open_cdb then
|
||||
local maintab = ui.find_by_name("maintab")
|
||||
local dlg = create_store_dlg("game")
|
||||
dlg:set_parent(maintab)
|
||||
maintab:hide()
|
||||
dlg:show()
|
||||
return true
|
||||
end
|
||||
-- Hide gamebar if no games are installed
|
||||
if #pkgmgr.games == 0 then
|
||||
return false
|
||||
end
|
||||
|
||||
local function game_buttonbar_button_handler(fields)
|
||||
for _, game in ipairs(pkgmgr.games) do
|
||||
if fields["game_btnbar_" .. game.id] then
|
||||
apply_game(game)
|
||||
@@ -77,9 +92,12 @@ function singleplayer_refresh_gamebar()
|
||||
end
|
||||
end
|
||||
|
||||
local btnbar = buttonbar_create("game_button_bar",
|
||||
game_buttonbar_button_handler,
|
||||
{x=-0.3,y=5.9}, "horizontal", {x=12.4,y=1.15})
|
||||
local btnbar = buttonbar_create(
|
||||
"game_button_bar",
|
||||
TOUCHSCREEN_GUI and {x = 0, y = 7.25} or {x = 0, y = 7.475},
|
||||
{x = 15.5, y = 1.25},
|
||||
"#000000",
|
||||
game_buttonbar_button_handler)
|
||||
|
||||
for _, game in ipairs(pkgmgr.games) do
|
||||
local btn_name = "game_btnbar_" .. game.id
|
||||
@@ -105,6 +123,7 @@ function singleplayer_refresh_gamebar()
|
||||
|
||||
local plus_image = core.formspec_escape(defaulttexturedir .. "plus.png")
|
||||
btnbar:add_button("game_open_cdb", "", plus_image, fgettext("Install games from ContentDB"))
|
||||
return true
|
||||
end
|
||||
|
||||
local function get_disabled_settings(game)
|
||||
@@ -134,6 +153,15 @@ local function get_disabled_settings(game)
|
||||
end
|
||||
|
||||
local function get_formspec(tabview, name, tabdata)
|
||||
|
||||
-- Point the player to ContentDB when no games are found
|
||||
if #pkgmgr.games == 0 then
|
||||
return table.concat({
|
||||
"style[label_button;border=false]",
|
||||
"button[2.75,1.5;10,1;label_button;", fgettext("You have no games installed."), "]",
|
||||
"button[5.25,3.5;5,1.2;game_open_cdb;", fgettext("Install a game"), "]"})
|
||||
end
|
||||
|
||||
local retval = ""
|
||||
|
||||
local index = filterlist.get_current_index(menudata.worldlist,
|
||||
@@ -151,8 +179,8 @@ local function get_formspec(tabview, name, tabdata)
|
||||
local creative, damage, host = "", "", ""
|
||||
|
||||
-- Y offsets for game settings checkboxes
|
||||
local y = -0.2
|
||||
local yo = 0.45
|
||||
local y = 0.2
|
||||
local yo = 0.5625
|
||||
|
||||
if disabled_settings["creative_mode"] == nil then
|
||||
creative = "checkbox[0,"..y..";cb_creative_mode;".. fgettext("Creative Mode") .. ";" ..
|
||||
@@ -171,41 +199,60 @@ local function get_formspec(tabview, name, tabdata)
|
||||
end
|
||||
|
||||
retval = retval ..
|
||||
"button[3.9,3.8;2.8,1;world_delete;".. fgettext("Delete") .. "]" ..
|
||||
"button[6.55,3.8;2.8,1;world_configure;".. fgettext("Select Mods") .. "]" ..
|
||||
"button[9.2,3.8;2.8,1;world_create;".. fgettext("New") .. "]" ..
|
||||
"label[3.9,-0.05;".. fgettext("Select World:") .. "]"..
|
||||
"container[5.25,4.875]" ..
|
||||
"button[0,0;3.225,0.8;world_delete;".. fgettext("Delete") .. "]" ..
|
||||
"button[3.325,0;3.225,0.8;world_configure;".. fgettext("Select Mods") .. "]" ..
|
||||
"button[6.65,0;3.225,0.8;world_create;".. fgettext("New") .. "]" ..
|
||||
"container_end[]" ..
|
||||
"container[0.375,0.375]" ..
|
||||
creative ..
|
||||
damage ..
|
||||
host ..
|
||||
"textlist[3.9,0.4;7.9,3.45;sp_worlds;" ..
|
||||
"container_end[]" ..
|
||||
"container[5.25,0.375]" ..
|
||||
"label[0,0.2;".. fgettext("Select World:") .. "]"..
|
||||
"textlist[0,0.5;9.875,3.9;sp_worlds;" ..
|
||||
menu_render_worldlist() ..
|
||||
";" .. index .. "]"
|
||||
";" .. index .. "]" ..
|
||||
"container_end[]"
|
||||
|
||||
if core.settings:get_bool("enable_server") and disabled_settings["enable_server"] == nil then
|
||||
retval = retval ..
|
||||
"button[7.9,4.75;4.1,1;play;".. fgettext("Host Game") .. "]" ..
|
||||
"button[10.1875,5.925;4.9375,0.8;play;".. fgettext("Host Game") .. "]" ..
|
||||
"container[0.375,0.375]" ..
|
||||
"checkbox[0,"..y..";cb_server_announce;" .. fgettext("Announce Server") .. ";" ..
|
||||
dump(core.settings:get_bool("server_announce")) .. "]" ..
|
||||
"field[0.3,2.85;3.8,0.5;te_playername;" .. fgettext("Name") .. ";" ..
|
||||
core.formspec_escape(core.settings:get("name")) .. "]" ..
|
||||
"pwdfield[0.3,4.05;3.8,0.5;te_passwd;" .. fgettext("Password") .. "]"
|
||||
dump(core.settings:get_bool("server_announce")) .. "]"
|
||||
|
||||
-- Reset y so that the text fields always start at the same position,
|
||||
-- regardless of whether some of the checkboxes are hidden.
|
||||
y = 0.2 + 4 * yo + 0.35
|
||||
|
||||
retval = retval .. "field[0," .. y .. ";4.5,0.75;te_playername;" .. fgettext("Name") .. ";" ..
|
||||
core.formspec_escape(current_name) .. "]"
|
||||
|
||||
y = y + 1.15 + 0.25
|
||||
|
||||
retval = retval .. "pwdfield[0," .. y .. ";4.5,0.75;te_passwd;" .. fgettext("Password") .. "]"
|
||||
|
||||
y = y + 1.15 + 0.25
|
||||
|
||||
local bind_addr = core.settings:get("bind_address")
|
||||
if bind_addr ~= nil and bind_addr ~= "" then
|
||||
retval = retval ..
|
||||
"field[0.3,5.25;2.5,0.5;te_serveraddr;" .. fgettext("Bind Address") .. ";" ..
|
||||
"field[0," .. y .. ";3,0.75;te_serveraddr;" .. fgettext("Bind Address") .. ";" ..
|
||||
core.formspec_escape(core.settings:get("bind_address")) .. "]" ..
|
||||
"field[2.85,5.25;1.25,0.5;te_serverport;" .. fgettext("Port") .. ";" ..
|
||||
core.formspec_escape(core.settings:get("port")) .. "]"
|
||||
"field[3.25," .. y .. ";1.25,0.75;te_serverport;" .. fgettext("Port") .. ";" ..
|
||||
core.formspec_escape(current_port) .. "]"
|
||||
else
|
||||
retval = retval ..
|
||||
"field[0.3,5.25;3.8,0.5;te_serverport;" .. fgettext("Server Port") .. ";" ..
|
||||
core.formspec_escape(core.settings:get("port")) .. "]"
|
||||
"field[0," .. y .. ";4.5,0.75;te_serverport;" .. fgettext("Server Port") .. ";" ..
|
||||
core.formspec_escape(current_port) .. "]"
|
||||
end
|
||||
|
||||
retval = retval .. "container_end[]"
|
||||
else
|
||||
retval = retval ..
|
||||
"button[7.9,4.75;4.1,1;play;" .. fgettext("Play Game") .. "]"
|
||||
"button[10.1875,5.925;4.9375,0.8;play;" .. fgettext("Play Game") .. "]"
|
||||
end
|
||||
|
||||
return retval
|
||||
@@ -215,12 +262,29 @@ local function main_button_handler(this, fields, name, tabdata)
|
||||
|
||||
assert(name == "local")
|
||||
|
||||
if fields.game_open_cdb then
|
||||
local maintab = ui.find_by_name("maintab")
|
||||
local dlg = create_store_dlg("game")
|
||||
dlg:set_parent(maintab)
|
||||
maintab:hide()
|
||||
dlg:show()
|
||||
return true
|
||||
end
|
||||
|
||||
if this.dlg_create_world_closed_at == nil then
|
||||
this.dlg_create_world_closed_at = 0
|
||||
end
|
||||
|
||||
local world_doubleclick = false
|
||||
|
||||
if fields["te_playername"] then
|
||||
current_name = fields["te_playername"]
|
||||
end
|
||||
|
||||
if fields["te_serverport"] then
|
||||
current_port = fields["te_serverport"]
|
||||
end
|
||||
|
||||
if fields["sp_worlds"] ~= nil then
|
||||
local event = core.explode_textlist_event(fields["sp_worlds"])
|
||||
local selected = core.get_textlist_index("sp_worlds")
|
||||
@@ -284,7 +348,7 @@ local function main_button_handler(this, fields, name, tabdata)
|
||||
|
||||
if selected == nil or gamedata.selected_world == 0 then
|
||||
gamedata.errormessage =
|
||||
fgettext("No world created or selected!")
|
||||
fgettext_ne("No world created or selected!")
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -331,7 +395,6 @@ local function main_button_handler(this, fields, name, tabdata)
|
||||
create_world_dlg:set_parent(this)
|
||||
this:hide()
|
||||
create_world_dlg:show()
|
||||
mm_game_theme.update("singleplayer", current_game())
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -348,7 +411,6 @@ local function main_button_handler(this, fields, name, tabdata)
|
||||
delete_world_dlg:set_parent(this)
|
||||
this:hide()
|
||||
delete_world_dlg:show()
|
||||
mm_game_theme.update("singleplayer",current_game())
|
||||
end
|
||||
end
|
||||
|
||||
@@ -366,7 +428,6 @@ local function main_button_handler(this, fields, name, tabdata)
|
||||
configdialog:set_parent(this)
|
||||
this:hide()
|
||||
configdialog:show()
|
||||
mm_game_theme.update("singleplayer",current_game())
|
||||
end
|
||||
end
|
||||
|
||||
@@ -374,26 +435,24 @@ local function main_button_handler(this, fields, name, tabdata)
|
||||
end
|
||||
end
|
||||
|
||||
local function on_change(type, old_tab, new_tab)
|
||||
if (type == "ENTER") then
|
||||
local function on_change(type)
|
||||
if type == "ENTER" then
|
||||
local game = current_game()
|
||||
if game then
|
||||
apply_game(game)
|
||||
else
|
||||
mm_game_theme.set_engine()
|
||||
end
|
||||
|
||||
singleplayer_refresh_gamebar()
|
||||
ui.find_by_name("game_button_bar"):show()
|
||||
else
|
||||
if singleplayer_refresh_gamebar() then
|
||||
ui.find_by_name("game_button_bar"):show()
|
||||
end
|
||||
elseif type == "LEAVE" then
|
||||
menudata.worldlist:set_filtercriteria(nil)
|
||||
local gamebar = ui.find_by_name("game_button_bar")
|
||||
if gamebar then
|
||||
gamebar:hide()
|
||||
end
|
||||
core.set_topleft_text("")
|
||||
-- If new_tab is nil, a dialog is being shown; avoid resetting the theme
|
||||
if new_tab then
|
||||
mm_game_theme.update(new_tab,nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ local function get_formspec(tabview, name, tabdata)
|
||||
local retval =
|
||||
-- Search
|
||||
"field[0.25,0.25;7,0.75;te_search;;" .. core.formspec_escape(tabdata.search_for) .. "]" ..
|
||||
"field_enter_after_edit[te_search;true]" ..
|
||||
"container[7.25,0.25]" ..
|
||||
"image_button[0,0;0.75,0.75;" .. core.formspec_escape(defaulttexturedir .. "search.png") .. ";btn_mp_search;]" ..
|
||||
"image_button[0.75,0;0.75,0.75;" .. core.formspec_escape(defaulttexturedir .. "clear.png") .. ";btn_mp_clear;]" ..
|
||||
@@ -114,7 +115,7 @@ local function get_formspec(tabview, name, tabdata)
|
||||
"server_favorite_delete.png") .. ";btn_delete_favorite;]"
|
||||
end
|
||||
if gamedata.serverdescription then
|
||||
retval = retval .. "textarea[0.25,1.85;5.2,2.75;;;" ..
|
||||
retval = retval .. "textarea[0.25,1.85;5.25,2.7;;;" ..
|
||||
core.formspec_escape(gamedata.serverdescription) .. "]"
|
||||
end
|
||||
end
|
||||
@@ -180,7 +181,7 @@ local function get_formspec(tabview, name, tabdata)
|
||||
retval = retval .. ";0]"
|
||||
end
|
||||
|
||||
return retval, "size[15.5,7.1,false]real_coordinates[true]"
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@@ -415,9 +416,11 @@ local function main_button_handler(tabview, fields, name, tabdata)
|
||||
return false
|
||||
end
|
||||
|
||||
local function on_change(type, old_tab, new_tab)
|
||||
if type == "LEAVE" then return end
|
||||
serverlistmgr.sync()
|
||||
local function on_change(type)
|
||||
if type == "ENTER" then
|
||||
mm_game_theme.set_engine()
|
||||
serverlistmgr.sync()
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,399 +0,0 @@
|
||||
--Minetest
|
||||
--Copyright (C) 2013 sapier
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local labels = {
|
||||
leaves = {
|
||||
fgettext("Opaque Leaves"),
|
||||
fgettext("Simple Leaves"),
|
||||
fgettext("Fancy Leaves")
|
||||
},
|
||||
node_highlighting = {
|
||||
fgettext("Node Outlining"),
|
||||
fgettext("Node Highlighting"),
|
||||
fgettext("None")
|
||||
},
|
||||
filters = {
|
||||
fgettext("No Filter"),
|
||||
fgettext("Bilinear Filter"),
|
||||
fgettext("Trilinear Filter")
|
||||
},
|
||||
mipmap = {
|
||||
fgettext("No Mipmap"),
|
||||
fgettext("Mipmap"),
|
||||
fgettext("Mipmap + Aniso. Filter")
|
||||
},
|
||||
antialiasing = {
|
||||
fgettext("None"),
|
||||
fgettext("2x"),
|
||||
fgettext("4x"),
|
||||
fgettext("8x")
|
||||
},
|
||||
shadow_levels = {
|
||||
fgettext("Disabled"),
|
||||
fgettext("Very Low"),
|
||||
fgettext("Low"),
|
||||
fgettext("Medium"),
|
||||
fgettext("High"),
|
||||
fgettext("Very High")
|
||||
}
|
||||
}
|
||||
|
||||
local dd_options = {
|
||||
leaves = {
|
||||
table.concat(labels.leaves, ","),
|
||||
{"opaque", "simple", "fancy"}
|
||||
},
|
||||
node_highlighting = {
|
||||
table.concat(labels.node_highlighting, ","),
|
||||
{"box", "halo", "none"}
|
||||
},
|
||||
filters = {
|
||||
table.concat(labels.filters, ","),
|
||||
{"", "bilinear_filter", "trilinear_filter"}
|
||||
},
|
||||
mipmap = {
|
||||
table.concat(labels.mipmap, ","),
|
||||
{"", "mip_map", "anisotropic_filter"}
|
||||
},
|
||||
antialiasing = {
|
||||
table.concat(labels.antialiasing, ","),
|
||||
{"0", "2", "4", "8"}
|
||||
},
|
||||
shadow_levels = {
|
||||
table.concat(labels.shadow_levels, ","),
|
||||
{ "0", "1", "2", "3", "4", "5" }
|
||||
}
|
||||
}
|
||||
|
||||
local getSettingIndex = {
|
||||
Leaves = function()
|
||||
local style = core.settings:get("leaves_style")
|
||||
for idx, name in pairs(dd_options.leaves[2]) do
|
||||
if style == name then return idx end
|
||||
end
|
||||
return 1
|
||||
end,
|
||||
NodeHighlighting = function()
|
||||
local style = core.settings:get("node_highlighting")
|
||||
for idx, name in pairs(dd_options.node_highlighting[2]) do
|
||||
if style == name then return idx end
|
||||
end
|
||||
return 1
|
||||
end,
|
||||
Filter = function()
|
||||
if core.settings:get(dd_options.filters[2][3]) == "true" then
|
||||
return 3
|
||||
elseif core.settings:get(dd_options.filters[2][3]) == "false" and
|
||||
core.settings:get(dd_options.filters[2][2]) == "true" then
|
||||
return 2
|
||||
end
|
||||
return 1
|
||||
end,
|
||||
Mipmap = function()
|
||||
if core.settings:get(dd_options.mipmap[2][3]) == "true" then
|
||||
return 3
|
||||
elseif core.settings:get(dd_options.mipmap[2][3]) == "false" and
|
||||
core.settings:get(dd_options.mipmap[2][2]) == "true" then
|
||||
return 2
|
||||
end
|
||||
return 1
|
||||
end,
|
||||
Antialiasing = function()
|
||||
local antialiasing_setting = core.settings:get("fsaa")
|
||||
for i = 1, #dd_options.antialiasing[2] do
|
||||
if antialiasing_setting == dd_options.antialiasing[2][i] then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return 1
|
||||
end,
|
||||
ShadowMapping = function()
|
||||
local shadow_setting = core.settings:get("shadow_levels")
|
||||
for i = 1, #dd_options.shadow_levels[2] do
|
||||
if shadow_setting == dd_options.shadow_levels[2][i] then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return 1
|
||||
end
|
||||
}
|
||||
|
||||
local function antialiasing_fname_to_name(fname)
|
||||
for i = 1, #labels.antialiasing do
|
||||
if fname == labels.antialiasing[i] then
|
||||
return dd_options.antialiasing[2][i]
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function formspec(tabview, name, tabdata)
|
||||
local tab_string =
|
||||
"box[0,0;3.75,4.5;#999999]" ..
|
||||
"checkbox[0.25,0;cb_smooth_lighting;" .. fgettext("Smooth Lighting") .. ";"
|
||||
.. dump(core.settings:get_bool("smooth_lighting")) .. "]" ..
|
||||
"checkbox[0.25,0.5;cb_particles;" .. fgettext("Particles") .. ";"
|
||||
.. dump(core.settings:get_bool("enable_particles")) .. "]" ..
|
||||
"checkbox[0.25,1;cb_3d_clouds;" .. fgettext("3D Clouds") .. ";"
|
||||
.. dump(core.settings:get_bool("enable_3d_clouds")) .. "]" ..
|
||||
"checkbox[0.25,1.5;cb_opaque_water;" .. fgettext("Opaque Water") .. ";"
|
||||
.. dump(core.settings:get_bool("opaque_water")) .. "]" ..
|
||||
"checkbox[0.25,2.0;cb_connected_glass;" .. fgettext("Connected Glass") .. ";"
|
||||
.. dump(core.settings:get_bool("connected_glass")) .. "]" ..
|
||||
"dropdown[0.25,2.8;3.5;dd_node_highlighting;" .. dd_options.node_highlighting[1] .. ";"
|
||||
.. getSettingIndex.NodeHighlighting() .. "]" ..
|
||||
"dropdown[0.25,3.6;3.5;dd_leaves_style;" .. dd_options.leaves[1] .. ";"
|
||||
.. getSettingIndex.Leaves() .. "]" ..
|
||||
"box[4,0;3.75,4.9;#999999]" ..
|
||||
"label[4.25,0.1;" .. fgettext("Texturing:") .. "]" ..
|
||||
"dropdown[4.25,0.55;3.5;dd_filters;" .. dd_options.filters[1] .. ";"
|
||||
.. getSettingIndex.Filter() .. "]" ..
|
||||
"dropdown[4.25,1.35;3.5;dd_mipmap;" .. dd_options.mipmap[1] .. ";"
|
||||
.. getSettingIndex.Mipmap() .. "]" ..
|
||||
"label[4.25,2.15;" .. fgettext("Antialiasing:") .. "]" ..
|
||||
"dropdown[4.25,2.6;3.5;dd_antialiasing;" .. dd_options.antialiasing[1] .. ";"
|
||||
.. getSettingIndex.Antialiasing() .. "]" ..
|
||||
"box[8,0;3.75,4.5;#999999]"
|
||||
|
||||
local video_driver = core.settings:get("video_driver")
|
||||
local shaders_enabled = core.settings:get_bool("enable_shaders")
|
||||
if video_driver == "opengl" then
|
||||
tab_string = tab_string ..
|
||||
"checkbox[8.25,0;cb_shaders;" .. fgettext("Shaders") .. ";"
|
||||
.. tostring(shaders_enabled) .. "]"
|
||||
elseif video_driver == "ogles2" then
|
||||
tab_string = tab_string ..
|
||||
"checkbox[8.25,0;cb_shaders;" .. fgettext("Shaders (experimental)") .. ";"
|
||||
.. tostring(shaders_enabled) .. "]"
|
||||
else
|
||||
core.settings:set_bool("enable_shaders", false)
|
||||
shaders_enabled = false
|
||||
tab_string = tab_string ..
|
||||
"label[8.38,0.2;" .. core.colorize("#888888",
|
||||
fgettext("Shaders (unavailable)")) .. "]"
|
||||
end
|
||||
|
||||
tab_string = tab_string ..
|
||||
"button[8,4.75;3.95,1;btn_change_keys;"
|
||||
.. fgettext("Change Keys") .. "]"
|
||||
|
||||
tab_string = tab_string ..
|
||||
"button[0,4.75;3.95,1;btn_advanced_settings;"
|
||||
.. fgettext("All Settings") .. "]"
|
||||
|
||||
|
||||
if core.settings:get("touchscreen_threshold") ~= nil then
|
||||
tab_string = tab_string ..
|
||||
"label[4.25,3.5;" .. fgettext("Touch threshold (px):") .. "]" ..
|
||||
"dropdown[4.25,3.95;3.5;dd_touchthreshold;0,10,20,30,40,50;" ..
|
||||
((tonumber(core.settings:get("touchscreen_threshold")) / 10) + 1) ..
|
||||
"]"
|
||||
else
|
||||
tab_string = tab_string ..
|
||||
"label[4.25,3.65;" .. fgettext("Screen:") .. "]" ..
|
||||
"checkbox[4.25,3.9;cb_autosave_screensize;" .. fgettext("Autosave Screen Size") .. ";"
|
||||
.. dump(core.settings:get_bool("autosave_screensize")) .. "]"
|
||||
end
|
||||
|
||||
if shaders_enabled then
|
||||
tab_string = tab_string ..
|
||||
"checkbox[8.25,0.5;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";"
|
||||
.. dump(core.settings:get_bool("tone_mapping")) .. "]" ..
|
||||
"checkbox[8.25,1;cb_waving_water;" .. fgettext("Waving Liquids") .. ";"
|
||||
.. dump(core.settings:get_bool("enable_waving_water")) .. "]" ..
|
||||
"checkbox[8.25,1.5;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";"
|
||||
.. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" ..
|
||||
"checkbox[8.25,2;cb_waving_plants;" .. fgettext("Waving Plants") .. ";"
|
||||
.. dump(core.settings:get_bool("enable_waving_plants")) .. "]"
|
||||
|
||||
if video_driver == "opengl" then
|
||||
tab_string = tab_string ..
|
||||
"label[8.25,2.8;" .. fgettext("Dynamic shadows:") .. "]" ..
|
||||
"label[8.25,3.2;" .. fgettext("(game support required)") .. "]" ..
|
||||
"dropdown[8.25,3.7;3.5;dd_shadows;" .. dd_options.shadow_levels[1] .. ";"
|
||||
.. getSettingIndex.ShadowMapping() .. "]"
|
||||
else
|
||||
tab_string = tab_string ..
|
||||
"label[8.38,2.7;" .. core.colorize("#888888",
|
||||
fgettext("Dynamic shadows")) .. "]"
|
||||
end
|
||||
else
|
||||
tab_string = tab_string ..
|
||||
"label[8.38,0.7;" .. core.colorize("#888888",
|
||||
fgettext("Tone Mapping")) .. "]" ..
|
||||
"label[8.38,1.2;" .. core.colorize("#888888",
|
||||
fgettext("Waving Liquids")) .. "]" ..
|
||||
"label[8.38,1.7;" .. core.colorize("#888888",
|
||||
fgettext("Waving Leaves")) .. "]" ..
|
||||
"label[8.38,2.2;" .. core.colorize("#888888",
|
||||
fgettext("Waving Plants")) .. "]"..
|
||||
"label[8.38,2.7;" .. core.colorize("#888888",
|
||||
fgettext("Dynamic shadows")) .. "]"
|
||||
end
|
||||
|
||||
return tab_string
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
local function handle_settings_buttons(this, fields, tabname, tabdata)
|
||||
|
||||
if fields["btn_advanced_settings"] ~= nil then
|
||||
local adv_settings_dlg = create_adv_settings_dlg()
|
||||
adv_settings_dlg:set_parent(this)
|
||||
this:hide()
|
||||
adv_settings_dlg:show()
|
||||
--mm_game_theme.update("singleplayer", current_game())
|
||||
return true
|
||||
end
|
||||
if fields["cb_smooth_lighting"] then
|
||||
core.settings:set("smooth_lighting", fields["cb_smooth_lighting"])
|
||||
return true
|
||||
end
|
||||
if fields["cb_particles"] then
|
||||
core.settings:set("enable_particles", fields["cb_particles"])
|
||||
return true
|
||||
end
|
||||
if fields["cb_3d_clouds"] then
|
||||
core.settings:set("enable_3d_clouds", fields["cb_3d_clouds"])
|
||||
return true
|
||||
end
|
||||
if fields["cb_opaque_water"] then
|
||||
core.settings:set("opaque_water", fields["cb_opaque_water"])
|
||||
return true
|
||||
end
|
||||
if fields["cb_connected_glass"] then
|
||||
core.settings:set("connected_glass", fields["cb_connected_glass"])
|
||||
return true
|
||||
end
|
||||
if fields["cb_autosave_screensize"] then
|
||||
core.settings:set("autosave_screensize", fields["cb_autosave_screensize"])
|
||||
return true
|
||||
end
|
||||
if fields["cb_shaders"] then
|
||||
core.settings:set("enable_shaders", fields["cb_shaders"])
|
||||
return true
|
||||
end
|
||||
if fields["cb_tonemapping"] then
|
||||
core.settings:set("tone_mapping", fields["cb_tonemapping"])
|
||||
return true
|
||||
end
|
||||
if fields["cb_waving_water"] then
|
||||
core.settings:set("enable_waving_water", fields["cb_waving_water"])
|
||||
return true
|
||||
end
|
||||
if fields["cb_waving_leaves"] then
|
||||
core.settings:set("enable_waving_leaves", fields["cb_waving_leaves"])
|
||||
end
|
||||
if fields["cb_waving_plants"] then
|
||||
core.settings:set("enable_waving_plants", fields["cb_waving_plants"])
|
||||
return true
|
||||
end
|
||||
if fields["btn_change_keys"] then
|
||||
core.show_keys_menu()
|
||||
return true
|
||||
end
|
||||
|
||||
--Note dropdowns have to be handled LAST!
|
||||
local ddhandled = false
|
||||
|
||||
for i = 1, #labels.leaves do
|
||||
if fields["dd_leaves_style"] == labels.leaves[i] then
|
||||
core.settings:set("leaves_style", dd_options.leaves[2][i])
|
||||
ddhandled = true
|
||||
end
|
||||
end
|
||||
for i = 1, #labels.node_highlighting do
|
||||
if fields["dd_node_highlighting"] == labels.node_highlighting[i] then
|
||||
core.settings:set("node_highlighting", dd_options.node_highlighting[2][i])
|
||||
ddhandled = true
|
||||
end
|
||||
end
|
||||
if fields["dd_filters"] == labels.filters[1] then
|
||||
core.settings:set("bilinear_filter", "false")
|
||||
core.settings:set("trilinear_filter", "false")
|
||||
ddhandled = true
|
||||
elseif fields["dd_filters"] == labels.filters[2] then
|
||||
core.settings:set("bilinear_filter", "true")
|
||||
core.settings:set("trilinear_filter", "false")
|
||||
ddhandled = true
|
||||
elseif fields["dd_filters"] == labels.filters[3] then
|
||||
core.settings:set("bilinear_filter", "false")
|
||||
core.settings:set("trilinear_filter", "true")
|
||||
ddhandled = true
|
||||
end
|
||||
if fields["dd_mipmap"] == labels.mipmap[1] then
|
||||
core.settings:set("mip_map", "false")
|
||||
core.settings:set("anisotropic_filter", "false")
|
||||
ddhandled = true
|
||||
elseif fields["dd_mipmap"] == labels.mipmap[2] then
|
||||
core.settings:set("mip_map", "true")
|
||||
core.settings:set("anisotropic_filter", "false")
|
||||
ddhandled = true
|
||||
elseif fields["dd_mipmap"] == labels.mipmap[3] then
|
||||
core.settings:set("mip_map", "true")
|
||||
core.settings:set("anisotropic_filter", "true")
|
||||
ddhandled = true
|
||||
end
|
||||
if fields["dd_antialiasing"] then
|
||||
core.settings:set("fsaa",
|
||||
antialiasing_fname_to_name(fields["dd_antialiasing"]))
|
||||
ddhandled = true
|
||||
end
|
||||
if fields["dd_touchthreshold"] then
|
||||
core.settings:set("touchscreen_threshold", fields["dd_touchthreshold"])
|
||||
ddhandled = true
|
||||
end
|
||||
|
||||
for i = 1, #labels.shadow_levels do
|
||||
if fields["dd_shadows"] == labels.shadow_levels[i] then
|
||||
core.settings:set("shadow_levels", dd_options.shadow_levels[2][i])
|
||||
ddhandled = true
|
||||
end
|
||||
end
|
||||
|
||||
if fields["dd_shadows"] == labels.shadow_levels[1] then
|
||||
core.settings:set("enable_dynamic_shadows", "false")
|
||||
else
|
||||
local shadow_presets = {
|
||||
[2] = { 62, 512, "true", 0, "false" },
|
||||
[3] = { 93, 1024, "true", 0, "false" },
|
||||
[4] = { 140, 2048, "true", 1, "false" },
|
||||
[5] = { 210, 4096, "true", 2, "true" },
|
||||
[6] = { 300, 8192, "true", 2, "true" },
|
||||
}
|
||||
local s = shadow_presets[table.indexof(labels.shadow_levels, fields["dd_shadows"])]
|
||||
if s then
|
||||
core.settings:set("enable_dynamic_shadows", "true")
|
||||
core.settings:set("shadow_map_max_distance", s[1])
|
||||
core.settings:set("shadow_map_texture_size", s[2])
|
||||
core.settings:set("shadow_map_texture_32bit", s[3])
|
||||
core.settings:set("shadow_filters", s[4])
|
||||
core.settings:set("shadow_map_color", s[5])
|
||||
end
|
||||
end
|
||||
|
||||
return ddhandled
|
||||
end
|
||||
|
||||
return {
|
||||
name = "settings",
|
||||
caption = fgettext("Settings"),
|
||||
cbf_formspec = formspec,
|
||||
cbf_button_handler = handle_settings_buttons
|
||||
}
|
||||
@@ -57,6 +57,25 @@
|
||||
# Comments and (Readable name) are handled by gettext.
|
||||
# Comments should be complete sentences that describe the setting and possibly
|
||||
# give the user additional useful insight.
|
||||
# The last line of a comment can contain the requirements for the setting, ie:
|
||||
#
|
||||
# # This is a comment
|
||||
# #
|
||||
# # Requires: shaders, enable_dynamic_shadows, !touchscreen_gui
|
||||
# name (Readable name) type type_args
|
||||
#
|
||||
# A requirement can be the name of a boolean setting or an engine-defined value.
|
||||
# These requirements may be:
|
||||
#
|
||||
# * The value of a boolean setting, such as enable_dynamic_shadows
|
||||
# * An engine-defined value:
|
||||
# * shaders_support (a video driver that supports shaders, may not be enabled)
|
||||
# * shaders (both enable_shaders and shaders_support)
|
||||
# * desktop / android
|
||||
# * touchscreen_gui / keyboard_mouse
|
||||
# * opengl / gles
|
||||
# * You can negate any requirement by prepending with !
|
||||
#
|
||||
# Sections are marked by a single line in the format: [Section Name]
|
||||
# Sub-section are marked by adding * in front of the section name: [*Sub-section]
|
||||
# Sub-sub-sections have two * etc.
|
||||
@@ -68,20 +87,18 @@
|
||||
|
||||
[*General]
|
||||
|
||||
# If enabled, you can place blocks at the position (feet + eye level) where you stand.
|
||||
# This is helpful when working with nodeboxes in small areas.
|
||||
enable_build_where_you_stand (Build inside player) bool false
|
||||
|
||||
# Smooths camera when looking around. Also called look or mouse smoothing.
|
||||
# Useful for recording videos.
|
||||
cinematic (Cinematic mode) bool false
|
||||
|
||||
# Smooths rotation of camera. 0 to disable.
|
||||
# Smooths rotation of camera, also called look or mouse smoothing. 0 to disable.
|
||||
camera_smoothing (Camera smoothing) float 0.0 0.0 0.99
|
||||
|
||||
# Smooths rotation of camera in cinematic mode. 0 to disable.
|
||||
# Smooths rotation of camera when in cinematic mode, 0 to disable. Enter cinematic mode by using the key set in Change Keys.
|
||||
#
|
||||
# Requires: keyboard_mouse
|
||||
cinematic_camera_smoothing (Camera smoothing in cinematic mode) float 0.7 0.0 0.99
|
||||
|
||||
# If enabled, you can place nodes at the position (feet + eye level) where you stand.
|
||||
# This is helpful when working with nodeboxes in small areas.
|
||||
enable_build_where_you_stand (Build inside player) bool false
|
||||
|
||||
# If enabled, "Aux1" key instead of "Sneak" key is used for climbing down and
|
||||
# descending.
|
||||
aux1_descends (Aux1 key for climbing/descending) bool false
|
||||
@@ -95,38 +112,68 @@ always_fly_fast (Always fly fast) bool true
|
||||
|
||||
# The time in seconds it takes between repeated node placements when holding
|
||||
# the place button.
|
||||
#
|
||||
# Requires: keyboard_mouse
|
||||
repeat_place_time (Place repetition interval) float 0.25 0.16 2
|
||||
|
||||
# Automatically jump up single-node obstacles.
|
||||
autojump (Automatic jumping) bool false
|
||||
|
||||
# Prevent digging and placing from repeating when holding the mouse buttons.
|
||||
# Prevent digging and placing from repeating when holding the respective buttons.
|
||||
# Enable this when you dig or place too often by accident.
|
||||
# On touchscreens, this only affects digging.
|
||||
safe_dig_and_place (Safe digging and placing) bool false
|
||||
|
||||
[*Keyboard and Mouse]
|
||||
|
||||
# Invert vertical mouse movement.
|
||||
#
|
||||
# Requires: keyboard_mouse
|
||||
invert_mouse (Invert mouse) bool false
|
||||
|
||||
# Mouse sensitivity multiplier.
|
||||
#
|
||||
# Requires: keyboard_mouse
|
||||
mouse_sensitivity (Mouse sensitivity) float 0.2 0.001 10.0
|
||||
|
||||
# Enable mouse wheel (scroll) for item selection in hotbar.
|
||||
#
|
||||
# Requires: keyboard_mouse
|
||||
enable_hotbar_mouse_wheel (Hotbar: Enable mouse wheel for selection) bool true
|
||||
|
||||
# Invert mouse wheel (scroll) direction for item selection in hotbar.
|
||||
#
|
||||
# Requires: keyboard_mouse
|
||||
invert_hotbar_mouse_wheel (Hotbar: Invert mouse wheel direction) bool false
|
||||
|
||||
[*Touchscreen]
|
||||
|
||||
# The length in pixels it takes for touch screen interaction to start.
|
||||
touchscreen_threshold (Touch screen threshold) int 20 0 100
|
||||
# The length in pixels it takes for touchscreen interaction to start.
|
||||
#
|
||||
# Requires: touchscreen_gui
|
||||
touchscreen_threshold (Touchscreen threshold) int 20 0 100
|
||||
|
||||
# Touchscreen sensitivity multiplier.
|
||||
#
|
||||
# Requires: touchscreen_gui
|
||||
touchscreen_sensitivity (Touchscreen sensitivity) float 0.2 0.001 10.0
|
||||
|
||||
# Use crosshair to select object instead of whole screen.
|
||||
# If enabled, a crosshair will be shown and will be used for selecting object.
|
||||
#
|
||||
# Requires: touchscreen_gui
|
||||
touch_use_crosshair (Use crosshair for touch screen) bool false
|
||||
|
||||
# (Android) Fixes the position of virtual joystick.
|
||||
# Fixes the position of virtual joystick.
|
||||
# If disabled, virtual joystick will center to first-touch's position.
|
||||
#
|
||||
# Requires: touchscreen_gui
|
||||
fixed_virtual_joystick (Fixed virtual joystick) bool false
|
||||
|
||||
# (Android) Use virtual joystick to trigger "Aux1" button.
|
||||
# Use virtual joystick to trigger "Aux1" button.
|
||||
# If enabled, virtual joystick will also tap "Aux1" button when out of main circle.
|
||||
#
|
||||
# Requires: touchscreen_gui
|
||||
virtual_joystick_triggers_aux1 (Virtual joystick triggers Aux1 button) bool false
|
||||
|
||||
|
||||
@@ -136,20 +183,38 @@ virtual_joystick_triggers_aux1 (Virtual joystick triggers Aux1 button) bool fals
|
||||
|
||||
[**Screen]
|
||||
|
||||
# Width component of the initial window size. Ignored in fullscreen mode.
|
||||
# Width component of the initial window size.
|
||||
#
|
||||
# Requires: desktop
|
||||
screen_w (Screen width) int 1024 1 65535
|
||||
|
||||
# Height component of the initial window size. Ignored in fullscreen mode.
|
||||
# Height component of the initial window size.
|
||||
#
|
||||
# Requires: desktop
|
||||
screen_h (Screen height) int 600 1 65535
|
||||
|
||||
# Whether the window is maximized.
|
||||
#
|
||||
# Requires: desktop
|
||||
window_maximized (Window maximized) bool false
|
||||
|
||||
# Save window size automatically when modified.
|
||||
autosave_screensize (Autosave screen size) bool true
|
||||
# If true, screen size is saved in screen_w and screen_h, and whether the window
|
||||
# is maximized is stored in window_maximized.
|
||||
# (Autosaving window_maximized only works if compiled with SDL.)
|
||||
#
|
||||
# Requires: desktop
|
||||
autosave_screensize (Remember screen size) bool true
|
||||
|
||||
# Fullscreen mode.
|
||||
#
|
||||
# Requires: desktop
|
||||
fullscreen (Full screen) bool false
|
||||
|
||||
# Open the pause menu when the window's focus is lost. Does not pause if a formspec is
|
||||
# open.
|
||||
#
|
||||
# Requires: desktop
|
||||
pause_on_lost_focus (Pause on lost window focus) bool false
|
||||
|
||||
[**FPS]
|
||||
@@ -158,7 +223,7 @@ pause_on_lost_focus (Pause on lost window focus) bool false
|
||||
# to not waste CPU power for no benefit.
|
||||
fps_max (Maximum FPS) int 60 1 4294967295
|
||||
|
||||
# Vertical screen synchronization.
|
||||
# Vertical screen synchronization. Your system may still force VSync on even if this is disabled.
|
||||
vsync (VSync) bool false
|
||||
|
||||
# Maximum FPS when the window is not focused, or when the game is paused.
|
||||
@@ -198,13 +263,13 @@ performance_tradeoffs (Tradeoffs for performance) bool false
|
||||
# Adds particles when digging a node.
|
||||
enable_particles (Digging particles) bool true
|
||||
|
||||
[**3d]
|
||||
[**3D]
|
||||
|
||||
# 3D support.
|
||||
# Currently supported:
|
||||
# - none: no 3d output.
|
||||
# - anaglyph: cyan/magenta color 3d.
|
||||
# - interlaced: odd/even line based polarisation screen support.
|
||||
# - interlaced: odd/even line based polarization screen support.
|
||||
# - topbottom: split screen top/bottom.
|
||||
# - sidebyside: split screen side by side.
|
||||
# - crossview: Cross-eyed 3d
|
||||
@@ -230,12 +295,6 @@ fall_bobbing_amount (Fall bobbing factor) float 0.03 0.0 100.0
|
||||
|
||||
[**Camera]
|
||||
|
||||
# Camera 'near clipping plane' distance in nodes, between 0 and 0.25
|
||||
# Only works on GLES platforms. Most users will not need to change this.
|
||||
# Increasing can reduce artifacting on weaker GPUs.
|
||||
# 0.1 = Default, 0.25 = Good value for weaker tablets.
|
||||
near_plane (Near plane) float 0.1 0 0.25
|
||||
|
||||
# Field of view in degrees.
|
||||
fov (Field of view) int 72 45 160
|
||||
|
||||
@@ -250,20 +309,26 @@ display_gamma (Light curve gamma) float 1.0 0.33 3.0
|
||||
# Lower is darker, Higher is lighter. The valid range of values for this
|
||||
# setting is 0.25 to 4.0 inclusive. If the value is out of range it will be
|
||||
# set to the nearest valid value.
|
||||
ambient_occlusion_gamma (Ambient occlusion gamma) float 2.2 0.25 4.0
|
||||
ambient_occlusion_gamma (Ambient occlusion gamma) float 1.8 0.25 4.0
|
||||
|
||||
[**Screenshots]
|
||||
|
||||
# Path to save screenshots at. Can be an absolute or relative path.
|
||||
# The folder will be created if it doesn't already exist.
|
||||
#
|
||||
# Requires: desktop
|
||||
screenshot_path (Screenshot folder) path screenshots
|
||||
|
||||
# Format of screenshots.
|
||||
#
|
||||
# Requires: desktop
|
||||
screenshot_format (Screenshot format) enum png png,jpg
|
||||
|
||||
# Screenshot quality. Only used for JPEG format.
|
||||
# 1 means worst quality; 100 means best quality.
|
||||
# Use 0 for default quality.
|
||||
#
|
||||
# Requires: desktop
|
||||
screenshot_quality (Screenshot quality) int 0 0 100
|
||||
|
||||
[**Node and Entity Highlighting]
|
||||
@@ -295,59 +360,81 @@ crosshair_alpha (Crosshair alpha) int 255 0 255
|
||||
enable_fog (Fog) bool true
|
||||
|
||||
# Make fog and sky colors depend on daytime (dawn/sunset) and view direction.
|
||||
#
|
||||
# Requires: enable_fog
|
||||
directional_colored_fog (Colored fog) bool true
|
||||
|
||||
# Fraction of the visible distance at which fog starts to be rendered
|
||||
#
|
||||
# Requires: enable_fog
|
||||
fog_start (Fog start) float 0.4 0.0 0.99
|
||||
|
||||
[**Clouds]
|
||||
|
||||
# Clouds are a client side effect.
|
||||
# Clouds are a client-side effect.
|
||||
enable_clouds (Clouds) bool true
|
||||
|
||||
# Use 3D cloud look instead of flat.
|
||||
#
|
||||
# Requires: enable_clouds
|
||||
enable_3d_clouds (3D clouds) bool true
|
||||
|
||||
[**Filtering and Antialiasing]
|
||||
|
||||
# Use mipmapping to scale textures. May slightly increase performance,
|
||||
# especially when using a high resolution texture pack.
|
||||
# Gamma correct downscaling is not supported.
|
||||
# Use mipmaps when scaling textures. May slightly increase performance,
|
||||
# especially when using a high-resolution texture pack.
|
||||
# Gamma-correct downscaling is not supported.
|
||||
mip_map (Mipmapping) bool false
|
||||
|
||||
# Use anisotropic filtering when viewing at textures from an angle.
|
||||
anisotropic_filter (Anisotropic filtering) bool false
|
||||
|
||||
# Use bilinear filtering when scaling textures.
|
||||
bilinear_filter (Bilinear filtering) bool false
|
||||
|
||||
# Use trilinear filtering when scaling textures.
|
||||
# If both bilinear and trilinear filtering are enabled, trilinear filtering
|
||||
# is applied.
|
||||
trilinear_filter (Trilinear filtering) bool false
|
||||
|
||||
# Filtered textures can blend RGB values with fully-transparent neighbors,
|
||||
# which PNG optimizers usually discard, often resulting in dark or
|
||||
# light edges to transparent textures. Apply a filter to clean that up
|
||||
# at texture load time. This is automatically enabled if mipmapping is enabled.
|
||||
texture_clean_transparent (Clean transparent textures) bool false
|
||||
# Use anisotropic filtering when looking at textures from an angle.
|
||||
anisotropic_filter (Anisotropic filtering) bool false
|
||||
|
||||
# When using bilinear/trilinear/anisotropic filters, low-resolution textures
|
||||
# can be blurred, so automatically upscale them with nearest-neighbor
|
||||
# interpolation to preserve crisp pixels. This sets the minimum texture size
|
||||
# for the upscaled textures; higher values look sharper, but require more
|
||||
# memory. Powers of 2 are recommended. This setting is ONLY applied if
|
||||
# bilinear/trilinear/anisotropic filtering is enabled.
|
||||
# This is also used as the base node texture size for world-aligned
|
||||
# texture autoscaling.
|
||||
texture_min_size (Minimum texture size) int 64 1 32768
|
||||
# Select the antialiasing method to apply.
|
||||
#
|
||||
# * None - No antialiasing (default)
|
||||
#
|
||||
# * FSAA - Hardware-provided full-screen antialiasing
|
||||
# (incompatible with Post Processing and Undersampling)
|
||||
# A.K.A multi-sample antialiasing (MSAA)
|
||||
# Smoothens out block edges but does not affect the insides of textures.
|
||||
# A restart is required to change this option.
|
||||
#
|
||||
# * FXAA - Fast approximate antialiasing (requires shaders)
|
||||
# Applies a post-processing filter to detect and smoothen high-contrast edges.
|
||||
# Provides balance between speed and image quality.
|
||||
#
|
||||
# * SSAA - Super-sampling antialiasing (requires shaders)
|
||||
# Renders higher-resolution image of the scene, then scales down to reduce
|
||||
# the aliasing effects. This is the slowest and the most accurate method.
|
||||
antialiasing (Antialiasing method) enum none none,fsaa,fxaa,ssaa
|
||||
|
||||
# Defines the size of the sampling grid for FSAA and SSAA antialiasing methods.
|
||||
# Value of 2 means taking 2x2 = 4 samples.
|
||||
fsaa (Anti-aliasing scale) enum 2 2,4,8,16
|
||||
|
||||
[**Occlusion Culling]
|
||||
|
||||
# Type of occlusion_culler
|
||||
#
|
||||
# "loops" is the legacy algorithm with nested loops and O(n³) complexity
|
||||
# "bfs" is the new algorithm based on breadth-first-search and side culling
|
||||
#
|
||||
# This setting should only be changed if you have performance problems.
|
||||
occlusion_culler (Occlusion Culler) enum bfs bfs,loops
|
||||
|
||||
# Use raytraced occlusion culling in the new culler.
|
||||
# This flag enables use of raytraced occlusion culling test for
|
||||
# client mesh sizes smaller than 4x4x4 map blocks.
|
||||
enable_raytraced_culling (Enable Raytraced Culling) bool true
|
||||
|
||||
# Use multi-sample antialiasing (MSAA) to smooth out block edges.
|
||||
# This algorithm smooths out the 3D viewport while keeping the image sharp,
|
||||
# but it doesn't affect the insides of textures
|
||||
# (which is especially noticeable with transparent textures).
|
||||
# Visible spaces appear between nodes when shaders are disabled.
|
||||
# If set to 0, MSAA is disabled.
|
||||
# A restart is required after changing this option.
|
||||
fsaa (FSAA) enum 0 0,1,2,4,8,16
|
||||
|
||||
|
||||
[*Shaders]
|
||||
@@ -355,138 +442,184 @@ fsaa (FSAA) enum 0 0,1,2,4,8,16
|
||||
# Shaders allow advanced visual effects and may increase performance on some video
|
||||
# cards.
|
||||
# This only works with the OpenGL video backend.
|
||||
#
|
||||
# Requires: shaders_support
|
||||
enable_shaders (Shaders) bool true
|
||||
|
||||
[**Tone Mapping]
|
||||
|
||||
# Enables Hable's 'Uncharted 2' filmic tone mapping.
|
||||
# Simulates the tone curve of photographic film and how this approximates the
|
||||
# appearance of high dynamic range images. Mid-range contrast is slightly
|
||||
# enhanced, highlights and shadows are gradually compressed.
|
||||
tone_mapping (Filmic tone mapping) bool false
|
||||
|
||||
[**Waving Nodes]
|
||||
|
||||
# Set to true to enable waving leaves.
|
||||
# Requires shaders to be enabled.
|
||||
#
|
||||
# Requires: shaders
|
||||
enable_waving_leaves (Waving leaves) bool false
|
||||
|
||||
# Set to true to enable waving plants.
|
||||
# Requires shaders to be enabled.
|
||||
#
|
||||
# Requires: shaders
|
||||
enable_waving_plants (Waving plants) bool false
|
||||
|
||||
# Set to true to enable waving liquids (like water).
|
||||
# Requires shaders to be enabled.
|
||||
#
|
||||
# Requires: shaders
|
||||
enable_waving_water (Waving liquids) bool false
|
||||
|
||||
# The maximum height of the surface of waving liquids.
|
||||
# 4.0 = Wave height is two nodes.
|
||||
# 0.0 = Wave doesn't move at all.
|
||||
# Default is 1.0 (1/2 node).
|
||||
# Requires waving liquids to be enabled.
|
||||
#
|
||||
# Requires: shaders, enable_waving_water
|
||||
water_wave_height (Waving liquids wave height) float 1.0 0.0 4.0
|
||||
|
||||
# Length of liquid waves.
|
||||
# Requires waving liquids to be enabled.
|
||||
#
|
||||
# Requires: shaders, enable_waving_water
|
||||
water_wave_length (Waving liquids wavelength) float 20.0 0.1
|
||||
|
||||
# How fast liquid waves will move. Higher = faster.
|
||||
# If negative, liquid waves will move backwards.
|
||||
# Requires waving liquids to be enabled.
|
||||
#
|
||||
# Requires: shaders, enable_waving_water
|
||||
water_wave_speed (Waving liquids wave speed) float 5.0
|
||||
|
||||
[**Dynamic shadows]
|
||||
|
||||
# Set to true to enable Shadow Mapping.
|
||||
# Requires shaders to be enabled.
|
||||
#
|
||||
# Requires: shaders, opengl
|
||||
enable_dynamic_shadows (Dynamic shadows) bool false
|
||||
|
||||
# Set the shadow strength gamma.
|
||||
# Adjusts the intensity of in-game dynamic shadows.
|
||||
# Lower value means lighter shadows, higher value means darker shadows.
|
||||
#
|
||||
# Requires: shaders, enable_dynamic_shadows, opengl
|
||||
shadow_strength_gamma (Shadow strength gamma) float 1.0 0.1 10.0
|
||||
|
||||
# Maximum distance to render shadows.
|
||||
shadow_map_max_distance (Shadow map max distance in nodes to render shadows) float 120.0 10.0 1000.0
|
||||
#
|
||||
# Requires: shaders, enable_dynamic_shadows, opengl
|
||||
shadow_map_max_distance (Shadow map max distance in nodes to render shadows) float 140.0 10.0 1000.0
|
||||
|
||||
# Texture size to render the shadow map on.
|
||||
# This must be a power of two.
|
||||
# Bigger numbers create better shadows but it is also more expensive.
|
||||
shadow_map_texture_size (Shadow map texture size) int 1024 128 8192
|
||||
#
|
||||
# Requires: shaders, enable_dynamic_shadows, opengl
|
||||
shadow_map_texture_size (Shadow map texture size) int 2048 128 8192
|
||||
|
||||
# Sets shadow texture quality to 32 bits.
|
||||
# On false, 16 bits texture will be used.
|
||||
# This can cause much more artifacts in the shadow.
|
||||
#
|
||||
# Requires: shaders, enable_dynamic_shadows, opengl
|
||||
shadow_map_texture_32bit (Shadow map texture in 32 bits) bool true
|
||||
|
||||
# Enable Poisson disk filtering.
|
||||
# On true uses Poisson disk to make "soft shadows". Otherwise uses PCF filtering.
|
||||
#
|
||||
# Requires: shaders, enable_dynamic_shadows, opengl
|
||||
shadow_poisson_filter (Poisson filtering) bool true
|
||||
|
||||
# Define shadow filtering quality.
|
||||
# This simulates the soft shadows effect by applying a PCF or Poisson disk
|
||||
# but also uses more resources.
|
||||
# Define shadow filtering quality.
|
||||
# This simulates the soft shadows effect by applying a PCF or Poisson disk
|
||||
# but also uses more resources.
|
||||
#
|
||||
# Requires: shaders, enable_dynamic_shadows, opengl
|
||||
shadow_filters (Shadow filter quality) enum 1 0,1,2
|
||||
|
||||
# Enable colored shadows.
|
||||
# On true translucent nodes cast colored shadows. This is expensive.
|
||||
#
|
||||
# Requires: shaders, enable_dynamic_shadows, opengl
|
||||
shadow_map_color (Colored shadows) bool false
|
||||
|
||||
# Spread a complete update of shadow map over given amount of frames.
|
||||
# Spread a complete update of shadow map over given number of frames.
|
||||
# Higher values might make shadows laggy, lower values
|
||||
# will consume more resources.
|
||||
# Minimum value: 1; maximum value: 16
|
||||
#
|
||||
# Requires: shaders, enable_dynamic_shadows, opengl
|
||||
shadow_update_frames (Map shadows update frames) int 8 1 16
|
||||
|
||||
# Set the soft shadow radius size.
|
||||
# Lower values mean sharper shadows, bigger values mean softer shadows.
|
||||
# Minimum value: 1.0; maximum value: 15.0
|
||||
#
|
||||
# Requires: shaders, enable_dynamic_shadows, opengl
|
||||
shadow_soft_radius (Soft shadow radius) float 5.0 1.0 15.0
|
||||
|
||||
# Set the default tilt of Sun/Moon orbit in degrees.
|
||||
# Games may change orbit tilt via API.
|
||||
# Value of 0 means no tilt / vertical orbit.
|
||||
# type: float min: -60 max: 60
|
||||
#
|
||||
# Requires: shaders, enable_dynamic_shadows, opengl
|
||||
shadow_sky_body_orbit_tilt (Sky Body Orbit Tilt) float 0.0 -60.0 60.0
|
||||
|
||||
[**Post processing]
|
||||
[**Post Processing]
|
||||
|
||||
# Set the exposure compensation in EV units.
|
||||
# Value of 0.0 (default) means no exposure compensation.
|
||||
# Range: from -1 to 1.0
|
||||
exposure_compensation (Exposure compensation) float 0.0 -1.0 1.0
|
||||
# Enables the post processing pipeline.
|
||||
#
|
||||
# Requires: shaders
|
||||
enable_post_processing (Enable Post Processing) bool true
|
||||
|
||||
# Enables Hable's 'Uncharted 2' filmic tone mapping.
|
||||
# Simulates the tone curve of photographic film and how this approximates the
|
||||
# appearance of high dynamic range images. Mid-range contrast is slightly
|
||||
# enhanced, highlights and shadows are gradually compressed.
|
||||
#
|
||||
# Requires: shaders, enable_post_processing
|
||||
tone_mapping (Filmic tone mapping) bool false
|
||||
|
||||
# Enable automatic exposure correction
|
||||
# When enabled, the post-processing engine will
|
||||
# automatically adjust to the brightness of the scene,
|
||||
# simulating the behavior of human eye.
|
||||
#
|
||||
# Requires: shaders, enable_post_processing
|
||||
enable_auto_exposure (Enable Automatic Exposure) bool false
|
||||
|
||||
# Set the exposure compensation in EV units.
|
||||
# Value of 0.0 (default) means no exposure compensation.
|
||||
# Range: from -1 to 1.0
|
||||
#
|
||||
# Requires: shaders, enable_post_processing, enable_auto_exposure
|
||||
exposure_compensation (Exposure compensation) float 0.0 -1.0 1.0
|
||||
|
||||
[**Bloom]
|
||||
|
||||
# Set to true to enable bloom effect.
|
||||
# Bright colors will bleed over the neighboring objects.
|
||||
#
|
||||
# Requires: shaders, enable_post_processing
|
||||
enable_bloom (Enable Bloom) bool false
|
||||
|
||||
# Set to true to render debugging breakdown of the bloom effect.
|
||||
# In debug mode, the screen is split into 4 quadrants:
|
||||
# top-left - processed base image, top-right - final image
|
||||
# bottom-left - raw base image, bottom-right - bloom texture.
|
||||
#
|
||||
# Requires: shaders, enable_post_processing, enable_bloom
|
||||
enable_bloom_debug (Enable Bloom Debug) bool false
|
||||
|
||||
# Defines how much bloom is applied to the rendered image
|
||||
# Smaller values make bloom more subtle
|
||||
# Range: from 0.01 to 1.0, default: 0.05
|
||||
#
|
||||
# Requires: shaders, enable_post_processing, enable_bloom
|
||||
bloom_intensity (Bloom Intensity) float 0.05 0.01 1.0
|
||||
|
||||
# Defines the magnitude of bloom overexposure.
|
||||
# Range: from 0.1 to 10.0, default: 1.0
|
||||
#
|
||||
# Requires: shaders, enable_post_processing, enable_bloom
|
||||
bloom_strength_factor (Bloom Strength Factor) float 1.0 0.1 10.0
|
||||
|
||||
# Logical value that controls how far the bloom effect spreads
|
||||
# from the bright objects.
|
||||
# Range: from 0.1 to 8, default: 1
|
||||
#
|
||||
# Requires: shaders, enable_post_processing, enable_bloom
|
||||
bloom_radius (Bloom Radius) float 1 0.1 8
|
||||
|
||||
|
||||
@@ -494,7 +627,7 @@ bloom_radius (Bloom Radius) float 1 0.1 8
|
||||
|
||||
# Volume of all sounds.
|
||||
# Requires the sound system to be enabled.
|
||||
sound_volume (Volume) float 0.7 0.0 1.0
|
||||
sound_volume (Volume) float 0.8 0.0 1.0
|
||||
|
||||
# Whether to mute sounds. You can unmute sounds at any time, unless the
|
||||
# sound system is disabled (enable_sound=false).
|
||||
@@ -508,7 +641,7 @@ mute_sound (Mute sound) bool false
|
||||
# A restart is required after changing this.
|
||||
language (Language) enum ,be,bg,ca,cs,da,de,el,en,eo,es,et,eu,fi,fr,gd,gl,hu,id,it,ja,jbo,kk,ko,lt,lv,ms,nb,nl,nn,pl,pt,pt_BR,ro,ru,sk,sl,sr_Cyrl,sr_Latn,sv,sw,tr,uk,vi,zh_CN,zh_TW
|
||||
|
||||
[**GUIs]
|
||||
[**GUI]
|
||||
|
||||
# Scale GUI by a user specified value.
|
||||
# Use a nearest-neighbor-anti-alias filter to scale the GUI.
|
||||
@@ -577,7 +710,7 @@ hud_hotbar_max_width (Maximum hotbar width) float 1.0 0.001 1.0
|
||||
clickable_chat_weblinks (Chat weblinks) bool true
|
||||
|
||||
# Optional override for chat weblink color.
|
||||
chat_weblink_color (Weblink color) string
|
||||
chat_weblink_color (Weblink color) string #8888FF
|
||||
|
||||
# Font size of the recent chat text and chat prompt in point (pt).
|
||||
# Value 0 will use the default font size.
|
||||
@@ -619,26 +752,6 @@ enable_split_login_register (Enable split login/register) bool true
|
||||
# URL to JSON file which provides information about the newest Minetest release
|
||||
update_information_url (Update information URL) string https://www.minetest.net/release_info.json
|
||||
|
||||
# Unix timestamp (integer) of when the client last checked for an update
|
||||
# Set this value to "disabled" to never check for updates.
|
||||
update_last_checked (Last update check) string
|
||||
|
||||
# Version number which was last seen during an update check.
|
||||
#
|
||||
# Representation: MMMIIIPPP, where M=Major, I=Minor, P=Patch
|
||||
# Ex: 5.5.0 is 005005000
|
||||
update_last_known (Last known version update) int 0
|
||||
|
||||
# Type of occlusion_culler
|
||||
# "loops" is the legacy algorithm with nested loops and O(N^3) complexity
|
||||
# "bfs" is the new algorithm based on breadth-first-search and side culling
|
||||
occlusion_culler (Occlusion Culler) enum bfs bfs,loops
|
||||
|
||||
# Use raytraced occlusion culling in the new culler.
|
||||
# This flag enables use of raytraced occlusion culling test for
|
||||
# client mesh sizes smaller than 4x4x4 map blocks.
|
||||
enable_raytraced_culling (Enable Raytraced Culling) bool true
|
||||
|
||||
[*Server]
|
||||
|
||||
# Name of the player.
|
||||
@@ -673,7 +786,7 @@ motd (Message of the day) string
|
||||
max_users (Maximum users) int 15 0 65535
|
||||
|
||||
# If this is set, players will always (re)spawn at the given position.
|
||||
static_spawnpoint (Static spawnpoint) string
|
||||
static_spawnpoint (Static spawn point) string
|
||||
|
||||
[**Networking]
|
||||
|
||||
@@ -738,7 +851,7 @@ csm_restriction_flags (Client side modding restrictions) int 62 0 63
|
||||
|
||||
# If the CSM restriction for node range is enabled, get_node calls are limited
|
||||
# to this distance from the player to the node.
|
||||
csm_restriction_noderange (Client side node lookup range restriction) int 0 0 4294967295
|
||||
csm_restriction_noderange (Client-side node lookup range restriction) int 0 0 4294967295
|
||||
|
||||
[**Chat]
|
||||
|
||||
@@ -749,8 +862,8 @@ strip_color_codes (Strip color codes) bool false
|
||||
# Set the maximum length of a chat message (in characters) sent by clients.
|
||||
chat_message_max_size (Chat message max length) int 500 10 65535
|
||||
|
||||
# Amount of messages a player may send per 10 seconds.
|
||||
chat_message_limit_per_10sec (Chat message count limit) float 10.0 1.0
|
||||
# Number of messages a player may send per 10 seconds.
|
||||
chat_message_limit_per_10sec (Chat message count limit) float 8.0 1.0
|
||||
|
||||
# Kick players who sent more than X messages per 10 seconds.
|
||||
chat_message_limit_trigger_kick (Chat message kick threshold) int 50 1 65535
|
||||
@@ -846,7 +959,7 @@ mapgen_limit (Map generation limit) int 31007 0 31007
|
||||
# and jungle grass, in all other mapgens this flag controls all decorations.
|
||||
mg_flags (Mapgen flags) flags caves,dungeons,light,decorations,biomes,ores caves,dungeons,light,decorations,biomes,ores,nocaves,nodungeons,nolight,nodecorations,nobiomes,noores
|
||||
|
||||
[*Biome API noise parameters]
|
||||
[*Biome API]
|
||||
|
||||
# Temperature variation for biomes.
|
||||
mg_biome_np_heat (Heat noise) noise_params_2d 50, 50, (1000, 1000, 1000), 5349, 3, 0.5, 2.0, eased
|
||||
@@ -1029,7 +1142,7 @@ mgv7_floatland_density (Floatland density) float -0.6
|
||||
# to above 'mgv7_floatland_ymax' - 'mgv7_floatland_taper' (the start of the
|
||||
# upper tapering).
|
||||
# ***WARNING, POTENTIAL DANGER TO WORLDS AND SERVER PERFORMANCE***:
|
||||
# When enabling water placement the floatlands must be configured and tested
|
||||
# When enabling water placement, floatlands must be configured and tested
|
||||
# to be a solid layer by setting 'mgv7_floatland_density' to 2.0 (or other
|
||||
# required value depending on 'mgv7_np_floatland'), to avoid
|
||||
# server-intensive extreme water flow and to avoid vast flooding of the
|
||||
@@ -1456,7 +1569,7 @@ mgfractal_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 500),
|
||||
mgvalleys_spflags (Mapgen Valleys specific flags) flags altitude_chill,humid_rivers,vary_river_depth,altitude_dry altitude_chill,humid_rivers,vary_river_depth,altitude_dry,noaltitude_chill,nohumid_rivers,novary_river_depth,noaltitude_dry
|
||||
|
||||
# The vertical distance over which heat drops by 20 if 'altitude_chill' is
|
||||
# enabled. Also the vertical distance over which humidity drops by 10 if
|
||||
# enabled. Also, the vertical distance over which humidity drops by 10 if
|
||||
# 'altitude_dry' is enabled.
|
||||
mgvalleys_altitude_chill (Altitude chill) int 90 0 65535
|
||||
|
||||
@@ -1512,7 +1625,7 @@ mgvalleys_np_cave1 (Cave noise #1) noise_params_3d 0, 12, (61, 61, 61), 52534, 3
|
||||
# Second of two 3D noises that together define tunnels.
|
||||
mgvalleys_np_cave2 (Cave noise #2) noise_params_3d 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0
|
||||
|
||||
# The depth of dirt or other biome filler node.
|
||||
# Variation of biome filler depth.
|
||||
mgvalleys_np_filler_depth (Filler depth) noise_params_2d 0, 1.2, (256, 256, 256), 1605, 3, 0.5, 2.0, eased
|
||||
|
||||
# 3D noise defining giant caverns.
|
||||
@@ -1609,8 +1722,8 @@ profiler.load (Load the game profiler) bool false
|
||||
# when calling `/profiler save [format]` without format.
|
||||
profiler.default_report_format (Default report format) enum txt txt,csv,lua,json,json_pretty
|
||||
|
||||
# The file path relative to your worldpath in which profiles will be saved to.
|
||||
profiler.report_path (Report path) string ""
|
||||
# The file path relative to your world path in which profiles will be saved to.
|
||||
profiler.report_path (Report path) string
|
||||
|
||||
# Instrument the methods of entities on registration.
|
||||
instrument.entity (Entity methods) bool true
|
||||
@@ -1638,7 +1751,7 @@ instrument.builtin (Builtin) bool false
|
||||
# * Instrument the sampler being used to update the statistics.
|
||||
instrument.profiler (Profiler) bool false
|
||||
|
||||
[**Engine profiler]
|
||||
[**Engine Profiler]
|
||||
|
||||
# Print the engine's profiling data in regular intervals (in seconds).
|
||||
# 0 = disable. Useful for developers.
|
||||
@@ -1658,13 +1771,15 @@ ignore_world_load_errors (Ignore world errors) bool false
|
||||
[**Graphics]
|
||||
|
||||
# Path to shader directory. If no path is defined, default location will be used.
|
||||
#
|
||||
# Requires: shaders
|
||||
shader_path (Shader path) path
|
||||
|
||||
# The rendering back-end.
|
||||
# Note: A restart is required after changing this!
|
||||
# OpenGL is the default for desktop, and OGLES2 for Android.
|
||||
# Shaders are supported by OpenGL and OGLES2 (experimental).
|
||||
video_driver (Video driver) enum opengl opengl,ogles1,ogles2
|
||||
video_driver (Video driver) enum ,opengl,ogles1,ogles2
|
||||
|
||||
# Distance in nodes at which transparency depth sorting is enabled
|
||||
# Use this to limit the performance impact of transparency depth sorting
|
||||
@@ -1679,7 +1794,7 @@ enable_vbo (VBO) bool true
|
||||
cloud_radius (Cloud radius) int 12 1 62
|
||||
|
||||
# Whether node texture animations should be desynchronized per mapblock.
|
||||
desynchronize_mapblock_texture_animation (Desynchronize block animation) bool true
|
||||
desynchronize_mapblock_texture_animation (Desynchronize block animation) bool false
|
||||
|
||||
# Enables caching of facedir rotated meshes.
|
||||
enable_mesh_cache (Mesh cache) bool false
|
||||
@@ -1692,7 +1807,6 @@ mesh_generation_interval (Mapblock mesh generation delay) int 0 0 50
|
||||
# Value of 0 (default) will let Minetest autodetect the number of available threads.
|
||||
mesh_generation_threads (Mapblock mesh generation threads) int 0 0 8
|
||||
|
||||
|
||||
# Size of the MapBlock cache of the mesh generator. Increasing this will
|
||||
# increase the cache hit %, reducing the data being copied from the main
|
||||
# thread, thus reducing jitter.
|
||||
@@ -1719,6 +1833,16 @@ world_aligned_mode (World-aligned textures mode) enum enable disable,enable,forc
|
||||
# Warning: This option is EXPERIMENTAL!
|
||||
autoscale_mode (Autoscaling mode) enum disable disable,enable,force
|
||||
|
||||
# When using bilinear/trilinear/anisotropic filters, low-resolution textures
|
||||
# can be blurred, so automatically upscale them with nearest-neighbor
|
||||
# interpolation to preserve crisp pixels. This sets the minimum texture size
|
||||
# for the upscaled textures; higher values look sharper, but require more
|
||||
# memory. Powers of 2 are recommended. This setting is ONLY applied if
|
||||
# bilinear/trilinear/anisotropic filtering is enabled.
|
||||
# This is also used as the base node texture size for world-aligned
|
||||
# texture autoscaling.
|
||||
texture_min_size (Base texture size) int 64 1 32768
|
||||
|
||||
# Side length of a cube of map blocks that the client will consider together
|
||||
# when generating meshes.
|
||||
# Larger values increase the utilization of the GPU by reducing the number of
|
||||
@@ -1808,9 +1932,9 @@ lighting_boost_spread (Light curve boost spread) float 0.2 0.0 0.4
|
||||
# Metrics can be fetched on http://127.0.0.1:30000/metrics
|
||||
prometheus_listener_address (Prometheus listener address) string 127.0.0.1:30000
|
||||
|
||||
# Maximum size of the out chat queue.
|
||||
# Maximum size of the outgoing chat queue.
|
||||
# 0 to disable queueing and -1 to make the queue size unlimited.
|
||||
max_out_chat_queue_size (Maximum size of the out chat queue) int 20 -1 32767
|
||||
max_out_chat_queue_size (Maximum size of the outgoing chat queue) int 20 -1 32767
|
||||
|
||||
# Timeout for client to remove unused map data from memory, in seconds.
|
||||
client_unload_unused_data_timeout (Mapblock unload timeout) float 600.0 0.0
|
||||
@@ -1943,16 +2067,16 @@ liquid_update (Liquid update tick) float 1.0 0.001
|
||||
# Stated in mapblocks (16 nodes).
|
||||
block_send_optimize_distance (Block send optimize distance) int 4 2 32767
|
||||
|
||||
# If enabled the server will perform map block occlusion culling based on
|
||||
# If enabled, the server will perform map block occlusion culling based on
|
||||
# on the eye position of the player. This can reduce the number of blocks
|
||||
# sent to the client 50-80%. The client will not longer receive most invisible
|
||||
# so that the utility of noclip mode is reduced.
|
||||
server_side_occlusion_culling (Server side occlusion culling) bool true
|
||||
# sent to the client by 50-80%. Clients will no longer receive most
|
||||
# invisible blocks, so that the utility of noclip mode is reduced.
|
||||
server_side_occlusion_culling (Server-side occlusion culling) bool true
|
||||
|
||||
[**Mapgen]
|
||||
|
||||
# Size of mapchunks generated by mapgen, stated in mapblocks (16 nodes).
|
||||
# WARNING!: There is no benefit, and there are several dangers, in
|
||||
# WARNING: There is no benefit, and there are several dangers, in
|
||||
# increasing this value above 5.
|
||||
# Reducing this value increases cave and dungeon density.
|
||||
# Altering this value is for special usage, leaving it unchanged is
|
||||
@@ -1988,7 +2112,7 @@ num_emerge_threads (Number of emerge threads) int 1 0 32767
|
||||
[**cURL]
|
||||
|
||||
# Maximum time an interactive request (e.g. server list fetch) may take, stated in milliseconds.
|
||||
curl_timeout (cURL interactive timeout) int 20000 100 2147483647
|
||||
curl_timeout (cURL interactive timeout) int 20000 1000 2147483647
|
||||
|
||||
# Limits number of parallel HTTP requests. Affects:
|
||||
# - Media fetch if server uses remote_media setting.
|
||||
@@ -1998,9 +2122,9 @@ curl_timeout (cURL interactive timeout) int 20000 100 2147483647
|
||||
curl_parallel_limit (cURL parallel limit) int 8 1 2147483647
|
||||
|
||||
# Maximum time a file download (e.g. a mod download) may take, stated in milliseconds.
|
||||
curl_file_download_timeout (cURL file download timeout) int 300000 100 2147483647
|
||||
curl_file_download_timeout (cURL file download timeout) int 300000 5000 2147483647
|
||||
|
||||
[**Misc]
|
||||
[**Miscellaneous]
|
||||
|
||||
# Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens.
|
||||
screen_dpi (DPI) int 72 1
|
||||
@@ -2063,7 +2187,7 @@ joystick_deadzone (Joystick dead zone) int 2048 0 65535
|
||||
joystick_frustum_sensitivity (Joystick frustum sensitivity) float 170.0 0.001
|
||||
|
||||
|
||||
[*Temporary Settings]
|
||||
[*Hide: Temporary Settings]
|
||||
|
||||
# Path to texture directory. All textures are first searched from here.
|
||||
texture_path (Texture path) path
|
||||
@@ -2083,10 +2207,6 @@ address (Server address) string
|
||||
# Note that the port field in the main menu overrides this setting.
|
||||
remote_port (Remote port) int 30000 1 65535
|
||||
|
||||
# Default game when creating a new world.
|
||||
# This will be overridden when creating a world from the main menu.
|
||||
default_game (Default game) string minetest
|
||||
|
||||
# Enable players getting damage and dying.
|
||||
enable_damage (Damage) bool false
|
||||
|
||||
@@ -2115,24 +2235,38 @@ noclip (Noclip) bool false
|
||||
# Press the autoforward key again or the backwards movement to disable.
|
||||
continuous_forward (Continuous forward) bool false
|
||||
|
||||
# Formspec default background opacity (between 0 and 255).
|
||||
formspec_default_bg_opacity (Formspec Default Background Opacity) int 140 0 255
|
||||
# This can be bound to a key to toggle camera smoothing when looking around.
|
||||
# Useful for recording videos
|
||||
cinematic (Cinematic mode) bool false
|
||||
|
||||
# Formspec default background color (R,G,B).
|
||||
formspec_default_bg_color (Formspec Default Background Color) string (0,0,0)
|
||||
|
||||
# Whether to show technical names.
|
||||
# Affects mods and texture packs in the Content and Select Mods menus, as well as
|
||||
# setting names in All Settings.
|
||||
# Controlled by the checkbox in the "All settings" menu.
|
||||
# setting names.
|
||||
# Controlled by a checkbox in the settings menu.
|
||||
show_technical_names (Show technical names) bool false
|
||||
|
||||
# Controlled by a checkbox in the settings menu.
|
||||
show_advanced (Show advanced settings) bool false
|
||||
|
||||
# Enables the sound system.
|
||||
# If disabled, this completely disables all sounds everywhere and the in-game
|
||||
# sound controls will be non-functional.
|
||||
# Changing this setting requires a restart.
|
||||
enable_sound (Sound) bool true
|
||||
|
||||
# Unix timestamp (integer) of when the client last checked for an update
|
||||
# Set this value to "disabled" to never check for updates.
|
||||
update_last_checked (Last update check) string
|
||||
|
||||
# Version number which was last seen during an update check.
|
||||
#
|
||||
# Representation: MMMIIIPPP, where M=Major, I=Minor, P=Patch
|
||||
# Ex: 5.5.0 is 005005000
|
||||
update_last_known (Last known version update) int 0
|
||||
|
||||
# If this is set to true, the user will never (again) be shown the
|
||||
# "reinstall Minetest Game" notification.
|
||||
no_mtg_notification (Don't show "reinstall Minetest Game" notification) bool false
|
||||
|
||||
# Key for moving the player forward.
|
||||
keymap_forward (Forward key) key KEY_KEY_W
|
||||
|
||||
@@ -2153,11 +2287,13 @@ keymap_jump (Jump key) key KEY_SPACE
|
||||
# Also used for climbing down and descending in water if aux1_descends is disabled.
|
||||
keymap_sneak (Sneak key) key KEY_LSHIFT
|
||||
|
||||
# Key for digging.
|
||||
keymap_dig (Dig key) key KEY_LBUTTON
|
||||
# Key for digging, punching or using something.
|
||||
# (Note: The actual meaning might vary on a per-game basis.)
|
||||
keymap_dig (Dig/punch/use key) key KEY_LBUTTON
|
||||
|
||||
# Key for placing.
|
||||
keymap_place (Place key) key KEY_RBUTTON
|
||||
# Key for placing an item/block or for using something.
|
||||
# (Note: The actual meaning might vary on a per-game basis.)
|
||||
keymap_place (Place/use key) key KEY_RBUTTON
|
||||
|
||||
# Key for opening the inventory.
|
||||
keymap_inventory (Inventory key) key KEY_KEY_I
|
||||
|
||||
116
client/shaders/fxaa/opengl_fragment.glsl
Normal file
116
client/shaders/fxaa/opengl_fragment.glsl
Normal file
@@ -0,0 +1,116 @@
|
||||
#define rendered texture0
|
||||
|
||||
uniform sampler2D rendered;
|
||||
uniform vec2 texelSize0;
|
||||
|
||||
varying vec2 sampleNW;
|
||||
varying vec2 sampleNE;
|
||||
varying vec2 sampleSW;
|
||||
varying vec2 sampleSE;
|
||||
|
||||
#ifdef GL_ES
|
||||
varying mediump vec2 varTexCoord;
|
||||
#else
|
||||
centroid varying vec2 varTexCoord;
|
||||
#endif
|
||||
|
||||
/**
|
||||
Basic FXAA implementation based on the code on geeks3d.com with the
|
||||
modification that the texture2DLod stuff was removed since it's
|
||||
unsupported by WebGL.
|
||||
--
|
||||
From:
|
||||
https://github.com/mitsuhiko/webgl-meincraft
|
||||
Copyright (c) 2011 by Armin Ronacher.
|
||||
Some rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* The names of the contributors may not be used to endorse or
|
||||
promote products derived from this software without specific
|
||||
prior written permission.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef FXAA_REDUCE_MIN
|
||||
#define FXAA_REDUCE_MIN (1.0/ 128.0)
|
||||
#endif
|
||||
#ifndef FXAA_REDUCE_MUL
|
||||
#define FXAA_REDUCE_MUL (1.0 / 8.0)
|
||||
#endif
|
||||
#ifndef FXAA_SPAN_MAX
|
||||
#define FXAA_SPAN_MAX 8.0
|
||||
#endif
|
||||
|
||||
//optimized version for mobile, where dependent
|
||||
//texture reads can be a bottleneck
|
||||
vec4 fxaa(sampler2D tex, vec2 fragCoord, vec2 inverseVP,
|
||||
vec2 v_rgbNW, vec2 v_rgbNE,
|
||||
vec2 v_rgbSW, vec2 v_rgbSE,
|
||||
vec2 v_rgbM) {
|
||||
vec4 color;
|
||||
vec3 rgbNW = texture2D(tex, v_rgbNW).xyz;
|
||||
vec3 rgbNE = texture2D(tex, v_rgbNE).xyz;
|
||||
vec3 rgbSW = texture2D(tex, v_rgbSW).xyz;
|
||||
vec3 rgbSE = texture2D(tex, v_rgbSE).xyz;
|
||||
vec4 texColor = texture2D(tex, v_rgbM);
|
||||
vec3 rgbM = texColor.xyz;
|
||||
vec3 luma = vec3(0.299, 0.587, 0.114);
|
||||
float lumaNW = dot(rgbNW, luma);
|
||||
float lumaNE = dot(rgbNE, luma);
|
||||
float lumaSW = dot(rgbSW, luma);
|
||||
float lumaSE = dot(rgbSE, luma);
|
||||
float lumaM = dot(rgbM, luma);
|
||||
float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
|
||||
float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
|
||||
|
||||
mediump vec2 dir;
|
||||
dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
|
||||
dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));
|
||||
|
||||
float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) *
|
||||
(0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);
|
||||
|
||||
float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);
|
||||
dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX),
|
||||
max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),
|
||||
dir * rcpDirMin)) * inverseVP;
|
||||
|
||||
vec3 rgbA = 0.5 * (
|
||||
texture2D(tex, fragCoord + dir * (1.0 / 3.0 - 0.5)).xyz +
|
||||
texture2D(tex, fragCoord + dir * (2.0 / 3.0 - 0.5)).xyz);
|
||||
vec3 rgbB = rgbA * 0.5 + 0.25 * (
|
||||
texture2D(tex, fragCoord + dir * -0.5).xyz +
|
||||
texture2D(tex, fragCoord + dir * 0.5).xyz);
|
||||
|
||||
float lumaB = dot(rgbB, luma);
|
||||
if ((lumaB < lumaMin) || (lumaB > lumaMax))
|
||||
color = vec4(rgbA, 1.0);
|
||||
else
|
||||
color = vec4(rgbB, 1.0);
|
||||
return color;
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
vec2 uv = varTexCoord.st;
|
||||
|
||||
gl_FragColor = fxaa(rendered, uv, texelSize0,
|
||||
sampleNW, sampleNE, sampleSW, sampleSE, uv);
|
||||
}
|
||||
27
client/shaders/fxaa/opengl_vertex.glsl
Normal file
27
client/shaders/fxaa/opengl_vertex.glsl
Normal file
@@ -0,0 +1,27 @@
|
||||
uniform vec2 texelSize0;
|
||||
|
||||
#ifdef GL_ES
|
||||
varying mediump vec2 varTexCoord;
|
||||
#else
|
||||
centroid varying vec2 varTexCoord;
|
||||
#endif
|
||||
|
||||
varying vec2 sampleNW;
|
||||
varying vec2 sampleNE;
|
||||
varying vec2 sampleSW;
|
||||
varying vec2 sampleSE;
|
||||
|
||||
/*
|
||||
Based on
|
||||
https://github.com/mattdesl/glsl-fxaa/
|
||||
Portions Copyright (c) 2011 by Armin Ronacher.
|
||||
*/
|
||||
void main(void)
|
||||
{
|
||||
varTexCoord.st = inTexCoord0.st;
|
||||
sampleNW = varTexCoord.st + vec2(-1.0, -1.0) * texelSize0;
|
||||
sampleNE = varTexCoord.st + vec2(1.0, -1.0) * texelSize0;
|
||||
sampleSW = varTexCoord.st + vec2(-1.0, 1.0) * texelSize0;
|
||||
sampleSE = varTexCoord.st + vec2(1.0, 1.0) * texelSize0;
|
||||
gl_Position = inVertexPosition;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ uniform sampler2D baseTexture;
|
||||
uniform vec3 dayLight;
|
||||
uniform vec4 skyBgColor;
|
||||
uniform float fogDistance;
|
||||
uniform float fogShadingParameter;
|
||||
uniform vec3 eyePosition;
|
||||
|
||||
// The cameraOffset is the current center of the visible world.
|
||||
@@ -20,7 +21,7 @@ uniform float animationTimer;
|
||||
uniform vec4 CameraPos;
|
||||
uniform float xyPerspectiveBias0;
|
||||
uniform float xyPerspectiveBias1;
|
||||
|
||||
|
||||
varying float adj_shadow_strength;
|
||||
varying float cosLight;
|
||||
varying float f_normal_length;
|
||||
@@ -49,9 +50,6 @@ varying vec3 tsEyeVec;
|
||||
varying vec3 lightVec;
|
||||
varying vec3 tsLightVec;
|
||||
|
||||
const float fogStart = FOG_START;
|
||||
const float fogShadingParameter = 1.0 / ( 1.0 - fogStart);
|
||||
|
||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||
|
||||
// assuming near is always 1.0
|
||||
|
||||
@@ -3,6 +3,7 @@ uniform sampler2D baseTexture;
|
||||
uniform vec3 dayLight;
|
||||
uniform vec4 skyBgColor;
|
||||
uniform float fogDistance;
|
||||
uniform float fogShadingParameter;
|
||||
uniform vec3 eyePosition;
|
||||
|
||||
// The cameraOffset is the current center of the visible world.
|
||||
@@ -20,7 +21,7 @@ uniform float animationTimer;
|
||||
uniform vec4 CameraPos;
|
||||
uniform float xyPerspectiveBias0;
|
||||
uniform float xyPerspectiveBias1;
|
||||
|
||||
|
||||
varying float adj_shadow_strength;
|
||||
varying float cosLight;
|
||||
varying float f_normal_length;
|
||||
@@ -48,9 +49,6 @@ varying float nightRatio;
|
||||
|
||||
varying float vIDiff;
|
||||
|
||||
const float fogStart = FOG_START;
|
||||
const float fogShadingParameter = 1.0 / (1.0 - fogStart);
|
||||
|
||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||
|
||||
// assuming near is always 1.0
|
||||
|
||||
@@ -148,7 +148,7 @@ void main(void)
|
||||
nNormal = normalize(vNormal);
|
||||
cosLight = max(1e-5, dot(nNormal, -v_LightDirection));
|
||||
float sinLight = pow(1 - pow(cosLight, 2.0), 0.5);
|
||||
normalOffsetScale = 0.1 * pFactor * pFactor * sinLight * min(f_shadowfar, 500.0) /
|
||||
normalOffsetScale = 0.1 * pFactor * pFactor * sinLight * min(f_shadowfar, 500.0) /
|
||||
xyPerspectiveBias1 / f_textureresolution;
|
||||
z_bias = 1e3 * sinLight / cosLight * (0.5 + f_textureresolution / 1024.0);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ struct ExposureParams {
|
||||
uniform sampler2D rendered;
|
||||
uniform sampler2D bloom;
|
||||
|
||||
uniform vec2 texelSize0;
|
||||
|
||||
uniform ExposureParams exposureParams;
|
||||
uniform lowp float bloomIntensity;
|
||||
uniform lowp float saturation;
|
||||
@@ -80,7 +82,15 @@ vec3 applySaturation(vec3 color, float factor)
|
||||
void main(void)
|
||||
{
|
||||
vec2 uv = varTexCoord.st;
|
||||
#ifdef ENABLE_SSAA
|
||||
vec4 color = vec4(0.);
|
||||
for (float dx = 1.; dx < SSAA_SCALE; dx += 2.)
|
||||
for (float dy = 1.; dy < SSAA_SCALE; dy += 2.)
|
||||
color += texture2D(rendered, uv + texelSize0 * vec2(dx, dy)).rgba;
|
||||
color /= SSAA_SCALE * SSAA_SCALE / 4.;
|
||||
#else
|
||||
vec4 color = texture2D(rendered, uv).rgba;
|
||||
#endif
|
||||
|
||||
// translate to linear colorspace (approximate)
|
||||
color.rgb = pow(color.rgb, vec3(2.2));
|
||||
|
||||
31
cmake/Modules/MinetestAndroidLibs.cmake
Normal file
31
cmake/Modules/MinetestAndroidLibs.cmake
Normal file
@@ -0,0 +1,31 @@
|
||||
set(DEPS "${CMAKE_SOURCE_DIR}/android/native/deps/${ANDROID_ABI}")
|
||||
|
||||
add_library(IrrlichtMt::IrrlichtMt STATIC IMPORTED)
|
||||
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${DEPS}/Irrlicht/include)
|
||||
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
|
||||
IMPORTED_LOCATION ${DEPS}/Irrlicht/libIrrlichtMt.a)
|
||||
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES "EGL;GLESv1_CM;GLESv2;${DEPS}/Irrlicht/libpng.a;${DEPS}/Irrlicht/libjpeg.a")
|
||||
|
||||
set(CURL_INCLUDE_DIR ${DEPS}/Curl/include)
|
||||
set(CURL_LIBRARY ${DEPS}/Curl/libcurl.a;${DEPS}/Curl/libmbedcrypto.a;${DEPS}/Curl/libmbedtls.a;${DEPS}/Curl/libmbedx509.a)
|
||||
set(FREETYPE_INCLUDE_DIR_ft2build ${DEPS}/Freetype/include/freetype2)
|
||||
set(FREETYPE_INCLUDE_DIR_freetype2 ${FREETYPE_INCLUDE_DIR_ft2build}/freetype)
|
||||
set(FREETYPE_LIBRARY ${DEPS}/Freetype/libfreetype.a)
|
||||
set(GETTEXT_INCLUDE_DIR ${DEPS}/Gettext/include;${DEPS}/Iconv/include)
|
||||
set(GETTEXT_LIBRARY ${DEPS}/Gettext/libintl.a)
|
||||
set(ICONV_LIBRARY ${DEPS}/Iconv/libiconv.a;${DEPS}/Iconv/libcharset.a)
|
||||
set(LUA_INCLUDE_DIR ${DEPS}/LuaJIT/include)
|
||||
set(LUA_LIBRARY ${DEPS}/LuaJIT/libluajit.a)
|
||||
set(OGG_INCLUDE_DIR ${DEPS}/Vorbis/include)
|
||||
set(OGG_LIBRARY ${DEPS}/Vorbis/libogg.a)
|
||||
set(OPENAL_INCLUDE_DIR ${DEPS}/OpenAL-Soft/include)
|
||||
set(OPENAL_LIBRARY ${DEPS}/OpenAL-Soft/libopenal.a;OpenSLES)
|
||||
set(SQLITE3_INCLUDE_DIR ${DEPS}/SQLite/include)
|
||||
set(SQLITE3_LIBRARY ${DEPS}/SQLite/libsqlite3.a)
|
||||
set(VORBIS_INCLUDE_DIR ${DEPS}/Vorbis/include)
|
||||
set(VORBISFILE_LIBRARY ${DEPS}/Vorbis/libvorbisfile.a)
|
||||
set(VORBIS_LIBRARY ${DEPS}/Vorbis/libvorbis.a)
|
||||
set(ZSTD_INCLUDE_DIR ${DEPS}/Zstd/include)
|
||||
set(ZSTD_LIBRARY ${DEPS}/Zstd/libzstd.a)
|
||||
@@ -1,81 +0,0 @@
|
||||
Minetest: Android version
|
||||
=========================
|
||||
|
||||
Controls
|
||||
--------
|
||||
The Android port doesn't support everything you can do on PC due to the
|
||||
limited capabilities of common devices. What can be done is described
|
||||
below:
|
||||
|
||||
While you're playing the game normally (that is, no menu or inventory is
|
||||
shown), the following controls are available:
|
||||
* Look around: touch screen and slide finger
|
||||
* double tap: place a node or use selected item
|
||||
* long tap: dig node
|
||||
* touch shown buttons: press button
|
||||
* Buttons:
|
||||
** left upper corner: chat
|
||||
** right lower corner: jump
|
||||
** right lower corner: crouch
|
||||
** left lower corner: walk/step...
|
||||
left up right
|
||||
down
|
||||
** left lower corner: display inventory
|
||||
|
||||
When a menu or inventory is displayed:
|
||||
* double tap outside menu area: close menu
|
||||
* tap on an item stack: select that stack
|
||||
* tap on an empty slot: if you selected a stack already, that stack is placed here
|
||||
* drag and drop: touch stack and hold finger down, move the stack to another
|
||||
slot, tap another finger while keeping first finger on screen
|
||||
--> places a single item from dragged stack into current (first touched) slot
|
||||
|
||||
Special settings
|
||||
----------------
|
||||
There are some settings especially useful for Android users. Minetest's config
|
||||
file can usually be found at /mnt/sdcard/Minetest.
|
||||
|
||||
* gui_scaling: this is a user-specified scaling factor for the GUI- In case
|
||||
main menu is too big or small on your device, try changing this
|
||||
value.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
In order to build, your PC has to be set up to build Minetest in the usual
|
||||
manner (see the regular Minetest documentation for how to get this done).
|
||||
In addition to what is required for Minetest in general, you will need the
|
||||
following software packages. The version number in parenthesis denotes the
|
||||
version that was tested at the time this README was drafted; newer/older
|
||||
versions may or may not work.
|
||||
|
||||
* Android SDK 29
|
||||
* Android NDK r21
|
||||
* Android Studio 3 [optional]
|
||||
|
||||
Additionally, you'll need to have an Internet connection available on the
|
||||
build system, as the Android build will download some source packages.
|
||||
|
||||
Build
|
||||
-----
|
||||
|
||||
The new build system Minetest Android is fully functional and is designed to
|
||||
speed up and simplify the work, as well as adding the possibility of
|
||||
cross-platform build.
|
||||
You can use `./gradlew assemblerelease` or `./gradlew assembledebug` from the
|
||||
command line or use Android Studio and click the build button.
|
||||
|
||||
When using gradlew, the newest NDK will be downloaded and installed
|
||||
automatically. Or you can create a `local.properties` file and specify
|
||||
`sdk.dir` and `ndk.dir` yourself.
|
||||
|
||||
* In order to make a release build you'll have to have a keystore setup to sign
|
||||
the resulting apk package. How this is done is not part of this README. There
|
||||
are different tutorials on the web explaining how to do it
|
||||
- choose one yourself.
|
||||
|
||||
* Once your keystore is setup, enter the android subdirectory and create a new
|
||||
file "ant.properties" there. Add following lines to that file:
|
||||
|
||||
> key.store=<path to your keystore>
|
||||
> key.alias=Minetest
|
||||
126
doc/android.md
Normal file
126
doc/android.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Minetest Android build
|
||||
All Minetest builds, including the Android variant, are based on the same code.
|
||||
However, additional Java code is used for proper Android integration.
|
||||
|
||||
## Controls
|
||||
Compared to Minetest binaries for PC, the Android port has limited functionality
|
||||
due to limited capabilities of common devices. What can be done is described below:
|
||||
|
||||
While you're playing the game normally (that is, no menu or inventory is
|
||||
shown), the following controls are available:
|
||||
* Look around: touch screen and slide finger
|
||||
* Tap: Place a node
|
||||
* Long tap: Dig node or use the held item
|
||||
* Press back: Pause menu
|
||||
* Touch buttons: Press button
|
||||
* Buttons:
|
||||
|
||||
1. Left upper corner: Chat
|
||||
2. Right lower corner: Jump
|
||||
3. Right lower corner: Crouch
|
||||
4. Left lower corner (Joystick): Walk/step...
|
||||
5. Left lower corner: Display inventory
|
||||
|
||||
When a menu or inventory is displayed:
|
||||
* Double tap outside menu area: Close menu
|
||||
* Press back: Close menu
|
||||
* Tap on an item stack: Select that stack
|
||||
* Tap on an empty slot: If a stack are selected already, that stack is placed here
|
||||
* Drag and drop: Touch stack and hold finger down, move the stack to another
|
||||
slot, tap another finger while keeping first finger on screen
|
||||
--> places a single item from dragged stack into current (first touched) slot. If a stack is selected, the stack will be split as half and one of the splitted stack will be selected
|
||||
|
||||
### Limitations
|
||||
* Some old Android device only support 2 touch at a time, some game/mod contain button combination that need 3 touch (example: jump + Aux1 + hold)
|
||||
* Complicated control can be difficult or impossible on Android device
|
||||
|
||||
## File Path
|
||||
There are some settings especially useful for Android users. The Minetest-wide
|
||||
configuration file can usually be found at:
|
||||
|
||||
* Before 5.4.2:
|
||||
* `/sdcard/Minetest/` or `/storage/emulated/0/` if stored on the device
|
||||
* `/storage/emulated/(varying folder name)/` if stored on an SD card
|
||||
* After 5.4.2:
|
||||
* `/sdcard/Android/data/net.minetest.minetest/` or `/storage/emulated/0/Android/data/net.minetest.minetest/` if stored on the device
|
||||
* `/storage/emulated/(varying folder name)/Android/data/net.minetest.minetest/` if stored on the SD card
|
||||
* [Learn more about Android directory](https://wiki.minetest.net/Accessing_Android_Data_Directory)
|
||||
|
||||
## Useful settings
|
||||
|
||||
### gui_scaling
|
||||
this is a user-specified scaling factor for the GUI in case main menu is too big or small on your device, try changing this value.
|
||||
|
||||
### mapblock_limit
|
||||
Mobile device generally have less RAM than PC, this setting limit how many mapblock can keep in RAM
|
||||
|
||||
### fps_limit
|
||||
this setting limit max FPS (Frame per second). Default value is 60, which lowest Android device screen refresh rate commonly found, but if you're using an device have lower refresh rate, change this
|
||||
|
||||
## Requirements
|
||||
The minimal and recommended system requirements for Minetest are listed below.
|
||||
|
||||
### CPU
|
||||
Supported architectures:
|
||||
1. ARM v7
|
||||
2. ARM v8
|
||||
3. x86
|
||||
4. x86_64
|
||||
|
||||
CPU architectures similar to ARM or x86 might run Minetest but are not tested.
|
||||
|
||||
### Minimum
|
||||
1. Graphics API: OpenGL ES 1.0
|
||||
* Shaders might not work correctly or work at all on OpenGL ES 1.0.
|
||||
2. Android version: Android 4.1 (API Level 16)
|
||||
3. Free RAM: 500 MB
|
||||
4. Free storage: 100 MB
|
||||
* More storage is highly recommended
|
||||
|
||||
### Recommended
|
||||
1. Graphics API: OpenGL ES 2.0
|
||||
2. Android version: Android 4.4 (API Level 19) or newer
|
||||
3. Empty RAM: 850 MB
|
||||
4. Free storage: 480 MB
|
||||
|
||||
## Rendering
|
||||
Unlike on PC, Android devices use OpenGL ES which less powerful than OpenGL, thus
|
||||
some shader settings cannot be used on OpenGL ES.
|
||||
Changing the graphic driver setting to OpenGL will result in undesirable behavior.
|
||||
|
||||
## Building Requirements
|
||||
In order to build, your PC has to be set up to build Minetest in the usual
|
||||
manner (see the regular Minetest documentation for how to get this done).
|
||||
In addition to what is required for Minetest in general, you will need the
|
||||
following software packages. The version number in parenthesis denotes the
|
||||
version that was tested at the time this README was drafted; newer/older
|
||||
versions may or may not work.
|
||||
|
||||
* Android SDK 29
|
||||
* Android NDK r21
|
||||
* Android Studio 3 [optional]
|
||||
|
||||
Additionally, you'll need to have an Internet connection available on the
|
||||
build system, as the Android build will download some source packages.
|
||||
|
||||
## Build
|
||||
The new build system Minetest Android is fully functional and is designed to
|
||||
speed up and simplify the work, as well as adding the possibility of
|
||||
cross-platform build.
|
||||
You can use `./gradlew assemblerelease` or `./gradlew assembledebug` from the
|
||||
command line or use Android Studio and click the build button.
|
||||
|
||||
When using gradlew, the newest NDK will be downloaded and installed
|
||||
automatically. Or you can create a `local.properties` file and specify
|
||||
`sdk.dir` and `ndk.dir` yourself.
|
||||
|
||||
* In order to make a release build you'll have to have a keystore setup to sign
|
||||
the resulting apk package. How this is done is not part of this README. There
|
||||
are different tutorials on the web explaining how to do it
|
||||
- choose one yourself.
|
||||
|
||||
* Once your keystore is setup, enter the android subdirectory and create a new
|
||||
file "ant.properties" there. Add following lines to that file:
|
||||
|
||||
> key.store=<path to your keystore>
|
||||
> key.alias=Minetest
|
||||
@@ -1,12 +1,23 @@
|
||||
# Minetest Major Breakages List
|
||||
|
||||
This document contains a list of breaking changes to be made in the next major version.
|
||||
This list is largely advisory and items may be reevaluated once the time comes.
|
||||
|
||||
* Remove attachment space multiplier (*10)
|
||||
* Remove player gravity multiplier (*2)
|
||||
* remove attachment space multiplier (*10)
|
||||
* remove space multiplier for models (*10)
|
||||
* remove player gravity multiplier (*2)
|
||||
* `get_sky()` returns a table (without arg)
|
||||
* `game.conf` name/id mess
|
||||
* remove `depends.txt` / `description.txt` (would simplify ContentDB and Minetest code a little)
|
||||
* rotate moon texture by 180°, making it coherent with the sun (see https://github.com/minetest/minetest/pull/11902)
|
||||
* rotate moon texture by 180°, making it coherent with the sun
|
||||
* https://github.com/minetest/minetest/pull/11902
|
||||
* remove undocumented `set_physics_override(num, num, num)`
|
||||
* remove special handling of `${key}` syntax in metadata values
|
||||
* remove old_move
|
||||
* change physics_override `sneak` to disable the speed change and not just the node clipping
|
||||
* https://github.com/minetest/minetest/issues/13699
|
||||
* migrate from player names to UUIDs, this would enable renaming of accounts and unicode player names (if desired)
|
||||
* harmonize use_texture_alpha between entities & nodes, change default to 'opaque' and remove bool compat code
|
||||
* merge `sound` and `sounds` table in itemdef
|
||||
* remove `DIR_DELIM` from Lua
|
||||
* stop reading initial properties from bare entity def
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Minetest Lua Client Modding API Reference 5.7.0
|
||||
Minetest Lua Client Modding API Reference 5.8.1
|
||||
================================================
|
||||
* More information at <http://www.minetest.net/>
|
||||
* Developer Wiki: <http://dev.minetest.net/>
|
||||
@@ -170,26 +170,28 @@ from the available ones of the following files:
|
||||
|
||||
Examples of sound parameter tables:
|
||||
|
||||
-- Play locationless
|
||||
{
|
||||
gain = 1.0, -- default
|
||||
}
|
||||
-- Play locationless, looped
|
||||
{
|
||||
gain = 1.0, -- default
|
||||
loop = true,
|
||||
}
|
||||
-- Play in a location
|
||||
{
|
||||
pos = {x = 1, y = 2, z = 3},
|
||||
gain = 1.0, -- default
|
||||
}
|
||||
-- Play connected to an object, looped
|
||||
{
|
||||
object = <an ObjectRef>,
|
||||
gain = 1.0, -- default
|
||||
loop = true,
|
||||
}
|
||||
```lua
|
||||
-- Play locationless
|
||||
{
|
||||
gain = 1.0, -- default
|
||||
}
|
||||
-- Play locationless, looped
|
||||
{
|
||||
gain = 1.0, -- default
|
||||
loop = true,
|
||||
}
|
||||
-- Play in a location
|
||||
{
|
||||
pos = {x = 1, y = 2, z = 3},
|
||||
gain = 1.0, -- default
|
||||
}
|
||||
-- Play connected to an object, looped
|
||||
{
|
||||
object = <an ObjectRef>,
|
||||
gain = 1.0, -- default
|
||||
loop = true,
|
||||
}
|
||||
```
|
||||
|
||||
Looped sounds must either be connected to an object or played locationless.
|
||||
|
||||
@@ -205,7 +207,9 @@ Representations of simple things
|
||||
|
||||
### Position/vector
|
||||
|
||||
{x=num, y=num, z=num}
|
||||
```lua
|
||||
{x=num, y=num, z=num}
|
||||
```
|
||||
|
||||
For helper functions see "Vector helpers".
|
||||
|
||||
@@ -231,19 +235,27 @@ is present, mapped to a boolean of any value, the specified flag is unset.
|
||||
|
||||
E.g. A flag field of value
|
||||
|
||||
{place_center_x = true, place_center_y=false, place_center_z=true}
|
||||
```lua
|
||||
{place_center_x = true, place_center_y=false, place_center_z=true}
|
||||
```
|
||||
|
||||
is equivalent to
|
||||
|
||||
{place_center_x = true, noplace_center_y=true, place_center_z=true}
|
||||
```lua
|
||||
{place_center_x = true, noplace_center_y=true, place_center_z=true}
|
||||
```
|
||||
|
||||
which is equivalent to
|
||||
|
||||
"place_center_x, noplace_center_y, place_center_z"
|
||||
```lua
|
||||
"place_center_x, noplace_center_y, place_center_z"
|
||||
```
|
||||
|
||||
or even
|
||||
|
||||
"place_center_x, place_center_z"
|
||||
```lua
|
||||
"place_center_x, place_center_z"
|
||||
```
|
||||
|
||||
since, by default, no schematic attributes are set.
|
||||
|
||||
@@ -660,6 +672,9 @@ Minetest namespace reference
|
||||
* If a flag in this table is set to true, the feature is RESTRICTED.
|
||||
* Possible flags: `load_client_mods`, `chat_messages`, `read_itemdefs`,
|
||||
`read_nodedefs`, `lookup_nodes`, `read_playerinfo`
|
||||
* `minetest.urlencode(str)`: Encodes non-unreserved URI characters by a
|
||||
percent sign followed by two hex digits. See
|
||||
[RFC 3986, section 2.3](https://datatracker.ietf.org/doc/html/rfc3986#section-2.3).
|
||||
|
||||
### Logging
|
||||
* `minetest.debug(...)`
|
||||
@@ -742,9 +757,9 @@ Call these functions only at load time!
|
||||
* `minetest.sound_play(spec, parameters)`: returns a handle
|
||||
* `spec` is a `SimpleSoundSpec`
|
||||
* `parameters` is a sound parameter table
|
||||
* `minetest.sound_stop(handle)`
|
||||
* `handle:stop()` or `minetest.sound_stop(handle)`
|
||||
* `handle` is a handle returned by `minetest.sound_play`
|
||||
* `minetest.sound_fade(handle, step, gain)`
|
||||
* `handle:fade(step, gain)` or `minetest.sound_fade(handle, step, gain)`
|
||||
* `handle` is a handle returned by `minetest.sound_play`
|
||||
* `step` determines how fast a sound will fade.
|
||||
Negative step will lower the sound volume, positive step will increase
|
||||
@@ -947,7 +962,7 @@ Setting-related
|
||||
---------------
|
||||
|
||||
* `minetest.settings`: Settings object containing all of the settings from the
|
||||
main config file (`minetest.conf`). Check lua_api.txt for class reference.
|
||||
main config file (`minetest.conf`). Check lua_api.md for class reference.
|
||||
* `minetest.setting_get_pos(name)`: Loads a setting from the main settings and
|
||||
parses it as a position (in the format `(1,2,3)`). Returns a position or nil.
|
||||
|
||||
@@ -996,14 +1011,14 @@ Please do not try to access the reference until the camera is initialized, other
|
||||
* `get_fov()`
|
||||
* Returns a table with X, Y, maximum and actual FOV in degrees:
|
||||
|
||||
```lua
|
||||
{
|
||||
x = number,
|
||||
y = number,
|
||||
max = number,
|
||||
actual = number
|
||||
}
|
||||
```
|
||||
```lua
|
||||
{
|
||||
x = number,
|
||||
y = number,
|
||||
max = number,
|
||||
actual = number
|
||||
}
|
||||
```
|
||||
|
||||
* `get_pos()`
|
||||
* Returns position of camera with view bobbing
|
||||
@@ -1053,16 +1068,16 @@ Methods:
|
||||
* `get_physics_override()`
|
||||
* returns:
|
||||
|
||||
```lua
|
||||
{
|
||||
speed = float,
|
||||
jump = float,
|
||||
gravity = float,
|
||||
sneak = boolean,
|
||||
sneak_glitch = boolean,
|
||||
new_move = boolean,
|
||||
}
|
||||
```
|
||||
```lua
|
||||
{
|
||||
speed = float,
|
||||
jump = float,
|
||||
gravity = float,
|
||||
sneak = boolean,
|
||||
sneak_glitch = boolean,
|
||||
new_move = boolean,
|
||||
}
|
||||
```
|
||||
|
||||
* `get_override_pos()`
|
||||
* returns override position
|
||||
@@ -1075,38 +1090,38 @@ Methods:
|
||||
* `get_movement_acceleration()`
|
||||
* returns acceleration of the player in different environments:
|
||||
|
||||
```lua
|
||||
{
|
||||
fast = float,
|
||||
air = float,
|
||||
default = float,
|
||||
}
|
||||
```
|
||||
```lua
|
||||
{
|
||||
fast = float,
|
||||
air = float,
|
||||
default = float,
|
||||
}
|
||||
```
|
||||
|
||||
* `get_movement_speed()`
|
||||
* returns player's speed in different environments:
|
||||
|
||||
```lua
|
||||
{
|
||||
walk = float,
|
||||
jump = float,
|
||||
crouch = float,
|
||||
fast = float,
|
||||
climb = float,
|
||||
}
|
||||
```
|
||||
```lua
|
||||
{
|
||||
walk = float,
|
||||
jump = float,
|
||||
crouch = float,
|
||||
fast = float,
|
||||
climb = float,
|
||||
}
|
||||
```
|
||||
|
||||
* `get_movement()`
|
||||
* returns player's movement in different environments:
|
||||
|
||||
```lua
|
||||
{
|
||||
liquid_fluidity = float,
|
||||
liquid_sink = float,
|
||||
liquid_fluidity_smooth = float,
|
||||
gravity = float,
|
||||
}
|
||||
```
|
||||
```lua
|
||||
{
|
||||
liquid_fluidity = float,
|
||||
liquid_sink = float,
|
||||
liquid_fluidity_smooth = float,
|
||||
gravity = float,
|
||||
}
|
||||
```
|
||||
|
||||
* `get_last_look_horizontal()`:
|
||||
* returns last look horizontal angle
|
||||
@@ -1115,20 +1130,20 @@ Methods:
|
||||
* `get_control()`:
|
||||
* returns pressed player controls
|
||||
|
||||
```lua
|
||||
{
|
||||
up = boolean,
|
||||
down = boolean,
|
||||
left = boolean,
|
||||
right = boolean,
|
||||
jump = boolean,
|
||||
aux1 = boolean,
|
||||
sneak = boolean,
|
||||
zoom = boolean,
|
||||
dig = boolean,
|
||||
place = boolean,
|
||||
}
|
||||
```
|
||||
```lua
|
||||
{
|
||||
up = boolean,
|
||||
down = boolean,
|
||||
left = boolean,
|
||||
right = boolean,
|
||||
jump = boolean,
|
||||
aux1 = boolean,
|
||||
sneak = boolean,
|
||||
zoom = boolean,
|
||||
dig = boolean,
|
||||
place = boolean,
|
||||
}
|
||||
```
|
||||
|
||||
* `get_armor_groups()`
|
||||
* returns a table with the armor group ratings
|
||||
@@ -1176,10 +1191,12 @@ Can be obtained via `minetest.get_meta(pos)`.
|
||||
A raycast on the map. It works with selection boxes.
|
||||
Can be used as an iterator in a for loop as:
|
||||
|
||||
local ray = Raycast(...)
|
||||
for pointed_thing in ray do
|
||||
...
|
||||
end
|
||||
```lua
|
||||
local ray = Raycast(...)
|
||||
for pointed_thing in ray do
|
||||
...
|
||||
end
|
||||
```
|
||||
|
||||
The map is loaded as the ray advances. If the map is modified after the
|
||||
`Raycast` is created, the changes may or may not have an effect on the object.
|
||||
@@ -1207,97 +1224,100 @@ It can be created via `Raycast(pos1, pos2, objects, liquids)` or
|
||||
#### Node Definition
|
||||
|
||||
```lua
|
||||
{
|
||||
has_on_construct = bool, -- Whether the node has the on_construct callback defined
|
||||
has_on_destruct = bool, -- Whether the node has the on_destruct callback defined
|
||||
has_after_destruct = bool, -- Whether the node has the after_destruct callback defined
|
||||
name = string, -- The name of the node e.g. "air", "default:dirt"
|
||||
groups = table, -- The groups of the node
|
||||
paramtype = string, -- Paramtype of the node
|
||||
paramtype2 = string, -- ParamType2 of the node
|
||||
drawtype = string, -- Drawtype of the node
|
||||
mesh = <string>, -- Mesh name if existent
|
||||
minimap_color = <Color>, -- Color of node on minimap *May not exist*
|
||||
visual_scale = number, -- Visual scale of node
|
||||
alpha = number, -- Alpha of the node. Only used for liquids
|
||||
color = <Color>, -- Color of node *May not exist*
|
||||
palette_name = <string>, -- Filename of palette *May not exist*
|
||||
palette = <{ -- List of colors
|
||||
Color,
|
||||
Color
|
||||
}>,
|
||||
waving = number, -- 0 of not waving, 1 if waving
|
||||
connect_sides = number, -- Used for connected nodes
|
||||
connects_to = { -- List of nodes to connect to
|
||||
"node1",
|
||||
"node2"
|
||||
},
|
||||
post_effect_color = Color, -- Color overlayed on the screen when the player is in the node
|
||||
leveled = number, -- Max level for node
|
||||
sunlight_propogates = bool, -- Whether light passes through the block
|
||||
light_source = number, -- Light emitted by the block
|
||||
is_ground_content = bool, -- Whether caves should cut through the node
|
||||
walkable = bool, -- Whether the player collides with the node
|
||||
pointable = bool, -- Whether the player can select the node
|
||||
diggable = bool, -- Whether the player can dig the node
|
||||
climbable = bool, -- Whether the player can climb up the node
|
||||
buildable_to = bool, -- Whether the player can replace the node by placing a node on it
|
||||
rightclickable = bool, -- Whether the player can place nodes pointing at this node
|
||||
damage_per_second = number, -- HP of damage per second when the player is in the node
|
||||
liquid_type = <string>, -- A string containing "none", "flowing", or "source" *May not exist*
|
||||
liquid_alternative_flowing = <string>, -- Alternative node for liquid *May not exist*
|
||||
liquid_alternative_source = <string>, -- Alternative node for liquid *May not exist*
|
||||
liquid_viscosity = <number>, -- How slow the liquid flows *May not exist*
|
||||
liquid_renewable = <boolean>, -- Whether the liquid makes an infinite source *May not exist*
|
||||
liquid_range = <number>, -- How far the liquid flows *May not exist*
|
||||
drowning = bool, -- Whether the player will drown in the node
|
||||
floodable = bool, -- Whether nodes will be replaced by liquids (flooded)
|
||||
node_box = table, -- Nodebox to draw the node with
|
||||
collision_box = table, -- Nodebox to set the collision area
|
||||
selection_box = table, -- Nodebox to set the area selected by the player
|
||||
sounds = { -- Table of sounds that the block makes
|
||||
sound_footstep = SimpleSoundSpec,
|
||||
sound_dig = SimpleSoundSpec,
|
||||
sound_dug = SimpleSoundSpec
|
||||
},
|
||||
legacy_facedir_simple = bool, -- Whether to use old facedir
|
||||
legacy_wallmounted = bool -- Whether to use old wallmounted
|
||||
move_resistance = <number>, -- How slow players can move through the node *May not exist*
|
||||
}
|
||||
{
|
||||
has_on_construct = bool, -- Whether the node has the on_construct callback defined
|
||||
has_on_destruct = bool, -- Whether the node has the on_destruct callback defined
|
||||
has_after_destruct = bool, -- Whether the node has the after_destruct callback defined
|
||||
name = string, -- The name of the node e.g. "air", "default:dirt"
|
||||
groups = table, -- The groups of the node
|
||||
paramtype = string, -- Paramtype of the node
|
||||
paramtype2 = string, -- ParamType2 of the node
|
||||
drawtype = string, -- Drawtype of the node
|
||||
mesh = <string>, -- Mesh name if existent
|
||||
minimap_color = <Color>, -- Color of node on minimap *May not exist*
|
||||
visual_scale = number, -- Visual scale of node
|
||||
alpha = number, -- Alpha of the node. Only used for liquids
|
||||
color = <Color>, -- Color of node *May not exist*
|
||||
palette_name = <string>, -- Filename of palette *May not exist*
|
||||
palette = <{ -- List of colors
|
||||
Color,
|
||||
Color
|
||||
}>,
|
||||
waving = number, -- 0 of not waving, 1 if waving
|
||||
connect_sides = number, -- Used for connected nodes
|
||||
connects_to = { -- List of nodes to connect to
|
||||
"node1",
|
||||
"node2"
|
||||
},
|
||||
post_effect_color = Color, -- Color overlayed on the screen when the player is in the node
|
||||
leveled = number, -- Max level for node
|
||||
sunlight_propogates = bool, -- Whether light passes through the block
|
||||
light_source = number, -- Light emitted by the block
|
||||
is_ground_content = bool, -- Whether caves should cut through the node
|
||||
walkable = bool, -- Whether the player collides with the node
|
||||
pointable = bool, -- Whether the player can select the node
|
||||
diggable = bool, -- Whether the player can dig the node
|
||||
climbable = bool, -- Whether the player can climb up the node
|
||||
buildable_to = bool, -- Whether the player can replace the node by placing a node on it
|
||||
rightclickable = bool, -- Whether the player can place nodes pointing at this node
|
||||
damage_per_second = number, -- HP of damage per second when the player is in the node
|
||||
liquid_type = <string>, -- A string containing "none", "flowing", or "source" *May not exist*
|
||||
liquid_alternative_flowing = <string>, -- Alternative node for liquid *May not exist*
|
||||
liquid_alternative_source = <string>, -- Alternative node for liquid *May not exist*
|
||||
liquid_viscosity = <number>, -- How slow the liquid flows *May not exist*
|
||||
liquid_renewable = <boolean>, -- Whether the liquid makes an infinite source *May not exist*
|
||||
liquid_range = <number>, -- How far the liquid flows *May not exist*
|
||||
drowning = bool, -- Whether the player will drown in the node
|
||||
floodable = bool, -- Whether nodes will be replaced by liquids (flooded)
|
||||
node_box = table, -- Nodebox to draw the node with
|
||||
collision_box = table, -- Nodebox to set the collision area
|
||||
selection_box = table, -- Nodebox to set the area selected by the player
|
||||
sounds = { -- Table of sounds that the block makes
|
||||
sound_footstep = SimpleSoundSpec,
|
||||
sound_dig = SimpleSoundSpec,
|
||||
sound_dug = SimpleSoundSpec
|
||||
},
|
||||
legacy_facedir_simple = bool, -- Whether to use old facedir
|
||||
legacy_wallmounted = bool -- Whether to use old wallmounted
|
||||
move_resistance = <number>, -- How slow players can move through the node *May not exist*
|
||||
}
|
||||
```
|
||||
|
||||
#### Item Definition
|
||||
|
||||
```lua
|
||||
{
|
||||
name = string, -- Name of the item e.g. "default:stone"
|
||||
description = string, -- Description of the item e.g. "Stone"
|
||||
type = string, -- Item type: "none", "node", "craftitem", "tool"
|
||||
inventory_image = string, -- Image in the inventory
|
||||
wield_image = string, -- Image in wieldmesh
|
||||
palette_image = string, -- Image for palette
|
||||
color = Color, -- Color for item
|
||||
wield_scale = Vector, -- Wieldmesh scale
|
||||
stack_max = number, -- Number of items stackable together
|
||||
usable = bool, -- Has on_use callback defined
|
||||
liquids_pointable = bool, -- Whether you can point at liquids with the item
|
||||
tool_capabilities = <table>, -- If the item is a tool, tool capabilities of the item
|
||||
groups = table, -- Groups of the item
|
||||
sound_place = SimpleSoundSpec, -- Sound played when placed
|
||||
sound_place_failed = SimpleSoundSpec, -- Sound played when placement failed
|
||||
node_placement_prediction = string -- Node placed in client until server catches up
|
||||
}
|
||||
{
|
||||
name = string, -- Name of the item e.g. "default:stone"
|
||||
description = string, -- Description of the item e.g. "Stone"
|
||||
type = string, -- Item type: "none", "node", "craftitem", "tool"
|
||||
inventory_image = string, -- Image in the inventory
|
||||
wield_image = string, -- Image in wieldmesh
|
||||
palette_image = string, -- Image for palette
|
||||
color = Color, -- Color for item
|
||||
wield_scale = Vector, -- Wieldmesh scale
|
||||
stack_max = number, -- Number of items stackable together
|
||||
usable = bool, -- Has on_use callback defined
|
||||
liquids_pointable = bool, -- Whether you can point at liquids with the item
|
||||
tool_capabilities = <table>, -- If the item is a tool, tool capabilities of the item
|
||||
groups = table, -- Groups of the item
|
||||
sound_place = SimpleSoundSpec, -- Sound played when placed
|
||||
sound_place_failed = SimpleSoundSpec, -- Sound played when placement failed
|
||||
node_placement_prediction = string -- Node placed in client until server catches up
|
||||
}
|
||||
```
|
||||
-----------------
|
||||
|
||||
### Chat command definition (`register_chatcommand`)
|
||||
|
||||
{
|
||||
params = "<name> <privilege>", -- Short parameter description
|
||||
description = "Remove privilege from player", -- Full description
|
||||
func = function(param), -- Called when command is run.
|
||||
-- Returns boolean success and text output.
|
||||
}
|
||||
```lua
|
||||
{
|
||||
params = "<name> <privilege>", -- Short parameter description
|
||||
description = "Remove privilege from player", -- Full description
|
||||
func = function(param), -- Called when command is run.
|
||||
-- Returns boolean success and text output.
|
||||
}
|
||||
```
|
||||
|
||||
### Server info
|
||||
```lua
|
||||
{
|
||||
@@ -1309,29 +1329,30 @@ It can be created via `Raycast(pos1, pos2, objects, liquids)` or
|
||||
```
|
||||
|
||||
### HUD Definition (`hud_add`, `hud_get`)
|
||||
|
||||
```lua
|
||||
{
|
||||
hud_elem_type = "image", -- see HUD element types, default "text"
|
||||
-- ^ type of HUD element, can be either of "image", "text", "statbar", or "inventory"
|
||||
position = {x=0.5, y=0.5},
|
||||
-- ^ Left corner position of element, default `{x=0,y=0}`.
|
||||
name = "<name>", -- default ""
|
||||
scale = {x=2, y=2}, -- default {x=0,y=0}
|
||||
text = "<text>", -- default ""
|
||||
number = 2, -- default 0
|
||||
item = 3, -- default 0
|
||||
-- ^ Selected item in inventory. 0 for no item selected.
|
||||
direction = 0, -- default 0
|
||||
-- ^ Direction: 0: left-right, 1: right-left, 2: top-bottom, 3: bottom-top
|
||||
alignment = {x=0, y=0}, -- default {x=0, y=0}
|
||||
-- ^ See "HUD Element Types"
|
||||
offset = {x=0, y=0}, -- default {x=0, y=0}
|
||||
-- ^ See "HUD Element Types"
|
||||
size = { x=100, y=100 }, -- default {x=0, y=0}
|
||||
-- ^ Size of element in pixels
|
||||
style = 0,
|
||||
-- ^ For "text" elements sets font style: bitfield with 1 = bold, 2 = italic, 4 = monospace
|
||||
}
|
||||
{
|
||||
hud_elem_type = "image", -- see HUD element types, default "text"
|
||||
-- ^ type of HUD element, can be either of "image", "text", "statbar", or "inventory"
|
||||
position = {x=0.5, y=0.5},
|
||||
-- ^ Left corner position of element, default `{x=0,y=0}`.
|
||||
name = "<name>", -- default ""
|
||||
scale = {x=2, y=2}, -- default {x=0,y=0}
|
||||
text = "<text>", -- default ""
|
||||
number = 2, -- default 0
|
||||
item = 3, -- default 0
|
||||
-- ^ Selected item in inventory. 0 for no item selected.
|
||||
direction = 0, -- default 0
|
||||
-- ^ Direction: 0: left-right, 1: right-left, 2: top-bottom, 3: bottom-top
|
||||
alignment = {x=0, y=0}, -- default {x=0, y=0}
|
||||
-- ^ See "HUD Element Types"
|
||||
offset = {x=0, y=0}, -- default {x=0, y=0}
|
||||
-- ^ See "HUD Element Types"
|
||||
size = { x=100, y=100 }, -- default {x=0, y=0}
|
||||
-- ^ Size of element in pixels
|
||||
style = 0,
|
||||
-- ^ For "text" elements sets font style: bitfield with 1 = bold, 2 = italic, 4 = monospace
|
||||
}
|
||||
```
|
||||
|
||||
Escape sequences
|
||||
@@ -1478,55 +1499,59 @@ Same as `image`, but does not accept a `position`; the position is instead deter
|
||||
|
||||
### Particle definition (`add_particle`)
|
||||
|
||||
{
|
||||
pos = {x=0, y=0, z=0},
|
||||
velocity = {x=0, y=0, z=0},
|
||||
acceleration = {x=0, y=0, z=0},
|
||||
```lua
|
||||
{
|
||||
pos = {x=0, y=0, z=0},
|
||||
velocity = {x=0, y=0, z=0},
|
||||
acceleration = {x=0, y=0, z=0},
|
||||
-- ^ Spawn particle at pos with velocity and acceleration
|
||||
expirationtime = 1,
|
||||
expirationtime = 1,
|
||||
-- ^ Disappears after expirationtime seconds
|
||||
size = 1,
|
||||
collisiondetection = false,
|
||||
size = 1,
|
||||
collisiondetection = false,
|
||||
-- ^ collisiondetection: if true collides with physical objects
|
||||
collision_removal = false,
|
||||
collision_removal = false,
|
||||
-- ^ collision_removal: if true then particle is removed when it collides,
|
||||
-- ^ requires collisiondetection = true to have any effect
|
||||
vertical = false,
|
||||
vertical = false,
|
||||
-- ^ vertical: if true faces player using y axis only
|
||||
texture = "image.png",
|
||||
texture = "image.png",
|
||||
-- ^ Uses texture (string)
|
||||
animation = {Tile Animation definition},
|
||||
animation = {Tile Animation definition},
|
||||
-- ^ optional, specifies how to animate the particle texture
|
||||
glow = 0
|
||||
glow = 0
|
||||
-- ^ optional, specify particle self-luminescence in darkness
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `ParticleSpawner` definition (`add_particlespawner`)
|
||||
|
||||
{
|
||||
amount = 1,
|
||||
time = 1,
|
||||
```lua
|
||||
{
|
||||
amount = 1,
|
||||
time = 1,
|
||||
-- ^ If time is 0 has infinite lifespan and spawns the amount on a per-second base
|
||||
minpos = {x=0, y=0, z=0},
|
||||
maxpos = {x=0, y=0, z=0},
|
||||
minvel = {x=0, y=0, z=0},
|
||||
maxvel = {x=0, y=0, z=0},
|
||||
minacc = {x=0, y=0, z=0},
|
||||
maxacc = {x=0, y=0, z=0},
|
||||
minexptime = 1,
|
||||
maxexptime = 1,
|
||||
minsize = 1,
|
||||
maxsize = 1,
|
||||
minpos = {x=0, y=0, z=0},
|
||||
maxpos = {x=0, y=0, z=0},
|
||||
minvel = {x=0, y=0, z=0},
|
||||
maxvel = {x=0, y=0, z=0},
|
||||
minacc = {x=0, y=0, z=0},
|
||||
maxacc = {x=0, y=0, z=0},
|
||||
minexptime = 1,
|
||||
maxexptime = 1,
|
||||
minsize = 1,
|
||||
maxsize = 1,
|
||||
-- ^ The particle's properties are random values in between the bounds:
|
||||
-- ^ minpos/maxpos, minvel/maxvel (velocity), minacc/maxacc (acceleration),
|
||||
-- ^ minsize/maxsize, minexptime/maxexptime (expirationtime)
|
||||
collisiondetection = false,
|
||||
collisiondetection = false,
|
||||
-- ^ collisiondetection: if true uses collision detection
|
||||
collision_removal = false,
|
||||
collision_removal = false,
|
||||
-- ^ collision_removal: if true then particle is removed when it collides,
|
||||
-- ^ requires collisiondetection = true to have any effect
|
||||
vertical = false,
|
||||
vertical = false,
|
||||
-- ^ vertical: if true faces player using y axis only
|
||||
texture = "image.png",
|
||||
texture = "image.png",
|
||||
-- ^ Uses texture (string)
|
||||
}
|
||||
}
|
||||
```
|
||||
88
doc/compiling/README.md
Normal file
88
doc/compiling/README.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# Compiling Minetest
|
||||
|
||||
- [Compiling on GNU/Linux](linux.md)
|
||||
- [Compiling on Windows](windows.md)
|
||||
- [Compiling on MacOS](macos.md)
|
||||
|
||||
|
||||
## CMake options
|
||||
|
||||
General options and their default values:
|
||||
|
||||
BUILD_CLIENT=TRUE - Build Minetest client
|
||||
BUILD_SERVER=FALSE - Build Minetest server
|
||||
BUILD_UNITTESTS=TRUE - Build unittest sources
|
||||
BUILD_BENCHMARKS=FALSE - Build benchmark sources
|
||||
BUILD_DOCUMENTATION=TRUE - Build doxygen documentation
|
||||
CMAKE_BUILD_TYPE=Release - Type of build (Release vs. Debug)
|
||||
Release - Release build
|
||||
Debug - Debug build
|
||||
SemiDebug - Partially optimized debug build
|
||||
RelWithDebInfo - Release build with debug information
|
||||
MinSizeRel - Release build with -Os passed to compiler to make executable as small as possible
|
||||
ENABLE_CURL=ON - Build with cURL; Enables use of online mod repo, public serverlist and remote media fetching via http
|
||||
ENABLE_CURSES=ON - Build with (n)curses; Enables a server side terminal (command line option: --terminal)
|
||||
ENABLE_GETTEXT=ON - Build with Gettext; Allows using translations
|
||||
ENABLE_LEVELDB=ON - Build with LevelDB; Enables use of LevelDB map backend
|
||||
ENABLE_POSTGRESQL=ON - Build with libpq; Enables use of PostgreSQL map backend (PostgreSQL 9.5 or greater recommended)
|
||||
ENABLE_REDIS=ON - Build with libhiredis; Enables use of Redis map backend
|
||||
ENABLE_SPATIAL=ON - Build with LibSpatial; Speeds up AreaStores
|
||||
ENABLE_SOUND=ON - Build with OpenAL, libogg & libvorbis; in-game sounds
|
||||
ENABLE_LTO=ON - Build with IPO/LTO optimizations (smaller and more efficient than regular build)
|
||||
ENABLE_LUAJIT=ON - Build with LuaJIT (much faster than non-JIT Lua)
|
||||
ENABLE_PROMETHEUS=OFF - Build with Prometheus metrics exporter (listens on tcp/30000 by default)
|
||||
ENABLE_SYSTEM_GMP=ON - Use GMP from system (much faster than bundled mini-gmp)
|
||||
ENABLE_SYSTEM_JSONCPP=ON - Use JsonCPP from system
|
||||
RUN_IN_PLACE=FALSE - Create a portable install (worlds, settings etc. in current directory)
|
||||
ENABLE_UPDATE_CHECKER=TRUE - Whether to enable update checks by default
|
||||
INSTALL_DEVTEST=FALSE - Whether the Development Test game should be installed alongside Minetest
|
||||
USE_GPROF=FALSE - Enable profiling using GProf
|
||||
VERSION_EXTRA= - Text to append to version (e.g. VERSION_EXTRA=foobar -> Minetest 0.4.9-foobar)
|
||||
ENABLE_TOUCH=FALSE - Enable Touchscreen support (requires support by IrrlichtMt)
|
||||
|
||||
Library specific options:
|
||||
|
||||
CURL_DLL - Only if building with cURL on Windows; path to libcurl.dll
|
||||
CURL_INCLUDE_DIR - Only if building with cURL; directory where curl.h is located
|
||||
CURL_LIBRARY - Only if building with cURL; path to libcurl.a/libcurl.so/libcurl.lib
|
||||
EXTRA_DLL - Only on Windows; optional paths to additional DLLs that should be packaged
|
||||
FREETYPE_INCLUDE_DIR_freetype2 - Directory that contains files such as ftimage.h
|
||||
FREETYPE_INCLUDE_DIR_ft2build - Directory that contains ft2build.h
|
||||
FREETYPE_LIBRARY - Path to libfreetype.a/libfreetype.so/freetype.lib
|
||||
FREETYPE_DLL - Only on Windows; path to libfreetype-6.dll
|
||||
GETTEXT_DLL - Only when building with gettext on Windows; paths to libintl + libiconv DLLs
|
||||
GETTEXT_INCLUDE_DIR - Only when building with gettext; directory that contains libintl.h
|
||||
GETTEXT_LIBRARY - Optional/platform-dependent with gettext; path to libintl.so/libintl.dll.a
|
||||
GETTEXT_MSGFMT - Only when building with gettext; path to msgfmt/msgfmt.exe
|
||||
ICONV_LIBRARY - Optional/platform-dependent; path to libiconv.so/libiconv.dylib
|
||||
IRRLICHT_DLL - Only on Windows; path to IrrlichtMt.dll
|
||||
IRRLICHT_INCLUDE_DIR - Directory that contains IrrCompileConfig.h (usable for server build only)
|
||||
LEVELDB_INCLUDE_DIR - Only when building with LevelDB; directory that contains db.h
|
||||
LEVELDB_LIBRARY - Only when building with LevelDB; path to libleveldb.a/libleveldb.so/libleveldb.dll.a
|
||||
LEVELDB_DLL - Only when building with LevelDB on Windows; path to libleveldb.dll
|
||||
PostgreSQL_INCLUDE_DIR - Only when building with PostgreSQL; directory that contains libpq-fe.h
|
||||
PostgreSQL_LIBRARY - Only when building with PostgreSQL; path to libpq.a/libpq.so/libpq.lib
|
||||
REDIS_INCLUDE_DIR - Only when building with Redis; directory that contains hiredis.h
|
||||
REDIS_LIBRARY - Only when building with Redis; path to libhiredis.a/libhiredis.so
|
||||
SPATIAL_INCLUDE_DIR - Only when building with LibSpatial; directory that contains spatialindex/SpatialIndex.h
|
||||
SPATIAL_LIBRARY - Only when building with LibSpatial; path to libspatialindex.so/spatialindex-32.lib
|
||||
LUA_INCLUDE_DIR - Only if you want to use LuaJIT; directory where luajit.h is located
|
||||
LUA_LIBRARY - Only if you want to use LuaJIT; path to libluajit.a/libluajit.so
|
||||
OGG_DLL - Only if building with sound on Windows; path to libogg.dll
|
||||
OGG_INCLUDE_DIR - Only if building with sound; directory that contains an ogg directory which contains ogg.h
|
||||
OGG_LIBRARY - Only if building with sound; path to libogg.a/libogg.so/libogg.dll.a
|
||||
OPENAL_DLL - Only if building with sound on Windows; path to OpenAL32.dll
|
||||
OPENAL_INCLUDE_DIR - Only if building with sound; directory where al.h is located
|
||||
OPENAL_LIBRARY - Only if building with sound; path to libopenal.a/libopenal.so/OpenAL32.lib
|
||||
SQLITE3_INCLUDE_DIR - Directory that contains sqlite3.h
|
||||
SQLITE3_LIBRARY - Path to libsqlite3.a/libsqlite3.so/sqlite3.lib
|
||||
VORBISFILE_LIBRARY - Only if building with sound; path to libvorbisfile.a/libvorbisfile.so/libvorbisfile.dll.a
|
||||
VORBIS_DLL - Only if building with sound on Windows; paths to vorbis DLLs
|
||||
VORBIS_INCLUDE_DIR - Only if building with sound; directory that contains a directory vorbis with vorbisenc.h inside
|
||||
VORBIS_LIBRARY - Only if building with sound; path to libvorbis.a/libvorbis.so/libvorbis.dll.a
|
||||
ZLIB_DLL - Only on Windows; path to zlib1.dll
|
||||
ZLIB_INCLUDE_DIR - Directory that contains zlib.h
|
||||
ZLIB_LIBRARY - Path to libz.a/libz.so/zlib.lib
|
||||
ZSTD_DLL - Only on Windows; path to libzstd.dll
|
||||
ZSTD_INCLUDE_DIR - Directory that contains zstd.h
|
||||
ZSTD_LIBRARY - Path to libzstd.a/libzstd.so/ztd.lib
|
||||
116
doc/compiling/linux.md
Normal file
116
doc/compiling/linux.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# Compiling on GNU/Linux
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Dependency | Version | Commentary |
|
||||
| ---------- | ------- | ---------- |
|
||||
| GCC | 7.5+ | or Clang 7.0.1+ |
|
||||
| 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 |
|
||||
| Curl | 7.56.0+ | Optional |
|
||||
| gettext | - | Optional |
|
||||
|
||||
For Debian/Ubuntu users:
|
||||
|
||||
sudo apt install g++ make libc6-dev cmake libpng-dev libjpeg-dev libxi-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev libzstd-dev libluajit-5.1-dev gettext
|
||||
|
||||
For Fedora users:
|
||||
|
||||
sudo dnf install make automake gcc gcc-c++ kernel-devel cmake libcurl-devel openal-soft-devel libpng-devel libjpeg-devel libvorbis-devel libXi-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel spatialindex-devel libzstd-devel gettext
|
||||
|
||||
For openSUSE users:
|
||||
|
||||
sudo zypper install gcc cmake libjpeg8-devel libpng16-devel openal-soft-devel libcurl-devel sqlite3-devel luajit-devel libzstd-devel Mesa-libGL-devel libXi-devel libvorbis-devel freetype2-devel
|
||||
|
||||
For Arch users:
|
||||
|
||||
sudo pacman -S base-devel libcurl-gnutls cmake libxi libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses zstd gettext
|
||||
|
||||
For Alpine users:
|
||||
|
||||
sudo apk add build-base cmake libpng-dev jpeg-dev libxi-dev mesa-dev sqlite-dev libogg-dev libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev gmp-dev jsoncpp-dev luajit-dev zstd-dev gettext
|
||||
|
||||
For Void users:
|
||||
|
||||
sudo xbps-install cmake libpng-devel jpeg-devel libXi-devel mesa sqlite-devel libogg-devel libvorbis-devel libopenal-devel libcurl-devel freetype-devel zlib-devel gmp-devel jsoncpp-devel LuaJIT-devel libzstd-devel gettext
|
||||
|
||||
## Download
|
||||
|
||||
You can install Git for easily keeping your copy up to date.
|
||||
If you don’t want Git, read below on how to get the source without Git.
|
||||
This is an example for installing Git on Debian/Ubuntu:
|
||||
|
||||
sudo apt install git
|
||||
|
||||
For Fedora users:
|
||||
|
||||
sudo dnf install git
|
||||
|
||||
For Arch users:
|
||||
|
||||
sudo pacman -S git
|
||||
|
||||
For Alpine users:
|
||||
|
||||
sudo apk add git
|
||||
|
||||
For Void users:
|
||||
|
||||
sudo xbps-install git
|
||||
|
||||
Download source (this is the URL to the latest of source repository, which might not work at all times) using Git:
|
||||
|
||||
git clone --depth 1 https://github.com/minetest/minetest.git
|
||||
cd minetest
|
||||
|
||||
Download IrrlichtMt to `lib/irrlichtmt`, it will be used to satisfy the IrrlichtMt dependency that way:
|
||||
|
||||
git clone --depth 1 https://github.com/minetest/irrlicht.git lib/irrlichtmt
|
||||
|
||||
Download source, without using Git:
|
||||
|
||||
wget https://github.com/minetest/minetest/archive/master.tar.gz
|
||||
tar xf master.tar.gz
|
||||
cd minetest-master
|
||||
|
||||
Download IrrlichtMt, without using Git:
|
||||
|
||||
cd lib/
|
||||
wget https://github.com/minetest/irrlicht/archive/master.tar.gz
|
||||
tar xf master.tar.gz
|
||||
mv irrlicht-master irrlichtmt
|
||||
cd ..
|
||||
|
||||
## Build
|
||||
|
||||
Build a version that runs directly from the source directory:
|
||||
|
||||
cmake . -DRUN_IN_PLACE=TRUE
|
||||
make -j$(nproc)
|
||||
|
||||
Run it:
|
||||
|
||||
./bin/minetest
|
||||
|
||||
- Use `cmake . -LH` to see all CMake options and their current state.
|
||||
- If you want to install it system-wide (or are making a distribution package),
|
||||
you will want to use `-DRUN_IN_PLACE=FALSE`.
|
||||
- You can build a bare server by specifying `-DBUILD_SERVER=TRUE`.
|
||||
- You can disable the client build by specifying `-DBUILD_CLIENT=FALSE`.
|
||||
- You can select between Release and Debug build by `-DCMAKE_BUILD_TYPE=<Debug or Release>`.
|
||||
- Debug build is slower, but gives much more useful output in a debugger.
|
||||
- If you build a bare server you don't need to compile IrrlichtMt, just the headers suffice.
|
||||
- In that case use `-DIRRLICHT_INCLUDE_DIR=/some/where/irrlichtmt/include`.
|
||||
|
||||
- Minetest will use the IrrlichtMt package that is found first, given by the following order:
|
||||
1. Specified `IRRLICHTMT_BUILD_DIR` CMake variable
|
||||
2. `${PROJECT_SOURCE_DIR}/lib/irrlichtmt` (if existent)
|
||||
3. Installation of IrrlichtMt in the system-specific library paths
|
||||
4. For server builds with disabled `BUILD_CLIENT` variable, the headers from `IRRLICHT_INCLUDE_DIR` will be used.
|
||||
- NOTE: Changing the IrrlichtMt build directory (includes system installs) requires regenerating the CMake cache (`rm CMakeCache.txt`)
|
||||
49
doc/compiling/macos.md
Normal file
49
doc/compiling/macos.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# 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 gettext
|
||||
```
|
||||
|
||||
## 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'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
|
||||
```
|
||||
73
doc/compiling/windows.md
Normal file
73
doc/compiling/windows.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# Compiling on Windows using MSVC
|
||||
|
||||
## Requirements
|
||||
|
||||
- [Visual Studio 2015 or newer](https://visualstudio.microsoft.com)
|
||||
- [CMake](https://cmake.org/download/)
|
||||
- [vcpkg](https://github.com/Microsoft/vcpkg)
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
|
||||
|
||||
## Compiling and installing the dependencies
|
||||
|
||||
It is highly recommended to use vcpkg as package manager.
|
||||
|
||||
After you successfully built vcpkg you can easily install the required libraries:
|
||||
```powershell
|
||||
vcpkg install zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry gettext --triplet x64-windows
|
||||
```
|
||||
|
||||
- **Don't forget about IrrlichtMt.** The easiest way is to clone it to `lib/irrlichtmt` as described in the Linux section.
|
||||
- `curl` is optional, but required to read the serverlist, `curl[winssl]` is required to use the content store.
|
||||
- `openal-soft`, `libvorbis` and `libogg` are optional, but required to use sound.
|
||||
- `luajit` is optional, it replaces the integrated Lua interpreter with a faster just-in-time interpreter.
|
||||
- `gmp` and `jsoncpp` are optional, otherwise the bundled versions will be compiled
|
||||
- `gettext` is optional, but required to use translations.
|
||||
|
||||
There are other optional libraries, but they are not tested if they can build and link correctly.
|
||||
|
||||
Use `--triplet` to specify the target triplet, e.g. `x64-windows` or `x86-windows`.
|
||||
|
||||
|
||||
## Compile Minetest
|
||||
|
||||
### a) Using the vcpkg toolchain and CMake GUI
|
||||
|
||||
1. Start up the CMake GUI
|
||||
2. Select **Browse Source...** and select DIR/minetest
|
||||
3. Select **Browse Build...** and select DIR/minetest-build
|
||||
4. Select **Configure**
|
||||
5. Choose the right visual Studio version and target platform. It has to match the version of the installed dependencies
|
||||
6. Choose **Specify toolchain file for cross-compiling**
|
||||
7. Click **Next**
|
||||
8. Select the vcpkg toolchain file e.g. `D:/vcpkg/scripts/buildsystems/vcpkg.cmake`
|
||||
9. Click Finish
|
||||
10. Wait until cmake have generated the cash file
|
||||
11. If there are any errors, solve them and hit **Configure**
|
||||
12. Click **Generate**
|
||||
13. Click **Open Project**
|
||||
14. Compile Minetest inside Visual studio.
|
||||
|
||||
### b) Using the vcpkg toolchain and the commandline
|
||||
|
||||
Run the following script in PowerShell:
|
||||
|
||||
```powershell
|
||||
cmake . -G"Visual Studio 15 2017 Win64" -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_GETTEXT=OFF -DENABLE_CURSES=OFF
|
||||
cmake --build . --config Release
|
||||
```
|
||||
Make sure that the right compiler is selected and the path to the vcpkg toolchain is correct.
|
||||
|
||||
|
||||
## Windows Installer using WiX Toolset
|
||||
|
||||
Requirements:
|
||||
* [Visual Studio 2017](https://visualstudio.microsoft.com/)
|
||||
* [WiX Toolset](https://wixtoolset.org/)
|
||||
|
||||
In the Visual Studio 2017 Installer select **Optional Features -> WiX Toolset**.
|
||||
|
||||
Build the binaries as described above, but make sure you unselect `RUN_IN_PLACE`.
|
||||
|
||||
Open the generated project file with Visual Studio. Right-click **Package** and choose **Generate**.
|
||||
It may take some minutes to generate the installer.
|
||||
25
doc/developing/docker.md
Normal file
25
doc/developing/docker.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Developing minetestserver with Docker
|
||||
|
||||
Docker provides an easy cross-platform solution to create a development environment. The advantage of this method is that it does not require you to install any development tools and libraries on your machine besides git and docker. This is a short guide describing how to set up a development environment for minetestserver; people unfamiliar with docker may also want to refer to the [Docker Documentation](https://docs.docker.com//) which is well written.
|
||||
|
||||
## Creating an image
|
||||
|
||||
The first step is to create a development image of minetestserver:
|
||||
```bash
|
||||
docker buildx build --target dev -t minetest-dev:0 .
|
||||
```
|
||||
The `--target dev` option instructs docker to build the intermediate development image, and the `-t minetest-dev:0` option tags the image so we can refer to it by that name.
|
||||
|
||||
## Opening a new container
|
||||
|
||||
Once an image has been created, a new container can be opened, with the source code mounted into it:
|
||||
```bash
|
||||
docker run -it \
|
||||
--mount type=bind,source=/home/bob/minetest,target=/minetest \
|
||||
minetest-dev:0
|
||||
```
|
||||
The `-it` runs the container interactively and puts you into a terminal in the container. The source and target of the bind mount should point to the directory on the host machine, and the directory in the container, respectively.
|
||||
|
||||
### For VSCode users
|
||||
|
||||
If you install the development container extension from the VSCode marketplace, you can attach VSCode to the running container, and open the source in VSCode to work with it as you would any other project. Note that extensions must be installed in the container from the extensions tab once the container has been attached if you wish to use them. For more information, see the VSCode documentation on [Developing inside a Container using Visual Studio Code Remote Development](https://code.visualstudio.com/docs/devcontainers/containers#:~:text=The%20Visual%20Studio%20Code%20Dev%20Containers%20extension%20lets,advantage%20of%20Visual%20Studio%20Code%27s%20full%20feature%20set.).
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user