mirror of
https://github.com/minetest/minetest.git
synced 2025-03-06 20:48:40 +01:00
Compare commits
2276 commits
Author | SHA1 | Date | |
---|---|---|---|
|
7892541383 | ||
|
358658fa34 | ||
|
68602b2eaf | ||
|
e84ac56e35 | ||
|
47c000a293 | ||
|
304ce4cd54 | ||
|
d54646d342 | ||
|
7abaa8d4cd | ||
|
2796283550 | ||
|
7602308835 | ||
|
bc43019467 | ||
|
8449f5f6db | ||
|
0eb047ca33 | ||
|
98048cb06d | ||
|
6e995972bb | ||
|
08fad862aa | ||
|
c3477a4d08 | ||
|
062207e696 | ||
|
24c1230c7b | ||
|
eb79a76742 | ||
|
c0328e5363 | ||
|
8d822d8231 | ||
|
a11b25f3f5 | ||
|
90121dc66f | ||
|
d74af2f1a7 | ||
|
b6c71b2379 | ||
|
c261c26456 | ||
|
5abf220979 | ||
|
1ceeea34f4 | ||
|
3ae1fd459a | ||
|
0e86366324 | ||
|
58ad604a4b | ||
|
415e96184d | ||
|
8654e16725 | ||
|
eb8b449817 | ||
|
22c81e5292 | ||
|
42a35cec83 | ||
|
fc8c6742c4 | ||
|
ee9258cefd | ||
|
5e89371ecd | ||
|
abcd2e0b81 | ||
|
d12ce68e64 | ||
|
83fd837d75 | ||
|
7d3f0628c4 | ||
|
27bbe3a873 | ||
|
5a8720a484 | ||
|
e51221d247 | ||
|
0890125962 | ||
|
0667cbf5a2 | ||
|
ba62808fe8 | ||
|
50819ace8f | ||
|
ef0219c2ed | ||
|
f4bdf72aa4 | ||
|
cc352f3b66 | ||
|
215b000793 | ||
|
e8728acc5c | ||
|
166e02955e | ||
|
138111a542 | ||
|
191cb117f9 | ||
|
a57677120a | ||
|
75dcd94b90 | ||
|
d027fc9a88 | ||
|
a11d526110 | ||
|
eb797c502a | ||
|
567b9a997a | ||
|
319e270664 | ||
|
d015944f6c | ||
|
b7f01b0cc7 | ||
|
54bf5d62f2 | ||
|
849a583f66 | ||
|
0cb7735125 | ||
|
028949beca | ||
|
6bdeb10c16 | ||
|
44cbae8fad | ||
|
f7b2d4760f | ||
|
1ec19c2ad2 | ||
|
9bfd39f036 | ||
|
cfff6c4fd7 | ||
|
147dd3d372 | ||
|
cda3dc08ca | ||
|
78b4f929ce | ||
|
2c50066c16 | ||
|
dd0070a6b8 | ||
|
e6cf08169e | ||
|
0549b6ed0d | ||
|
3f58def52f | ||
|
0f8723b021 | ||
|
323b31b89d | ||
|
e9574586ea | ||
|
584595a78b | ||
|
24452785c2 | ||
|
c7dacec94b | ||
|
4255ea42ac | ||
|
36f904b705 | ||
|
309a394e06 | ||
|
4f855ae3fd | ||
|
5fcd4bd7d0 | ||
|
ae61c66dd2 | ||
|
4167dc7dfc | ||
|
8f29b8f2ae | ||
|
fca69a9b2a | ||
|
785b8a2400 | ||
|
5ed14fe8c4 | ||
|
fa6c61bd69 | ||
|
61d7dc91c7 | ||
|
2f95b14f09 | ||
|
23527b5263 | ||
|
4cd58b8b04 | ||
|
ccccfa8d5e | ||
|
fbc3094065 | ||
|
0f8984be2d | ||
|
0bea91a7f5 | ||
|
6adb8f92d4 | ||
|
9bd650ada2 | ||
|
0fbedf828d | ||
|
5a790ad702 | ||
|
a7fe400db9 | ||
|
ea9f8a9349 | ||
|
0db69655e1 | ||
|
2515a40fff | ||
|
045951b23c | ||
|
9166b57c2a | ||
|
fd8d04ff76 | ||
|
6def21b5e3 | ||
|
2fb9e4d18a | ||
|
36c9742c0a | ||
|
ec83312540 | ||
|
1548b2ae9e | ||
|
0fa56a9f7c | ||
|
88b007907a | ||
|
a73e71510a | ||
|
b2a6c3ba23 | ||
|
8caf922df6 | ||
|
5419345dff | ||
|
a8c4c55d58 | ||
|
f17498b049 | ||
|
63e9b01f7d | ||
|
db97b2bd93 | ||
|
c0422b18e7 | ||
|
29cfb6efff | ||
|
ffb4a5b565 | ||
|
99a27b7495 | ||
|
e9826f7819 | ||
|
bee541f378 | ||
|
45c5ef8798 | ||
|
282c81fe3a | ||
|
b861f0c5c5 | ||
|
7c6ade0fc5 | ||
|
41dfac96c1 | ||
|
b5e084c9a5 | ||
|
f592b57dc6 | ||
|
a99e985674 | ||
|
af3f696423 | ||
|
24e9db07ec | ||
|
547e1476bb | ||
|
eeb6cab4c4 | ||
|
3cb07d5fb6 | ||
|
a262be6a47 | ||
|
e5f276ecee | ||
|
94239153b5 | ||
|
f6a0bf915d | ||
|
8719a816e7 | ||
|
c8b5e3b741 | ||
|
cf074dd271 | ||
|
7053348e31 | ||
|
2bfcd45b35 | ||
|
636a734d78 | ||
|
464cc92521 | ||
|
5aeaf20849 | ||
|
1427a98c59 | ||
|
903d13ffff | ||
|
d0d7c11fe1 | ||
|
9dd09d1056 | ||
|
2cdf3af1b8 | ||
|
be75e42d77 | ||
|
d4a6df3389 | ||
|
d15214af52 | ||
|
d044c27b5f | ||
|
c0ce918d77 | ||
|
9a60b83061 | ||
|
7ba5973108 | ||
|
c4bfa65201 | ||
|
6a1b4a93c7 | ||
|
3becbda0aa | ||
|
a14b8d0976 | ||
|
966abc85da | ||
|
1e81c454c8 | ||
|
cbc074feb5 | ||
|
436b391a80 | ||
|
37899f7a14 | ||
|
e5542e5b02 | ||
|
41f7031e49 | ||
|
7f1316236b | ||
|
c346612468 | ||
|
431c5c8b36 | ||
|
f467bde6ac | ||
|
06f39e1915 | ||
|
4774e65ed9 | ||
|
5b14c03301 | ||
|
dfd7628950 | ||
|
4c4918b154 | ||
|
5bcb7983ec | ||
|
0614b175b5 | ||
|
4e2ca05f08 | ||
|
9554e3d43a | ||
|
d2004d32f6 | ||
|
e8f6127779 | ||
|
81f51492ff | ||
|
eb512cc36a | ||
|
c4d624083d | ||
|
a1b8d20f18 | ||
|
0a67e6180d | ||
|
2db4ad8c77 | ||
|
f54d209bc8 | ||
|
a4d2633ac6 | ||
|
f37f9a6f0b | ||
|
ded8c25e34 | ||
|
1a6ae148b7 | ||
|
8c52d5f2dd | ||
|
1ea8763259 | ||
|
40afc84597 | ||
|
a2058f7f3a | ||
|
d884a1624f | ||
|
b50b619be7 | ||
|
27c3aade5d | ||
|
f2b1cc3e61 | ||
|
cca65fde08 | ||
|
35bc217ba8 | ||
|
412cc96bc9 | ||
|
d2a7875b5b | ||
|
612d4f9656 | ||
|
33b8307119 | ||
|
bb550158fc | ||
|
b087e2554f | ||
|
d1dd044455 | ||
|
c49ff76955 | ||
|
0bfd9bc09e | ||
|
9f52f84f2b | ||
|
83bc27d99d | ||
|
d4ccc8de79 | ||
|
7bf0b1fc7e | ||
|
a6293b9861 | ||
|
eb8beb335e | ||
|
7354cbe463 | ||
|
f06383f78c | ||
|
b172e67295 | ||
|
10f1e142f6 | ||
|
a37bdbf8b7 | ||
|
f99a1a7c7c | ||
|
52a6673dab | ||
|
fef28aced9 | ||
|
23e502fa0e | ||
|
f7a695c212 | ||
|
ba63c1505a | ||
|
ac7406c8a1 | ||
|
1e59b9a756 | ||
|
d123bc0951 | ||
|
9f71e74158 | ||
|
bcbee873e8 | ||
|
4f800dd2b4 | ||
|
67126cbd1b | ||
|
8957739cdf | ||
|
3c5e0d10fc | ||
|
21437090b8 | ||
|
eb6731bdc6 | ||
|
480eb7d816 | ||
|
50928b9759 | ||
|
c7fe2ee5c9 | ||
|
8d43ad2522 | ||
|
ae96a8d4fa | ||
|
b857798848 | ||
|
5a8412dd23 | ||
|
88c845166c | ||
|
05d31222f7 | ||
|
3e10d9ccf5 | ||
|
810f39767c | ||
|
a799a54894 | ||
|
1fb7202028 | ||
|
36edc3f161 | ||
|
e545e96d2b | ||
|
18caf3a18d | ||
|
a45b04ffb4 | ||
|
03813a5b5e | ||
|
818bca68d1 | ||
|
a4d1b5b155 | ||
|
e9080f91f2 | ||
|
7cc5a6ec68 | ||
|
c3db9492a7 | ||
|
d068f34753 | ||
|
df4e70b2c7 | ||
|
c175046d30 | ||
|
6c324cb871 | ||
|
8c56434bd3 | ||
|
b77ad82fb9 | ||
|
11b19cd126 | ||
|
6a1d22b2c5 | ||
|
4faa16fe0d | ||
|
b6eaf7b5a4 | ||
|
946b3a4222 | ||
|
b63e988bd6 | ||
|
15e8f9e6a0 | ||
|
138052adfc | ||
|
f493e73aeb | ||
|
9b6a399011 | ||
|
a8ea165042 | ||
|
7295b6c88c | ||
|
8d2e770361 | ||
|
cc8c3d501c | ||
|
58ccf0ba82 | ||
|
4aae31ad5e | ||
|
3c42cc8684 | ||
|
11837d4623 | ||
|
46f0baff09 | ||
|
8f03b70584 | ||
|
4838eb2f7d | ||
|
87ac32edea | ||
|
58dd42166d | ||
|
a9fe83126a | ||
|
d4378a74d3 | ||
|
0c3117f9b3 | ||
|
794aea8e92 | ||
|
0fde9ab7e8 | ||
|
11e04ec113 | ||
|
ea4ae55e24 | ||
|
1fd4e0b82d | ||
|
4c44942a39 | ||
|
44b261d136 | ||
|
c00129360e | ||
|
9a44d835d6 | ||
|
f916f5de78 | ||
|
1c92d6243f | ||
|
af61de7777 | ||
|
a5e3fca40c | ||
|
8503d8de5e | ||
|
568f7a8e8f | ||
|
a983b72713 | ||
|
e55ba9c390 | ||
|
ec7738934b | ||
|
fbab80fced | ||
|
3b2abbea70 | ||
|
998d1a2b8c | ||
|
9a7471c5c0 | ||
|
21c8c141aa | ||
|
c2e89c5b6f | ||
|
cf76dac464 | ||
|
d9df06cda3 | ||
|
66a5ddca25 | ||
|
0be1fe11ca | ||
|
812abba33b | ||
|
2d6592a804 | ||
|
e23c191232 | ||
|
58ebe0a58f | ||
|
000f0c78bc | ||
|
aaf4877730 | ||
|
0c61461b07 | ||
|
31c50c470c | ||
|
62bee9f502 | ||
|
112c0719cd | ||
|
c96455b2e4 | ||
|
eed109c724 | ||
|
f25eaf1261 | ||
|
e1be22a6ff | ||
|
5891d0f5ec | ||
|
2424c64099 | ||
|
8b27340b2e | ||
|
7557a287e5 | ||
|
122b2d70d9 | ||
|
4bb9c8c61b | ||
|
0391d91e5d | ||
|
77e78193a0 | ||
|
cce4fe5a3f | ||
|
50b7523336 | ||
|
fced6ff240 | ||
|
bafc477919 | ||
|
60cd1e4529 | ||
|
1fa4ca7c59 | ||
|
db04964697 | ||
|
294a30e445 | ||
|
9982c56373 | ||
|
c884e7181f | ||
|
3064f3ccb7 | ||
|
0e06590ffd | ||
|
e952a0807b | ||
|
ba370d9841 | ||
|
5c5538685e | ||
|
8b85a62310 | ||
|
d1728199bb | ||
|
3ad6aee9b2 | ||
|
8c0c8334c3 | ||
|
47d551d780 | ||
|
67740b0e59 | ||
|
2633f85473 | ||
|
cb5723e409 | ||
|
cd81b4db7e | ||
|
9f910ab873 | ||
|
8f2a6863a8 | ||
|
10b0d45ec9 | ||
|
0cf3df7f3e | ||
|
009e04fb63 | ||
|
e7bb7b2fc1 | ||
|
218f3da4b8 | ||
|
0f4f56d768 | ||
|
5e17ce8c81 | ||
|
c02c855b73 | ||
|
934f5ca87e | ||
|
5ab12d33a4 | ||
|
d37e6bc1de | ||
|
1fc4f22a7a | ||
|
28c2e587e5 | ||
|
135f30913a | ||
|
8ac443d650 | ||
|
bf8655dfdc | ||
|
076c1d1623 | ||
|
e12f0c4216 | ||
|
ac224bd82c | ||
|
375b21e81a | ||
|
a7249ba653 | ||
|
7e9c4a9baa | ||
|
1d251e0f13 | ||
|
72a2cd2aab | ||
|
458ac94fc4 | ||
|
c3e8036bb9 | ||
|
286b03aac1 | ||
|
6c8941c417 | ||
|
86127c3609 | ||
|
5e070a0d17 | ||
|
33178a38b9 | ||
|
03cf7a8e05 | ||
|
ad4b13a0e9 | ||
|
409e75b94d | ||
|
c81cc4fa60 | ||
|
38f4d11d53 | ||
|
721e06451e | ||
|
8d648364c0 | ||
|
d60189915c | ||
|
88c7a54e08 | ||
|
d849d51c2d | ||
|
a450301686 | ||
|
806fba6448 | ||
|
0d85e826f4 | ||
|
4b90e582b4 | ||
|
b7073df68c | ||
|
4deb5b999c | ||
|
6ead789509 | ||
|
cb6c8eb2f0 | ||
|
c3b5cc8611 | ||
|
3f306a407c | ||
|
2d135cc1bb | ||
|
e441b5d240 | ||
|
d52e4cdbdb | ||
|
aa273119f2 | ||
|
d4daa9fd40 | ||
|
e5d321d286 | ||
|
f2ab887644 | ||
|
4975afb5ff | ||
|
e3813cf027 | ||
|
9f43018df2 | ||
|
24704b01d9 | ||
|
b61c83a19d | ||
|
4e9aa7dc77 | ||
|
a18355e7e8 | ||
|
37095f3e49 | ||
|
e2ea359925 | ||
|
4c419c4020 | ||
|
c7938ce81c | ||
|
6d7a519740 | ||
|
6431ef7324 | ||
|
7435ea0d4e | ||
|
ecf8488406 | ||
|
067a5b5ac3 | ||
|
e3aa79cffb | ||
|
dbbe0ca065 | ||
|
cbc741f464 | ||
|
d95e916a42 | ||
|
244f4f285a | ||
|
99b6315c1a | ||
|
dbf103da32 | ||
|
3778ed7466 | ||
|
c8dc9c2b8d | ||
|
41091a147c | ||
|
6d5103900f | ||
|
5532248cd7 | ||
|
2188adc0f9 | ||
|
4e6e8b7bf1 | ||
|
1b2d24791a | ||
|
d2b4c27f21 | ||
|
72801d0233 | ||
|
f1a436619f | ||
|
06907aa99b | ||
|
323fc0a798 | ||
|
d8274af670 | ||
|
224066c1d3 | ||
|
2fee37f31b | ||
|
521e678d39 | ||
|
7e4919c6ed | ||
|
c8f1efebea | ||
|
3f5a58a4e5 | ||
|
bd15f26c35 | ||
|
f5076723e8 | ||
|
4952f17df4 | ||
|
3c5f05b284 | ||
|
87a42d62b2 | ||
|
3a7c8279bf | ||
|
07ff2a5c01 | ||
|
6ac4447134 | ||
|
13f533d490 | ||
|
291c3ad0c1 | ||
|
1037ee2a55 | ||
|
78aab8c95d | ||
|
05cbd84ae0 | ||
|
84b9321977 | ||
|
95d7348a08 | ||
|
a19d0033bc | ||
|
57ca92e0eb | ||
|
3397950a0e | ||
|
3eef1ca28f | ||
|
132e43346e | ||
|
eefaef53b7 | ||
|
3797ca52c4 | ||
|
22ef4c8be1 | ||
|
6569fdd4d1 | ||
|
53d949bd9f | ||
|
c6fc694ea6 | ||
|
bca44574d5 | ||
|
9e14f5f053 | ||
|
d6da80fe24 | ||
|
700fbc803d | ||
|
610ddaba7c | ||
|
fbb0e82679 | ||
|
65ec371b78 | ||
|
d08d34d803 | ||
|
c1ea49940b | ||
|
5f308deb50 | ||
|
0220d0d492 | ||
|
588a0f83e9 | ||
|
526a2f7b8c | ||
|
3c48671076 | ||
|
f65fe80e81 | ||
|
d8f1daac25 | ||
|
4ac86db8e3 | ||
|
811adf5d42 | ||
|
7ae51382c8 | ||
|
387856a1c3 | ||
|
24efd7dc91 | ||
|
9827f9df1b | ||
|
2b2f2dee20 | ||
|
b93ae33f85 | ||
|
17c041a65c | ||
|
37b374cb92 | ||
|
ecf8c7696a | ||
|
e3efaa1733 | ||
|
6d01ed5d74 | ||
|
70e169f165 | ||
|
6dfd61cba0 | ||
|
58ea11c2b3 | ||
|
6f275e2ba0 | ||
|
cc26b5384c | ||
|
0fdcba197f | ||
|
65af606729 | ||
|
740dc0162e | ||
|
47f199e6cb | ||
|
4aec4fbe6f | ||
|
6f23de41fb | ||
|
7bab390413 | ||
|
f9c0354af1 | ||
|
c54f5a2137 | ||
|
a6219ab955 | ||
|
38b4505ad7 | ||
|
b12e67699a | ||
|
72c306d920 | ||
|
f54f2c1601 | ||
|
42af7cc1c5 | ||
|
9c2b2c002c | ||
|
af67353f7a | ||
|
8617993386 | ||
|
733a019bf5 | ||
|
2208fc0632 | ||
|
c8ebc2e5d0 | ||
|
3feec87d52 | ||
|
275bef0633 | ||
|
e90ef85e7d | ||
|
9e5d6bc162 | ||
|
041d67ceca | ||
|
1527cdf6a4 | ||
|
197d09cc53 | ||
|
4fd744cdf6 | ||
|
3af226cb06 | ||
|
486dc3288d | ||
|
074700b35e | ||
|
8349846333 | ||
|
08de047033 | ||
|
88397c2908 | ||
|
2e567b7d40 | ||
|
d5d8fb629b | ||
|
2bc9dc54ff | ||
|
e55fb6da71 | ||
|
3fb4049612 | ||
|
62131fe295 | ||
|
6b7fc1e9fe | ||
|
be9aa19208 | ||
|
435a89b5a4 | ||
|
47e4c33a50 | ||
|
5d6e15bc49 | ||
|
538b8b9b34 | ||
|
6105804f00 | ||
|
f23d7459b3 | ||
|
0c4f03d9a5 | ||
|
b8b99d5cf1 | ||
|
ac11a14509 | ||
|
8972c80d7d | ||
|
1380bf9b88 | ||
|
7afa78ec82 | ||
|
43363ee066 | ||
|
48e65ac846 | ||
|
1b8b84bee8 | ||
|
6608057971 | ||
|
5c171f6d61 | ||
|
eae9a70385 | ||
|
322a9c2f74 | ||
|
52376fd87a | ||
|
efd7792add | ||
|
a6ba5304c4 | ||
|
3971b6afcc | ||
|
7f5a19792c | ||
|
3a59fabefe | ||
|
1977517d7a | ||
|
bf4d31227b | ||
|
1298d6c020 | ||
|
19a58745c9 | ||
|
fa4529b4f1 | ||
|
39970fed38 | ||
|
c00fed20b7 | ||
|
c52a4369eb | ||
|
0f7ee126de | ||
|
2e883189c1 | ||
|
04f0a4a1c6 | ||
|
3441fd6e04 | ||
|
c893e0b72b | ||
|
5d18b6fcd0 | ||
|
8109563a02 | ||
|
21ed680b10 | ||
|
5583831c40 | ||
|
da1fc9a536 | ||
|
df8afe3dc4 | ||
|
56123b2fbe | ||
|
274c223d00 | ||
|
7968ab6928 | ||
|
c6ef5ab259 | ||
|
ab7af5d15a | ||
|
66b3db3601 | ||
|
b2f6a65bc9 | ||
|
6cc0452503 | ||
|
1bccb4e48c | ||
|
f2c66b9ceb | ||
|
dc21924f31 | ||
|
2664afd832 | ||
|
9ccd9d341f | ||
|
ec115ffe2a | ||
|
b010714426 | ||
|
48845de46e | ||
|
03e600a721 | ||
|
c65444c43b | ||
|
3df070f352 | ||
|
5d226268df | ||
|
5acc2736db | ||
|
1fb49e9ca7 | ||
|
b0ad9a6c33 | ||
|
792fb13ac6 | ||
|
d3ca269c79 | ||
|
44db47e64a | ||
|
6874c358ea | ||
|
cc8e7a569e | ||
|
603eb57943 | ||
|
9046379b30 | ||
|
ea96f6e1e3 | ||
|
dc7a7a0ed9 | ||
|
a677d33bdf | ||
|
0fb67ccb34 | ||
|
e6f77b95f3 | ||
|
cb0bbea2a5 | ||
|
98e51a0159 | ||
|
39c2af9710 | ||
|
013c6ee166 | ||
|
a3838dd0e8 | ||
|
53a50e0b0d | ||
|
85e717fcd1 | ||
|
a0e33ba9ea | ||
|
c7642c3c6c | ||
|
e236ad8348 | ||
|
f04cdc00a6 | ||
|
cfa9c83d33 | ||
|
5b19d315b3 | ||
|
c6c2c4f60f | ||
|
1222750c50 | ||
|
835dd01fa1 | ||
|
20afc762cc | ||
|
dfb23c8db0 | ||
|
78e94b299d | ||
|
b52f7c76cd | ||
|
2877e8e624 | ||
|
0f166aa7ec | ||
|
a760faa3fa | ||
|
c0fd23e688 | ||
|
e558e44af4 | ||
|
0dcf3c57c7 | ||
|
6c19e68d6b | ||
|
5142f32878 | ||
|
aeda08c109 | ||
|
3d556da8b8 | ||
|
aa11c1a278 | ||
|
1cd9591997 | ||
|
4208022d55 | ||
|
4a4730be9b | ||
|
16c4ba599e | ||
|
b66aa9a954 | ||
|
ebaf3c8d77 | ||
|
62a8c5ca32 | ||
|
f5a53647f9 | ||
|
8ef2c42150 | ||
|
10fd41b4a8 | ||
|
95a0cc8f9a | ||
|
c489cef875 | ||
|
8bff2f23c6 | ||
|
d566b0e280 | ||
|
26deb26f17 | ||
|
2ba1d60ba5 | ||
|
30dcd41d91 | ||
|
90fccc15eb | ||
|
ac284e61b4 | ||
|
7625f88a0c | ||
|
4e1661eded | ||
|
60f8c02e18 | ||
|
768fd4adee | ||
|
682b789dd5 | ||
|
02f40e5b0d | ||
|
a7a719261e | ||
|
eba0806d77 | ||
|
b03e9ef1c7 | ||
|
8e59d8b682 | ||
|
ecf6295b4a | ||
|
569df37442 | ||
|
d733e1abea | ||
|
c3efcb3896 | ||
|
1e7f554bcd | ||
|
0e9fe7f194 | ||
|
f5716ef141 | ||
|
652a21ba8a | ||
|
4d39943d0f | ||
|
540ea34ade | ||
|
183a2b5579 | ||
|
d2ece3c165 | ||
|
a31d6b0886 | ||
|
2e62025d7e | ||
|
42522a277c | ||
|
987ade361d | ||
|
385df95ef4 | ||
|
5b9fb5ac02 | ||
|
c96f246200 | ||
|
e55a8ab77a | ||
|
fe137c0941 | ||
|
285a73cf0e | ||
|
306a6f57f3 | ||
|
105f5ec092 | ||
|
275e2d8ff8 | ||
|
ca33bcf2ab | ||
|
386a249b0c | ||
|
dd8b1680f5 | ||
|
6acfa2babf | ||
|
7025e9dff9 | ||
|
67cdbb54bc | ||
|
2028a2c2f3 | ||
|
570fdbf9fd | ||
|
090a270246 | ||
|
fcb09f440f | ||
|
ab2ad8aec1 | ||
|
55334cfbc0 | ||
|
590273e2fd | ||
|
a813e261b4 | ||
|
043961fc3e | ||
|
c85cefde67 | ||
|
34326df680 | ||
|
6af09cb2ad | ||
|
70fc175e74 | ||
|
4fd4ed6ca3 | ||
|
9c6d5a8408 | ||
|
6079d4488a | ||
|
0163e0b099 | ||
|
ace8c56199 | ||
|
abd05a38ad | ||
|
e7c8346861 | ||
|
69dd8c61c9 | ||
|
4e3185af88 | ||
|
b69c02a824 | ||
|
606da069b4 | ||
|
607f473be2 | ||
|
e283b6fb25 | ||
|
da7e95a221 | ||
|
754842c6af | ||
|
675b6e9228 | ||
|
070c5ee632 | ||
|
b78042fc2f | ||
|
a55c689d7c | ||
|
f0a28c381b | ||
|
3d9a5e6866 | ||
|
857d5fe6a1 | ||
|
1e485d5dbc | ||
|
20bc6d6789 | ||
|
ed07a70327 | ||
|
dfc10f4a32 | ||
|
64cc3b90c7 | ||
|
02776f9768 | ||
|
2ab6662a13 | ||
|
be56413593 | ||
|
f0de9c100d | ||
|
5693327ca1 | ||
|
a1f82dcfb7 | ||
|
2b7fd07150 | ||
|
9c197693d7 | ||
|
6afd79c771 | ||
|
35485379bd | ||
|
da3deefae2 | ||
|
1a53086f7c | ||
|
0ca6bb3a1f | ||
|
c5d461c379 | ||
|
531e2b442d | ||
|
7ead579c67 | ||
|
2e91094afa | ||
|
b991941227 | ||
|
65aa894749 | ||
|
3f478d8597 | ||
|
470f6742bc | ||
|
3f2f38842c | ||
|
a6121c7f67 | ||
|
bb3f271a20 | ||
|
3de42f56c5 | ||
|
fcb4f258f5 | ||
|
8ed55b3aff | ||
|
d5444e1172 | ||
|
ea827e4c5d | ||
|
95e77bd7cb | ||
|
321b217feb | ||
|
3958c19f83 | ||
|
7709d92289 | ||
|
868b548dd0 | ||
|
837aab0e98 | ||
|
77ac20a66b | ||
|
7362ecb3b4 | ||
|
88ffe75b58 | ||
|
c1520c9e11 | ||
|
9a1501ae89 | ||
|
514e106414 | ||
|
4c001bd248 | ||
|
fb6ceb2664 | ||
|
50da26da91 | ||
|
28857841aa | ||
|
157d129e30 | ||
|
9ab447843b | ||
|
fe6da3a16b | ||
|
fac9aac821 | ||
|
d7f4ce6cff | ||
|
a9cca5e76c | ||
|
7a64527db5 | ||
|
bc23a610d3 | ||
|
9def45aa80 | ||
|
85878d894a | ||
|
ae4cd1ebf1 | ||
|
781c7a800f | ||
|
5133ae52df | ||
|
3539af7d77 | ||
|
c03894321a | ||
|
b7e886a740 | ||
|
8268c61b9f | ||
|
fee6e8e11b | ||
|
558d749d54 | ||
|
27cb54c1db | ||
|
4c9be808a7 | ||
|
71893807b3 | ||
|
08485f6781 | ||
|
87232358d3 | ||
|
ec9c000be9 | ||
|
75f3a2183f | ||
|
445e485fc5 | ||
|
508b5ccc63 | ||
|
833bb542fc | ||
|
981d67324b | ||
|
140b9e5a5a | ||
|
a4768d1638 | ||
|
abf353c178 | ||
|
670bb32b2c | ||
|
f836a47bc1 | ||
|
43df39c4f7 | ||
|
9da5c5e2d0 | ||
|
cdbbac5b6d | ||
|
3120558dd1 | ||
|
f3c91e4f96 | ||
|
b1fa430dad | ||
|
83bc362dac | ||
|
a86baefda7 | ||
|
82790b1556 | ||
|
728f643ea7 | ||
|
d5fc040d2d | ||
|
6c0b8229ec | ||
|
bc60693a5d | ||
|
1298374818 | ||
|
a078cfee3e | ||
|
bd4572cfd1 | ||
|
df8a600b22 | ||
|
567f85752d | ||
|
5009259473 | ||
|
b21a974342 | ||
|
ab783b9bb2 | ||
|
36d236c5e0 | ||
|
408faa11a1 | ||
|
8972e829f2 | ||
|
c4703a7f19 | ||
|
f6cfe6b873 | ||
|
bceef8f529 | ||
|
b23042839b | ||
|
5625be70fa | ||
|
5c187363b2 | ||
|
93f4844c9c | ||
|
39fd9b93c3 | ||
|
c38e0d05bf | ||
|
5a4d7fb0d6 | ||
|
6303334cc2 | ||
|
472742266b | ||
|
0889048cb5 | ||
|
57b6e74abb | ||
|
a8af0c0ca4 | ||
|
07fe8d4481 | ||
|
e0e1d0855d | ||
|
178591b6d5 | ||
|
dd475d8af4 | ||
|
121d22f137 | ||
|
59bf1d8cd9 | ||
|
780543f0a2 | ||
|
de78ffb551 | ||
|
1aba7f1fde | ||
|
3017b0213b | ||
|
d748c8c653 | ||
|
af8cb63292 | ||
|
d7f9da49eb | ||
|
70bddcf318 | ||
|
f0bb5313d3 | ||
|
c352fbf5c9 | ||
|
9c3c286aab | ||
|
e10adf83d5 | ||
|
c24a04d246 | ||
|
2e89529eef | ||
|
92d03f3832 | ||
|
2efd0996e6 | ||
|
ac4f13e78f | ||
|
2bdd0a6bdb | ||
|
c044a3c1ca | ||
|
72cb4e9bea | ||
|
fc0ac64277 | ||
|
df4c9e2903 | ||
|
73dbd2f0ab | ||
|
05d5dc4cec | ||
|
815b5cb086 | ||
|
0837d674eb | ||
|
a7bde8e523 | ||
|
c63c05b141 | ||
|
12ccbe6f12 | ||
|
be540043ee | ||
|
de8d80dee0 | ||
|
98fd5bd453 | ||
|
15b569fb71 | ||
|
58eccc7a2a | ||
|
e39e47b21f | ||
|
e7f6e7d7b6 | ||
|
af27d97003 | ||
|
c8e46749a4 | ||
|
eb432d3da0 | ||
|
4ab3c54f5b | ||
|
de1d8ec070 | ||
|
c8a41409d9 | ||
|
d2a089ffd9 | ||
|
3bd5169aee | ||
|
b7887a339d | ||
|
1b89d4d541 | ||
|
d8190e1c5f | ||
|
2af5191070 | ||
|
4027e08cc8 | ||
|
72eeb9fecb | ||
|
5a07f5a652 | ||
|
d767ab0890 | ||
|
df2fd399df | ||
|
f2b5c35fa2 | ||
|
b2057a5da7 | ||
|
38cacfa577 | ||
|
13e271c6cb | ||
|
7a6ca85081 | ||
|
ecfe9c5c2f | ||
|
9f263319ca | ||
|
00ef597639 | ||
|
30e280b694 | ||
|
b9adf244e5 | ||
|
8a5e49c856 | ||
|
284f6d3682 | ||
|
cc1bfc6d03 | ||
|
07fdf7158d | ||
|
fca60e2a41 | ||
|
e12db0c182 | ||
|
1d673ce075 | ||
|
7e4462e0ac | ||
|
f87994edc7 | ||
|
ef0c19477c | ||
|
9bee6d899b | ||
|
2d8e4df7bc | ||
|
4e1679d2a2 | ||
|
fd8e02195e | ||
|
b2982a6f14 | ||
|
a9a0f1e129 | ||
|
0ea1ec31fc | ||
|
f8bff346f4 | ||
|
ff88ed7c75 | ||
|
57a737c417 | ||
|
eb8785a209 | ||
|
d4b10db998 | ||
|
3a35db6e67 | ||
|
e79587c934 | ||
|
8935f2af3c | ||
|
517f1602aa | ||
|
e8a8525bcd | ||
|
6e3246c5fd | ||
|
97066bf795 | ||
|
5df60d85f7 | ||
|
008d6be900 | ||
|
d1a1aed23e | ||
|
673d2499e8 | ||
|
6ac053bbaa | ||
|
6c6e48f006 | ||
|
bb6782ca58 | ||
|
d307d01b18 | ||
|
08284e420d | ||
|
d53ef90a73 | ||
|
b487341c32 | ||
|
6a7a613741 | ||
|
9cee9bc279 | ||
|
f638482fba | ||
|
a7908da968 | ||
|
20bfaba0b7 | ||
|
5a27c05b6a | ||
|
24cc33e704 | ||
|
6c4a110679 | ||
|
b42b03bc40 | ||
|
5727d74d37 | ||
|
bc4ab8b99e | ||
|
24f2c38093 | ||
|
dfba79f8ff | ||
|
0d817ff4ff | ||
|
178943b4b7 | ||
|
751ede516b | ||
|
cda112493a | ||
|
fa072c1d2c | ||
|
a862e4290c | ||
|
234b01a8c2 | ||
|
e3b9828f24 | ||
|
8339594206 | ||
|
2386bfda7e | ||
|
ef0009aea7 | ||
|
4d24537590 | ||
|
4f84b01356 | ||
|
c8b615acc9 | ||
|
61a5733692 | ||
|
4245a7604b | ||
|
d5d6e36ae0 | ||
|
658bc9fcc8 | ||
|
60810c2d37 | ||
|
f07e1026ac | ||
|
32f68f35cf | ||
|
02a893d613 | ||
|
f596c36f4f | ||
|
8ff0e1da15 | ||
|
4660310db6 | ||
|
58bf4f04b1 | ||
|
d88f0866b7 | ||
|
bf52d1e624 | ||
|
fc80f65a6d | ||
|
badd42789a | ||
|
c524c52baa | ||
|
585ca90ae0 | ||
|
e734b3f0d8 | ||
|
91ea47fddf | ||
|
9b97147637 | ||
|
d4d4712361 | ||
|
879f7e9f03 | ||
|
aaf77025b6 | ||
|
cdce33dd05 | ||
|
b10797b3d5 | ||
|
e73e562a63 | ||
|
e40417f687 | ||
|
aac616fcc5 | ||
|
fa0745f7da | ||
|
00a3e6bbd7 | ||
|
bb7f57b095 | ||
|
d85c842ce9 | ||
|
dce166dc93 | ||
|
13a0e5fb4a | ||
|
0c3a4cc7b9 | ||
|
e9ab5bc223 | ||
|
5da18d34ba | ||
|
09d542dfe0 | ||
|
5280863300 | ||
|
fbec378869 | ||
|
9fcd7f2dc0 | ||
|
229389b7f6 | ||
|
5d8a22066c | ||
|
63a9853811 | ||
|
39b1311a1b | ||
|
f4eba3bfba | ||
|
4caf0e4cb9 | ||
|
762fca538c | ||
|
fa1d80b53b | ||
|
b4be483d3e | ||
|
57de599a29 | ||
|
492aab20fe | ||
|
6952bab519 | ||
|
87fa4de59c | ||
|
0d4b489545 | ||
|
0d30a3071a | ||
|
753f03ff6a | ||
|
bf2098c07f | ||
|
2f35b121a4 | ||
|
34286d77c7 | ||
|
e3cc26cb7c | ||
|
84dd812da4 | ||
|
4acbd59162 | ||
|
1e316a9704 | ||
|
6ca214fefc | ||
|
fa47af737f | ||
|
0f2517070e | ||
|
f483d10c95 | ||
|
8c3a6a819e | ||
|
933432e62d | ||
|
2b97fead9e | ||
|
9ac6d330b4 | ||
|
4843890c56 | ||
|
c81e0b7433 | ||
|
ce97210eb1 | ||
|
cb5fa56e17 | ||
|
6cbb9193ea | ||
|
3cac17d23e | ||
|
d4b107e2e8 | ||
|
c90ebad46b | ||
|
af69d4f7a9 | ||
|
6c8ae2b72a | ||
|
7901087466 | ||
|
e2ccd14c05 | ||
|
a14320fc44 | ||
|
eb52a149a0 | ||
|
adaa4cc2f3 | ||
|
f2b99332d9 | ||
|
4859cf44ce | ||
|
83f779c52d | ||
|
c9e10e1dd9 | ||
|
4259ac96ea | ||
|
714c9361ea | ||
|
93381014a0 | ||
|
16aaef097a | ||
|
1d9c9710d7 | ||
|
e7dbd325d2 | ||
|
893594d81a | ||
|
176e674a51 | ||
|
e10d8080ba | ||
|
9da1354f3a | ||
|
e1f6108789 | ||
|
40bf88ac74 | ||
|
ffec698d3e | ||
|
b1ee137177 | ||
|
fbec168e91 | ||
|
89f3502b56 | ||
|
5dbc1d4c08 | ||
|
c0f852e016 | ||
|
397682a5b0 | ||
|
2b99dabdac | ||
|
df9975f35d | ||
|
4158759265 | ||
|
8927e7caf6 | ||
|
a46fe79939 | ||
|
2ef080a51b | ||
|
4468813d47 | ||
|
6a2eb4da07 | ||
|
731b84d725 | ||
|
362e4505e8 | ||
|
13013d1b8b | ||
|
6df0de565f | ||
|
89eabb5803 | ||
|
6aa4f14a28 | ||
|
be7844192b | ||
|
9e3a11534f | ||
|
a29d3cf074 | ||
|
f6ecd931dc | ||
|
f0180ad488 | ||
|
afc48cf224 | ||
|
5958714309 | ||
|
fb461d21a5 | ||
|
404a063fdf | ||
|
8cbd629010 | ||
|
e9233bc169 | ||
|
bec080be8d | ||
|
371b9a7fc2 | ||
|
699d1bf27c | ||
|
225aa107f6 | ||
|
e416c99419 | ||
|
7c9706fdcf | ||
|
f08e4bb27d | ||
|
432988a4ad | ||
|
a8cf10b0b5 | ||
|
e985b7a0bf | ||
|
6caa06eaed | ||
|
02fa33252a | ||
|
2211f4f8f7 | ||
|
5ceb327e55 | ||
|
b0f76d82c5 | ||
|
2bcebc4e4e | ||
|
cd55a533e8 | ||
|
021eddac73 | ||
|
5756d6262e | ||
|
56943bef48 | ||
|
ee727eb65e | ||
|
e8008c1b21 | ||
|
bdc124ba41 | ||
|
f27f701251 | ||
|
b2f0a37b18 | ||
|
050152eb90 | ||
|
3987318f09 | ||
|
9f684eac92 | ||
|
abf3142b26 | ||
|
eeb873b23c | ||
|
84d4647329 | ||
|
7acb14f7a1 | ||
|
2587302987 | ||
|
db88d24ff8 | ||
|
2ea8d9ca11 | ||
|
0383c44f0d | ||
|
08ee6d8d4b | ||
|
e7dd9737bd | ||
|
ceaa7e2fb0 | ||
|
8093044f07 | ||
|
9cca12ff0b | ||
|
1b0d2a37bb | ||
|
92c55c27cf | ||
|
ed7d4037b2 | ||
|
dd094d7606 | ||
|
1ba26d67bd | ||
|
e824e9023f | ||
|
d20f1182f2 | ||
|
e83530d40b | ||
|
6f494a968d | ||
|
133f706bf3 | ||
|
863c9b55b4 | ||
|
45561b89a4 | ||
|
6b9250e4ef | ||
|
5089e8342f | ||
|
b12be0498e | ||
|
59abf1bb42 | ||
|
518ecd7f4e | ||
|
025516a005 | ||
|
345e93d19c | ||
|
d98ea7fdb6 | ||
|
a7eaee77ca | ||
|
0d41996562 | ||
|
7bae8ab838 | ||
|
4bf95703a0 | ||
|
2766c70ad3 | ||
|
2c390b5473 | ||
|
20692d54de | ||
|
dc7fb26921 | ||
|
171f911237 | ||
|
bd42cc2c77 | ||
|
8db4ba9e58 | ||
|
7c7ae79f9f | ||
|
8674dc831d | ||
|
3fbe42c3a2 | ||
|
6550bc252f | ||
|
e04f618979 | ||
|
c2c8d4d410 | ||
|
15f73258fd | ||
|
34ce86a8f5 | ||
|
05a53cd330 | ||
|
e17455cb22 | ||
|
995c192874 | ||
|
c9cd0d20ef | ||
|
3eab5e9002 | ||
|
a22b1700a4 | ||
|
8e9d7611ae | ||
|
de4cc5c20a | ||
|
0b423dd061 | ||
|
2c44620e5e | ||
|
d0753dddb1 | ||
|
c9ab61aa8c | ||
|
431444ba9f | ||
|
c99196d363 | ||
|
22a1653702 | ||
|
edd947b645 | ||
|
b8dc349099 | ||
|
93c2aff2cf | ||
|
ad5e9aa5e3 | ||
|
467d3a8c62 | ||
|
bc336480e6 | ||
|
32e492837c | ||
|
4f1dbb127a | ||
|
93dfa8a6d8 | ||
|
5054918efc | ||
|
335af393f0 | ||
|
524721ee27 | ||
|
5405a558fd | ||
|
094c433e58 | ||
|
961652c2e9 | ||
|
322c4a5b2b | ||
|
b6c7c5a7ab | ||
|
46c930cf70 | ||
|
e0d4a9d575 | ||
|
04f0d545da | ||
|
cad8e895f2 | ||
|
cb38b841af | ||
|
d58cc7fb7a | ||
|
7e143cb33d | ||
|
04dc4a10f0 | ||
|
47e557b96a | ||
|
3b346fd3c9 | ||
|
0d61598d8a | ||
|
61d0f613df | ||
|
00d9d96e48 | ||
|
b1aec1b5c8 | ||
|
5d3e830176 | ||
|
91ba02449b | ||
|
7162b536eb | ||
|
ca1a723890 | ||
|
16c22477c2 | ||
|
128ed87dd8 | ||
|
9408a1a025 | ||
|
777dca7043 | ||
|
f5b35a074f | ||
|
c6cf90f67b | ||
|
cb6e3ac6e1 | ||
|
2c2bc4a427 | ||
|
e5a6048eec | ||
|
3c60d359ed | ||
|
94a54375e2 | ||
|
c871b6dd4e | ||
|
62c6667b0b | ||
|
704b5d88b9 | ||
|
a292cc42aa | ||
|
da832a295e | ||
|
64b59184d1 | ||
|
bd06466d3a | ||
|
d4123a387c | ||
|
e7be135b78 | ||
|
d1a55e9ca4 | ||
|
6eb9269741 | ||
|
a98200bb4c | ||
|
55fafb7d25 | ||
|
321bcf5c44 | ||
|
689aaf50b3 | ||
|
2ec3325381 | ||
|
634e49b961 | ||
|
55f40a7f8d | ||
|
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 | ||
|
c2a9ac24ac | ||
|
1b95998d11 | ||
|
cc29bb473d | ||
|
bdebacd3a5 | ||
|
036d08fb70 | ||
|
803a57a490 | ||
|
f2433c7b63 | ||
|
f9376ac763 | ||
|
6885fd4fd0 | ||
|
cee1c5d11d | ||
|
22041c5e8a | ||
|
f0fcc150d0 | ||
|
469a2eb4a4 | ||
|
7ae5c91d62 | ||
|
051102cde0 | ||
|
76b61f07c9 | ||
|
52956464a5 | ||
|
046ca78dfa | ||
|
aa28084604 | ||
|
a3a4640257 | ||
|
87b3e9cc5f | ||
|
4083fcf93a | ||
|
1036c2a639 | ||
|
7bfea9b0fe | ||
|
ba2fee0751 | ||
|
f8e0778fc9 | ||
|
9c9309cdbb | ||
|
d975ebdcb9 | ||
|
7048fc25dd | ||
|
b01f85d573 | ||
|
1c1f1b1615 | ||
|
6c0a6925fc | ||
|
819e9fc615 | ||
|
48fc286a95 | ||
|
93898957b6 | ||
|
bd88d299b9 | ||
|
0a698d92c3 | ||
|
0f496f1ed2 | ||
|
ea095d3f69 | ||
|
2180dc14ef | ||
|
baf99f826c | ||
|
9c07bf68f1 | ||
|
ed632f3854 | ||
|
f3b198e490 | ||
|
9af587c54e | ||
|
6cd2eea487 | ||
|
09342c0811 | ||
|
0050f12b32 | ||
|
5fa63a0b0c | ||
|
6fe9bc8ae1 | ||
|
38e005294f | ||
|
b1c8a7d055 | ||
|
2bed338ef7 | ||
|
b7359f5fa9 | ||
|
cac1dca95b | ||
|
02346819f9 | ||
|
3e148e2810 | ||
|
b1ed0ef721 | ||
|
1aeb0280df | ||
|
e73a4ea506 | ||
|
705195b43e | ||
|
1de8a1e962 | ||
|
ad41d0af9d | ||
|
c77f3d4dc4 | ||
|
225659f6e0 | ||
|
ad203b75e1 | ||
|
37f4d6e447 | ||
|
b12eb5e490 | ||
|
a32bbc8941 | ||
|
0263069255 | ||
|
8ac5c45da5 | ||
|
7dd28f0751 | ||
|
0f50d2d329 | ||
|
306b86ee91 | ||
|
ff22d650ed | ||
|
be5710810b | ||
|
8d691e6633 | ||
|
6f53af2416 | ||
|
7e22d472a3 | ||
|
ecb1bd7ac8 | ||
|
78d5c95006 | ||
|
36c5ea2d5d | ||
|
cd0801a4fa | ||
|
fb7bfb62d8 | ||
|
bc2d863492 | ||
|
87d3c773bf | ||
|
22350290c5 | ||
|
9cb12e3565 | ||
|
34097e3a6f | ||
|
cbe35af9c9 | ||
|
1ca1ea2ca9 | ||
|
bf7478b0a8 | ||
|
611f6b0cff | ||
|
c1d2c0d4ec | ||
|
1e4b6ba1a3 | ||
|
880c2ecb3b | ||
|
a5a991576f | ||
|
b37886375f | ||
|
7fa9dbccf3 | ||
|
0fbdc58303 | ||
|
15239d1449 | ||
|
6fd412c745 | ||
|
1f0d042377 | ||
|
9ef3c8ce38 | ||
|
4f06df719d | ||
|
c91f3f99fd | ||
|
847ed04e0a | ||
|
e66e583f5e | ||
|
dcf6a6a67b | ||
|
915befecc5 | ||
|
fee2e3ee27 | ||
|
a93f3542d9 | ||
|
8c7276c9d4 | ||
|
514bf3582c | ||
|
d6be6682ec | ||
|
39f4d26177 | ||
|
fbbdae93ee | ||
|
fe3ea090d1 | ||
|
2083252c05 | ||
|
6e1c70e02b | ||
|
63c378fb5b | ||
|
2553db5c81 | ||
|
af4009d924 | ||
|
cf19167e99 | ||
|
aa5dc0968b | ||
|
3bafbaac49 | ||
|
2dafce6206 | ||
|
0e721f7571 | ||
|
c1e430ef68 | ||
|
75e6cc190a | ||
|
2a8becd650 | ||
|
4cd6b773bb | ||
|
d3a6ee00e6 | ||
|
56d2567b5d | ||
|
8bbb673c0b | ||
|
69fc206109 | ||
|
cded6a3945 | ||
|
b8aaad4f1e | ||
|
8478796226 | ||
|
87d509e462 | ||
|
6f5703baf1 | ||
|
cf5add1472 | ||
|
47c8b5d57a | ||
|
ca13c51024 | ||
|
e21d5613a6 | ||
|
ecd6d61697 | ||
|
a2a280691c | ||
|
2f9f0c0900 | ||
|
8fded9d990 | ||
|
3992a13f24 | ||
|
5f2925c59c | ||
|
956026bb6b | ||
|
ab1fe80150 | ||
|
a3177b89d8 | ||
|
139db66901 | ||
|
d69cb4fb5d | ||
|
d0b6f217ae | ||
|
390b5caaaa | ||
|
fb28ca463e | ||
|
55804c56e9 | ||
|
d82d18bfb1 | ||
|
d603619ad3 | ||
|
4685849f89 | ||
|
07624125ef | ||
|
bb74da5903 | ||
|
6d45c243f8 | ||
|
2715cc8bf6 | ||
|
059f62d7d6 | ||
|
0dbb20fd63 | ||
|
6377ce921d | ||
|
89e7f72c92 | ||
|
03e710160f | ||
|
1e7804aaf6 | ||
|
afd5caa26a | ||
|
7f01471141 | ||
|
33363c2a7e | ||
|
d13b12b791 | ||
|
5c248c2d7d | ||
|
7701e70dc9 | ||
|
62ee02b8ba | ||
|
475f85fc91 | ||
|
0fc97a1483 | ||
|
1f3b5e553b | ||
|
981d79157a | ||
|
111d047b0a | ||
|
024c99fc47 | ||
|
219904dc54 | ||
|
0bff85f7b0 | ||
|
b30e0681a0 | ||
|
8cba08d423 | ||
|
6ae870b27d | ||
|
f004432ec2 | ||
|
5cfe76bb60 | ||
|
866fbf9149 | ||
|
8660f90868 | ||
|
569c83a8e7 | ||
|
4a1ca5e16c | ||
|
49183e3555 | ||
|
1fb1a9e211 | ||
|
f858bb9a75 | ||
|
fbe97a1b08 | ||
|
d1055aa8d8 | ||
|
1dde03dfec | ||
|
5240febeef | ||
|
c6aea54497 | ||
|
15aac97790 | ||
|
68493bd14a | ||
|
41960c0019 | ||
|
eb6c88f8c0 | ||
|
928b6d659c | ||
|
058bf1e6ec | ||
|
7e793438a3 | ||
|
c476bc3772 | ||
|
5a9982b7e0 | ||
|
08279a7565 | ||
|
baac5459c4 | ||
|
4f77005614 | ||
|
ee2d3bc293 | ||
|
8797c5bea4 | ||
|
9d6a2bda24 | ||
|
1cdd690630 | ||
|
692163275d | ||
|
042c7490ed | ||
|
c1aed7b4d3 | ||
|
bebf1d1f3e | ||
|
db7151be53 | ||
|
e160505595 | ||
|
9bfbe80d06 | ||
|
4159b2860f | ||
|
c86adf87ae | ||
|
fb738ae85a | ||
|
0aae94ba7e | ||
|
73c8e1f03a | ||
|
3cd99d8e6d | ||
|
3e6098f5c6 | ||
|
56fc164fdd | ||
|
8f881b04f4 | ||
|
80c9c66e16 | ||
|
5d68026c2d | ||
|
ed8dce4e72 | ||
|
af8151d827 | ||
|
c18c082059 | ||
|
1798ad0ec4 | ||
|
504e43e0da | ||
|
37386b9c3c | ||
|
aa3505a9e4 | ||
|
b85831e389 | ||
|
291c42ed57 | ||
|
f80ea73bfb | ||
|
b3ffc4b327 | ||
|
e84d259ec7 | ||
|
281f9a9f88 | ||
|
055fc69c11 | ||
|
3ff8adf599 | ||
|
da4a4086cf | ||
|
aac1635bf7 | ||
|
38169db765 | ||
|
d0a118f5b1 | ||
|
3fd5bff128 | ||
|
40a45b8c99 | ||
|
f04d4d0291 | ||
|
8b26bab37d | ||
|
7c21347a40 | ||
|
8817af07fb | ||
|
3c7f26d937 | ||
|
1c10988d6a | ||
|
9527cc3fa0 | ||
|
386bfcda2b | ||
|
4da8a18c8c | ||
|
70a82b0784 | ||
|
5f24a3c0c7 | ||
|
475005012a | ||
|
7bf64fc61a | ||
|
dac05a500e | ||
|
b89eb605b7 | ||
|
6b6cd42ce4 | ||
|
6bf662cb9e | ||
|
8de9e2ac84 | ||
|
cd8a7fe472 | ||
|
1a045da0dd | ||
|
00eb65915f | ||
|
f8c781b46c | ||
|
aaa05f901a | ||
|
9dbac989bd | ||
|
408af9d17d | ||
|
88b04eadc9 | ||
|
042f7917e7 | ||
|
81bfc9c7a2 | ||
|
d1b80b462e | ||
|
6191bafcad | ||
|
322c8cf270 | ||
|
957a3e52fe | ||
|
9b24041394 | ||
|
fb3085a2c5 | ||
|
987277de52 | ||
|
88af36dd10 | ||
|
260de1c2b5 | ||
|
485b3b1203 | ||
|
bf1cc1bb84 | ||
|
3e7ee499d6 | ||
|
a075d83752 | ||
|
fcd670e6f7 | ||
|
077627181e | ||
|
b829231992 | ||
|
9aaed75eea | ||
|
88820cd31c | ||
|
16266397ed | ||
|
02c293ec63 | ||
|
e86d23daed | ||
|
0a82cb4072 | ||
|
7a8ac00f9c | ||
|
9e186a42bd | ||
|
c73d79841c | ||
|
11d1a9cc37 | ||
|
9f11753930 | ||
|
fd1930142e | ||
|
a852ebe993 | ||
|
0152d39215 | ||
|
c761aa268d | ||
|
72b83acadc | ||
|
998e50725c | ||
|
a23701b5e6 | ||
|
c1e732448c | ||
|
23ef0d0916 | ||
|
2da92ed81e | ||
|
cb7b96fc90 | ||
|
68df0fb2ea | ||
|
3a7fffc587 | ||
|
48530ccbc0 | ||
|
6b9984b7e7 | ||
|
97a80b4816 | ||
|
8f1593e4e8 | ||
|
7e11b8eb72 | ||
|
c78d565e01 | ||
|
8bdedd2bcf | ||
|
7153cb8a0b | ||
|
9f0d88407d | ||
|
dafdb3edb4 | ||
|
b38ffdec27 | ||
|
23e9f5db43 | ||
|
862419c76f | ||
|
4586f3342f | ||
|
87051fca26 | ||
|
5d8a4917c5 | ||
|
25c5400250 | ||
|
f680d10259 | ||
|
e8ee4cb40d | ||
|
af38bae57f | ||
|
558cbd89fb | ||
|
cb725a4555 | ||
|
6b6f886bcd | ||
|
f7ae70c3d9 | ||
|
f073e37d2f | ||
|
b3503e7853 | ||
|
8f996e4a7c | ||
|
7a28f2c4fa | ||
|
9676364c1f | ||
|
440d966b93 | ||
|
b2a3f53b29 | ||
|
c1c68775b2 | ||
|
804a318189 | ||
|
be5c675263 | ||
|
1e96403954 | ||
|
579fc93c24 | ||
|
7632af3c73 | ||
|
b21fb18379 | ||
|
b10d6542db | ||
|
22cbc05808 | ||
|
977f656e09 | ||
|
525fc3833c | ||
|
bbdb1929c6 | ||
|
13a8948edd | ||
|
e832cee1e6 | ||
|
6eb7d57ed3 | ||
|
5e7ea0664a | ||
|
9df79a4b2d | ||
|
3978b9b8ed | ||
|
b1233056b7 | ||
|
0251b01da6 | ||
|
6ac38aa2c8 | ||
|
907dcdcf7b | ||
|
3f801bc096 | ||
|
f4a01f3a5d | ||
|
03428d9825 | ||
|
f916398a54 | ||
|
8dec3a5ecb | ||
|
7069d99aa6 | ||
|
5ced5c9b27 | ||
|
1317cd12d7 | ||
|
11905a6db6 | ||
|
2c3f641e0b | ||
|
9acf2d3db7 | ||
|
310b12b5ed | ||
|
006d974c58 | ||
|
b89608c624 | ||
|
6f5a68b7f7 | ||
|
c9ed059d91 | ||
|
a428a0cf37 | ||
|
9428917870 | ||
|
19e936362a | ||
|
1d04903c19 | ||
|
b5e7280708 | ||
|
f3f3b752f2 | ||
|
cea5fd56a4 | ||
|
482cc99b2c | ||
|
bc3dccca5c | ||
|
fe13f9dfd1 | ||
|
7486f184c3 | ||
|
6c24dc4e23 | ||
|
c607bee19e | ||
|
adb03ccc6d | ||
|
2133fc84c4 | ||
|
643971c948 | ||
|
038da00e79 | ||
|
ff6dcfea82 | ||
|
464043b8ab | ||
|
2854c19792 | ||
|
75d88dcae2 | ||
|
0ab9bf926d | ||
|
2d10fa7867 | ||
|
8c29c4f620 | ||
|
3f67215df9 | ||
|
c4ffe630f1 | ||
|
7c5e3cac6a | ||
|
adc89f7977 | ||
|
db612c10ee | ||
|
0090446ccf | ||
|
cae7ec1eb4 | ||
|
bce1078ced | ||
|
aa2fdc6ef6 | ||
|
2690585e99 | ||
|
760242c076 | ||
|
c7059c4981 | ||
|
d1cbb4bd8a | ||
|
0e439b2fa3 | ||
|
3132efcc01 | ||
|
ab8dfb45b4 | ||
|
59601eb922 | ||
|
023a1c2427 | ||
|
8bf1609ccc | ||
|
c8ee755c05 | ||
|
df1d215f48 | ||
|
bcc56803d7 | ||
|
4fbcc33ee0 | ||
|
ec778508df |
1878 changed files with 459420 additions and 228439 deletions
|
@ -1,33 +0,0 @@
|
||||||
BasedOnStyle: LLVM
|
|
||||||
IndentWidth: 4
|
|
||||||
UseTab: Always
|
|
||||||
TabWidth: 4
|
|
||||||
BreakBeforeBraces: Custom
|
|
||||||
Standard: Cpp11
|
|
||||||
BraceWrapping:
|
|
||||||
AfterClass: true
|
|
||||||
AfterControlStatement: false
|
|
||||||
AfterEnum: true
|
|
||||||
AfterFunction: true
|
|
||||||
AfterNamespace: true
|
|
||||||
AfterStruct: true
|
|
||||||
AfterUnion: true
|
|
||||||
BeforeCatch: false
|
|
||||||
BeforeElse: false
|
|
||||||
FixNamespaceComments: false
|
|
||||||
AllowShortIfStatementsOnASingleLine: false
|
|
||||||
IndentCaseLabels: false
|
|
||||||
AccessModifierOffset: -4
|
|
||||||
ColumnLimit: 90
|
|
||||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
|
||||||
SortIncludes: false
|
|
||||||
IncludeCategories:
|
|
||||||
- Regex: '^".*'
|
|
||||||
Priority: 2
|
|
||||||
- Regex: '^<.*'
|
|
||||||
Priority: 1
|
|
||||||
AlignAfterOpenBracket: DontAlign
|
|
||||||
ContinuationIndentWidth: 8
|
|
||||||
ConstructorInitializerIndentWidth: 8
|
|
||||||
BreakConstructorInitializers: AfterColon
|
|
||||||
AlwaysBreakTemplateDeclarations: Yes
|
|
|
@ -1,4 +1,4 @@
|
||||||
Checks: '-*,modernize-use-emplace,modernize-avoid-bind,misc-throw-by-value-catch-by-reference,misc-unconventional-assign-operator,performance-*'
|
Checks: '-*,modernize-use-emplace,modernize-avoid-bind,misc-throw-by-value-catch-by-reference,misc-unconventional-assign-operator,performance-*,-performance-avoid-endl,performance-inefficient-string-concatenation'
|
||||||
WarningsAsErrors: '-*,modernize-use-emplace,performance-type-promotion-in-math-fn,performance-faster-string-find,performance-implicit-cast-in-loop'
|
WarningsAsErrors: '-*,modernize-use-emplace,performance-type-promotion-in-math-fn,performance-faster-string-find,performance-implicit-cast-in-loop'
|
||||||
CheckOptions:
|
CheckOptions:
|
||||||
- key: performance-unnecessary-value-param.AllowedTypes
|
- key: performance-unnecessary-value-param.AllowedTypes
|
||||||
|
|
11
.editorconfig
Executable file → Normal file
11
.editorconfig
Executable file → Normal file
|
@ -1,9 +1,16 @@
|
||||||
[*]
|
[*]
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
|
|
||||||
[*.{cpp,h,lua,txt,glsl,md,c,cmake,java,gradle}]
|
[*.{cpp,h,lua,txt,glsl,c,cmake,java,gradle}]
|
||||||
charset = utf8
|
charset = utf-8
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
charset = utf-8
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
6
.gitattributes
vendored
6
.gitattributes
vendored
|
@ -1,2 +1,8 @@
|
||||||
|
# Forces all files which git considers text files to use LF line endings
|
||||||
|
* text=auto eol=lf
|
||||||
|
|
||||||
*.cpp diff=cpp
|
*.cpp diff=cpp
|
||||||
*.h diff=cpp
|
*.h diff=cpp
|
||||||
|
|
||||||
|
*.gltf binary
|
||||||
|
*.x binary
|
||||||
|
|
69
.github/CONTRIBUTING.md
vendored
69
.github/CONTRIBUTING.md
vendored
|
@ -14,28 +14,31 @@ Contributions are welcome! Here's how you can help:
|
||||||
[clone](https://help.github.com/articles/cloning-a-repository/) your fork.
|
[clone](https://help.github.com/articles/cloning-a-repository/) your fork.
|
||||||
|
|
||||||
2. Before you start coding, consider opening an
|
2. Before you start coding, consider opening an
|
||||||
[issue at Github](https://github.com/minetest/minetest/issues) to discuss the
|
[issue on Github](https://github.com/luanti-org/luanti/issues) to discuss the
|
||||||
suitability and implementation of your intended contribution with the core
|
suitability and implementation of your intended contribution with the core
|
||||||
developers.
|
developers.
|
||||||
|
|
||||||
Any Pull Request that isn't a bug fix and isn't covered by
|
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
|
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
|
recommended that you open an issue for any such pull requests before doing
|
||||||
the work, to avoid disappointment.
|
the work, to avoid disappointment.
|
||||||
|
|
||||||
You may also benefit from discussing on our IRC development channel
|
You may also benefit from discussing on our IRC development channel
|
||||||
[#minetest-dev](http://www.minetest.net/irc/). Note that a proper IRC client
|
[#luanti-dev](http://www.luanti.org/irc/). Note that a proper IRC client
|
||||||
is required to speak on this channel.
|
is required to speak on this channel.
|
||||||
|
|
||||||
3. Start coding!
|
3. Start coding!
|
||||||
- Refer to the
|
- Refer to the
|
||||||
[Lua API](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt),
|
[Lua API](https://github.com/luanti-org/luanti/blob/master/doc/lua_api.md),
|
||||||
[Developer Wiki](http://dev.minetest.net/Main_Page) and other
|
[Developer Wiki](https://dev.luanti.org/) and other
|
||||||
[documentation](https://github.com/minetest/minetest/tree/master/doc).
|
[documentation](https://github.com/luanti-org/luanti/tree/master/doc).
|
||||||
- Follow the [C/C++](http://dev.minetest.net/Code_style_guidelines) and
|
- Follow the [C/C++](https://dev.luanti.org/Code_style_guidelines) and
|
||||||
[Lua](http://dev.minetest.net/Lua_code_style_guidelines) code style guidelines.
|
[Lua](https://dev.luanti.org/Lua_code_style_guidelines) code style guidelines.
|
||||||
- Check your code works as expected and document any changes to the Lua API.
|
- Check your code works as expected and document any changes to the Lua API.
|
||||||
|
- To avoid conflicting changes between contributions, do not do the following manually. They will be done before each release.
|
||||||
|
- Run `updatepo.sh` or update `luanti.po{,t}` even if your code adds new translatable strings.
|
||||||
|
- Update `minetest.conf.example` and `settings_translation_file.cpp` even if your code adds new core settings.
|
||||||
|
|
||||||
4. Commit & [push](https://help.github.com/articles/pushing-to-a-remote/) your changes to a new branch (not `master`, one change per branch)
|
4. Commit & [push](https://help.github.com/articles/pushing-to-a-remote/) your changes to a new branch (not `master`, one change per branch)
|
||||||
- Commit messages should:
|
- Commit messages should:
|
||||||
|
@ -50,7 +53,7 @@ Contributions are welcome! Here's how you can help:
|
||||||
- The following lines should describe the commit, starting a new line for each point.
|
- The following lines should describe the commit, starting a new line for each point.
|
||||||
|
|
||||||
5. Once you are happy with your changes, submit a pull request.
|
5. Once you are happy with your changes, submit a pull request.
|
||||||
- Open the [pull-request form](https://github.com/minetest/minetest/pull/new/master).
|
- Open the [pull-request form](https://github.com/luanti-org/luanti/pull/new/master).
|
||||||
- Add a description explaining what you've done (or if it's a
|
- Add a description explaining what you've done (or if it's a
|
||||||
work-in-progress - what you need to do).
|
work-in-progress - what you need to do).
|
||||||
- Make sure to fill out the pull request template.
|
- Make sure to fill out the pull request template.
|
||||||
|
@ -61,40 +64,26 @@ Contributions are welcome! Here's how you can help:
|
||||||
picture of the project.
|
picture of the project.
|
||||||
2. It works.
|
2. It works.
|
||||||
3. It follows the code style for
|
3. It follows the code style for
|
||||||
[C/C++](http://dev.minetest.net/Code_style_guidelines) or
|
[C/C++](https://dev.luanti.org/Code_style_guidelines) or
|
||||||
[Lua](http://dev.minetest.net/Lua_code_style_guidelines).
|
[Lua](https://dev.luanti.org/Lua_code_style_guidelines).
|
||||||
4. The code's interfaces are well designed, regardless of other aspects that
|
4. The code's interfaces are well designed, regardless of other aspects that
|
||||||
might need more work in the future.
|
might need more work in the future.
|
||||||
5. It uses protocols and formats which include the required compatibility.
|
5. It uses protocols and formats which include the required compatibility.
|
||||||
|
|
||||||
### Important note about automated GitHub checks
|
|
||||||
|
|
||||||
When you submit a pull request, GitHub automatically runs checks on the Minetest
|
|
||||||
Engine combined with your changes. One of these checks is called 'cpp lint /
|
|
||||||
clang format', which checks code formatting. Because formatting for readability
|
|
||||||
requires human judgement this check often fails and often makes unsuitable
|
|
||||||
formatting requests which make code readability worse.
|
|
||||||
|
|
||||||
If this check fails, look at the details to check for any clear mistakes and
|
|
||||||
correct those. However, you should not apply everything ClangFormat requests.
|
|
||||||
Ignore requests that make code readability worse and any other clearly
|
|
||||||
unsuitable requests. Discuss in the pull request with a core developer about how
|
|
||||||
to progress.
|
|
||||||
|
|
||||||
## Issues
|
## Issues
|
||||||
|
|
||||||
If you experience an issue, we would like to know the details - especially when
|
If you experience an issue, we would like to know the details - especially when
|
||||||
a stable release is on the way.
|
a stable release is on the way.
|
||||||
|
|
||||||
1. Do a quick search on GitHub to check if the issue has already been reported.
|
1. Do a quick search on GitHub to check if the issue has already been reported.
|
||||||
2. Is it an issue with the Minetest *engine*? If not, report it
|
2. Is it an issue with the Luanti *engine*? If not, report it
|
||||||
[elsewhere](http://www.minetest.net/development/#reporting-issues).
|
[elsewhere](http://www.luanti.org/development/#reporting-issues).
|
||||||
3. [Open an issue](https://github.com/minetest/minetest/issues/new) and describe
|
3. [Open an issue](https://github.com/luanti-org/luanti/issues/new) and describe
|
||||||
the issue you are having - you could include:
|
the issue you are having - you could include:
|
||||||
- Error logs (check the bottom of the `debug.txt` file).
|
- Error logs (check the bottom of the `debug.txt` file).
|
||||||
- Screenshots.
|
- Screenshots.
|
||||||
- Ways you have tried to solve the issue, and whether they worked or not.
|
- Ways you have tried to solve the issue, and whether they worked or not.
|
||||||
- Your Minetest version and the content (games, mods or texture packs) you have installed.
|
- Your Luanti version and the content (games, mods or texture packs) you have installed.
|
||||||
- Your platform (e.g. Windows 10 or Ubuntu 15.04 x64).
|
- Your platform (e.g. Windows 10 or Ubuntu 15.04 x64).
|
||||||
|
|
||||||
After reporting you should aim to answer questions or clarifications as this
|
After reporting you should aim to answer questions or clarifications as this
|
||||||
|
@ -110,35 +99,35 @@ possible.
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
The core translations of Minetest are performed using Weblate. You can access
|
The core translations of Luanti are performed using Weblate. You can access
|
||||||
the project page with a list of current languages
|
the project page with a list of current languages
|
||||||
[here](https://hosted.weblate.org/projects/minetest/minetest/).
|
[here](https://hosted.weblate.org/projects/minetest/minetest/).
|
||||||
|
|
||||||
Builtin (the component which contains things like server messages, chat command
|
Builtin (the component which contains things like server messages, chat command
|
||||||
descriptions, privilege descriptions) is translated separately; it needs to be
|
descriptions, privilege descriptions) is translated separately; it needs to be
|
||||||
translated by editing a `.tr` text file. See
|
translated by editing a `.tr` text file. See
|
||||||
[Translation](https://dev.minetest.net/Translation) for more information.
|
[Translation](https://dev.luanti.org/Translation) for more information.
|
||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
If you'd like to monetarily support Minetest development, you can find donation
|
If you'd like to monetarily support Luanti development, you can find donation
|
||||||
methods on [our website](http://www.minetest.net/development/#donate).
|
methods on [our website](http://www.luanti.org/development/#donate).
|
||||||
|
|
||||||
# Maintaining
|
# Maintaining
|
||||||
|
|
||||||
* This is a concise version of the
|
* This is a concise version of the
|
||||||
[Rules & Guidelines](http://dev.minetest.net/Category:Rules_and_Guidelines) on the developer wiki.*
|
[Rules & Guidelines](https://dev.luanti.org/engine-dev-process/) on the developer wiki.*
|
||||||
|
|
||||||
These notes are for those who have push access Minetest (core developers / maintainers).
|
These notes are for those who have push access Luanti (core developers / maintainers).
|
||||||
|
|
||||||
- See the [project organisation](http://dev.minetest.net/Organisation) for the people involved.
|
- See the [project organisation](https://dev.luanti.org/Organisation) for the people involved.
|
||||||
|
|
||||||
## Concept approvals and roadmaps
|
## Concept approvals and roadmaps
|
||||||
|
|
||||||
If a Pull Request is not a bug fix:
|
If a Pull Request is not a bug fix:
|
||||||
|
|
||||||
* If it matches a goal in [the roadmap](../doc/direction.md), then the PR should
|
* If it matches a goal in [the roadmap](../doc/direction.md), then the PR should
|
||||||
be labelled as "Roadmap" and the goal stated by number in the description.
|
be labeled as "Roadmap" and the goal stated by number in the description.
|
||||||
* If it doesn't match a goal, then it needs to receive a concept approval within
|
* If it doesn't match a goal, then it needs to receive a concept approval within
|
||||||
a week of being opened to remain open. This 1 week deadline does not apply to
|
a week of being opened to remain open. This 1 week deadline does not apply to
|
||||||
PRs opened before the roadmap was adopted; instead, they may remain open or be
|
PRs opened before the roadmap was adopted; instead, they may remain open or be
|
||||||
|
@ -170,14 +159,14 @@ Submit a :+1: (+1) or "Looks good" comment to show you believe the pull-request
|
||||||
- The title should follow the commit guidelines (title starts with a capital letter, present tense, descriptive).
|
- The title should follow the commit guidelines (title starts with a capital letter, present tense, descriptive).
|
||||||
- Don't modify history older than 10 minutes.
|
- Don't modify history older than 10 minutes.
|
||||||
- Use rebase, not merge to get linear history:
|
- Use rebase, not merge to get linear history:
|
||||||
- `curl https://github.com/minetest/minetest/pull/1.patch | git am`
|
- `curl -Ls https://github.com/luanti-org/luanti/pull/1.patch | git am`
|
||||||
|
|
||||||
## Reviewing issues and feature requests
|
## Reviewing issues and feature requests
|
||||||
|
|
||||||
- If an issue does not get a response from its author within 1 month (when requiring more details), it can be closed.
|
- If an issue does not get a response from its author within 1 month (when requiring more details), it can be closed.
|
||||||
- When an issue is a duplicate, refer to the first ones and close the later ones.
|
- When an issue is a duplicate, refer to the first ones and close the later ones.
|
||||||
- Tag issues with the appropriate [labels](https://github.com/minetest/minetest/labels) for devices, platforms etc.
|
- Tag issues with the appropriate [labels](https://github.com/luanti-org/luanti/labels) for devices, platforms etc.
|
||||||
|
|
||||||
## Releasing a new version
|
## Releasing a new version
|
||||||
|
|
||||||
*Refer to [dev.minetest.net/Releasing_Minetest](http://dev.minetest.net/Releasing_Minetest)*
|
*Refer to [dev.luanti.org/Releasing_Luanti](https://dev.luanti.org/Releasing_Luanti)*
|
||||||
|
|
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 -->
|
|
79
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
79
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
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 Luanti 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.luanti.org/packages/?type=game) and submit a bug report in their issue trackers.
|
||||||
|
* For example, you can submit issues about the Minetest Game [in its own repository](https://github.com/luanti-org/minetest_game/issues).
|
||||||
|
3. Please provide as many details as possible for us to spot the problem quicker.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Luanti version
|
||||||
|
description: |
|
||||||
|
Paste the Luanti version below.
|
||||||
|
If you are on a dev version, please also indicate the git commit hash.
|
||||||
|
Refer to the "About" tab of the menu or run `luanti --version` on the command line.
|
||||||
|
placeholder: |
|
||||||
|
Example:
|
||||||
|
Luanti 5.10.0-3ad6aee9b (Linux)
|
||||||
|
Using LuaJIT 2.1.1727870382
|
||||||
|
Built by GCC 14.2
|
||||||
|
Running on Linux/6.11.5 x86_64
|
||||||
|
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: Operating system and version
|
||||||
|
description: It is recommended to upgrade your operating system to see if the problem persists.
|
||||||
|
placeholder: "Example: Ubuntu 22.04"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: CPU model
|
||||||
|
description: Usually found in OS/system settings.
|
||||||
|
placeholder: "Example: Intel Core i5-2410M"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: The GPU model and renderer can be omitted if the bug is not a graphical issue.
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: GPU model
|
||||||
|
description: Usually found in OS/system settings.
|
||||||
|
placeholder: "Example: NVIDIA GeForce GTX 1660"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Active renderer
|
||||||
|
description: You can find this in the "About" tab in the main menu.
|
||||||
|
placeholder: "Example: ES 3.2 / ogles2 / X11"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Summary
|
||||||
|
description: Describe your problem here.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: Explain how the problem has happened, providing a minimal test (e.g. a minimized code snippet) where possible.
|
||||||
|
validations:
|
||||||
|
required: true
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
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/luanti-org/minetest_game/issues/new
|
||||||
|
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.luanti.org/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.luanti.org/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
|
- Goal of the PR
|
||||||
- How does the PR work?
|
- How does the PR work?
|
||||||
- Does it resolve any reported issue?
|
- 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/luanti-org/luanti/blob/master/doc/direction.md)?
|
||||||
- If not a bug fix, why is this PR needed? What usecases does it solve?
|
- If not a bug fix, why is this PR needed? What usecases does it solve?
|
||||||
|
|
||||||
## To do
|
## To do
|
||||||
|
|
6
.github/SECURITY.md
vendored
6
.github/SECURITY.md
vendored
|
@ -3,7 +3,7 @@
|
||||||
## Supported Versions
|
## Supported Versions
|
||||||
|
|
||||||
We only support the latest stable version for security issues.
|
We only support the latest stable version for security issues.
|
||||||
See the [releases page](https://github.com/minetest/minetest/releases).
|
See the [releases page](https://github.com/luanti-org/luanti/releases).
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
@ -11,10 +11,10 @@ We ask that you report vulnerabilities privately, by contacting a core developer
|
||||||
to give us time to fix them. You can do that by emailing one of the following addresses:
|
to give us time to fix them. You can do that by emailing one of the following addresses:
|
||||||
|
|
||||||
* celeron55@gmail.com
|
* celeron55@gmail.com
|
||||||
* rubenwardy@minetest.net
|
* rw@rubenwardy.com
|
||||||
|
|
||||||
Depending on severity, we will either create a private issue for the vulnerability
|
Depending on severity, we will either create a private issue for the vulnerability
|
||||||
and release a patch version of Minetest, or give you permission to file the issue publicly.
|
and release a patch version of Luanti, or give you permission to file the issue publicly.
|
||||||
|
|
||||||
For more information on the justification of this policy, see
|
For more information on the justification of this policy, see
|
||||||
[Responsible Disclosure](https://en.wikipedia.org/wiki/Responsible_disclosure).
|
[Responsible Disclosure](https://en.wikipedia.org/wiki/Responsible_disclosure).
|
||||||
|
|
54
.github/workflows/android.yml
vendored
54
.github/workflows/android.yml
vendored
|
@ -8,6 +8,11 @@ on:
|
||||||
- 'lib/**.cpp'
|
- 'lib/**.cpp'
|
||||||
- 'src/**.[ch]'
|
- 'src/**.[ch]'
|
||||||
- 'src/**.cpp'
|
- 'src/**.cpp'
|
||||||
|
- 'irr/**.[ch]'
|
||||||
|
- 'irr/**.cpp'
|
||||||
|
- '**/CMakeLists.txt'
|
||||||
|
- 'cmake/Modules/**'
|
||||||
|
- 'po/**.po'
|
||||||
- 'android/**'
|
- 'android/**'
|
||||||
- '.github/workflows/android.yml'
|
- '.github/workflows/android.yml'
|
||||||
pull_request:
|
pull_request:
|
||||||
|
@ -16,27 +21,56 @@ on:
|
||||||
- 'lib/**.cpp'
|
- 'lib/**.cpp'
|
||||||
- 'src/**.[ch]'
|
- 'src/**.[ch]'
|
||||||
- 'src/**.cpp'
|
- 'src/**.cpp'
|
||||||
|
- 'irr/**.[ch]'
|
||||||
|
- 'irr/**.cpp'
|
||||||
|
- '**/CMakeLists.txt'
|
||||||
|
- 'cmake/Modules/**'
|
||||||
|
- 'po/**.po'
|
||||||
- 'android/**'
|
- 'android/**'
|
||||||
- '.github/workflows/android.yml'
|
- '.github/workflows/android.yml'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y --no-install-recommends gettext openjdk-11-jdk-headless
|
sudo apt-get install -y --no-install-recommends gettext
|
||||||
- name: Build with Gradle
|
- name: Set up JDK 17
|
||||||
run: cd android; ./gradlew assemblerelease
|
uses: actions/setup-java@v4
|
||||||
- name: Save armeabi artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
with:
|
||||||
name: Minetest-armeabi-v7a.apk
|
distribution: 'temurin'
|
||||||
|
java-version: '17'
|
||||||
|
- 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: Luanti-release.aab
|
||||||
|
path: android/app/build/outputs/bundle/release/app-release.aab
|
||||||
|
- name: Save armeabi artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Luanti-armeabi-v7a.apk
|
||||||
path: android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk
|
path: android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk
|
||||||
- name: Save arm64 artifact
|
- name: Save arm64 artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Minetest-arm64-v8a.apk
|
name: Luanti-arm64-v8a.apk
|
||||||
path: android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
|
path: android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
|
||||||
|
- name: Save x86 artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Luanti-x86.apk
|
||||||
|
path: android/app/build/outputs/apk/release/app-x86-release-unsigned.apk
|
||||||
|
- name: Save x86_64 artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Luanti-x86_64.apk
|
||||||
|
path: android/app/build/outputs/apk/release/app-x86_64-release-unsigned.apk
|
||||||
|
|
272
.github/workflows/build.yml
vendored
272
.github/workflows/build.yml
vendored
|
@ -1,272 +0,0 @@
|
||||||
name: build
|
|
||||||
|
|
||||||
# build on c/cpp changes or workflow changes
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
paths:
|
|
||||||
- 'lib/**.[ch]'
|
|
||||||
- 'lib/**.cpp'
|
|
||||||
- 'src/**.[ch]'
|
|
||||||
- 'src/**.cpp'
|
|
||||||
- '**/CMakeLists.txt'
|
|
||||||
- 'cmake/Modules/**'
|
|
||||||
- 'util/buildbot/**'
|
|
||||||
- 'util/ci/**'
|
|
||||||
- '.github/workflows/**.yml'
|
|
||||||
- 'Dockerfile'
|
|
||||||
- '.dockerignore'
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- 'lib/**.[ch]'
|
|
||||||
- 'lib/**.cpp'
|
|
||||||
- 'src/**.[ch]'
|
|
||||||
- 'src/**.cpp'
|
|
||||||
- '**/CMakeLists.txt'
|
|
||||||
- 'cmake/Modules/**'
|
|
||||||
- 'util/buildbot/**'
|
|
||||||
- 'util/ci/**'
|
|
||||||
- '.github/workflows/**.yml'
|
|
||||||
- 'Dockerfile'
|
|
||||||
- '.dockerignore'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# Older gcc version (should be close to our minimum supported version)
|
|
||||||
gcc_5:
|
|
||||||
runs-on: ubuntu-18.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install deps
|
|
||||||
run: |
|
|
||||||
source ./util/ci/common.sh
|
|
||||||
install_linux_deps g++-5
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
./util/ci/build.sh
|
|
||||||
env:
|
|
||||||
CC: gcc-5
|
|
||||||
CXX: g++-5
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: |
|
|
||||||
./bin/minetest --run-unittests
|
|
||||||
|
|
||||||
# Current gcc version
|
|
||||||
gcc_10:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install deps
|
|
||||||
run: |
|
|
||||||
source ./util/ci/common.sh
|
|
||||||
install_linux_deps g++-10
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
./util/ci/build.sh
|
|
||||||
env:
|
|
||||||
CC: gcc-10
|
|
||||||
CXX: g++-10
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: |
|
|
||||||
./bin/minetest --run-unittests
|
|
||||||
|
|
||||||
# Older clang version (should be close to our minimum supported version)
|
|
||||||
clang_3_9:
|
|
||||||
runs-on: ubuntu-18.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install deps
|
|
||||||
run: |
|
|
||||||
source ./util/ci/common.sh
|
|
||||||
install_linux_deps clang-3.9 gdb
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
./util/ci/build.sh
|
|
||||||
env:
|
|
||||||
CC: clang-3.9
|
|
||||||
CXX: clang++-3.9
|
|
||||||
|
|
||||||
- name: Unittest
|
|
||||||
run: |
|
|
||||||
./bin/minetest --run-unittests
|
|
||||||
|
|
||||||
- name: Integration test + devtest
|
|
||||||
run: |
|
|
||||||
./util/test_multiplayer.sh
|
|
||||||
|
|
||||||
# Current clang version
|
|
||||||
clang_10:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install deps
|
|
||||||
run: |
|
|
||||||
source ./util/ci/common.sh
|
|
||||||
install_linux_deps clang-10 valgrind libluajit-5.1-dev
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
./util/ci/build.sh
|
|
||||||
env:
|
|
||||||
CC: clang-10
|
|
||||||
CXX: clang++-10
|
|
||||||
CMAKE_FLAGS: "-DREQUIRE_LUAJIT=1"
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
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
|
|
||||||
|
|
||||||
# Build with prometheus-cpp (server-only)
|
|
||||||
clang_9_prometheus:
|
|
||||||
name: "clang_9 (PROMETHEUS=1)"
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install deps
|
|
||||||
run: |
|
|
||||||
source ./util/ci/common.sh
|
|
||||||
install_linux_deps clang-9
|
|
||||||
|
|
||||||
- name: Build prometheus-cpp
|
|
||||||
run: |
|
|
||||||
./util/ci/build_prometheus_cpp.sh
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
./util/ci/build.sh
|
|
||||||
env:
|
|
||||||
CC: clang-9
|
|
||||||
CXX: clang++-9
|
|
||||||
CMAKE_FLAGS: "-DENABLE_PROMETHEUS=1 -DBUILD_CLIENT=0"
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: |
|
|
||||||
./bin/minetestserver --run-unittests
|
|
||||||
|
|
||||||
docker:
|
|
||||||
name: "Docker image"
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Build docker image
|
|
||||||
run: |
|
|
||||||
docker build . -t minetest:latest
|
|
||||||
docker run --rm minetest:latest /usr/local/bin/minetestserver --version
|
|
||||||
|
|
||||||
win32:
|
|
||||||
name: "MinGW cross-compiler (32-bit)"
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install compiler
|
|
||||||
run: |
|
|
||||||
sudo apt-get update && sudo apt-get install -y gettext
|
|
||||||
wget http://minetest.kitsunemimi.pw/mingw-w64-i686_11.2.0_ubuntu20.04.tar.xz -O mingw.tar.xz
|
|
||||||
sudo tar -xaf mingw.tar.xz -C /usr
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh winbuild
|
|
||||||
env:
|
|
||||||
NO_MINETEST_GAME: 1
|
|
||||||
NO_PACKAGE: 1
|
|
||||||
|
|
||||||
win64:
|
|
||||||
name: "MinGW cross-compiler (64-bit)"
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install compiler
|
|
||||||
run: |
|
|
||||||
sudo apt-get update && sudo apt-get install -y gettext
|
|
||||||
wget http://minetest.kitsunemimi.pw/mingw-w64-x86_64_11.2.0_ubuntu20.04.tar.xz -O mingw.tar.xz
|
|
||||||
sudo tar -xaf mingw.tar.xz -C /usr
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh winbuild
|
|
||||||
env:
|
|
||||||
NO_MINETEST_GAME: 1
|
|
||||||
NO_PACKAGE: 1
|
|
||||||
|
|
||||||
msvc:
|
|
||||||
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
|
|
||||||
runs-on: windows-2019
|
|
||||||
env:
|
|
||||||
VCPKG_VERSION: 5cf60186a241e84e8232641ee973395d4fde90e1
|
|
||||||
# 2022.02
|
|
||||||
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
config:
|
|
||||||
- {
|
|
||||||
arch: x86,
|
|
||||||
generator: "-G'Visual Studio 16 2019' -A Win32",
|
|
||||||
vcpkg_triplet: x86-windows
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
arch: x64,
|
|
||||||
generator: "-G'Visual Studio 16 2019' -A x64",
|
|
||||||
vcpkg_triplet: x64-windows
|
|
||||||
}
|
|
||||||
type: [portable]
|
|
||||||
# type: [portable, installer]
|
|
||||||
# The installer type is working, but disabled, to save runner jobs.
|
|
||||||
# Enable it, when working on the installer.
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Checkout IrrlichtMt
|
|
||||||
run: |
|
|
||||||
$ref = @(Get-Content misc\irrlichtmt_tag.txt)
|
|
||||||
git clone https://github.com/minetest/irrlicht lib\irrlichtmt --depth 1 -b $ref[0]
|
|
||||||
|
|
||||||
- name: Restore from cache and run vcpkg
|
|
||||||
uses: lukka/run-vcpkg@v7
|
|
||||||
with:
|
|
||||||
vcpkgArguments: ${{env.vcpkg_packages}}
|
|
||||||
vcpkgDirectory: '${{ github.workspace }}\vcpkg'
|
|
||||||
appendedCacheKey: ${{ matrix.config.vcpkg_triplet }}
|
|
||||||
vcpkgGitCommitId: ${{ env.VCPKG_VERSION }}
|
|
||||||
vcpkgTriplet: ${{ matrix.config.vcpkg_triplet }}
|
|
||||||
|
|
||||||
- name: Minetest CMake
|
|
||||||
run: |
|
|
||||||
cmake ${{matrix.config.generator}} `
|
|
||||||
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
|
|
||||||
-DCMAKE_BUILD_TYPE=Release `
|
|
||||||
-DENABLE_POSTGRESQL=OFF `
|
|
||||||
-DRUN_IN_PLACE=${{ contains(matrix.type, 'portable') }} .
|
|
||||||
|
|
||||||
- name: Build Minetest
|
|
||||||
run: cmake --build . --config Release
|
|
||||||
|
|
||||||
- name: CPack
|
|
||||||
run: |
|
|
||||||
If ($env:TYPE -eq "installer")
|
|
||||||
{
|
|
||||||
cpack -G WIX -B "$env:GITHUB_WORKSPACE\Package"
|
|
||||||
}
|
|
||||||
ElseIf($env:TYPE -eq "portable")
|
|
||||||
{
|
|
||||||
cpack -G ZIP -B "$env:GITHUB_WORKSPACE\Package"
|
|
||||||
}
|
|
||||||
env:
|
|
||||||
TYPE: ${{matrix.type}}
|
|
||||||
|
|
||||||
- name: Package Clean
|
|
||||||
run: rm -r $env:GITHUB_WORKSPACE\Package\_CPack_Packages
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: msvc-${{ matrix.config.arch }}-${{ matrix.type }}
|
|
||||||
path: .\Package\
|
|
30
.github/workflows/cpp_lint.yml
vendored
30
.github/workflows/cpp_lint.yml
vendored
|
@ -8,6 +8,8 @@ on:
|
||||||
- 'lib/**.cpp'
|
- 'lib/**.cpp'
|
||||||
- 'src/**.[ch]'
|
- 'src/**.[ch]'
|
||||||
- 'src/**.cpp'
|
- 'src/**.cpp'
|
||||||
|
- 'irr/**.[ch]'
|
||||||
|
- 'irr/**.cpp'
|
||||||
- '**/CMakeLists.txt'
|
- '**/CMakeLists.txt'
|
||||||
- 'cmake/Modules/**'
|
- 'cmake/Modules/**'
|
||||||
- 'util/ci/**'
|
- 'util/ci/**'
|
||||||
|
@ -18,37 +20,25 @@ on:
|
||||||
- 'lib/**.cpp'
|
- 'lib/**.cpp'
|
||||||
- 'src/**.[ch]'
|
- 'src/**.[ch]'
|
||||||
- 'src/**.cpp'
|
- 'src/**.cpp'
|
||||||
|
- 'irr/**.[ch]'
|
||||||
|
- 'irr/**.cpp'
|
||||||
- '**/CMakeLists.txt'
|
- '**/CMakeLists.txt'
|
||||||
- 'cmake/Modules/**'
|
- 'cmake/Modules/**'
|
||||||
- 'util/ci/**'
|
- 'util/ci/**'
|
||||||
- '.github/workflows/**.yml'
|
- '.github/workflows/**.yml'
|
||||||
|
|
||||||
|
env:
|
||||||
|
CLANG_TIDY: clang-tidy-15
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
# clang_format:
|
|
||||||
# runs-on: ubuntu-20.04
|
|
||||||
# steps:
|
|
||||||
# - uses: actions/checkout@v3
|
|
||||||
# - name: Install clang-format
|
|
||||||
# run: |
|
|
||||||
# sudo apt-get update
|
|
||||||
# sudo apt-get install -y clang-format-9
|
|
||||||
#
|
|
||||||
# - name: Run clang-format
|
|
||||||
# run: |
|
|
||||||
# source ./util/ci/clang-format.sh
|
|
||||||
# check_format
|
|
||||||
# env:
|
|
||||||
# CLANG_FORMAT: clang-format-9
|
|
||||||
|
|
||||||
clang_tidy:
|
clang_tidy:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
source ./util/ci/common.sh
|
source ./util/ci/common.sh
|
||||||
install_linux_deps clang-tidy-9
|
install_linux_deps $CLANG_TIDY
|
||||||
|
|
||||||
- name: Run clang-tidy
|
- name: Run clang-tidy
|
||||||
run: |
|
run: |
|
||||||
|
|
98
.github/workflows/docker_image.yml
vendored
Normal file
98
.github/workflows/docker_image.yml
vendored
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
---
|
||||||
|
name: docker_image
|
||||||
|
|
||||||
|
# https://docs.github.com/en/actions/publishing-packages/publishing-docker-images
|
||||||
|
# https://docs.docker.com/build/ci/github-actions/multi-platform
|
||||||
|
# https://github.com/opencontainers/image-spec/blob/main/annotations.md
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master" ]
|
||||||
|
# Publish semver tags as releases.
|
||||||
|
tags: [ "*" ]
|
||||||
|
pull_request:
|
||||||
|
# Build docker image on pull requests. (but do not publish)
|
||||||
|
paths:
|
||||||
|
- 'lib/**.[ch]'
|
||||||
|
- 'lib/**.cpp'
|
||||||
|
- 'src/**.[ch]'
|
||||||
|
- 'src/**.cpp'
|
||||||
|
- 'irr/**.[ch]'
|
||||||
|
- 'irr/**.cpp'
|
||||||
|
- '**/CMakeLists.txt'
|
||||||
|
- 'cmake/Modules/**'
|
||||||
|
- 'util/ci/**'
|
||||||
|
- 'misc/irrlichtmt_tag.txt'
|
||||||
|
- 'Dockerfile'
|
||||||
|
- '.dockerignore'
|
||||||
|
- '.github/workflows/docker_image.yml'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
use_cache:
|
||||||
|
description: "Use build cache"
|
||||||
|
required: true
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
# github.repository as <account>/<repo>
|
||||||
|
IMAGE_NAME: ${{ github.repository }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Docker buildx
|
||||||
|
uses: docker/setup-buildx-action@v3.0.0
|
||||||
|
|
||||||
|
# Login against the Docker registry except on PR
|
||||||
|
# https://github.com/docker/login-action
|
||||||
|
- name: Log into registry ${{ env.REGISTRY }}
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: docker/login-action@v3.0.0
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
# Extract metadata (tags, labels) for Docker
|
||||||
|
# https://github.com/docker/metadata-action
|
||||||
|
- name: Extract Docker metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5.5.0
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
labels: |
|
||||||
|
org.opencontainers.image.title=Luanti
|
||||||
|
org.opencontainers.image.vendor=Luanti
|
||||||
|
org.opencontainers.image.licenses=LGPL-2.1-only
|
||||||
|
|
||||||
|
# Build and push Docker image
|
||||||
|
# https://github.com/docker/build-push-action
|
||||||
|
# No arm support for now. Require cross-compilation support in Dockerfile to not use QEMU.
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v5.1.0
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
load: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
no-cache: ${{ (github.event_name == 'workflow_dispatch' && !inputs.use_cache) || startsWith(github.ref, 'refs/tags/') }}
|
||||||
|
|
||||||
|
- name: Test Docker Image
|
||||||
|
run: |
|
||||||
|
docker run --rm $(cut -d, -f1 <<<"$DOCKER_METADATA_OUTPUT_TAGS") luantiserver --version
|
||||||
|
shell: bash
|
159
.github/workflows/linux.yml
vendored
Normal file
159
.github/workflows/linux.yml
vendored
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
name: linux
|
||||||
|
|
||||||
|
# build on c/cpp changes or workflow changes
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'lib/**.[ch]'
|
||||||
|
- 'lib/**.cpp'
|
||||||
|
- 'src/**.[ch]'
|
||||||
|
- 'src/**.cpp'
|
||||||
|
- 'irr/**.[ch]'
|
||||||
|
- 'irr/**.cpp'
|
||||||
|
- '**/CMakeLists.txt'
|
||||||
|
- 'cmake/Modules/**'
|
||||||
|
- 'po/**.po'
|
||||||
|
- 'util/ci/**'
|
||||||
|
- '.github/workflows/linux.yml'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'lib/**.[ch]'
|
||||||
|
- 'lib/**.cpp'
|
||||||
|
- 'src/**.[ch]'
|
||||||
|
- 'src/**.cpp'
|
||||||
|
- 'irr/**.[ch]'
|
||||||
|
- 'irr/**.cpp'
|
||||||
|
- '**/CMakeLists.txt'
|
||||||
|
- 'cmake/Modules/**'
|
||||||
|
- 'po/**.po'
|
||||||
|
- 'util/ci/**'
|
||||||
|
- '.github/workflows/linux.yml'
|
||||||
|
|
||||||
|
env:
|
||||||
|
MINETEST_POSTGRESQL_CONNECT_STRING: 'host=localhost user=minetest password=minetest dbname=minetest'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Older gcc version (should be close to our minimum supported version)
|
||||||
|
gcc_7:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install deps
|
||||||
|
run: |
|
||||||
|
source ./util/ci/common.sh
|
||||||
|
install_linux_deps g++-7
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
./util/ci/build.sh
|
||||||
|
env:
|
||||||
|
CC: gcc-7
|
||||||
|
CXX: g++-7
|
||||||
|
# Test fallback SHA implementations
|
||||||
|
CMAKE_FLAGS: '-DENABLE_OPENSSL=0'
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
./bin/luanti --run-unittests
|
||||||
|
|
||||||
|
# Current gcc version
|
||||||
|
gcc_14:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install deps
|
||||||
|
run: |
|
||||||
|
source ./util/ci/common.sh
|
||||||
|
install_linux_deps g++-14 libluajit-5.1-dev
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
./util/ci/build.sh
|
||||||
|
env:
|
||||||
|
CC: gcc-14
|
||||||
|
CXX: g++-14
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
mkdir nowrite
|
||||||
|
chmod a-w nowrite
|
||||||
|
cd nowrite
|
||||||
|
../bin/luanti --run-unittests
|
||||||
|
|
||||||
|
# Older clang version (should be close to our minimum supported version)
|
||||||
|
clang_7:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install deps
|
||||||
|
run: |
|
||||||
|
source ./util/ci/common.sh
|
||||||
|
install_linux_deps clang-7 llvm-7
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
./util/ci/build.sh
|
||||||
|
env:
|
||||||
|
CC: clang-7
|
||||||
|
CXX: clang++-7
|
||||||
|
CMAKE_FLAGS: '-DCMAKE_C_FLAGS="-fsanitize=address" -DCMAKE_CXX_FLAGS="-fsanitize=address"'
|
||||||
|
|
||||||
|
- name: Unittest
|
||||||
|
run: |
|
||||||
|
./bin/luanti --run-unittests
|
||||||
|
|
||||||
|
# Do this here because we have ASan and error paths are sensitive to dangling pointers
|
||||||
|
- name: Test error cases
|
||||||
|
run: |
|
||||||
|
./util/test_error_cases.sh
|
||||||
|
|
||||||
|
# Current clang version
|
||||||
|
clang_18:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install deps
|
||||||
|
run: |
|
||||||
|
source ./util/ci/common.sh
|
||||||
|
install_linux_deps clang-18 lldb
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
./util/ci/build.sh
|
||||||
|
env:
|
||||||
|
CC: clang-18
|
||||||
|
CXX: clang++-18
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
./bin/luanti --run-unittests
|
||||||
|
|
||||||
|
- name: Integration test + devtest
|
||||||
|
run: |
|
||||||
|
./util/test_multiplayer.sh
|
||||||
|
|
||||||
|
# Build with prometheus-cpp (server-only)
|
||||||
|
clang_11_prometheus:
|
||||||
|
name: "clang_11 (PROMETHEUS=1)"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install deps
|
||||||
|
run: |
|
||||||
|
source ./util/ci/common.sh
|
||||||
|
install_linux_deps clang-11
|
||||||
|
|
||||||
|
- name: Build prometheus-cpp
|
||||||
|
run: ./util/ci/build_prometheus_cpp.sh
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
./util/ci/build.sh
|
||||||
|
env:
|
||||||
|
CC: clang-11
|
||||||
|
CXX: clang++-11
|
||||||
|
CMAKE_FLAGS: "-DENABLE_PROMETHEUS=1 -DBUILD_CLIENT=0 -DENABLE_CURSES=0"
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
./bin/luantiserver --run-unittests
|
30
.github/workflows/lua.yml
vendored
30
.github/workflows/lua.yml
vendored
|
@ -14,47 +14,43 @@ on:
|
||||||
- '.github/workflows/**.yml'
|
- '.github/workflows/**.yml'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# Note that the integration tests are also run build.yml, but only when C++ code is changed.
|
# Note that the integration tests are also run in build.yml, but only when C++ code is changed.
|
||||||
integration_tests:
|
integration_tests:
|
||||||
name: "Compile and run multiplayer tests"
|
name: "Compile and run multiplayer tests"
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
source ./util/ci/common.sh
|
source ./util/ci/common.sh
|
||||||
install_linux_deps clang-10 gdb libluajit-5.1-dev
|
install_linux_deps clang gdb libluajit-5.1-dev
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
./util/ci/build.sh
|
./util/ci/build.sh
|
||||||
env:
|
env:
|
||||||
CC: clang-10
|
CC: clang
|
||||||
CXX: clang++-10
|
CXX: clang++
|
||||||
CMAKE_FLAGS: "-DENABLE_GETTEXT=0 -DBUILD_SERVER=0"
|
CMAKE_FLAGS: "-DENABLE_GETTEXT=0 -DBUILD_SERVER=0 -DBUILD_UNITTESTS=0"
|
||||||
|
|
||||||
- name: Integration test + devtest
|
- name: Integration test + devtest
|
||||||
run: |
|
run: |
|
||||||
./util/test_multiplayer.sh
|
serverconf="profiler.load=true" ./util/test_multiplayer.sh
|
||||||
|
|
||||||
luacheck:
|
luacheck:
|
||||||
name: "Builtin Luacheck and Unit Tests"
|
name: "Builtin Luacheck and Unit Tests"
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: leafo/gh-actions-lua@v9
|
- uses: leafo/gh-actions-lua@v10
|
||||||
with:
|
with:
|
||||||
luaVersion: "5.1.5"
|
luaVersion: "5.1.5"
|
||||||
- uses: leafo/gh-actions-luarocks@v4
|
- uses: leafo/gh-actions-luarocks@v4.3.0
|
||||||
|
|
||||||
- name: Install LuaJIT
|
- name: Install LuaJIT
|
||||||
run: |
|
run: ./util/ci/build_luajit.sh
|
||||||
cd $HOME
|
|
||||||
git clone https://github.com/LuaJIT/LuaJIT/
|
|
||||||
cd LuaJIT
|
|
||||||
make -j$(nproc)
|
|
||||||
|
|
||||||
- name: Install luarocks tools
|
- name: Install luarocks tools
|
||||||
run: |
|
run: |
|
||||||
|
|
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:
|
||||||
|
if: github.repository == 'luanti-org/luanti'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: 3.11
|
||||||
|
|
||||||
|
- name: Install mkdocs
|
||||||
|
run: |
|
||||||
|
pip install -U -r doc/mkdocs/requirements.txt
|
||||||
|
|
||||||
|
- name: Build documentation
|
||||||
|
run: |
|
||||||
|
cd doc/mkdocs/
|
||||||
|
./build.sh
|
||||||
|
|
||||||
|
- name: Setup Pages
|
||||||
|
uses: actions/configure-pages@v4
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-pages-artifact@v3
|
||||||
|
with:
|
||||||
|
path: 'public/'
|
||||||
|
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
id: deployment
|
||||||
|
uses: actions/deploy-pages@v4
|
84
.github/workflows/macos.yml
vendored
84
.github/workflows/macos.yml
vendored
|
@ -8,8 +8,12 @@ on:
|
||||||
- 'lib/**.cpp'
|
- 'lib/**.cpp'
|
||||||
- 'src/**.[ch]'
|
- 'src/**.[ch]'
|
||||||
- 'src/**.cpp'
|
- 'src/**.cpp'
|
||||||
|
- 'irr/**.[ch]'
|
||||||
|
- 'irr/**.cpp'
|
||||||
|
- 'irr/**.mm' # Objective-C(++)
|
||||||
- '**/CMakeLists.txt'
|
- '**/CMakeLists.txt'
|
||||||
- 'cmake/Modules/**'
|
- 'cmake/Modules/**'
|
||||||
|
- 'po/**.po'
|
||||||
- '.github/workflows/macos.yml'
|
- '.github/workflows/macos.yml'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
|
@ -17,55 +21,93 @@ on:
|
||||||
- 'lib/**.cpp'
|
- 'lib/**.cpp'
|
||||||
- 'src/**.[ch]'
|
- 'src/**.[ch]'
|
||||||
- 'src/**.cpp'
|
- 'src/**.cpp'
|
||||||
|
- 'irr/**.[ch]'
|
||||||
|
- 'irr/**.cpp'
|
||||||
|
- 'irr/**.mm' # Objective-C(++)
|
||||||
- '**/CMakeLists.txt'
|
- '**/CMakeLists.txt'
|
||||||
- 'cmake/Modules/**'
|
- 'cmake/Modules/**'
|
||||||
|
- 'po/**.po'
|
||||||
- '.github/workflows/macos.yml'
|
- '.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:
|
jobs:
|
||||||
build:
|
build-intel-macos:
|
||||||
runs-on: macos-10.15
|
# use lowest possible macOS running on x86_64 supported by brew to support more users
|
||||||
|
runs-on: macos-13
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
pkgs=(cmake freetype gettext gmp hiredis jpeg jsoncpp leveldb libogg libpng libvorbis luajit zstd)
|
source ./util/ci/common.sh
|
||||||
brew update
|
install_macos_deps
|
||||||
brew install ${pkgs[@]}
|
|
||||||
brew unlink $(brew ls --formula)
|
|
||||||
brew link ${pkgs[@]}
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
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
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. \
|
cmake .. \
|
||||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
|
||||||
-DCMAKE_FIND_FRAMEWORK=LAST \
|
-DCMAKE_FIND_FRAMEWORK=LAST \
|
||||||
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
|
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
|
||||||
-DRUN_IN_PLACE=FALSE \
|
-DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE \
|
||||||
-DENABLE_FREETYPE=TRUE -DENABLE_GETTEXT=TRUE
|
-DINSTALL_DEVTEST=TRUE
|
||||||
make -j2
|
cmake --build . -j$(sysctl -n hw.logicalcpu)
|
||||||
make install
|
make install
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: |
|
run: |
|
||||||
./build/macos/minetest.app/Contents/MacOS/minetest --run-unittests
|
./build/macos/luanti.app/Contents/MacOS/luanti --run-unittests
|
||||||
|
|
||||||
# Zipping the built .app preserves permissions on the contained files,
|
# Zipping the built .app preserves permissions on the contained files,
|
||||||
# which the GitHub artifact pipeline would otherwise strip away.
|
# which the GitHub artifact pipeline would otherwise strip away.
|
||||||
- name: CPack
|
- name: CPack
|
||||||
run: |
|
run: |
|
||||||
cd build
|
cd build
|
||||||
|
rm -rf macos
|
||||||
|
cmake .. -DINSTALL_DEVTEST=FALSE
|
||||||
cpack -G ZIP -B macos
|
cpack -G ZIP -B macos
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: minetest-macos
|
name: luanti-macos
|
||||||
path: ./build/macos/*.zip
|
path: ./build/macos/*.zip
|
||||||
|
build-arm-macos-xcode:
|
||||||
|
runs-on: macos-14
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install deps
|
||||||
|
run: |
|
||||||
|
source ./util/ci/common.sh
|
||||||
|
install_macos_deps
|
||||||
|
# brew jsoncpp do not include libjsoncpp.a, and if installed header conflict caused build failure
|
||||||
|
brew uninstall jsoncpp
|
||||||
|
|
||||||
|
- name: Build with Cmake
|
||||||
|
run: |
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. \
|
||||||
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=14 \
|
||||||
|
-DCMAKE_FIND_FRAMEWORK=LAST \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
|
||||||
|
-DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE \
|
||||||
|
-DENABLE_SYSTEM_JSONCPP=OFF
|
||||||
|
cmake --build . -j$(sysctl -n hw.logicalcpu)
|
||||||
|
make install
|
||||||
|
|
||||||
|
- name: Build and Archive with Xcode
|
||||||
|
run: |
|
||||||
|
mkdir build_xcode
|
||||||
|
cd build_xcode
|
||||||
|
../util/ci/build_xcode.sh
|
||||||
|
|
||||||
|
- name: Tests
|
||||||
|
run: |
|
||||||
|
mkdir -p "${HOME}/Library/Application Support/minetest/games/"
|
||||||
|
ln -s "${PWD}/games/devtest" "${HOME}/Library/Application Support/minetest/games/"
|
||||||
|
./build/macos/luanti.app/Contents/MacOS/luanti --run-unittests
|
||||||
|
./build_xcode/luanti.xcarchive/Products/Applications/luanti.app/Contents/MacOS/luanti --run-unittests
|
||||||
|
|
||||||
|
- name: Diff Resources
|
||||||
|
run: |
|
||||||
|
diff -rd ./build/macos/luanti.app/Contents/Resources ./build_xcode/build/Release/luanti.app/Contents/Resources || exit 1
|
||||||
|
diff -rd ./build/macos/luanti.app/Contents/Resources ./build_xcode/luanti.xcarchive/Products/Applications/luanti.app/Contents/Resources || exit 1
|
||||||
|
|
26
.github/workflows/png_file_checks.yml
vendored
Normal file
26
.github/workflows/png_file_checks.yml
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
name: png_file_checks
|
||||||
|
|
||||||
|
# Check whether all png files are in a valid format
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- '**.png'
|
||||||
|
- '.github/workflows/**.yml'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- '**.png'
|
||||||
|
- '.github/workflows/**.yml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
png_optimized:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install deps
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt install -y optipng
|
||||||
|
|
||||||
|
- name: Check whether all png files are optimized
|
||||||
|
run: |
|
||||||
|
./util/ci/check_png_optimized.sh
|
118
.github/workflows/whitespace_checks.yml
vendored
Normal file
118
.github/workflows/whitespace_checks.yml
vendored
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
name: whitespace_checks
|
||||||
|
|
||||||
|
# Check whitespaces of the following file types
|
||||||
|
# Not checked: .lua, .yml, .properties, .conf, .java, .py, .svg, .gradle, .xml, ...
|
||||||
|
# (luacheck already checks .lua files)
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- '**.txt'
|
||||||
|
- '**.md'
|
||||||
|
- '**.[ch]'
|
||||||
|
- '**.cpp'
|
||||||
|
- '**.hpp'
|
||||||
|
- '**.sh'
|
||||||
|
- '**.cmake'
|
||||||
|
- '**.glsl'
|
||||||
|
- '**.lua'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- '**.txt'
|
||||||
|
- '**.md'
|
||||||
|
- '**.[ch]'
|
||||||
|
- '**.cpp'
|
||||||
|
- '**.hpp'
|
||||||
|
- '**.sh'
|
||||||
|
- '**.cmake'
|
||||||
|
- '**.glsl'
|
||||||
|
- '**.lua'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
trailing_whitespaces:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
# Line endings are already ensured by .gitattributes
|
||||||
|
- name: Check trailing whitespaces
|
||||||
|
run: |
|
||||||
|
if git ls-files |\
|
||||||
|
grep -E '\.txt$|\.md$|\.[ch]$|\.cpp$|\.hpp$|\.sh$|\.cmake$|\.glsl$' |\
|
||||||
|
xargs grep -n '\s$';\
|
||||||
|
then\
|
||||||
|
echo -e "\033[0;31mFound trailing whitespace";\
|
||||||
|
(exit 1);\
|
||||||
|
else\
|
||||||
|
(exit 0);\
|
||||||
|
fi
|
||||||
|
|
||||||
|
indent_spaces:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
# Line endings are already ensured by .gitattributes
|
||||||
|
# Multiple multline comments in one line is not supported by this check
|
||||||
|
# and there is no reason to use them
|
||||||
|
# So lines like: "/* */ /*" or "*/ a = 5; /*" will result in error
|
||||||
|
- name: Check for unsupported multiline comments
|
||||||
|
run: |
|
||||||
|
if git ls-files |\
|
||||||
|
grep -E '^src/.*\.cpp$|^src/.*\.[ch]$' |\
|
||||||
|
xargs grep -n '\*/.*/\*';\
|
||||||
|
then
|
||||||
|
echo -e "\033[0;31mUnsupported combination of multiline comments. New multiline comment should begin on new line.";\
|
||||||
|
(exit 1);\
|
||||||
|
else\
|
||||||
|
(exit 0);\
|
||||||
|
fi
|
||||||
|
if git ls-files |\
|
||||||
|
grep -E '\.lua$' |\
|
||||||
|
xargs grep -n -e '\]\].*--\[\[';\
|
||||||
|
then
|
||||||
|
echo -e "\033[0;31mUnsupported combination of multiline comments. New multiline comment should begin on new line.";\
|
||||||
|
(exit 1);\
|
||||||
|
else\
|
||||||
|
(exit 0);\
|
||||||
|
fi
|
||||||
|
# This prepare files for final check
|
||||||
|
# See python script ./util/ci/indent_tab_preprocess.py for details.
|
||||||
|
- name: Preprocess files
|
||||||
|
run: |
|
||||||
|
git ls-files |\
|
||||||
|
grep -E '^src/.*\.cpp$|^src/.*\.[ch]$' |\
|
||||||
|
xargs -L 1 -P $(($(nproc) + 1)) \
|
||||||
|
python3 ./util/ci/indent_tab_preprocess.py "/*" "*/"
|
||||||
|
git ls-files |\
|
||||||
|
grep -E '\.lua$' |\
|
||||||
|
xargs -L 1 -P $(($(nproc) + 1)) \
|
||||||
|
python3 ./util/ci/indent_tab_preprocess.py "--[[" "]]"
|
||||||
|
# Check for bad indent.
|
||||||
|
# This runs over preprocessed files.
|
||||||
|
# If there is any remaining space on line beginning or after tab,
|
||||||
|
# error is generated
|
||||||
|
- name: Check indent spaces
|
||||||
|
run: |
|
||||||
|
if git ls-files |\
|
||||||
|
grep -E '^src/.*\.cpp$|^src/.*\.[ch]$|\.lua' |\
|
||||||
|
xargs grep -n -P '^\t*[ ]';\
|
||||||
|
then\
|
||||||
|
echo -e "\033[0;31mFound incorrect indent whitespaces";\
|
||||||
|
(exit 1);\
|
||||||
|
else\
|
||||||
|
(exit 0);\
|
||||||
|
fi
|
||||||
|
|
||||||
|
tabs_lua_api_files:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
# Some files should not contain tabs
|
||||||
|
- name: Check tabs in Lua API files
|
||||||
|
run: |
|
||||||
|
if grep -n $'\t' doc/lua_api.md doc/client_lua_api.md;\
|
||||||
|
then\
|
||||||
|
echo -e "\033[0;31mFound tab in markdown file";\
|
||||||
|
(exit 1);\
|
||||||
|
else\
|
||||||
|
(exit 0);\
|
||||||
|
fi
|
||||||
|
|
146
.github/workflows/windows.yml
vendored
Normal file
146
.github/workflows/windows.yml
vendored
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
name: windows
|
||||||
|
|
||||||
|
# build on c/cpp changes or workflow changes
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'lib/**.[ch]'
|
||||||
|
- 'lib/**.cpp'
|
||||||
|
- 'src/**.[ch]'
|
||||||
|
- 'src/**.cpp'
|
||||||
|
- 'irr/**.[ch]'
|
||||||
|
- 'irr/**.cpp'
|
||||||
|
- '**/CMakeLists.txt'
|
||||||
|
- 'cmake/Modules/**'
|
||||||
|
- 'po/**.po'
|
||||||
|
- 'util/buildbot/**'
|
||||||
|
- 'misc/*.manifest'
|
||||||
|
- '.github/workflows/windows.yml'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'lib/**.[ch]'
|
||||||
|
- 'lib/**.cpp'
|
||||||
|
- 'src/**.[ch]'
|
||||||
|
- 'src/**.cpp'
|
||||||
|
- 'irr/**.[ch]'
|
||||||
|
- 'irr/**.cpp'
|
||||||
|
- '**/CMakeLists.txt'
|
||||||
|
- 'cmake/Modules/**'
|
||||||
|
- 'po/**.po'
|
||||||
|
- 'util/buildbot/**'
|
||||||
|
- 'misc/*.manifest'
|
||||||
|
- '.github/workflows/windows.yml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
mingw:
|
||||||
|
name: "MinGW cross-compiler (${{ matrix.bits }}-bit)"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
bits: [32, 64]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install compiler
|
||||||
|
run: |
|
||||||
|
sudo dpkg --add-architecture i386
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y --no-install-recommends gettext wine wine${{ matrix.bits }}
|
||||||
|
sudo ./util/buildbot/download_toolchain.sh /usr
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
EXISTING_MINETEST_DIR=$PWD \
|
||||||
|
./util/buildbot/buildwin${{ matrix.bits }}.sh B
|
||||||
|
|
||||||
|
# Check that the resulting binary can run (DLLs etc.)
|
||||||
|
- name: Runtime test
|
||||||
|
run: |
|
||||||
|
dest=$(mktemp -d)
|
||||||
|
unzip -q -d "$dest" B/build/*.zip
|
||||||
|
cd "$dest"/luanti-*-win*
|
||||||
|
wine bin/luanti.exe --version
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: "mingw${{ matrix.bits }}"
|
||||||
|
path: B/build/*.zip
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
msvc:
|
||||||
|
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
|
||||||
|
runs-on: windows-2019
|
||||||
|
env:
|
||||||
|
VCPKG_VERSION: 01f602195983451bc83e72f4214af2cbc495aa94
|
||||||
|
# 2024.05.24
|
||||||
|
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp sdl2
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
config:
|
||||||
|
- {
|
||||||
|
arch: x86,
|
||||||
|
generator: "-G'Visual Studio 16 2019' -A Win32",
|
||||||
|
vcpkg_triplet: x86-windows
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
arch: x64,
|
||||||
|
generator: "-G'Visual Studio 16 2019' -A x64",
|
||||||
|
vcpkg_triplet: x64-windows
|
||||||
|
}
|
||||||
|
type: [portable]
|
||||||
|
# type: [portable, installer]
|
||||||
|
# The installer type is working, but disabled, to save runner jobs.
|
||||||
|
# Enable it, when working on the installer.
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Restore from cache and run vcpkg
|
||||||
|
uses: lukka/run-vcpkg@v7
|
||||||
|
with:
|
||||||
|
vcpkgArguments: ${{env.vcpkg_packages}}
|
||||||
|
vcpkgDirectory: '${{ github.workspace }}\vcpkg'
|
||||||
|
appendedCacheKey: ${{ matrix.config.vcpkg_triplet }}
|
||||||
|
vcpkgGitCommitId: ${{ env.VCPKG_VERSION }}
|
||||||
|
vcpkgTriplet: ${{ matrix.config.vcpkg_triplet }}
|
||||||
|
|
||||||
|
- name: CMake
|
||||||
|
run: |
|
||||||
|
cmake ${{matrix.config.generator}} `
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
|
||||||
|
-DCMAKE_BUILD_TYPE=Release `
|
||||||
|
-DENABLE_POSTGRESQL=OFF `
|
||||||
|
-DENABLE_LUAJIT=TRUE `
|
||||||
|
-DREQUIRE_LUAJIT=TRUE `
|
||||||
|
-DRUN_IN_PLACE=${{ contains(matrix.type, 'portable') }} .
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build . --config Release
|
||||||
|
|
||||||
|
- name: Unittests
|
||||||
|
# need this workaround for stdout to work
|
||||||
|
run: |
|
||||||
|
$proc = start .\bin\Release\luanti.exe --run-unittests -NoNewWindow -Wait -PassThru
|
||||||
|
exit $proc.ExitCode
|
||||||
|
continue-on-error: true # FIXME!!
|
||||||
|
|
||||||
|
- name: CPack
|
||||||
|
run: |
|
||||||
|
If ($env:TYPE -eq "installer")
|
||||||
|
{
|
||||||
|
cpack -G WIX -B "$env:GITHUB_WORKSPACE\Package"
|
||||||
|
}
|
||||||
|
ElseIf($env:TYPE -eq "portable")
|
||||||
|
{
|
||||||
|
cpack -G ZIP -B "$env:GITHUB_WORKSPACE\Package"
|
||||||
|
}
|
||||||
|
rm -r $env:GITHUB_WORKSPACE\Package\_CPack_Packages
|
||||||
|
env:
|
||||||
|
TYPE: ${{matrix.type}}
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: msvc-${{ matrix.config.arch }}-${{ matrix.type }}
|
||||||
|
path: .\Package\
|
||||||
|
if-no-files-found: error
|
47
.gitignore
vendored
47
.gitignore
vendored
|
@ -1,5 +1,8 @@
|
||||||
## Editors and development environments
|
## Editors and development environments
|
||||||
*~
|
*~
|
||||||
|
.cmake
|
||||||
|
CMakeUserPresets.json
|
||||||
|
Testing/*
|
||||||
*.swp
|
*.swp
|
||||||
*.bak*
|
*.bak*
|
||||||
*.orig
|
*.orig
|
||||||
|
@ -23,45 +26,61 @@ tags
|
||||||
!tags/
|
!tags/
|
||||||
gtags.files
|
gtags.files
|
||||||
.idea
|
.idea
|
||||||
|
.qtcreator/
|
||||||
# Codelite
|
# Codelite
|
||||||
*.project
|
*.project
|
||||||
# Visual Studio Code & plugins
|
# Visual Studio Code & plugins
|
||||||
.vscode/
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
build/.cmake/
|
build/.cmake/
|
||||||
|
# Fleet
|
||||||
|
.fleet
|
||||||
# Gradle
|
# Gradle
|
||||||
.gradle
|
.gradle
|
||||||
|
# Clang
|
||||||
|
.cache
|
||||||
|
# AppImage
|
||||||
|
*.AppImage
|
||||||
|
*.zsync
|
||||||
|
appimage-build
|
||||||
|
AppDir
|
||||||
|
# Direnv
|
||||||
|
.direnv/
|
||||||
|
# Nix
|
||||||
|
/result
|
||||||
|
|
||||||
## Files related to Minetest development cycle
|
## Files related to Luanti development cycle
|
||||||
/*.patch
|
/*.patch
|
||||||
*.diff
|
*.diff
|
||||||
# GNU Patch reject file
|
# GNU Patch reject file
|
||||||
*.rej
|
*.rej
|
||||||
|
|
||||||
## Non-static Minetest directories or symlinks to these
|
## Non-static Luanti directories or symlinks to these
|
||||||
/bin/
|
/bin/
|
||||||
/games/*
|
/games/*
|
||||||
!/games/devtest/
|
!/games/devtest/
|
||||||
|
/games/devtest/mods/soundstuff/sounds/gitignored_sounds/*
|
||||||
|
!/games/devtest/mods/soundstuff/sounds/gitignored_sounds/custom_sounds_here.txt
|
||||||
/cache
|
/cache
|
||||||
/textures/*
|
/textures/*
|
||||||
!/textures/base/
|
!/textures/base/
|
||||||
/screenshots
|
/screenshots
|
||||||
/sounds
|
/sounds
|
||||||
/mods/*
|
/mods/*
|
||||||
!/mods/minetest/
|
!/mods/mods_here.txt
|
||||||
/mods/minetest/*
|
/worlds/*
|
||||||
!/mods/minetest/mods_here.txt
|
!/worlds/worlds_here.txt
|
||||||
/worlds
|
|
||||||
/world/
|
|
||||||
/clientmods/*
|
/clientmods/*
|
||||||
!/clientmods/preview/
|
!/clientmods/preview/
|
||||||
/client/mod_storage/
|
/client/mod_storage/
|
||||||
|
/mod_data
|
||||||
|
|
||||||
## Configuration/log files
|
## Configuration/log files
|
||||||
minetest.conf
|
minetest.conf
|
||||||
debug.txt
|
debug.txt
|
||||||
debug.txt.1
|
debug.txt.1
|
||||||
|
|
||||||
## Other files generated by Minetest
|
## Other files generated by Luanti
|
||||||
screenshot_*.png
|
screenshot_*.png
|
||||||
testbm.txt
|
testbm.txt
|
||||||
|
|
||||||
|
@ -83,24 +102,26 @@ cmake_install.cmake
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
CPackConfig.cmake
|
CPackConfig.cmake
|
||||||
CPackSourceConfig.cmake
|
CPackSourceConfig.cmake
|
||||||
src/test_config.h
|
|
||||||
src/cmake_config.h
|
src/cmake_config.h
|
||||||
src/cmake_config_githash.h
|
src/cmake_config_githash.h
|
||||||
src/unittest/test_world/world.mt
|
|
||||||
games/devtest/mods/testnodes/textures/testnodes_generated_*.png
|
|
||||||
/locale/
|
/locale/
|
||||||
.directory
|
.directory
|
||||||
*.cbp
|
*.cbp
|
||||||
*.layout
|
*.layout
|
||||||
*.o
|
*.o
|
||||||
*.a
|
*.a
|
||||||
|
*.dump
|
||||||
|
*.dmp
|
||||||
*.ninja
|
*.ninja
|
||||||
.ninja*
|
.ninja*
|
||||||
*.gch
|
*.gch
|
||||||
*.iml
|
*.iml
|
||||||
test_config.h
|
test_config.h
|
||||||
cmake-build-debug/
|
cmake-build-debug/
|
||||||
|
cmake-build-minsizerel/
|
||||||
cmake-build-release/
|
cmake-build-release/
|
||||||
|
cmake-build-relwithdebinfo/
|
||||||
|
cmake-build-default/
|
||||||
cmake_config.h
|
cmake_config.h
|
||||||
cmake_config_githash.h
|
cmake_config_githash.h
|
||||||
CMakeDoxy*
|
CMakeDoxy*
|
||||||
|
@ -112,7 +133,7 @@ compile_commands.json
|
||||||
*.sln
|
*.sln
|
||||||
.vs/
|
.vs/
|
||||||
|
|
||||||
# Optional user provided library folder
|
# Old irrlichtmt. Still ignored to make bisecting easier.
|
||||||
lib/irrlichtmt
|
lib/irrlichtmt
|
||||||
|
|
||||||
# Generated mod storage database
|
# Generated mod storage database
|
||||||
|
|
275
.gitlab-ci.yml
275
.gitlab-ci.yml
|
@ -3,287 +3,14 @@
|
||||||
# https://gitlab.com/minetest/minetest
|
# https://gitlab.com/minetest/minetest
|
||||||
# Pipelines URL: https://gitlab.com/minetest/minetest/pipelines
|
# Pipelines URL: https://gitlab.com/minetest/minetest/pipelines
|
||||||
|
|
||||||
stages:
|
|
||||||
- build
|
|
||||||
- package
|
|
||||||
- deploy
|
|
||||||
|
|
||||||
variables:
|
|
||||||
MINETEST_GAME_REPO: "https://github.com/minetest/minetest_game.git"
|
|
||||||
CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH
|
|
||||||
|
|
||||||
.build_template:
|
|
||||||
stage: build
|
|
||||||
before_script:
|
|
||||||
- apt-get update
|
|
||||||
- DEBIAN_FRONTEND=noninteractive apt-get -y install build-essential git cmake libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libleveldb-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev libzstd-dev
|
|
||||||
script:
|
|
||||||
- git clone https://github.com/minetest/irrlicht lib/irrlichtmt --depth 1 -b $(cat misc/irrlichtmt_tag.txt)
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmake -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DBUILD_SERVER=TRUE ..
|
|
||||||
- make -j $(($(nproc) + 1))
|
|
||||||
- make install
|
|
||||||
artifacts:
|
|
||||||
when: on_success
|
|
||||||
expire_in: 1h
|
|
||||||
paths:
|
|
||||||
- artifact/*
|
|
||||||
|
|
||||||
.debpkg_template:
|
|
||||||
stage: package
|
|
||||||
before_script:
|
|
||||||
- apt-get update
|
|
||||||
- apt-get install -y git
|
|
||||||
- mkdir -p build/deb/minetest/DEBIAN/
|
|
||||||
- cp misc/debpkg-control build/deb/minetest/DEBIAN/control
|
|
||||||
- cp -a artifact/minetest/usr build/deb/minetest/
|
|
||||||
script:
|
|
||||||
- git clone $MINETEST_GAME_REPO build/deb/minetest/usr/share/minetest/games/minetest_game
|
|
||||||
- rm -rf build/deb/minetest/usr/share/minetest/games/minetest/.git
|
|
||||||
- sed -i 's/DATEPLACEHOLDER/'$(date +%y.%m.%d)'/g' build/deb/minetest/DEBIAN/control
|
|
||||||
- sed -i 's/JPEG_PLACEHOLDER/'$JPEG_PKG'/g' build/deb/minetest/DEBIAN/control
|
|
||||||
- sed -i 's/LEVELDB_PLACEHOLDER/'$LEVELDB_PKG'/g' build/deb/minetest/DEBIAN/control
|
|
||||||
- sed -i 's/JSONCPP_PLACEHOLDER/'$JSONCPP_PKG'/g' build/deb/minetest/DEBIAN/control
|
|
||||||
- cd build/deb/ && dpkg-deb -b minetest/ && mv minetest.deb ../../
|
|
||||||
artifacts:
|
|
||||||
expire_in: 90 day
|
|
||||||
paths:
|
|
||||||
- ./*.deb
|
|
||||||
|
|
||||||
.debpkg_install:
|
|
||||||
stage: deploy
|
|
||||||
before_script:
|
|
||||||
- apt-get update -qy
|
|
||||||
script:
|
|
||||||
- apt-get install -y ./*.deb
|
|
||||||
- minetest --version
|
|
||||||
|
|
||||||
##
|
|
||||||
## Debian
|
|
||||||
##
|
|
||||||
|
|
||||||
# Stretch
|
|
||||||
|
|
||||||
build:debian-9:
|
|
||||||
extends: .build_template
|
|
||||||
image: debian:9
|
|
||||||
|
|
||||||
package:debian-9:
|
|
||||||
extends: .debpkg_template
|
|
||||||
image: debian:9
|
|
||||||
needs:
|
|
||||||
- build:debian-9
|
|
||||||
variables:
|
|
||||||
JSONCPP_PKG: libjsoncpp1
|
|
||||||
LEVELDB_PKG: libleveldb1v5
|
|
||||||
JPEG_PKG: libjpeg62-turbo
|
|
||||||
|
|
||||||
deploy:debian-9:
|
|
||||||
extends: .debpkg_install
|
|
||||||
image: debian:9
|
|
||||||
needs:
|
|
||||||
- package:debian-9
|
|
||||||
|
|
||||||
# Buster
|
|
||||||
|
|
||||||
build:debian-10:
|
|
||||||
extends: .build_template
|
|
||||||
image: debian:10
|
|
||||||
|
|
||||||
package:debian-10:
|
|
||||||
extends: .debpkg_template
|
|
||||||
image: debian:10
|
|
||||||
needs:
|
|
||||||
- build:debian-10
|
|
||||||
variables:
|
|
||||||
JSONCPP_PKG: libjsoncpp1
|
|
||||||
LEVELDB_PKG: libleveldb1d
|
|
||||||
JPEG_PKG: libjpeg62-turbo
|
|
||||||
|
|
||||||
deploy:debian-10:
|
|
||||||
extends: .debpkg_install
|
|
||||||
image: debian:10
|
|
||||||
needs:
|
|
||||||
- package:debian-10
|
|
||||||
|
|
||||||
# Bullseye
|
|
||||||
|
|
||||||
build:debian-11:
|
|
||||||
extends: .build_template
|
|
||||||
image: debian:11
|
|
||||||
|
|
||||||
package:debian-11:
|
|
||||||
extends: .debpkg_template
|
|
||||||
image: debian:11
|
|
||||||
needs:
|
|
||||||
- build:debian-11
|
|
||||||
variables:
|
|
||||||
JSONCPP_PKG: libjsoncpp24
|
|
||||||
LEVELDB_PKG: libleveldb1d
|
|
||||||
JPEG_PKG: libjpeg62-turbo
|
|
||||||
|
|
||||||
deploy:debian-11:
|
|
||||||
extends: .debpkg_install
|
|
||||||
image: debian:11
|
|
||||||
needs:
|
|
||||||
- package:debian-11
|
|
||||||
|
|
||||||
##
|
|
||||||
## Ubuntu
|
|
||||||
##
|
|
||||||
|
|
||||||
# Bionic
|
|
||||||
|
|
||||||
build:ubuntu-18.04:
|
|
||||||
extends: .build_template
|
|
||||||
image: ubuntu:bionic
|
|
||||||
|
|
||||||
package:ubuntu-18.04:
|
|
||||||
extends: .debpkg_template
|
|
||||||
image: ubuntu:bionic
|
|
||||||
needs:
|
|
||||||
- build:ubuntu-18.04
|
|
||||||
variables:
|
|
||||||
JSONCPP_PKG: libjsoncpp1
|
|
||||||
LEVELDB_PKG: libleveldb1v5
|
|
||||||
JPEG_PKG: libjpeg-turbo8
|
|
||||||
|
|
||||||
deploy:ubuntu-18.04:
|
|
||||||
extends: .debpkg_install
|
|
||||||
image: ubuntu:bionic
|
|
||||||
needs:
|
|
||||||
- package:ubuntu-18.04
|
|
||||||
|
|
||||||
# Focal
|
|
||||||
|
|
||||||
build:ubuntu-20.04:
|
|
||||||
extends: .build_template
|
|
||||||
image: ubuntu:focal
|
|
||||||
|
|
||||||
package:ubuntu-20.04:
|
|
||||||
extends: .debpkg_template
|
|
||||||
image: ubuntu:focal
|
|
||||||
needs:
|
|
||||||
- build:ubuntu-20.04
|
|
||||||
variables:
|
|
||||||
JSONCPP_PKG: libjsoncpp1
|
|
||||||
LEVELDB_PKG: libleveldb1d
|
|
||||||
JPEG_PKG: libjpeg-turbo8
|
|
||||||
|
|
||||||
deploy:ubuntu-20.04:
|
|
||||||
extends: .debpkg_install
|
|
||||||
image: ubuntu:focal
|
|
||||||
needs:
|
|
||||||
- package:ubuntu-20.04
|
|
||||||
|
|
||||||
##
|
|
||||||
## Fedora
|
|
||||||
##
|
|
||||||
|
|
||||||
# Fedora 28 <-> RHEL 8
|
|
||||||
build:fedora-28:
|
|
||||||
extends: .build_template
|
|
||||||
image: fedora:28
|
|
||||||
before_script:
|
|
||||||
- dnf -y install make git gcc gcc-c++ kernel-devel cmake libjpeg-devel libpng-devel libcurl-devel openal-soft-devel libvorbis-devel libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel spatialindex-devel libzstd-devel
|
|
||||||
|
|
||||||
##
|
|
||||||
## MinGW for Windows
|
|
||||||
##
|
|
||||||
|
|
||||||
.generic_win_template:
|
|
||||||
image: ubuntu:focal
|
|
||||||
before_script:
|
|
||||||
- apt-get update
|
|
||||||
- DEBIAN_FRONTEND=noninteractive apt-get install -y wget xz-utils unzip git cmake gettext
|
|
||||||
- wget -nv http://minetest.kitsunemimi.pw/mingw-w64-${WIN_ARCH}_11.2.0_ubuntu20.04.tar.xz -O mingw.tar.xz
|
|
||||||
- tar -xaf mingw.tar.xz -C /usr
|
|
||||||
|
|
||||||
.build_win_template:
|
|
||||||
extends: .generic_win_template
|
|
||||||
stage: build
|
|
||||||
artifacts:
|
|
||||||
expire_in: 90 day
|
|
||||||
paths:
|
|
||||||
- minetest-*-win*/*
|
|
||||||
|
|
||||||
build:win32:
|
|
||||||
extends: .build_win_template
|
|
||||||
script:
|
|
||||||
- EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh build
|
|
||||||
- unzip -q build/build/*.zip
|
|
||||||
variables:
|
|
||||||
WIN_ARCH: "i686"
|
|
||||||
|
|
||||||
build:win64:
|
|
||||||
extends: .build_win_template
|
|
||||||
script:
|
|
||||||
- EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh build
|
|
||||||
- unzip -q build/build/*.zip
|
|
||||||
variables:
|
|
||||||
WIN_ARCH: "x86_64"
|
|
||||||
|
|
||||||
##
|
|
||||||
## Docker
|
|
||||||
##
|
|
||||||
|
|
||||||
package:docker:
|
|
||||||
stage: package
|
|
||||||
image: docker:stable
|
|
||||||
services:
|
|
||||||
- docker:dind
|
|
||||||
before_script:
|
|
||||||
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com
|
|
||||||
script:
|
|
||||||
- docker build . -t ${CONTAINER_IMAGE}/server:$CI_COMMIT_SHA -t ${CONTAINER_IMAGE}/server:$CI_COMMIT_REF_NAME -t ${CONTAINER_IMAGE}/server:latest
|
|
||||||
- docker push ${CONTAINER_IMAGE}/server:$CI_COMMIT_SHA
|
|
||||||
- docker push ${CONTAINER_IMAGE}/server:$CI_COMMIT_REF_NAME
|
|
||||||
- docker push ${CONTAINER_IMAGE}/server:latest
|
|
||||||
|
|
||||||
##
|
|
||||||
## Gitlab Pages (Lua API documentation)
|
|
||||||
##
|
|
||||||
|
|
||||||
pages:
|
pages:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
image: python:3.8
|
image: python:3.8
|
||||||
before_script:
|
|
||||||
- pip install git+https://github.com/Python-Markdown/markdown.git
|
|
||||||
- pip install git+https://github.com/mkdocs/mkdocs.git
|
|
||||||
- pip install pygments
|
|
||||||
script:
|
script:
|
||||||
- cd doc/mkdocs && ./build.sh
|
- ./misc/make_redirects.sh
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- public
|
- public
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
##
|
|
||||||
## AppImage
|
|
||||||
##
|
|
||||||
|
|
||||||
package:appimage-client:
|
|
||||||
stage: package
|
|
||||||
image: appimagecrafters/appimage-builder
|
|
||||||
needs:
|
|
||||||
- build:ubuntu-18.04
|
|
||||||
before_script:
|
|
||||||
- apt-get update -y
|
|
||||||
- apt-get install -y git
|
|
||||||
# Collect files
|
|
||||||
- mkdir AppDir
|
|
||||||
- cp -a artifact/minetest/usr/ AppDir/usr/
|
|
||||||
- rm AppDir/usr/bin/minetestserver
|
|
||||||
- cp -a clientmods AppDir/usr/share/minetest
|
|
||||||
script:
|
|
||||||
- git clone $MINETEST_GAME_REPO AppDir/usr/share/minetest/games/minetest_game
|
|
||||||
- rm -rf AppDir/usr/share/minetest/games/minetest/.git
|
|
||||||
- export VERSION=$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA
|
|
||||||
# Remove PrefersNonDefaultGPU property due to validation errors
|
|
||||||
- sed -i '/PrefersNonDefaultGPU/d' AppDir/usr/share/applications/net.minetest.minetest.desktop
|
|
||||||
- appimage-builder --skip-test
|
|
||||||
artifacts:
|
|
||||||
expire_in: 90 day
|
|
||||||
paths:
|
|
||||||
- ./*.AppImage
|
|
||||||
|
|
17
.luacheckrc
17
.luacheckrc
|
@ -10,16 +10,19 @@ ignore = {
|
||||||
read_globals = {
|
read_globals = {
|
||||||
"ItemStack",
|
"ItemStack",
|
||||||
"INIT",
|
"INIT",
|
||||||
|
"PLATFORM",
|
||||||
"DIR_DELIM",
|
"DIR_DELIM",
|
||||||
"dump", "dump2",
|
"dump", "dump2",
|
||||||
"fgettext", "fgettext_ne",
|
"fgettext", "fgettext_ne",
|
||||||
"vector",
|
"vector",
|
||||||
"VoxelArea",
|
"VoxelArea",
|
||||||
|
"VoxelManip",
|
||||||
"profiler",
|
"profiler",
|
||||||
"Settings",
|
"Settings",
|
||||||
|
"PerlinNoise", "PerlinNoiseMap",
|
||||||
|
|
||||||
string = {fields = {"split", "trim"}},
|
string = {fields = {"split", "trim"}},
|
||||||
table = {fields = {"copy", "getn", "indexof", "insert_all"}},
|
table = {fields = {"copy", "getn", "indexof", "keyof", "insert_all"}},
|
||||||
math = {fields = {"hypot", "round"}},
|
math = {fields = {"hypot", "round"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +39,12 @@ files["builtin/client/register.lua"] = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
files["builtin/common/math.lua"] = {
|
||||||
|
globals = {
|
||||||
|
"math",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
files["builtin/common/misc_helpers.lua"] = {
|
files["builtin/common/misc_helpers.lua"] = {
|
||||||
globals = {
|
globals = {
|
||||||
"dump", "dump2", "table", "math", "string",
|
"dump", "dump2", "table", "math", "string",
|
||||||
|
@ -45,7 +54,7 @@ files["builtin/common/misc_helpers.lua"] = {
|
||||||
}
|
}
|
||||||
|
|
||||||
files["builtin/common/vector.lua"] = {
|
files["builtin/common/vector.lua"] = {
|
||||||
globals = { "vector" },
|
globals = { "vector", "math" },
|
||||||
}
|
}
|
||||||
|
|
||||||
files["builtin/game/voxelarea.lua"] = {
|
files["builtin/game/voxelarea.lua"] = {
|
||||||
|
@ -67,10 +76,6 @@ files["builtin/mainmenu"] = {
|
||||||
globals = {
|
globals = {
|
||||||
"gamedata",
|
"gamedata",
|
||||||
},
|
},
|
||||||
|
|
||||||
read_globals = {
|
|
||||||
"PLATFORM",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
files["builtin/common/tests"] = {
|
files["builtin/common/tests"] = {
|
||||||
|
|
10
.mailmap
10
.mailmap
|
@ -37,7 +37,7 @@ numzero <numzer0@yandex.ru> <silverunicorn2011@yandex.ru>
|
||||||
Jean-Patrick Guerrero <kilbith@users.noreply.github.com>
|
Jean-Patrick Guerrero <kilbith@users.noreply.github.com>
|
||||||
Jean-Patrick Guerrero <kilbith@users.noreply.github.com> <jeanpatrick.guerrero@gmail.com>
|
Jean-Patrick Guerrero <kilbith@users.noreply.github.com> <jeanpatrick.guerrero@gmail.com>
|
||||||
HybridDog <3192173+HybridDog@users.noreply.github.com> <ovvv@web.de>
|
HybridDog <3192173+HybridDog@users.noreply.github.com> <ovvv@web.de>
|
||||||
srfqi <muhammadrifqipriyosusanto@gmail.com>
|
srifqi <muhammadrifqipriyosusanto@gmail.com>
|
||||||
Dániel Juhász <juhdanad@gmail.com>
|
Dániel Juhász <juhdanad@gmail.com>
|
||||||
rubenwardy <rw@rubenwardy.com>
|
rubenwardy <rw@rubenwardy.com>
|
||||||
rubenwardy <rw@rubenwardy.com> <rubenwardy@gmail.com>
|
rubenwardy <rw@rubenwardy.com> <rubenwardy@gmail.com>
|
||||||
|
@ -48,7 +48,8 @@ ClobberXD <ClobberXD@gmail.com> <ClobberXD@protonmail.com>
|
||||||
ClobberXD <ClobberXD@gmail.com> <36130650+ClobberXD@users.noreply.github.com>
|
ClobberXD <ClobberXD@gmail.com> <36130650+ClobberXD@users.noreply.github.com>
|
||||||
Auke Kok <sofar+github@foo-projects.org>
|
Auke Kok <sofar+github@foo-projects.org>
|
||||||
Auke Kok <sofar+github@foo-projects.org> <sofar@foo-projects.org>
|
Auke Kok <sofar+github@foo-projects.org> <sofar@foo-projects.org>
|
||||||
Desour <vorunbekannt75@web.de>
|
DS <ds.desour@proton.me>
|
||||||
|
DS <ds.desour@proton.me> <vorunbekannt75@web.de>
|
||||||
Nathanaëlle Courant <Ekdohibs@users.noreply.github.com> <nathanael.courant@laposte.net>
|
Nathanaëlle Courant <Ekdohibs@users.noreply.github.com> <nathanael.courant@laposte.net>
|
||||||
Ezhh <owlecho@live.com>
|
Ezhh <owlecho@live.com>
|
||||||
paramat <paramat@users.noreply.github.com>
|
paramat <paramat@users.noreply.github.com>
|
||||||
|
@ -67,3 +68,8 @@ gregorycu <gregory.currie@gmail.com>
|
||||||
Rogier <rogier777@gmail.com>
|
Rogier <rogier777@gmail.com>
|
||||||
Rogier <rogier777@gmail.com> <Rogier-5@users.noreply.github.com>
|
Rogier <rogier777@gmail.com> <Rogier-5@users.noreply.github.com>
|
||||||
x2048 <codeforsmile@gmail.com>
|
x2048 <codeforsmile@gmail.com>
|
||||||
|
Lars Müller <appgurulars@gmx.de>
|
||||||
|
Lars Müller <appgurulars@gmx.de> <34514239+appgurueu@users.noreply.github.com>
|
||||||
|
ROllerozxa <rollerozxa@voxelmanip.se>
|
||||||
|
ROllerozxa <rollerozxa@voxelmanip.se> <temporaryemail4meh+github@gmail.com>
|
||||||
|
Ælla Chiana Moskopp <erle@dieweltistgarnichtso.net> <nils@dieweltistgarnichtso.net>
|
||||||
|
|
5
.vscode/extensions.json
vendored
Normal file
5
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"ms-vscode.cpptools-extension-pack"
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,54 +0,0 @@
|
||||||
version: 1
|
|
||||||
|
|
||||||
AppDir:
|
|
||||||
path: ./AppDir
|
|
||||||
|
|
||||||
app_info:
|
|
||||||
id: minetest
|
|
||||||
name: Minetest
|
|
||||||
icon: minetest
|
|
||||||
version: !ENV ${VERSION}
|
|
||||||
exec: usr/bin/minetest
|
|
||||||
exec_args: $@
|
|
||||||
runtime:
|
|
||||||
env:
|
|
||||||
APPDIR_LIBRARY_PATH: $APPDIR/usr/lib/x86_64-linux-gnu
|
|
||||||
|
|
||||||
apt:
|
|
||||||
arch: amd64
|
|
||||||
sources:
|
|
||||||
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic main universe
|
|
||||||
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3b4fe6acc0b21f32'
|
|
||||||
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-updates main universe
|
|
||||||
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-backports main universe
|
|
||||||
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-security main universe
|
|
||||||
|
|
||||||
include:
|
|
||||||
- libc6
|
|
||||||
- libcurl3-gnutls
|
|
||||||
- libfreetype6
|
|
||||||
- libgl1
|
|
||||||
- libjpeg-turbo8
|
|
||||||
- libjsoncpp1
|
|
||||||
- libleveldb1v5
|
|
||||||
- libopenal1
|
|
||||||
- libpng16-16
|
|
||||||
- libsqlite3-0
|
|
||||||
- libstdc++6
|
|
||||||
- libvorbisfile3
|
|
||||||
- libx11-6
|
|
||||||
- libxxf86vm1
|
|
||||||
- zlib1g
|
|
||||||
|
|
||||||
files:
|
|
||||||
exclude:
|
|
||||||
- usr/share/man
|
|
||||||
- usr/share/doc/*/README.*
|
|
||||||
- usr/share/doc/*/changelog.*
|
|
||||||
- usr/share/doc/*/NEWS.*
|
|
||||||
- usr/share/doc/*/TODO.*
|
|
||||||
|
|
||||||
AppImage:
|
|
||||||
update-information: None
|
|
||||||
sign-key: None
|
|
||||||
arch: x86_64
|
|
275
CMakeLists.txt
275
CMakeLists.txt
|
@ -1,29 +1,22 @@
|
||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.12)
|
||||||
|
|
||||||
# Set policies up to 3.9 since we want to enable the IPO option
|
|
||||||
if(${CMAKE_VERSION} VERSION_LESS 3.9)
|
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
|
||||||
else()
|
|
||||||
cmake_policy(VERSION 3.9)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# This can be read from ${PROJECT_NAME} after project() is called
|
# This can be read from ${PROJECT_NAME} after project() is called
|
||||||
project(minetest)
|
project(luanti)
|
||||||
set(PROJECT_NAME_CAPITALIZED "Minetest")
|
set(PROJECT_NAME_CAPITALIZED "Luanti")
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 14)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||||
set(GCC_MINIMUM_VERSION "5.1")
|
set(GCC_MINIMUM_VERSION "7.5")
|
||||||
set(CLANG_MINIMUM_VERSION "3.5")
|
set(CLANG_MINIMUM_VERSION "7.0.1")
|
||||||
|
|
||||||
# Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing
|
# You should not need to edit these manually, use util/bump_version.sh
|
||||||
set(VERSION_MAJOR 5)
|
set(VERSION_MAJOR 5)
|
||||||
set(VERSION_MINOR 6)
|
set(VERSION_MINOR 12)
|
||||||
set(VERSION_PATCH 0)
|
set(VERSION_PATCH 0)
|
||||||
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
|
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
|
||||||
|
|
||||||
# Change to false for releases
|
# Change to false for releases
|
||||||
set(DEVELOPMENT_BUILD FALSE)
|
set(DEVELOPMENT_BUILD TRUE)
|
||||||
|
|
||||||
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
|
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
|
||||||
if(VERSION_EXTRA)
|
if(VERSION_EXTRA)
|
||||||
|
@ -37,10 +30,37 @@ if (CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||||
set(VERSION_STRING "${VERSION_STRING}-debug")
|
set(VERSION_STRING "${VERSION_STRING}-debug")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
message(STATUS "*** Will build version ${VERSION_STRING} ***")
|
|
||||||
|
|
||||||
|
|
||||||
# Configuration options
|
# Configuration options
|
||||||
|
set(BUILD_CLIENT TRUE CACHE BOOL "Build client")
|
||||||
|
set(BUILD_SERVER FALSE CACHE BOOL "Build server")
|
||||||
|
set(BUILD_UNITTESTS TRUE CACHE BOOL "Build unittests")
|
||||||
|
set(BUILD_BENCHMARKS FALSE CACHE BOOL "Build benchmarks")
|
||||||
|
set(BUILD_DOCUMENTATION TRUE CACHE BOOL "Build documentation")
|
||||||
|
|
||||||
|
set(DEFAULT_ENABLE_LTO TRUE)
|
||||||
|
# by default don't enable on Debug builds to get faster builds
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
set(DEFAULT_ENABLE_LTO FALSE)
|
||||||
|
endif()
|
||||||
|
#### LTO testing list ####
|
||||||
|
# - Linux: seems to work always
|
||||||
|
# - win32/msvc: works
|
||||||
|
# - win32/gcc: fails to link
|
||||||
|
# - win32/clang: works
|
||||||
|
# - macOS on x86: seems to be fine
|
||||||
|
# - macOS on ARM: crashes, see <https://github.com/luanti-org/luanti/issues/14397>
|
||||||
|
# Note: since CMake has no easy architecture detection disabling for Mac entirely
|
||||||
|
#### ####
|
||||||
|
if((WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR APPLE)
|
||||||
|
set(DEFAULT_ENABLE_LTO FALSE)
|
||||||
|
endif()
|
||||||
|
set(ENABLE_LTO ${DEFAULT_ENABLE_LTO} CACHE BOOL "Use Link Time Optimization")
|
||||||
|
|
||||||
|
set(BUILD_WITH_TRACY FALSE CACHE BOOL
|
||||||
|
"Fetch and build with the Tracy profiler client")
|
||||||
|
set(FETCH_TRACY_GIT_TAG "master" CACHE STRING
|
||||||
|
"Git tag for fetching Tracy client. Match with your server (gui) version")
|
||||||
|
|
||||||
set(DEFAULT_RUN_IN_PLACE FALSE)
|
set(DEFAULT_RUN_IN_PLACE FALSE)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(DEFAULT_RUN_IN_PLACE TRUE)
|
set(DEFAULT_RUN_IN_PLACE TRUE)
|
||||||
|
@ -48,11 +68,13 @@ endif()
|
||||||
set(RUN_IN_PLACE ${DEFAULT_RUN_IN_PLACE} CACHE BOOL
|
set(RUN_IN_PLACE ${DEFAULT_RUN_IN_PLACE} CACHE BOOL
|
||||||
"Run directly in source directory structure")
|
"Run directly in source directory structure")
|
||||||
|
|
||||||
|
message(STATUS "*** Will build version ${VERSION_STRING} ***")
|
||||||
set(BUILD_CLIENT TRUE CACHE BOOL "Build client")
|
message(STATUS "BUILD_CLIENT: " ${BUILD_CLIENT})
|
||||||
set(BUILD_SERVER FALSE CACHE BOOL "Build server")
|
message(STATUS "BUILD_SERVER: " ${BUILD_SERVER})
|
||||||
set(BUILD_UNITTESTS TRUE CACHE BOOL "Build unittests")
|
message(STATUS "BUILD_UNITTESTS: " ${BUILD_UNITTESTS})
|
||||||
set(BUILD_BENCHMARKS FALSE CACHE BOOL "Build benchmarks")
|
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")
|
set(WARN_ALL TRUE CACHE BOOL "Enable -Wall for Release build")
|
||||||
|
|
||||||
|
@ -67,27 +89,17 @@ set(ENABLE_UPDATE_CHECKER (NOT ${DEVELOPMENT_BUILD}) CACHE BOOL
|
||||||
# Included stuff
|
# Included stuff
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
|
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(AndroidLibs)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(IRRLICHTMT_BUILD_DIR "" CACHE PATH "Path to IrrlichtMt build directory.")
|
|
||||||
if(NOT "${IRRLICHTMT_BUILD_DIR}" STREQUAL "")
|
|
||||||
find_package(IrrlichtMt QUIET
|
|
||||||
PATHS "${IRRLICHTMT_BUILD_DIR}"
|
|
||||||
NO_DEFAULT_PATH
|
|
||||||
)
|
|
||||||
|
|
||||||
if(NOT TARGET IrrlichtMt::IrrlichtMt)
|
if(TRUE)
|
||||||
# find_package() searches certain subdirectories. ${PATH}/cmake is not
|
message(STATUS "Using imported IrrlichtMt at subdirectory 'irr'")
|
||||||
# the only one, but it is the one where IrrlichtMt is supposed to export
|
|
||||||
# IrrlichtMtConfig.cmake
|
|
||||||
message(FATAL_ERROR "Could not find IrrlichtMtConfig.cmake in ${IRRLICHTMT_BUILD_DIR}/cmake.")
|
|
||||||
endif()
|
|
||||||
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/lib/irrlichtmt")
|
|
||||||
message(STATUS "Using user-provided IrrlichtMt at subdirectory 'lib/irrlichtmt'")
|
|
||||||
if(BUILD_CLIENT)
|
if(BUILD_CLIENT)
|
||||||
# tell IrrlichtMt to create a static library
|
add_subdirectory(irr EXCLUDE_FROM_ALL)
|
||||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared library" FORCE)
|
|
||||||
add_subdirectory(lib/irrlichtmt EXCLUDE_FROM_ALL)
|
|
||||||
unset(BUILD_SHARED_LIBS CACHE)
|
|
||||||
|
|
||||||
if(NOT TARGET IrrlichtMt)
|
if(NOT TARGET IrrlichtMt)
|
||||||
message(FATAL_ERROR "IrrlichtMt project is missing a CMake target?!")
|
message(FATAL_ERROR "IrrlichtMt project is missing a CMake target?!")
|
||||||
|
@ -95,48 +107,36 @@ elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/lib/irrlichtmt")
|
||||||
else()
|
else()
|
||||||
add_library(IrrlichtMt::IrrlichtMt INTERFACE IMPORTED)
|
add_library(IrrlichtMt::IrrlichtMt INTERFACE IMPORTED)
|
||||||
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
|
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/irrlichtmt/include")
|
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/irr/include")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (ENABLE_LTO OR CMAKE_INTERPROCEDURAL_OPTIMIZATION)
|
||||||
|
include(CheckIPOSupported)
|
||||||
|
check_ipo_supported(RESULT lto_supported OUTPUT lto_output)
|
||||||
|
if(lto_supported)
|
||||||
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||||
|
message(STATUS "LTO/IPO is enabled")
|
||||||
|
else()
|
||||||
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE)
|
||||||
|
message(STATUS "LTO/IPO was requested but is not supported by the compiler: ${lto_output}")
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
find_package(IrrlichtMt QUIET)
|
message(STATUS "LTO/IPO is not enabled")
|
||||||
if(NOT TARGET IrrlichtMt::IrrlichtMt)
|
|
||||||
string(CONCAT explanation_msg
|
|
||||||
"The Minetest team has forked Irrlicht to make their own customizations. "
|
|
||||||
"It can be found here: https://github.com/minetest/irrlicht\n"
|
|
||||||
"For example use: git clone --depth=1 https://github.com/minetest/irrlicht lib/irrlichtmt\n")
|
|
||||||
if(BUILD_CLIENT)
|
|
||||||
message(FATAL_ERROR "IrrlichtMt is required to build the client, but it was not found.\n${explanation_msg}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include(MinetestFindIrrlichtHeaders)
|
|
||||||
if(NOT IRRLICHT_INCLUDE_DIR)
|
|
||||||
message(FATAL_ERROR "IrrlichtMt headers are required to build the server, but none found.\n${explanation_msg}")
|
|
||||||
endif()
|
|
||||||
message(STATUS "Found IrrlichtMt headers: ${IRRLICHT_INCLUDE_DIR}")
|
|
||||||
add_library(IrrlichtMt::IrrlichtMt INTERFACE IMPORTED)
|
|
||||||
# Note that we can't use target_include_directories() since that doesn't work for IMPORTED targets before CMake 3.11
|
|
||||||
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${IRRLICHT_INCLUDE_DIR}")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(BUILD_CLIENT AND TARGET IrrlichtMt::IrrlichtMt)
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
# retrieve version somehow
|
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "${GCC_MINIMUM_VERSION}")
|
||||||
if(NOT IrrlichtMt_VERSION)
|
message(FATAL_ERROR "Insufficient gcc version, found ${CMAKE_CXX_COMPILER_VERSION}. "
|
||||||
get_target_property(IrrlichtMt_VERSION IrrlichtMt VERSION)
|
"Version ${GCC_MINIMUM_VERSION} or higher is required.")
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "Found IrrlichtMt ${IrrlichtMt_VERSION}")
|
elseif(CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?Clang")
|
||||||
|
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "${CLANG_MINIMUM_VERSION}")
|
||||||
set(TARGET_VER_S 1.9.0mt7)
|
message(FATAL_ERROR "Insufficient clang version, found ${CMAKE_CXX_COMPILER_VERSION}. "
|
||||||
string(REPLACE "mt" "." TARGET_VER ${TARGET_VER_S})
|
"Version ${CLANG_MINIMUM_VERSION} or higher is required.")
|
||||||
if(IrrlichtMt_VERSION VERSION_LESS ${TARGET_VER})
|
|
||||||
message(FATAL_ERROR "At least IrrlichtMt ${TARGET_VER_S} is required to build")
|
|
||||||
elseif(NOT DEVELOPMENT_BUILD AND IrrlichtMt_VERSION VERSION_GREATER ${TARGET_VER})
|
|
||||||
message(FATAL_ERROR "IrrlichtMt ${TARGET_VER_S} is required to build")
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
|
@ -161,7 +161,7 @@ elseif(UNIX) # Linux, BSD etc
|
||||||
set(EXAMPLE_CONF_DIR ".")
|
set(EXAMPLE_CONF_DIR ".")
|
||||||
set(MANDIR "unix/man")
|
set(MANDIR "unix/man")
|
||||||
set(XDG_APPS_DIR "unix/applications")
|
set(XDG_APPS_DIR "unix/applications")
|
||||||
set(APPDATADIR "unix/metainfo")
|
set(METAINFODIR "unix/metainfo")
|
||||||
set(ICONDIR "unix/icons")
|
set(ICONDIR "unix/icons")
|
||||||
set(LOCALEDIR "locale")
|
set(LOCALEDIR "locale")
|
||||||
else()
|
else()
|
||||||
|
@ -172,7 +172,7 @@ elseif(UNIX) # Linux, BSD etc
|
||||||
set(MANDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_MANDIR}")
|
set(MANDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_MANDIR}")
|
||||||
set(EXAMPLE_CONF_DIR ${DOCDIR})
|
set(EXAMPLE_CONF_DIR ${DOCDIR})
|
||||||
set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/applications")
|
set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/applications")
|
||||||
set(APPDATADIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/metainfo")
|
set(METAINFODIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/metainfo")
|
||||||
set(ICONDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/icons")
|
set(ICONDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/icons")
|
||||||
set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALEDIR}")
|
set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALEDIR}")
|
||||||
endif()
|
endif()
|
||||||
|
@ -236,10 +236,12 @@ if(RUN_IN_PLACE)
|
||||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/textures/texture_packs_here.txt" DESTINATION "${SHAREDIR}/textures")
|
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/textures/texture_packs_here.txt" DESTINATION "${SHAREDIR}/textures")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/minetest_game" DESTINATION "${SHAREDIR}/games/"
|
set(INSTALL_DEVTEST FALSE CACHE BOOL "Install Development Test")
|
||||||
COMPONENT "SUBGAME_MINETEST_GAME" OPTIONAL PATTERN ".git*" EXCLUDE )
|
|
||||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/devtest" DESTINATION "${SHAREDIR}/games/"
|
if(INSTALL_DEVTEST)
|
||||||
COMPONENT "SUBGAME_MINIMAL" OPTIONAL PATTERN ".git*" EXCLUDE )
|
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/devtest" DESTINATION "${SHAREDIR}/games/"
|
||||||
|
PATTERN ".git*" EXCLUDE )
|
||||||
|
endif()
|
||||||
|
|
||||||
if(BUILD_CLIENT)
|
if(BUILD_CLIENT)
|
||||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/client/shaders" DESTINATION "${SHAREDIR}/client")
|
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/client/shaders" DESTINATION "${SHAREDIR}/client")
|
||||||
|
@ -251,59 +253,51 @@ if(BUILD_CLIENT)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install(FILES "README.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
install(FILES "README.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||||
install(FILES "doc/lua_api.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
install(FILES "doc/lua_api.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||||
install(FILES "doc/client_lua_api.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
install(FILES "doc/client_lua_api.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||||
install(FILES "doc/menu_lua_api.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
install(FILES "doc/menu_lua_api.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||||
install(FILES "doc/texture_packs.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
install(FILES "doc/texture_packs.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||||
install(FILES "doc/world_format.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
install(FILES "doc/world_format.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
|
||||||
install(FILES "minetest.conf.example" DESTINATION "${EXAMPLE_CONF_DIR}")
|
install(FILES "minetest.conf.example" DESTINATION "${EXAMPLE_CONF_DIR}")
|
||||||
|
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
install(FILES "doc/minetest.6" "doc/minetestserver.6" DESTINATION "${MANDIR}/man6")
|
install(FILES "doc/luanti.6" "doc/luantiserver.6" DESTINATION "${MANDIR}/man6")
|
||||||
install(FILES "misc/net.minetest.minetest.desktop" DESTINATION "${XDG_APPS_DIR}")
|
install(FILES "misc/net.minetest.minetest.desktop" DESTINATION "${XDG_APPS_DIR}")
|
||||||
install(FILES "misc/net.minetest.minetest.appdata.xml" DESTINATION "${APPDATADIR}")
|
install(FILES "misc/net.minetest.minetest.metainfo.xml" DESTINATION "${METAINFODIR}")
|
||||||
install(FILES "misc/minetest.svg" DESTINATION "${ICONDIR}/hicolor/scalable/apps")
|
install(FILES "misc/luanti.svg" DESTINATION "${ICONDIR}/hicolor/scalable/apps")
|
||||||
install(FILES "misc/minetest-xorg-icon-128.png"
|
install(FILES "misc/luanti-xorg-icon-128.png"
|
||||||
DESTINATION "${ICONDIR}/hicolor/128x128/apps"
|
DESTINATION "${ICONDIR}/hicolor/128x128/apps"
|
||||||
RENAME "minetest.png")
|
RENAME "luanti.png")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
install(FILES "misc/minetest-icon.icns" DESTINATION "${SHAREDIR}")
|
install(FILES "misc/luanti-icon.icns" DESTINATION "${SHAREDIR}")
|
||||||
install(FILES "misc/Info.plist" DESTINATION "${BUNDLE_PATH}/Contents")
|
install(FILES "${CMAKE_BINARY_DIR}/Info.plist" DESTINATION "${BUNDLE_PATH}/Contents")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_GENERATOR STREQUAL "Xcode")
|
||||||
|
set(client_RESOURCES "${CMAKE_SOURCE_DIR}/misc/luanti-icon.icns")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Library pack
|
# Library pack
|
||||||
find_package(GMP REQUIRED)
|
find_package(GMP REQUIRED)
|
||||||
find_package(Json REQUIRED)
|
find_package(Json 1.0.0 REQUIRED)
|
||||||
find_package(Lua REQUIRED)
|
find_package(Lua REQUIRED)
|
||||||
if(NOT USE_LUAJIT)
|
if(NOT USE_LUAJIT)
|
||||||
set(LUA_BIT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/bitop)
|
|
||||||
set(LUA_BIT_LIBRARY bitop)
|
|
||||||
add_subdirectory(lib/bitop)
|
add_subdirectory(lib/bitop)
|
||||||
endif()
|
endif()
|
||||||
|
add_subdirectory(lib/sha256)
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
if(BUILD_UNITTESTS OR BUILD_BENCHMARKS)
|
||||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "${GCC_MINIMUM_VERSION}")
|
|
||||||
message(FATAL_ERROR "Insufficient gcc version, found ${CMAKE_CXX_COMPILER_VERSION}. "
|
|
||||||
"Version ${GCC_MINIMUM_VERSION} or higher is required.")
|
|
||||||
endif()
|
|
||||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?Clang")
|
|
||||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "${CLANG_MINIMUM_VERSION}")
|
|
||||||
message(FATAL_ERROR "Insufficient clang version, found ${CMAKE_CXX_COMPILER_VERSION}. "
|
|
||||||
"Version ${CLANG_MINIMUM_VERSION} or higher is required.")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_BENCHMARKS)
|
|
||||||
add_subdirectory(lib/catch2)
|
add_subdirectory(lib/catch2)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory(lib/tiniergltf)
|
||||||
|
|
||||||
# Subdirectories
|
# Subdirectories
|
||||||
# Be sure to add all relevant definitions above this
|
# Be sure to add all relevant definitions above this
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
||||||
|
|
||||||
# CPack
|
# CPack
|
||||||
|
|
||||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A free open-source voxel game engine with easy modding and game creation.")
|
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A free open-source voxel game engine with easy modding and game creation.")
|
||||||
|
@ -317,28 +311,11 @@ include(CPackComponent)
|
||||||
|
|
||||||
cpack_add_component(Docs
|
cpack_add_component(Docs
|
||||||
DISPLAY_NAME "Documentation"
|
DISPLAY_NAME "Documentation"
|
||||||
DESCRIPTION "Documentation about Minetest and Minetest modding"
|
DESCRIPTION "Documentation about ${PROJECT_NAME_CAPITALIZED} and ${PROJECT_NAME_CAPITALIZED} 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(SUBGAME_MINIMAL
|
|
||||||
DISPLAY_NAME "Development Test"
|
|
||||||
DESCRIPTION "A basic testing environment used for engine development and sometimes for testing mods."
|
|
||||||
DISABLED #DISABLED does not mean it is disabled, and is just not selected by default.
|
|
||||||
GROUP "Games"
|
|
||||||
)
|
|
||||||
|
|
||||||
cpack_add_component_group(Subgames
|
|
||||||
DESCRIPTION "Games for the Minetest engine."
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
# Include all dynamically linked runtime libaries such as MSVCRxxx.dll
|
# Include all dynamically linked runtime libraries such as MSVCRxxx.dll
|
||||||
include(InstallRequiredSystemLibraries)
|
include(InstallRequiredSystemLibraries)
|
||||||
|
|
||||||
if(RUN_IN_PLACE)
|
if(RUN_IN_PLACE)
|
||||||
|
@ -358,7 +335,7 @@ if(WIN32)
|
||||||
set(CPACK_CREATE_DESKTOP_LINKS ${PROJECT_NAME})
|
set(CPACK_CREATE_DESKTOP_LINKS ${PROJECT_NAME})
|
||||||
set(CPACK_PACKAGING_INSTALL_PREFIX "/${PROJECT_NAME_CAPITALIZED}")
|
set(CPACK_PACKAGING_INSTALL_PREFIX "/${PROJECT_NAME_CAPITALIZED}")
|
||||||
|
|
||||||
set(CPACK_WIX_PRODUCT_ICON "${CMAKE_CURRENT_SOURCE_DIR}/misc/minetest-icon.ico")
|
set(CPACK_WIX_PRODUCT_ICON "${CMAKE_CURRENT_SOURCE_DIR}/misc/luanti-icon.ico")
|
||||||
# Supported languages can be found at
|
# Supported languages can be found at
|
||||||
# http://wixtoolset.org/documentation/manual/v3/wixui/wixui_localization.html
|
# http://wixtoolset.org/documentation/manual/v3/wixui/wixui_localization.html
|
||||||
#set(CPACK_WIX_CULTURES "ar-SA,bg-BG,ca-ES,hr-HR,cs-CZ,da-DK,nl-NL,en-US,et-EE,fi-FI,fr-FR,de-DE")
|
#set(CPACK_WIX_CULTURES "ar-SA,bg-BG,ca-ES,hr-HR,cs-CZ,da-DK,nl-NL,en-US,et-EE,fi-FI,fr-FR,de-DE")
|
||||||
|
@ -390,13 +367,31 @@ include(CPack)
|
||||||
|
|
||||||
|
|
||||||
# Add a target to generate API documentation with Doxygen
|
# Add a target to generate API documentation with Doxygen
|
||||||
find_package(Doxygen)
|
if(BUILD_DOCUMENTATION)
|
||||||
if(DOXYGEN_FOUND)
|
find_package(Doxygen)
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile.in
|
if(DOXYGEN_FOUND)
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile @ONLY)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile.in
|
||||||
add_custom_target(doc
|
${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile @ONLY)
|
||||||
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile
|
add_custom_target(doc
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc
|
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile
|
||||||
COMMENT "Generating API documentation with Doxygen" VERBATIM
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc
|
||||||
)
|
COMMENT "Generating API documentation with Doxygen" VERBATIM
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Fetch Tracy
|
||||||
|
if(BUILD_WITH_TRACY)
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
message(STATUS "Fetching Tracy (${FETCH_TRACY_GIT_TAG})...")
|
||||||
|
FetchContent_Declare(
|
||||||
|
tracy
|
||||||
|
GIT_REPOSITORY https://github.com/wolfpld/tracy.git
|
||||||
|
GIT_TAG ${FETCH_TRACY_GIT_TAG}
|
||||||
|
GIT_SHALLOW TRUE
|
||||||
|
GIT_PROGRESS TRUE
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(tracy)
|
||||||
|
message(STATUS "Fetching Tracy - done")
|
||||||
endif()
|
endif()
|
||||||
|
|
41
CMakePresets.json
Normal file
41
CMakePresets.json
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"cmakeMinimumRequired": {
|
||||||
|
"major": 3,
|
||||||
|
"minor": 12
|
||||||
|
},
|
||||||
|
"configurePresets": [
|
||||||
|
{
|
||||||
|
"name": "Debug",
|
||||||
|
"displayName": "Debug",
|
||||||
|
"description": "Debug preset with debug symbols and no optimizations",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Release",
|
||||||
|
"displayName": "Release",
|
||||||
|
"description": "Release preset with optimizations and no debug symbols",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Release"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "RelWithDebInfo",
|
||||||
|
"displayName": "RelWithDebInfo",
|
||||||
|
"description": "Release with debug symbols",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MinSizeRel",
|
||||||
|
"displayName": "MinSizeRel",
|
||||||
|
"description": "Release with minimal code size",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "MinSizeRel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
1
CNAME
Normal file
1
CNAME
Normal file
|
@ -0,0 +1 @@
|
||||||
|
api.luanti.org
|
502
COPYING.LESSER
Normal file
502
COPYING.LESSER
Normal file
|
@ -0,0 +1,502 @@
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 2.1, February 1999
|
||||||
|
|
||||||
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the Lesser GPL. It also counts
|
||||||
|
as the successor of the GNU Library Public License, version 2, hence
|
||||||
|
the version number 2.1.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
Licenses are intended to guarantee your freedom to share and change
|
||||||
|
free software--to make sure the software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Lesser General Public License, applies to some
|
||||||
|
specially designated software packages--typically libraries--of the
|
||||||
|
Free Software Foundation and other authors who decide to use it. You
|
||||||
|
can use it too, but we suggest you first think carefully about whether
|
||||||
|
this license or the ordinary General Public License is the better
|
||||||
|
strategy to use in any particular case, based on the explanations below.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom of use,
|
||||||
|
not price. Our General Public Licenses are designed to make sure that
|
||||||
|
you have the freedom to distribute copies of free software (and charge
|
||||||
|
for this service if you wish); that you receive source code or can get
|
||||||
|
it if you want it; that you can change the software and use pieces of
|
||||||
|
it in new free programs; and that you are informed that you can do
|
||||||
|
these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
distributors to deny you these rights or to ask you to surrender these
|
||||||
|
rights. These restrictions translate to certain responsibilities for
|
||||||
|
you if you distribute copies of the library or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis
|
||||||
|
or for a fee, you must give the recipients all the rights that we gave
|
||||||
|
you. You must make sure that they, too, receive or can get the source
|
||||||
|
code. If you link other code with the library, you must provide
|
||||||
|
complete object files to the recipients, so that they can relink them
|
||||||
|
with the library after making changes to the library and recompiling
|
||||||
|
it. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with a two-step method: (1) we copyright the
|
||||||
|
library, and (2) we offer you this license, which gives you legal
|
||||||
|
permission to copy, distribute and/or modify the library.
|
||||||
|
|
||||||
|
To protect each distributor, we want to make it very clear that
|
||||||
|
there is no warranty for the free library. Also, if the library is
|
||||||
|
modified by someone else and passed on, the recipients should know
|
||||||
|
that what they have is not the original version, so that the original
|
||||||
|
author's reputation will not be affected by problems that might be
|
||||||
|
introduced by others.
|
||||||
|
|
||||||
|
Finally, software patents pose a constant threat to the existence of
|
||||||
|
any free program. We wish to make sure that a company cannot
|
||||||
|
effectively restrict the users of a free program by obtaining a
|
||||||
|
restrictive license from a patent holder. Therefore, we insist that
|
||||||
|
any patent license obtained for a version of the library must be
|
||||||
|
consistent with the full freedom of use specified in this license.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the
|
||||||
|
ordinary GNU General Public License. This license, the GNU Lesser
|
||||||
|
General Public License, applies to certain designated libraries, and
|
||||||
|
is quite different from the ordinary General Public License. We use
|
||||||
|
this license for certain libraries in order to permit linking those
|
||||||
|
libraries into non-free programs.
|
||||||
|
|
||||||
|
When a program is linked with a library, whether statically or using
|
||||||
|
a shared library, the combination of the two is legally speaking a
|
||||||
|
combined work, a derivative of the original library. The ordinary
|
||||||
|
General Public License therefore permits such linking only if the
|
||||||
|
entire combination fits its criteria of freedom. The Lesser General
|
||||||
|
Public License permits more lax criteria for linking other code with
|
||||||
|
the library.
|
||||||
|
|
||||||
|
We call this license the "Lesser" General Public License because it
|
||||||
|
does Less to protect the user's freedom than the ordinary General
|
||||||
|
Public License. It also provides other free software developers Less
|
||||||
|
of an advantage over competing non-free programs. These disadvantages
|
||||||
|
are the reason we use the ordinary General Public License for many
|
||||||
|
libraries. However, the Lesser license provides advantages in certain
|
||||||
|
special circumstances.
|
||||||
|
|
||||||
|
For example, on rare occasions, there may be a special need to
|
||||||
|
encourage the widest possible use of a certain library, so that it becomes
|
||||||
|
a de-facto standard. To achieve this, non-free programs must be
|
||||||
|
allowed to use the library. A more frequent case is that a free
|
||||||
|
library does the same job as widely used non-free libraries. In this
|
||||||
|
case, there is little to gain by limiting the free library to free
|
||||||
|
software only, so we use the Lesser General Public License.
|
||||||
|
|
||||||
|
In other cases, permission to use a particular library in non-free
|
||||||
|
programs enables a greater number of people to use a large body of
|
||||||
|
free software. For example, permission to use the GNU C Library in
|
||||||
|
non-free programs enables many more people to use the whole GNU
|
||||||
|
operating system, as well as its variant, the GNU/Linux operating
|
||||||
|
system.
|
||||||
|
|
||||||
|
Although the Lesser General Public License is Less protective of the
|
||||||
|
users' freedom, it does ensure that the user of a program that is
|
||||||
|
linked with the Library has the freedom and the wherewithal to run
|
||||||
|
that program using a modified version of the Library.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow. Pay close attention to the difference between a
|
||||||
|
"work based on the library" and a "work that uses the library". The
|
||||||
|
former contains code derived from the library, whereas the latter must
|
||||||
|
be combined with the library in order to run.
|
||||||
|
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library or other
|
||||||
|
program which contains a notice placed by the copyright holder or
|
||||||
|
other authorized party saying it may be distributed under the terms of
|
||||||
|
this Lesser General Public License (also called "this License").
|
||||||
|
Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data
|
||||||
|
prepared so as to be conveniently linked with application programs
|
||||||
|
(which use some of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work
|
||||||
|
which has been distributed under these terms. A "work based on the
|
||||||
|
Library" means either the Library or any derivative work under
|
||||||
|
copyright law: that is to say, a work containing the Library or a
|
||||||
|
portion of it, either verbatim or with modifications and/or translated
|
||||||
|
straightforwardly into another language. (Hereinafter, translation is
|
||||||
|
included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For a library, complete source code means
|
||||||
|
all the source code for all modules it contains, plus any associated
|
||||||
|
interface definition files, plus the scripts used to control compilation
|
||||||
|
and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running a program using the Library is not restricted, and output from
|
||||||
|
such a program is covered only if its contents constitute a work based
|
||||||
|
on the Library (independent of the use of the Library in a tool for
|
||||||
|
writing it). Whether that is true depends on what the Library does
|
||||||
|
and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's
|
||||||
|
complete source code as you receive it, in any medium, provided that
|
||||||
|
you conspicuously and appropriately publish on each copy an
|
||||||
|
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||||
|
all the notices that refer to this License and to the absence of any
|
||||||
|
warranty; and distribute a copy of this License along with the
|
||||||
|
Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy,
|
||||||
|
and you may at your option offer warranty protection in exchange for a
|
||||||
|
fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion
|
||||||
|
of it, thus forming a work based on the Library, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no
|
||||||
|
charge to all third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a
|
||||||
|
table of data to be supplied by an application program that uses
|
||||||
|
the facility, other than as an argument passed when the facility
|
||||||
|
is invoked, then you must make a good faith effort to ensure that,
|
||||||
|
in the event an application does not supply such function or
|
||||||
|
table, the facility still operates, and performs whatever part of
|
||||||
|
its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has
|
||||||
|
a purpose that is entirely well-defined independent of the
|
||||||
|
application. Therefore, Subsection 2d requires that any
|
||||||
|
application-supplied function or table used by this function must
|
||||||
|
be optional: if the application does not supply it, the square
|
||||||
|
root function must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Library,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Library, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library
|
||||||
|
with the Library (or with a work based on the Library) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||||
|
License instead of this License to a given copy of the Library. To do
|
||||||
|
this, you must alter all the notices that refer to this License, so
|
||||||
|
that they refer to the ordinary GNU General Public License, version 2,
|
||||||
|
instead of to this License. (If a newer version than version 2 of the
|
||||||
|
ordinary GNU General Public License has appeared, then you can specify
|
||||||
|
that version instead if you wish.) Do not make any other change in
|
||||||
|
these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for
|
||||||
|
that copy, so the ordinary GNU General Public License applies to all
|
||||||
|
subsequent copies and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of
|
||||||
|
the Library into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or
|
||||||
|
derivative of it, under Section 2) in object code or executable form
|
||||||
|
under the terms of Sections 1 and 2 above provided that you accompany
|
||||||
|
it with the complete corresponding machine-readable source code, which
|
||||||
|
must be distributed under the terms of Sections 1 and 2 above on a
|
||||||
|
medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy
|
||||||
|
from a designated place, then offering equivalent access to copy the
|
||||||
|
source code from the same place satisfies the requirement to
|
||||||
|
distribute the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the
|
||||||
|
Library, but is designed to work with the Library by being compiled or
|
||||||
|
linked with it, is called a "work that uses the Library". Such a
|
||||||
|
work, in isolation, is not a derivative work of the Library, and
|
||||||
|
therefore falls outside the scope of this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library
|
||||||
|
creates an executable that is a derivative of the Library (because it
|
||||||
|
contains portions of the Library), rather than a "work that uses the
|
||||||
|
library". The executable is therefore covered by this License.
|
||||||
|
Section 6 states terms for distribution of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file
|
||||||
|
that is part of the Library, the object code for the work may be a
|
||||||
|
derivative work of the Library even though the source code is not.
|
||||||
|
Whether this is true is especially significant if the work can be
|
||||||
|
linked without the Library, or if the work is itself a library. The
|
||||||
|
threshold for this to be true is not precisely defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data
|
||||||
|
structure layouts and accessors, and small macros and small inline
|
||||||
|
functions (ten lines or less in length), then the use of the object
|
||||||
|
file is unrestricted, regardless of whether it is legally a derivative
|
||||||
|
work. (Executables containing this object code plus portions of the
|
||||||
|
Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may
|
||||||
|
distribute the object code for the work under the terms of Section 6.
|
||||||
|
Any executables containing that work also fall under Section 6,
|
||||||
|
whether or not they are linked directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also combine or
|
||||||
|
link a "work that uses the Library" with the Library to produce a
|
||||||
|
work containing portions of the Library, and distribute that work
|
||||||
|
under terms of your choice, provided that the terms permit
|
||||||
|
modification of the work for the customer's own use and reverse
|
||||||
|
engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the
|
||||||
|
Library is used in it and that the Library and its use are covered by
|
||||||
|
this License. You must supply a copy of this License. If the work
|
||||||
|
during execution displays copyright notices, you must include the
|
||||||
|
copyright notice for the Library among them, as well as a reference
|
||||||
|
directing the user to the copy of this License. Also, you must do one
|
||||||
|
of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding
|
||||||
|
machine-readable source code for the Library including whatever
|
||||||
|
changes were used in the work (which must be distributed under
|
||||||
|
Sections 1 and 2 above); and, if the work is an executable linked
|
||||||
|
with the Library, with the complete machine-readable "work that
|
||||||
|
uses the Library", as object code and/or source code, so that the
|
||||||
|
user can modify the Library and then relink to produce a modified
|
||||||
|
executable containing the modified Library. (It is understood
|
||||||
|
that the user who changes the contents of definitions files in the
|
||||||
|
Library will not necessarily be able to recompile the application
|
||||||
|
to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (1) uses at run time a
|
||||||
|
copy of the library already present on the user's computer system,
|
||||||
|
rather than copying library functions into the executable, and (2)
|
||||||
|
will operate properly with a modified version of the library, if
|
||||||
|
the user installs one, as long as the modified version is
|
||||||
|
interface-compatible with the version that the work was made with.
|
||||||
|
|
||||||
|
c) Accompany the work with a written offer, valid for at
|
||||||
|
least three years, to give the same user the materials
|
||||||
|
specified in Subsection 6a, above, for a charge no more
|
||||||
|
than the cost of performing this distribution.
|
||||||
|
|
||||||
|
d) If distribution of the work is made by offering access to copy
|
||||||
|
from a designated place, offer equivalent access to copy the above
|
||||||
|
specified materials from the same place.
|
||||||
|
|
||||||
|
e) Verify that the user has already received a copy of these
|
||||||
|
materials or that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the
|
||||||
|
Library" must include any data and utility programs needed for
|
||||||
|
reproducing the executable from it. However, as a special exception,
|
||||||
|
the materials to be distributed need not include anything that is
|
||||||
|
normally distributed (in either source or binary form) with the major
|
||||||
|
components (compiler, kernel, and so on) of the operating system on
|
||||||
|
which the executable runs, unless that component itself accompanies
|
||||||
|
the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license
|
||||||
|
restrictions of other proprietary libraries that do not normally
|
||||||
|
accompany the operating system. Such a contradiction means you cannot
|
||||||
|
use both them and the Library together in an executable that you
|
||||||
|
distribute.
|
||||||
|
|
||||||
|
7. You may place library facilities that are a work based on the
|
||||||
|
Library side-by-side in a single library together with other library
|
||||||
|
facilities not covered by this License, and distribute such a combined
|
||||||
|
library, provided that the separate distribution of the work based on
|
||||||
|
the Library and of the other library facilities is otherwise
|
||||||
|
permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work
|
||||||
|
based on the Library, uncombined with any other library
|
||||||
|
facilities. This must be distributed under the terms of the
|
||||||
|
Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact
|
||||||
|
that part of it is a work based on the Library, and explaining
|
||||||
|
where to find the accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute
|
||||||
|
the Library except as expressly provided under this License. Any
|
||||||
|
attempt otherwise to copy, modify, sublicense, link with, or
|
||||||
|
distribute the Library is void, and will automatically terminate your
|
||||||
|
rights under this License. However, parties who have received copies,
|
||||||
|
or rights, from you under this License will not have their licenses
|
||||||
|
terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Library or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Library (or any work based on the
|
||||||
|
Library), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the
|
||||||
|
Library), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute, link with or modify the Library
|
||||||
|
subject to these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties with
|
||||||
|
this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Library at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Library by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Library under this License may add
|
||||||
|
an explicit geographical distribution limitation excluding those countries,
|
||||||
|
so that distribution is permitted only in or among countries not thus
|
||||||
|
excluded. In such case, this License incorporates the limitation as if
|
||||||
|
written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new
|
||||||
|
versions of the Lesser General Public License from time to time.
|
||||||
|
Such new versions will be similar in spirit to the present version,
|
||||||
|
but may differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Library
|
||||||
|
specifies a version number of this License which applies to it and
|
||||||
|
"any later version", you have the option of following the terms and
|
||||||
|
conditions either of that version or of any later version published by
|
||||||
|
the Free Software Foundation. If the Library does not specify a
|
||||||
|
license version number, you may choose any version ever published by
|
||||||
|
the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free
|
||||||
|
programs whose distribution conditions are incompatible with these,
|
||||||
|
write to the author to ask for permission. For software which is
|
||||||
|
copyrighted by the Free Software Foundation, write to the Free
|
||||||
|
Software Foundation; we sometimes make exceptions for this. Our
|
||||||
|
decision will be guided by the two goals of preserving the free status
|
||||||
|
of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||||
|
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||||
|
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||||
|
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||||
|
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||||
|
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||||
|
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||||
|
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||||
|
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||||
|
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||||
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||||
|
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||||
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest
|
||||||
|
possible use to the public, we recommend making it free software that
|
||||||
|
everyone can redistribute and change. You can do so by permitting
|
||||||
|
redistribution under these terms (or, alternatively, under the terms of the
|
||||||
|
ordinary General Public License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It is
|
||||||
|
safest to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least the
|
||||||
|
"copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the library's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This library 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 library 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 library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||||
|
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1990
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
99
Dockerfile
99
Dockerfile
|
@ -1,71 +1,82 @@
|
||||||
ARG DOCKER_IMAGE=alpine:3.14
|
ARG DOCKER_IMAGE=alpine:3.19
|
||||||
FROM $DOCKER_IMAGE AS builder
|
FROM $DOCKER_IMAGE AS dev
|
||||||
|
|
||||||
ENV MINETEST_GAME_VERSION master
|
ENV LUAJIT_VERSION v2.1
|
||||||
ENV IRRLICHT_VERSION master
|
|
||||||
|
|
||||||
COPY .git /usr/src/minetest/.git
|
RUN apk add --no-cache git build-base cmake curl-dev zlib-dev zstd-dev \
|
||||||
COPY CMakeLists.txt /usr/src/minetest/CMakeLists.txt
|
sqlite-dev postgresql-dev hiredis-dev leveldb-dev \
|
||||||
COPY README.md /usr/src/minetest/README.md
|
gmp-dev jsoncpp-dev ninja ca-certificates
|
||||||
COPY minetest.conf.example /usr/src/minetest/minetest.conf.example
|
|
||||||
COPY builtin /usr/src/minetest/builtin
|
|
||||||
COPY cmake /usr/src/minetest/cmake
|
|
||||||
COPY doc /usr/src/minetest/doc
|
|
||||||
COPY fonts /usr/src/minetest/fonts
|
|
||||||
COPY lib /usr/src/minetest/lib
|
|
||||||
COPY misc /usr/src/minetest/misc
|
|
||||||
COPY po /usr/src/minetest/po
|
|
||||||
COPY src /usr/src/minetest/src
|
|
||||||
COPY textures /usr/src/minetest/textures
|
|
||||||
|
|
||||||
WORKDIR /usr/src/minetest
|
|
||||||
|
|
||||||
RUN apk add --no-cache git build-base cmake sqlite-dev curl-dev zlib-dev zstd-dev \
|
|
||||||
gmp-dev jsoncpp-dev postgresql-dev ninja luajit-dev ca-certificates && \
|
|
||||||
git clone --depth=1 -b ${MINETEST_GAME_VERSION} https://github.com/minetest/minetest_game.git ./games/minetest_game && \
|
|
||||||
rm -fr ./games/minetest_game/.git
|
|
||||||
|
|
||||||
WORKDIR /usr/src/
|
WORKDIR /usr/src/
|
||||||
RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp/ && \
|
RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp && \
|
||||||
cd prometheus-cpp && \
|
cd prometheus-cpp && \
|
||||||
cmake -B build \
|
cmake -B build \
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DENABLE_TESTING=0 \
|
-DENABLE_TESTING=0 \
|
||||||
-GNinja && \
|
-GNinja && \
|
||||||
cmake --build build && \
|
cmake --build build && \
|
||||||
cmake --install build
|
cmake --install build && \
|
||||||
|
cd /usr/src/ && \
|
||||||
|
git clone --recursive https://github.com/libspatialindex/libspatialindex && \
|
||||||
|
cd libspatialindex && \
|
||||||
|
cmake -B build \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/usr/local && \
|
||||||
|
cmake --build build && \
|
||||||
|
cmake --install build && \
|
||||||
|
cd /usr/src/ && \
|
||||||
|
git clone --recursive https://luajit.org/git/luajit.git -b ${LUAJIT_VERSION} && \
|
||||||
|
cd luajit && \
|
||||||
|
make amalg && make install && \
|
||||||
|
cd /usr/src/
|
||||||
|
|
||||||
RUN git clone --depth=1 https://github.com/minetest/irrlicht/ -b ${IRRLICHT_VERSION} && \
|
FROM dev as builder
|
||||||
cp -r irrlicht/include /usr/include/irrlichtmt
|
|
||||||
|
|
||||||
WORKDIR /usr/src/minetest
|
COPY .git /usr/src/luanti/.git
|
||||||
|
COPY CMakeLists.txt /usr/src/luanti/CMakeLists.txt
|
||||||
|
COPY README.md /usr/src/luanti/README.md
|
||||||
|
COPY minetest.conf.example /usr/src/luanti/minetest.conf.example
|
||||||
|
COPY builtin /usr/src/luanti/builtin
|
||||||
|
COPY cmake /usr/src/luanti/cmake
|
||||||
|
COPY doc /usr/src/luanti/doc
|
||||||
|
COPY fonts /usr/src/luanti/fonts
|
||||||
|
COPY lib /usr/src/luanti/lib
|
||||||
|
COPY misc /usr/src/luanti/misc
|
||||||
|
COPY po /usr/src/luanti/po
|
||||||
|
COPY src /usr/src/luanti/src
|
||||||
|
COPY irr /usr/src/luanti/irr
|
||||||
|
COPY textures /usr/src/luanti/textures
|
||||||
|
|
||||||
|
WORKDIR /usr/src/luanti
|
||||||
RUN cmake -B build \
|
RUN cmake -B build \
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DBUILD_SERVER=TRUE \
|
-DBUILD_SERVER=TRUE \
|
||||||
-DENABLE_PROMETHEUS=TRUE \
|
-DENABLE_PROMETHEUS=TRUE \
|
||||||
-DBUILD_UNITTESTS=FALSE \
|
-DBUILD_UNITTESTS=FALSE -DBUILD_BENCHMARKS=FALSE \
|
||||||
-DBUILD_CLIENT=FALSE \
|
-DBUILD_CLIENT=FALSE \
|
||||||
-GNinja && \
|
-GNinja && \
|
||||||
cmake --build build && \
|
cmake --build build && \
|
||||||
cmake --install build
|
cmake --install build
|
||||||
|
|
||||||
ARG DOCKER_IMAGE=alpine:3.14
|
|
||||||
FROM $DOCKER_IMAGE AS runtime
|
FROM $DOCKER_IMAGE AS runtime
|
||||||
|
|
||||||
RUN apk add --no-cache sqlite-libs curl gmp libstdc++ libgcc libpq luajit jsoncpp zstd-libs && \
|
RUN apk add --no-cache curl gmp libstdc++ libgcc libpq jsoncpp zstd-libs \
|
||||||
|
sqlite-libs postgresql hiredis leveldb && \
|
||||||
adduser -D minetest --uid 30000 -h /var/lib/minetest && \
|
adduser -D minetest --uid 30000 -h /var/lib/minetest && \
|
||||||
chown -R minetest:minetest /var/lib/minetest
|
chown -R minetest:minetest /var/lib/minetest
|
||||||
|
|
||||||
WORKDIR /var/lib/minetest
|
WORKDIR /var/lib/minetest
|
||||||
|
|
||||||
COPY --from=builder /usr/local/share/minetest /usr/local/share/minetest
|
COPY --from=builder /usr/local/share/luanti /usr/local/share/luanti
|
||||||
COPY --from=builder /usr/local/bin/minetestserver /usr/local/bin/minetestserver
|
COPY --from=builder /usr/local/bin/luantiserver /usr/local/bin/luantiserver
|
||||||
COPY --from=builder /usr/local/share/doc/minetest/minetest.conf.example /etc/minetest/minetest.conf
|
COPY --from=builder /usr/local/share/doc/luanti/minetest.conf.example /etc/minetest/minetest.conf
|
||||||
|
COPY --from=builder /usr/local/lib/libspatialindex* /usr/local/lib/
|
||||||
|
COPY --from=builder /usr/local/lib/libluajit* /usr/local/lib/
|
||||||
USER minetest:minetest
|
USER minetest:minetest
|
||||||
|
|
||||||
EXPOSE 30000/udp 30000/tcp
|
EXPOSE 30000/udp 30000/tcp
|
||||||
|
VOLUME /var/lib/minetest/ /etc/minetest/
|
||||||
|
|
||||||
CMD ["/usr/local/bin/minetestserver", "--config", "/etc/minetest/minetest.conf"]
|
ENTRYPOINT ["/usr/local/bin/luantiserver"]
|
||||||
|
CMD ["--config", "/etc/minetest/minetest.conf"]
|
||||||
|
|
65
LICENSE.txt
65
LICENSE.txt
|
@ -1,8 +1,8 @@
|
||||||
|
|
||||||
License of Minetest textures and sounds
|
License of Luanti textures and sounds
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
This applies to textures and sounds contained in the main Minetest
|
This applies to textures and sounds contained in the main Luanti
|
||||||
distribution.
|
distribution.
|
||||||
|
|
||||||
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
|
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
|
||||||
|
@ -14,6 +14,12 @@ https://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
Textures by Zughy are under CC BY-SA 4.0
|
Textures by Zughy are under CC BY-SA 4.0
|
||||||
https://creativecommons.org/licenses/by-sa/4.0/
|
https://creativecommons.org/licenses/by-sa/4.0/
|
||||||
|
|
||||||
|
Textures by cx384 are under CC BY-SA 4.0
|
||||||
|
https://creativecommons.org/licenses/by-sa/4.0/
|
||||||
|
|
||||||
|
Media files by DS are under CC BY-SA 4.0
|
||||||
|
https://creativecommons.org/licenses/by-sa/4.0/
|
||||||
|
|
||||||
textures/base/pack/server_public.png is under CC-BY 4.0, taken from Twitter's Twemoji set
|
textures/base/pack/server_public.png is under CC-BY 4.0, taken from Twitter's Twemoji set
|
||||||
https://creativecommons.org/licenses/by/4.0/
|
https://creativecommons.org/licenses/by/4.0/
|
||||||
|
|
||||||
|
@ -26,7 +32,6 @@ ShadowNinja:
|
||||||
textures/base/pack/smoke_puff.png
|
textures/base/pack/smoke_puff.png
|
||||||
|
|
||||||
paramat:
|
paramat:
|
||||||
textures/base/pack/menu_header.png
|
|
||||||
textures/base/pack/next_icon.png
|
textures/base/pack/next_icon.png
|
||||||
textures/base/pack/prev_icon.png
|
textures/base/pack/prev_icon.png
|
||||||
textures/base/pack/clear.png
|
textures/base/pack/clear.png
|
||||||
|
@ -36,10 +41,10 @@ rubenwardy, paramat:
|
||||||
textures/base/pack/start_icon.png
|
textures/base/pack/start_icon.png
|
||||||
textures/base/pack/end_icon.png
|
textures/base/pack/end_icon.png
|
||||||
|
|
||||||
erlehmann:
|
erle:
|
||||||
misc/minetest-icon-24x24.png
|
misc/luanti-icon-24x24.png
|
||||||
misc/minetest-icon.ico
|
misc/luanti-icon.ico
|
||||||
misc/minetest.svg
|
misc/luanti.svg
|
||||||
textures/base/pack/logo.png
|
textures/base/pack/logo.png
|
||||||
|
|
||||||
JRottm:
|
JRottm:
|
||||||
|
@ -54,30 +59,56 @@ srifqi:
|
||||||
textures/base/pack/minimap_btn.png
|
textures/base/pack/minimap_btn.png
|
||||||
|
|
||||||
Zughy:
|
Zughy:
|
||||||
textures/base/pack/cdb_add.png
|
|
||||||
textures/base/pack/cdb_clear.png
|
|
||||||
textures/base/pack/cdb_downloading.png
|
textures/base/pack/cdb_downloading.png
|
||||||
textures/base/pack/cdb_queued.png
|
textures/base/pack/cdb_queued.png
|
||||||
textures/base/pack/cdb_update.png
|
textures/base/pack/cdb_update.png
|
||||||
textures/base/pack/cdb_viewonline.png
|
textures/base/pack/cdb_update_cropped.png
|
||||||
|
textures/base/pack/settings_btn.png
|
||||||
|
textures/base/pack/settings_info.png
|
||||||
|
textures/base/pack/settings_reset.png
|
||||||
|
textures/base/pack/server_url.png
|
||||||
|
textures/base/pack/server_url_unavailable.png
|
||||||
|
textures/base/pack/server_view_clients.png
|
||||||
|
textures/base/pack/server_view_clients_unavailable.png
|
||||||
|
|
||||||
|
cx384:
|
||||||
|
textures/base/pack/server_view_mods.png
|
||||||
|
textures/base/pack/server_view_mods_unavailable.png
|
||||||
|
|
||||||
appgurueu:
|
appgurueu:
|
||||||
textures/base/pack/server_incompatible.png
|
textures/base/pack/server_incompatible.png
|
||||||
|
|
||||||
erlehmann, Warr1024, rollerozxa:
|
erle, Warr1024, rollerozxa:
|
||||||
textures/base/pack/no_screenshot.png
|
textures/base/pack/no_screenshot.png
|
||||||
|
|
||||||
kilbith:
|
kilbith:
|
||||||
textures/base/pack/server_favorite.png
|
textures/base/pack/server_favorite.png
|
||||||
|
textures/base/pack/progress_bar.png
|
||||||
|
textures/base/pack/progress_bar_bg.png
|
||||||
|
|
||||||
SmallJoker
|
SmallJoker:
|
||||||
textures/base/pack/server_favorite_delete.png (based on server_favorite.png)
|
textures/base/pack/server_favorite_delete.png (based on server_favorite.png)
|
||||||
|
|
||||||
License of Minetest source code
|
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
|
||||||
|
textures/base/pack/menu_header.png
|
||||||
|
using the font "undefined medium" (https://undefined-medium.com/),
|
||||||
|
which is licensed under the SIL Open Font License, Version 1.1
|
||||||
|
modified by DS
|
||||||
|
|
||||||
|
License of Luanti source code
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
Minetest
|
Luanti
|
||||||
Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
|
Copyright (C) 2010-2024 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||||
|
and contributors (see source file comments and the version control log)
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
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
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
@ -96,7 +127,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
Irrlicht
|
Irrlicht
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
This program uses IrrlichtMt, Minetest's fork of
|
This program uses IrrlichtMt, Luanti's fork of
|
||||||
the Irrlicht Engine. http://irrlicht.sourceforge.net/
|
the Irrlicht Engine. http://irrlicht.sourceforge.net/
|
||||||
|
|
||||||
The Irrlicht Engine License
|
The Irrlicht Engine License
|
||||||
|
|
384
README.md
384
README.md
|
@ -1,21 +1,17 @@
|
||||||
Minetest
|
<div align="center">
|
||||||
========
|
<img src="textures/base/pack/logo.png" width="32%">
|
||||||
|
<h1>Luanti (formerly Minetest)</h1>
|
||||||
|
<img src="https://github.com/luanti-org/luanti/workflows/build/badge.svg" alt="Build Status">
|
||||||
|
<a href="https://hosted.weblate.org/engage/minetest/?utm_source=widget"><img src="https://hosted.weblate.org/widgets/minetest/-/svg-badge.svg" alt="Translation status"></a>
|
||||||
|
<a href="https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html"><img src="https://img.shields.io/badge/license-LGPLv2.1%2B-blue.svg" alt="License"></a>
|
||||||
|
</div>
|
||||||
|
<br><br>
|
||||||
|
|
||||||

|
Luanti is a free open-source voxel game engine with easy modding and game creation.
|
||||||
[](https://hosted.weblate.org/engage/minetest/?utm_source=widget)
|
|
||||||
[](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html)
|
|
||||||
|
|
||||||
Minetest is a free open-source voxel game engine with easy modding and game creation.
|
Copyright (C) 2010-2025 Perttu Ahola <celeron55@gmail.com>
|
||||||
|
|
||||||
Copyright (C) 2010-2022 Perttu Ahola <celeron55@gmail.com>
|
|
||||||
and contributors (see source file comments and the version control log)
|
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
|
Table of Contents
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@ -31,11 +27,11 @@ Table of Contents
|
||||||
|
|
||||||
Further documentation
|
Further documentation
|
||||||
----------------------
|
----------------------
|
||||||
- Website: https://minetest.net/
|
- Website: https://www.luanti.org/
|
||||||
- Wiki: https://wiki.minetest.net/
|
- Wiki: https://wiki.luanti.org/
|
||||||
- Developer wiki: https://dev.minetest.net/
|
- Forum: https://forum.luanti.org/
|
||||||
- Forum: https://forum.minetest.net/
|
- GitHub: https://github.com/luanti-org/luanti/
|
||||||
- GitHub: https://github.com/minetest/minetest/
|
- [Developer documentation](doc/developing/)
|
||||||
- [doc/](doc/) directory of source distribution
|
- [doc/](doc/) directory of source distribution
|
||||||
|
|
||||||
Default controls
|
Default controls
|
||||||
|
@ -51,7 +47,7 @@ Some can be changed in the key config dialog in the settings tab.
|
||||||
| Shift | Sneak/move down |
|
| Shift | Sneak/move down |
|
||||||
| Q | Drop itemstack |
|
| Q | Drop itemstack |
|
||||||
| Shift + Q | Drop single item |
|
| Shift + Q | Drop single item |
|
||||||
| Left mouse button | Dig/punch/take item |
|
| Left mouse button | Dig/punch/use |
|
||||||
| Right mouse button | Place/use |
|
| Right mouse button | Place/use |
|
||||||
| Shift + right mouse button | Build (without using) |
|
| Shift + right mouse button | Build (without using) |
|
||||||
| I | Inventory menu |
|
| I | Inventory menu |
|
||||||
|
@ -61,11 +57,9 @@ Some can be changed in the key config dialog in the settings tab.
|
||||||
| T | Chat |
|
| T | Chat |
|
||||||
| / | Command |
|
| / | Command |
|
||||||
| Esc | Pause menu/abort/exit (pauses only singleplayer game) |
|
| Esc | Pause menu/abort/exit (pauses only singleplayer game) |
|
||||||
| R | Enable/disable full range view |
|
|
||||||
| + | Increase view range |
|
| + | Increase view range |
|
||||||
| - | Decrease view range |
|
| - | Decrease view range |
|
||||||
| K | Enable/disable fly mode (needs fly privilege) |
|
| K | Enable/disable fly mode (needs fly privilege) |
|
||||||
| P | Enable/disable pitch move mode |
|
|
||||||
| J | Enable/disable fast mode (needs fast privilege) |
|
| J | Enable/disable fast mode (needs fast privilege) |
|
||||||
| H | Enable/disable noclip mode (needs noclip privilege) |
|
| H | Enable/disable noclip mode (needs noclip privilege) |
|
||||||
| E | Aux1 (Move fast in fast mode. Games may add special features) |
|
| E | Aux1 (Move fast in fast mode. Games may add special features) |
|
||||||
|
@ -98,15 +92,15 @@ Where each location is on each platform:
|
||||||
* Windows installed:
|
* Windows installed:
|
||||||
* `bin` = `C:\Program Files\Minetest\bin (Depends on the install location)`
|
* `bin` = `C:\Program Files\Minetest\bin (Depends on the install location)`
|
||||||
* `share` = `C:\Program Files\Minetest (Depends on the install location)`
|
* `share` = `C:\Program Files\Minetest (Depends on the install location)`
|
||||||
* `user` = `%APPDATA%\Minetest`
|
* `user` = `%APPDATA%\Minetest` or `%MINETEST_USER_PATH%`
|
||||||
* Linux installed:
|
* Linux installed:
|
||||||
* `bin` = `/usr/bin`
|
* `bin` = `/usr/bin`
|
||||||
* `share` = `/usr/share/minetest`
|
* `share` = `/usr/share/minetest`
|
||||||
* `user` = `~/.minetest`
|
* `user` = `~/.minetest` or `$MINETEST_USER_PATH`
|
||||||
* macOS:
|
* macOS:
|
||||||
* `bin` = `Contents/MacOS`
|
* `bin` = `Contents/MacOS`
|
||||||
* `share` = `Contents/Resources`
|
* `share` = `Contents/Resources`
|
||||||
* `user` = `Contents/User OR ~/Library/Application Support/minetest`
|
* `user` = `Contents/User` or `~/Library/Application Support/minetest` or `$MINETEST_USER_PATH`
|
||||||
|
|
||||||
Worlds can be found as separate folders in: `user/worlds/`
|
Worlds can be found as separate folders in: `user/worlds/`
|
||||||
|
|
||||||
|
@ -114,7 +108,7 @@ Configuration file
|
||||||
------------------
|
------------------
|
||||||
- Default location:
|
- Default location:
|
||||||
`user/minetest.conf`
|
`user/minetest.conf`
|
||||||
- This file is created by closing Minetest for the first time.
|
- This file is created by closing Luanti for the first time.
|
||||||
- A specific file can be specified on the command line:
|
- A specific file can be specified on the command line:
|
||||||
`--config <path-to-file>`
|
`--config <path-to-file>`
|
||||||
- A run-in-place build will look for the configuration file in
|
- A run-in-place build will look for the configuration file in
|
||||||
|
@ -126,343 +120,17 @@ Command-line options
|
||||||
|
|
||||||
Compiling
|
Compiling
|
||||||
---------
|
---------
|
||||||
### Compiling on GNU/Linux
|
|
||||||
|
|
||||||
#### Dependencies
|
- [Compiling - common information](doc/compiling/README.md)
|
||||||
|
- [Compiling on GNU/Linux](doc/compiling/linux.md)
|
||||||
| Dependency | Version | Commentary |
|
- [Compiling on Windows](doc/compiling/windows.md)
|
||||||
|------------|---------|------------|
|
- [Compiling on MacOS](doc/compiling/macos.md)
|
||||||
| 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 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
|
|
||||||
|
|
||||||
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
|
|
||||||
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 iconv.h
|
|
||||||
GETTEXT_LIBRARY - Only when building with gettext on Windows; path to libintl.dll.a
|
|
||||||
GETTEXT_MSGFMT - Only when building with gettext; path to msgfmt/msgfmt.exe
|
|
||||||
IRRLICHT_DLL - Only on Windows; path to 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_c.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
|
|
||||||
```
|
|
||||||
|
|
||||||
Docker
|
Docker
|
||||||
------
|
------
|
||||||
We provide Minetest server Docker images using the GitLab mirror registry.
|
|
||||||
|
|
||||||
Images are built on each commit and available using the following tag scheme:
|
|
||||||
|
|
||||||
* `registry.gitlab.com/minetest/minetest/server:latest` (latest build)
|
|
||||||
* `registry.gitlab.com/minetest/minetest/server:<branch/tag>` (current branch or current tag)
|
|
||||||
* `registry.gitlab.com/minetest/minetest/server:<commit-id>` (current commit id)
|
|
||||||
|
|
||||||
If you want to test it on a Docker server you can easily run:
|
|
||||||
|
|
||||||
sudo docker run registry.gitlab.com/minetest/minetest/server:<docker tag>
|
|
||||||
|
|
||||||
If you want to use it in a production environment you should use volumes bound to the Docker host
|
|
||||||
to persist data and modify the configuration:
|
|
||||||
|
|
||||||
sudo docker create -v /home/minetest/data/:/var/lib/minetest/ -v /home/minetest/conf/:/etc/minetest/ registry.gitlab.com/minetest/minetest/server:master
|
|
||||||
|
|
||||||
Data will be written to `/home/minetest/data` on the host, and configuration will be read from `/home/minetest/conf/minetest.conf`.
|
|
||||||
|
|
||||||
**Note:** If you don't understand the previous commands please read the official Docker documentation before use.
|
|
||||||
|
|
||||||
You can also host your Minetest server inside a Kubernetes cluster. See our example implementation in [`misc/kubernetes.yml`](misc/kubernetes.yml).
|
|
||||||
|
|
||||||
|
- [Developing minetestserver with Docker](doc/developing/docker.md)
|
||||||
|
- [Running a server with Docker](doc/docker_server.md)
|
||||||
|
|
||||||
Version scheme
|
Version scheme
|
||||||
--------------
|
--------------
|
||||||
|
|
17
android/SDL_Java_RMB_fix.patch
Normal file
17
android/SDL_Java_RMB_fix.patch
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
diff --git a/android/app/src/main/java/org/libsdl/app/SDLActivity.java b/android/app/src/main/java/org/libsdl/app/SDLActivity.java
|
||||||
|
--- a/android/app/src/main/java/org/libsdl/app/SDLActivity.java
|
||||||
|
+++ b/android/app/src/main/java/org/libsdl/app/SDLActivity.java
|
||||||
|
@@ -1345,7 +1345,12 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
|
||||||
|
+ if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE ||
|
||||||
|
+ /*
|
||||||
|
+ * CUSTOM ADDITION FOR LUANTI
|
||||||
|
+ * should be upstreamed
|
||||||
|
+ */
|
||||||
|
+ (source & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE) {
|
||||||
|
// on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
|
||||||
|
// they are ignored here because sending them as mouse input to SDL is messy
|
||||||
|
if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
|
|
@ -1,14 +1,18 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 30
|
|
||||||
buildToolsVersion '30.0.3'
|
|
||||||
ndkVersion "$ndk_version"
|
ndkVersion "$ndk_version"
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId 'net.minetest.minetest'
|
applicationId 'net.minetest.minetest'
|
||||||
minSdkVersion 16
|
minSdkVersion 21
|
||||||
targetSdkVersion 30
|
compileSdk 34
|
||||||
|
targetSdkVersion 34
|
||||||
versionName "${versionMajor}.${versionMinor}.${versionPatch}"
|
versionName "${versionMajor}.${versionMinor}.${versionPatch}"
|
||||||
versionCode project.versionCode
|
versionCode versionMajor * 1000000 + versionMinor * 10000 + versionPatch * 100 + versionBuild
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
buildConfig true
|
||||||
}
|
}
|
||||||
|
|
||||||
// load properties
|
// load properties
|
||||||
|
@ -40,7 +44,7 @@ android {
|
||||||
abi {
|
abi {
|
||||||
enable true
|
enable true
|
||||||
reset()
|
reset()
|
||||||
include 'armeabi-v7a', 'arm64-v8a'
|
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,69 +52,71 @@ android {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
namespace 'net.minetest.minetest'
|
||||||
}
|
}
|
||||||
|
|
||||||
task prepareAssets() {
|
task prepareAssets() {
|
||||||
def assetsFolder = "build/assets"
|
def assetsFolder = "build/assets"
|
||||||
def projRoot = "../.."
|
def projRoot = rootDir.parent
|
||||||
def gameToCopy = "minetest_game"
|
|
||||||
|
|
||||||
copy {
|
// See issue #4638
|
||||||
from "${projRoot}/minetest.conf.example", "${projRoot}/README.md" into assetsFolder
|
def unsupportedLanguages = new File("${projRoot}/src/unsupported_language_list.txt").text.readLines()
|
||||||
|
|
||||||
|
doFirst {
|
||||||
|
logger.lifecycle('Preparing assets at {}', assetsFolder)
|
||||||
}
|
}
|
||||||
copy {
|
doLast {
|
||||||
from "${projRoot}/doc/lgpl-2.1.txt" into "${assetsFolder}"
|
copy {
|
||||||
}
|
from "${projRoot}/minetest.conf.example", "${projRoot}/README.md" into assetsFolder
|
||||||
copy {
|
|
||||||
from "${projRoot}/builtin" into "${assetsFolder}/builtin"
|
|
||||||
}
|
|
||||||
copy {
|
|
||||||
from "${projRoot}/client/shaders" into "${assetsFolder}/client/shaders"
|
|
||||||
}
|
|
||||||
copy {
|
|
||||||
from "../native/deps/armeabi-v7a/Irrlicht/Shaders" into "${assetsFolder}/client/shaders/Irrlicht"
|
|
||||||
}
|
|
||||||
copy {
|
|
||||||
from "${projRoot}/fonts" include "*.ttf" into "${assetsFolder}/fonts"
|
|
||||||
}
|
|
||||||
copy {
|
|
||||||
from "${projRoot}/games/${gameToCopy}" into "${assetsFolder}/games/${gameToCopy}"
|
|
||||||
}
|
|
||||||
fileTree("${projRoot}/po").include("**/*.po").forEach { poFile ->
|
|
||||||
def moPath = "${assetsFolder}/locale/${poFile.parentFile.name}/LC_MESSAGES/"
|
|
||||||
file(moPath).mkdirs()
|
|
||||||
exec {
|
|
||||||
commandLine 'msgfmt', '-o', "${moPath}/minetest.mo", poFile
|
|
||||||
}
|
}
|
||||||
}
|
copy {
|
||||||
copy {
|
from "${projRoot}/doc/lgpl-2.1.txt" into assetsFolder
|
||||||
from "${projRoot}/textures" into "${assetsFolder}/textures"
|
}
|
||||||
|
copy {
|
||||||
|
from "${projRoot}/builtin" into "${assetsFolder}/builtin"
|
||||||
|
}
|
||||||
|
copy {
|
||||||
|
from "${projRoot}/client/shaders" into "${assetsFolder}/client/shaders"
|
||||||
|
}
|
||||||
|
copy {
|
||||||
|
from "${projRoot}/irr/media/Shaders" into "${assetsFolder}/client/shaders/Irrlicht"
|
||||||
|
}
|
||||||
|
copy {
|
||||||
|
from "${projRoot}/fonts" include "*.ttf" into "${assetsFolder}/fonts"
|
||||||
|
}
|
||||||
|
copy {
|
||||||
|
from "${projRoot}/textures/base/pack" into "${assetsFolder}/textures/base/pack"
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile translations
|
||||||
|
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 {
|
||||||
|
commandLine 'msgfmt', '-o', "${moPath}/luanti.mo", poFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file("${assetsFolder}/.nomedia").text = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
file("${assetsFolder}/.nomedia").text = "";
|
task zipAssets(dependsOn: prepareAssets, type: Zip) {
|
||||||
|
archiveFileName = "assets.zip"
|
||||||
task zipAssets(type: Zip) {
|
from assetsFolder
|
||||||
archiveName "Minetest.zip"
|
destinationDirectory = file("src/main/assets")
|
||||||
from "${assetsFolder}"
|
|
||||||
destinationDir file("src/main/assets")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
preBuild.dependsOn zipAssets
|
preBuild.dependsOn zipAssets
|
||||||
|
prepareAssets.dependsOn ':native:getDeps'
|
||||||
|
|
||||||
// Map for the version code that gives each ABI a value.
|
clean {
|
||||||
import com.android.build.OutputFile
|
delete new File("src/main/assets", "assets.zip")
|
||||||
|
|
||||||
def abiCodes = ['armeabi-v7a': 0, 'arm64-v8a': 1]
|
|
||||||
android.applicationVariants.all { variant ->
|
|
||||||
variant.outputs.each {
|
|
||||||
output ->
|
|
||||||
def abiName = output.getFilter(OutputFile.ABI)
|
|
||||||
output.versionCodeOverride = abiCodes.get(abiName, 0) + variant.versionCode
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':native')
|
implementation project(':native')
|
||||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,16 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="net.minetest.minetest"
|
android:installLocation="auto">
|
||||||
android:installLocation="auto">
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<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
|
<application
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/label"
|
android:label="@string/label"
|
||||||
android:requestLegacyExternalStorage="true"
|
|
||||||
android:resizeableActivity="false"
|
android:resizeableActivity="false"
|
||||||
tools:ignore="UnusedAttribute">
|
tools:ignore="UnusedAttribute">
|
||||||
|
|
||||||
|
@ -50,9 +43,6 @@
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<meta-data
|
|
||||||
android:name="android.app.lib_name"
|
|
||||||
android:value="Minetest" />
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
Minetest
|
Minetest
|
||||||
Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
|
Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
|
||||||
Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
|
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
|
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
|
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;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class CustomEditText extends AppCompatEditText {
|
public class CustomEditText extends AppCompatEditText {
|
||||||
|
private int editType = 2; // single line text input as default
|
||||||
|
private boolean wantsToShowKeyboard = false;
|
||||||
|
|
||||||
public CustomEditText(Context context) {
|
public CustomEditText(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CustomEditText(Context context, int _editType) {
|
||||||
|
super(context);
|
||||||
|
editType = _editType;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
|
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)
|
InputMethodManager mgr = (InputMethodManager)
|
||||||
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
Objects.requireNonNull(mgr).hideSoftInputFromWindow(this.getWindowToken(), 0);
|
Objects.requireNonNull(mgr).hideSoftInputFromWindow(this.getWindowToken(), 0);
|
||||||
}
|
}
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
package net.minetest.minetest;
|
package net.minetest.minetest;
|
||||||
|
|
||||||
import android.app.NativeActivity;
|
import org.libsdl.app.SDLActivity;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
@ -32,87 +33,85 @@ import android.view.View;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
|
||||||
import androidx.annotation.Keep;
|
import androidx.annotation.Keep;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.core.content.FileProvider;
|
import androidx.core.content.FileProvider;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
// Native code finds these methods by name (see porting_android.cpp).
|
// Native code finds these methods by name (see porting_android.cpp).
|
||||||
// This annotation prevents the minifier/Proguard from mangling them.
|
// This annotation prevents the minifier/Proguard from mangling them.
|
||||||
@Keep
|
@Keep
|
||||||
public class GameActivity extends NativeActivity {
|
@SuppressWarnings("unused")
|
||||||
static {
|
public class GameActivity extends SDLActivity {
|
||||||
System.loadLibrary("c++_shared");
|
@Override
|
||||||
System.loadLibrary("Minetest");
|
protected String getMainSharedObject() {
|
||||||
|
return getContext().getApplicationInfo().nativeLibraryDir + "/libluanti.so";
|
||||||
}
|
}
|
||||||
|
|
||||||
private int messageReturnCode = -1;
|
@Override
|
||||||
|
protected String getMainFunction() {
|
||||||
|
return "SDL_Main";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String[] getLibraries() {
|
||||||
|
return new String[] {
|
||||||
|
"luanti"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent SDL from changing orientation settings since we already set the
|
||||||
|
// correct orientation in our AndroidManifest.xml
|
||||||
|
@Override
|
||||||
|
public void setOrientationBis(int w, int h, boolean resizable, String hint) {}
|
||||||
|
|
||||||
|
enum DialogType { TEXT_INPUT, SELECTION_INPUT }
|
||||||
|
enum DialogState { DIALOG_SHOWN, DIALOG_INPUTTED, DIALOG_CANCELED }
|
||||||
|
|
||||||
|
private DialogType lastDialogType = DialogType.TEXT_INPUT;
|
||||||
|
private DialogState inputDialogState = DialogState.DIALOG_CANCELED;
|
||||||
private String messageReturnValue = "";
|
private String messageReturnValue = "";
|
||||||
|
private int selectionReturnValue = 0;
|
||||||
|
|
||||||
public static native void putMessageBoxResult(String text);
|
private native void saveSettings();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
protected void onStop() {
|
||||||
super.onCreate(savedInstanceState);
|
super.onStop();
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
// 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void makeFullScreen() {
|
public void showTextInputDialog(String hint, String current, int editType) {
|
||||||
if (Build.VERSION.SDK_INT >= 19)
|
runOnUiThread(() -> showTextInputDialogUI(hint, current, editType));
|
||||||
this.getWindow().getDecorView().setSystemUiVisibility(
|
|
||||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
|
||||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
|
||||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void showSelectionInputDialog(String[] optionList, int selectedIdx) {
|
||||||
public void onWindowFocusChanged(boolean hasFocus) {
|
runOnUiThread(() -> showSelectionInputDialogUI(optionList, selectedIdx));
|
||||||
super.onWindowFocusChanged(hasFocus);
|
|
||||||
if (hasFocus)
|
|
||||||
makeFullScreen();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void showTextInputDialogUI(String hint, String current, int editType) {
|
||||||
protected void onResume() {
|
lastDialogType = DialogType.TEXT_INPUT;
|
||||||
super.onResume();
|
inputDialogState = DialogState.DIALOG_SHOWN;
|
||||||
makeFullScreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
// Ignore the back press so Minetest can handle it
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showDialog(String acceptButton, String hint, String current, int editType) {
|
|
||||||
runOnUiThread(() -> showDialogUI(hint, current, editType));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showDialogUI(String hint, String current, int editType) {
|
|
||||||
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
LinearLayout container = new LinearLayout(this);
|
LinearLayout container = new LinearLayout(this);
|
||||||
container.setOrientation(LinearLayout.VERTICAL);
|
container.setOrientation(LinearLayout.VERTICAL);
|
||||||
builder.setView(container);
|
builder.setView(container);
|
||||||
AlertDialog alertDialog = builder.create();
|
AlertDialog alertDialog = builder.create();
|
||||||
EditText editText;
|
CustomEditText editText = new CustomEditText(this, editType);
|
||||||
// For multi-line, do not close the dialog after pressing back button
|
|
||||||
if (editType == 1) {
|
|
||||||
editText = new EditText(this);
|
|
||||||
} else {
|
|
||||||
editText = new CustomEditText(this);
|
|
||||||
}
|
|
||||||
container.addView(editText);
|
container.addView(editText);
|
||||||
editText.setMaxLines(8);
|
editText.setMaxLines(8);
|
||||||
editText.requestFocus();
|
|
||||||
editText.setHint(hint);
|
editText.setHint(hint);
|
||||||
editText.setText(current);
|
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)
|
if (editType == 1)
|
||||||
editText.setInputType(InputType.TYPE_CLASS_TEXT |
|
editText.setInputType(InputType.TYPE_CLASS_TEXT |
|
||||||
InputType.TYPE_TEXT_FLAG_MULTI_LINE);
|
InputType.TYPE_TEXT_FLAG_MULTI_LINE);
|
||||||
|
@ -121,12 +120,13 @@ public class GameActivity extends NativeActivity {
|
||||||
InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||||
else
|
else
|
||||||
editText.setInputType(InputType.TYPE_CLASS_TEXT);
|
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) -> {
|
editText.setOnKeyListener((view, keyCode, event) -> {
|
||||||
// For multi-line, do not submit the text after pressing Enter key
|
// For multi-line, do not submit the text after pressing Enter key
|
||||||
if (keyCode == KeyEvent.KEYCODE_ENTER && editType != 1) {
|
if (keyCode == KeyEvent.KEYCODE_ENTER && editType != 1) {
|
||||||
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
||||||
messageReturnCode = 0;
|
inputDialogState = DialogState.DIALOG_INPUTTED;
|
||||||
messageReturnValue = editText.getText().toString();
|
messageReturnValue = editText.getText().toString();
|
||||||
alertDialog.dismiss();
|
alertDialog.dismiss();
|
||||||
return true;
|
return true;
|
||||||
|
@ -140,28 +140,55 @@ public class GameActivity extends NativeActivity {
|
||||||
doneButton.setText(R.string.ime_dialog_done);
|
doneButton.setText(R.string.ime_dialog_done);
|
||||||
doneButton.setOnClickListener((view -> {
|
doneButton.setOnClickListener((view -> {
|
||||||
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
||||||
messageReturnCode = 0;
|
inputDialogState = DialogState.DIALOG_INPUTTED;
|
||||||
messageReturnValue = editText.getText().toString();
|
messageReturnValue = editText.getText().toString();
|
||||||
alertDialog.dismiss();
|
alertDialog.dismiss();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
alertDialog.show();
|
|
||||||
alertDialog.setOnCancelListener(dialog -> {
|
alertDialog.setOnCancelListener(dialog -> {
|
||||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||||
|
inputDialogState = DialogState.DIALOG_CANCELED;
|
||||||
messageReturnValue = current;
|
messageReturnValue = current;
|
||||||
messageReturnCode = -1;
|
|
||||||
});
|
});
|
||||||
|
alertDialog.show();
|
||||||
|
editText.requestFocusTryShow();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDialogState() {
|
public void showSelectionInputDialogUI(String[] optionList, int selectedIdx) {
|
||||||
return messageReturnCode;
|
lastDialogType = DialogType.SELECTION_INPUT;
|
||||||
|
inputDialogState = DialogState.DIALOG_SHOWN;
|
||||||
|
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setSingleChoiceItems(optionList, selectedIdx, (dialog, selection) -> {
|
||||||
|
inputDialogState = DialogState.DIALOG_INPUTTED;
|
||||||
|
selectionReturnValue = selection;
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
builder.setOnCancelListener(dialog -> {
|
||||||
|
inputDialogState = DialogState.DIALOG_CANCELED;
|
||||||
|
selectionReturnValue = selectedIdx;
|
||||||
|
});
|
||||||
|
AlertDialog alertDialog = builder.create();
|
||||||
|
alertDialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDialogValue() {
|
public int getLastDialogType() {
|
||||||
messageReturnCode = -1;
|
return lastDialogType.ordinal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInputDialogState() {
|
||||||
|
return inputDialogState.ordinal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDialogMessage() {
|
||||||
|
inputDialogState = DialogState.DIALOG_CANCELED;
|
||||||
return messageReturnValue;
|
return messageReturnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getDialogSelection() {
|
||||||
|
inputDialogState = DialogState.DIALOG_CANCELED;
|
||||||
|
return selectionReturnValue;
|
||||||
|
}
|
||||||
|
|
||||||
public float getDensity() {
|
public float getDensity() {
|
||||||
return getResources().getDisplayMetrics().density;
|
return getResources().getDisplayMetrics().density;
|
||||||
}
|
}
|
||||||
|
@ -176,7 +203,11 @@ public class GameActivity extends NativeActivity {
|
||||||
|
|
||||||
public void openURI(String uri) {
|
public void openURI(String uri) {
|
||||||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
|
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
|
||||||
startActivity(browserIntent);
|
try {
|
||||||
|
startActivity(browserIntent);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
runOnUiThread(() -> Toast.makeText(this, R.string.no_web_browser, Toast.LENGTH_SHORT).show());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUserDataPath() {
|
public String getUserDataPath() {
|
||||||
|
@ -204,4 +235,32 @@ public class GameActivity extends NativeActivity {
|
||||||
Intent shareIntent = Intent.createChooser(intent, null);
|
Intent shareIntent = Intent.createChooser(intent, null);
|
||||||
startActivity(shareIntent);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasPhysicalKeyboard() {
|
||||||
|
return getContext().getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,38 +20,31 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
package net.minetest.minetest;
|
package net.minetest.minetest;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.RequiresApi;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
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.*;
|
import static net.minetest.minetest.UnzipService.*;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
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 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 SETTINGS = "MinetestSettings";
|
||||||
private static final String TAG_VERSION_CODE = "versionCode";
|
private static final String TAG_VERSION_CODE = "versionCode";
|
||||||
|
|
||||||
|
@ -91,63 +84,30 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
IntentFilter filter = new IntentFilter(ACTION_UPDATE);
|
IntentFilter filter = new IntentFilter(ACTION_UPDATE);
|
||||||
registerReceiver(myReceiver, filter);
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
registerReceiver(myReceiver, filter, RECEIVER_NOT_EXPORTED);
|
||||||
|
} else {
|
||||||
|
registerReceiver(myReceiver, filter);
|
||||||
|
}
|
||||||
|
|
||||||
mProgressBar = findViewById(R.id.progressBar);
|
mProgressBar = findViewById(R.id.progressBar);
|
||||||
mTextView = findViewById(R.id.textView);
|
mTextView = findViewById(R.id.textView);
|
||||||
sharedPreferences = getSharedPreferences(SETTINGS, Context.MODE_PRIVATE);
|
sharedPreferences = getSharedPreferences(SETTINGS, Context.MODE_PRIVATE);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
|
checkAppVersion();
|
||||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
|
|
||||||
checkPermission();
|
|
||||||
else
|
|
||||||
checkAppVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkPermission() {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||||
final List<String> missingPermissions = new ArrayList<>();
|
createNotificationChannel();
|
||||||
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) {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkAppVersion() {
|
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()) {
|
if (UnzipService.getIsRunning()) {
|
||||||
mProgressBar.setVisibility(View.VISIBLE);
|
mProgressBar.setVisibility(View.VISIBLE);
|
||||||
mProgressBar.setIndeterminate(true);
|
mProgressBar.setIndeterminate(true);
|
||||||
|
@ -172,6 +132,28 @@ public class MainActivity extends AppCompatActivity {
|
||||||
startActivity(intent);
|
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
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
// Prevent abrupt interruption when copy game files from assets
|
// Prevent abrupt interruption when copy game files from assets
|
||||||
|
|
|
@ -22,17 +22,15 @@ package net.minetest.minetest;
|
||||||
|
|
||||||
import android.app.IntentService;
|
import android.app.IntentService;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationChannel;
|
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -59,9 +57,11 @@ public class UnzipService extends IntentService {
|
||||||
private String failureMessage;
|
private String failureMessage;
|
||||||
|
|
||||||
private static boolean isRunning = false;
|
private static boolean isRunning = false;
|
||||||
|
|
||||||
public static synchronized boolean getIsRunning() {
|
public static synchronized boolean getIsRunning() {
|
||||||
return isRunning;
|
return isRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static synchronized void setIsRunning(boolean v) {
|
private static synchronized void setIsRunning(boolean v) {
|
||||||
isRunning = v;
|
isRunning = v;
|
||||||
}
|
}
|
||||||
|
@ -73,13 +73,10 @@ public class UnzipService extends IntentService {
|
||||||
@Override
|
@Override
|
||||||
protected void onHandleIntent(Intent intent) {
|
protected void onHandleIntent(Intent intent) {
|
||||||
Notification.Builder notificationBuilder = createNotification();
|
Notification.Builder notificationBuilder = createNotification();
|
||||||
final File zipFile = new File(getCacheDir(), "Minetest.zip");
|
final File zipFile = new File(getCacheDir(), "assets.zip");
|
||||||
try {
|
try {
|
||||||
setIsRunning(true);
|
setIsRunning(true);
|
||||||
File userDataDirectory = Utils.getUserDataDirectory(this);
|
File userDataDirectory = Utils.getUserDataDirectory(this);
|
||||||
if (userDataDirectory == null) {
|
|
||||||
throw new IOException("Unable to find user data directory");
|
|
||||||
}
|
|
||||||
|
|
||||||
try (InputStream in = this.getAssets().open(zipFile.getName())) {
|
try (InputStream in = this.getAssets().open(zipFile.getName())) {
|
||||||
try (OutputStream out = new FileOutputStream(zipFile)) {
|
try (OutputStream out = new FileOutputStream(zipFile)) {
|
||||||
|
@ -91,39 +88,25 @@ public class UnzipService extends IntentService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
migrate(notificationBuilder, userDataDirectory);
|
|
||||||
unzip(notificationBuilder, zipFile, userDataDirectory);
|
unzip(notificationBuilder, zipFile, userDataDirectory);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
isSuccess = false;
|
isSuccess = false;
|
||||||
failureMessage = e.getLocalizedMessage();
|
failureMessage = e.getLocalizedMessage();
|
||||||
} finally {
|
} finally {
|
||||||
setIsRunning(false);
|
setIsRunning(false);
|
||||||
zipFile.delete();
|
if (!zipFile.delete()) {
|
||||||
|
Log.w("UnzipService", "Minetest installation ZIP cannot be deleted");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
private Notification.Builder createNotification() {
|
private Notification.Builder createNotification() {
|
||||||
String name = "net.minetest.minetest";
|
|
||||||
String channelId = "Minetest channel";
|
|
||||||
String description = "notifications from Minetest";
|
|
||||||
Notification.Builder builder;
|
Notification.Builder builder;
|
||||||
if (mNotifyManager == null)
|
if (mNotifyManager == null)
|
||||||
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
int importance = NotificationManager.IMPORTANCE_LOW;
|
builder = new Notification.Builder(this, MainActivity.NOTIFICATION_CHANNEL_ID);
|
||||||
NotificationChannel mChannel = null;
|
|
||||||
if (mNotifyManager != null)
|
|
||||||
mChannel = mNotifyManager.getNotificationChannel(channelId);
|
|
||||||
if (mChannel == null) {
|
|
||||||
mChannel = new NotificationChannel(channelId, name, importance);
|
|
||||||
mChannel.setDescription(description);
|
|
||||||
// Configure the notification channel, NO SOUND
|
|
||||||
mChannel.setSound(null, null);
|
|
||||||
mChannel.enableLights(false);
|
|
||||||
mChannel.enableVibration(false);
|
|
||||||
mNotifyManager.createNotificationChannel(mChannel);
|
|
||||||
}
|
|
||||||
builder = new Notification.Builder(this, channelId);
|
|
||||||
} else {
|
} else {
|
||||||
builder = new Notification.Builder(this);
|
builder = new Notification.Builder(this);
|
||||||
}
|
}
|
||||||
|
@ -131,12 +114,16 @@ public class UnzipService extends IntentService {
|
||||||
Intent notificationIntent = new Intent(this, MainActivity.class);
|
Intent notificationIntent = new Intent(this, MainActivity.class);
|
||||||
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
|
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||||
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||||
|
int pendingIntentFlag = 0;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
pendingIntentFlag = PendingIntent.FLAG_MUTABLE;
|
||||||
|
}
|
||||||
PendingIntent intent = PendingIntent.getActivity(this, 0,
|
PendingIntent intent = PendingIntent.getActivity(this, 0,
|
||||||
notificationIntent, 0);
|
notificationIntent, pendingIntentFlag);
|
||||||
|
|
||||||
builder.setContentTitle(getString(R.string.notification_title))
|
builder.setContentTitle(getString(R.string.unzip_notification_title))
|
||||||
.setSmallIcon(R.mipmap.ic_launcher)
|
.setSmallIcon(R.mipmap.ic_launcher)
|
||||||
.setContentText(getString(R.string.notification_description))
|
.setContentText(getString(R.string.unzip_notification_description))
|
||||||
.setContentIntent(intent)
|
.setContentIntent(intent)
|
||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
.setProgress(0, 0, true);
|
.setProgress(0, 0, true);
|
||||||
|
@ -179,9 +166,9 @@ public class UnzipService extends IntentService {
|
||||||
try {
|
try {
|
||||||
Process p = new ProcessBuilder("/system/bin/mv",
|
Process p = new ProcessBuilder("/system/bin/mv",
|
||||||
src.getAbsolutePath(), dst.getAbsolutePath()).start();
|
src.getAbsolutePath(), dst.getAbsolutePath()).start();
|
||||||
int exitcode = p.waitFor();
|
int exitCode = p.waitFor();
|
||||||
if (exitcode != 0)
|
if (exitCode != 0)
|
||||||
throw new IOException("Move failed with exit code " + exitcode);
|
throw new IOException("Move failed with exit code " + exitCode);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
throw new IOException("Move operation interrupted");
|
throw new IOException("Move operation interrupted");
|
||||||
}
|
}
|
||||||
|
@ -197,42 +184,9 @@ public class UnzipService extends IntentService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void publishProgress(@Nullable Notification.Builder notificationBuilder, @StringRes int message, int progress) {
|
||||||
* Migrates user data from deprecated external storage to app scoped storage
|
|
||||||
*/
|
|
||||||
private void migrate(Notification.Builder notificationBuilder, File newLocation) throws IOException {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
File oldLocation = new File(Environment.getExternalStorageDirectory(), "Minetest");
|
|
||||||
if (!oldLocation.isDirectory())
|
|
||||||
return;
|
|
||||||
|
|
||||||
publishProgress(notificationBuilder, R.string.migrating, 0);
|
|
||||||
newLocation.mkdir();
|
|
||||||
|
|
||||||
String[] dirs = new String[] { "worlds", "games", "mods", "textures", "client" };
|
|
||||||
for (int i = 0; i < dirs.length; i++) {
|
|
||||||
publishProgress(notificationBuilder, R.string.migrating, 100 * i / dirs.length);
|
|
||||||
File dir = new File(oldLocation, dirs[i]), dir2 = new File(newLocation, dirs[i]);
|
|
||||||
if (dir.isDirectory() && !dir2.isDirectory()) {
|
|
||||||
moveFileOrDir(dir, dir2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String filename : new String[] { "minetest.conf" }) {
|
|
||||||
File file = new File(oldLocation, filename), file2 = new File(newLocation, filename);
|
|
||||||
if (file.isFile() && !file2.isFile()) {
|
|
||||||
moveFileOrDir(file, file2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
recursivelyDeleteDirectory(oldLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void publishProgress(@Nullable Notification.Builder notificationBuilder, @StringRes int message, int progress) {
|
|
||||||
Intent intentUpdate = new Intent(ACTION_UPDATE);
|
Intent intentUpdate = new Intent(ACTION_UPDATE);
|
||||||
|
intentUpdate.setPackage(getPackageName());
|
||||||
intentUpdate.putExtra(ACTION_PROGRESS, progress);
|
intentUpdate.putExtra(ACTION_PROGRESS, progress);
|
||||||
intentUpdate.putExtra(ACTION_PROGRESS_MESSAGE, message);
|
intentUpdate.putExtra(ACTION_PROGRESS_MESSAGE, message);
|
||||||
if (!isSuccess)
|
if (!isSuccess)
|
||||||
|
|
|
@ -1,37 +1,43 @@
|
||||||
package net.minetest.minetest;
|
package net.minetest.minetest;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class Utils {
|
public class Utils {
|
||||||
public static @NonNull File createDirs(File root, String dir) {
|
@NonNull
|
||||||
|
public static File createDirs(@NonNull File root, @NonNull String dir) {
|
||||||
File f = new File(root, dir);
|
File f = new File(root, dir);
|
||||||
if (!f.isDirectory())
|
if (!f.isDirectory())
|
||||||
f.mkdirs();
|
if (!f.mkdirs())
|
||||||
|
Log.e("Utils", "Directory " + dir + " cannot be created");
|
||||||
|
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @Nullable File getUserDataDirectory(Context context) {
|
@NonNull
|
||||||
File extDir = context.getExternalFilesDir(null);
|
public static File getUserDataDirectory(@NonNull Context context) {
|
||||||
if (extDir == null) {
|
File extDir = Objects.requireNonNull(
|
||||||
return null;
|
context.getExternalFilesDir(null),
|
||||||
}
|
"Cannot get external file directory"
|
||||||
|
);
|
||||||
return createDirs(extDir, "Minetest");
|
return createDirs(extDir, "Minetest");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @Nullable File getCacheDirectory(Context context) {
|
@NonNull
|
||||||
return context.getCacheDir();
|
public static File getCacheDirectory(@NonNull Context context) {
|
||||||
|
return Objects.requireNonNull(
|
||||||
|
context.getCacheDir(),
|
||||||
|
"Cannot get cache directory"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isInstallValid(Context context) {
|
public static boolean isInstallValid(@NonNull Context context) {
|
||||||
File userDataDirectory = getUserDataDirectory(context);
|
File userDataDirectory = getUserDataDirectory(context);
|
||||||
return userDataDirectory != null && userDataDirectory.isDirectory() &&
|
return userDataDirectory.isDirectory() &&
|
||||||
new File(userDataDirectory, "games").isDirectory() &&
|
|
||||||
new File(userDataDirectory, "builtin").isDirectory() &&
|
new File(userDataDirectory, "builtin").isDirectory() &&
|
||||||
new File(userDataDirectory, "client").isDirectory() &&
|
new File(userDataDirectory, "client").isDirectory() &&
|
||||||
new File(userDataDirectory, "textures").isDirectory();
|
new File(userDataDirectory, "textures").isDirectory();
|
||||||
|
|
22
android/app/src/main/java/org/libsdl/app/HIDDevice.java
Normal file
22
android/app/src/main/java/org/libsdl/app/HIDDevice.java
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.hardware.usb.UsbDevice;
|
||||||
|
|
||||||
|
interface HIDDevice
|
||||||
|
{
|
||||||
|
public int getId();
|
||||||
|
public int getVendorId();
|
||||||
|
public int getProductId();
|
||||||
|
public String getSerialNumber();
|
||||||
|
public int getVersion();
|
||||||
|
public String getManufacturerName();
|
||||||
|
public String getProductName();
|
||||||
|
public UsbDevice getDevice();
|
||||||
|
public boolean open();
|
||||||
|
public int sendFeatureReport(byte[] report);
|
||||||
|
public int sendOutputReport(byte[] report);
|
||||||
|
public boolean getFeatureReport(byte[] report);
|
||||||
|
public void setFrozen(boolean frozen);
|
||||||
|
public void close();
|
||||||
|
public void shutdown();
|
||||||
|
}
|
|
@ -0,0 +1,650 @@
|
||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothGatt;
|
||||||
|
import android.bluetooth.BluetoothGattCallback;
|
||||||
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
|
import android.bluetooth.BluetoothGattDescriptor;
|
||||||
|
import android.bluetooth.BluetoothManager;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.bluetooth.BluetoothGattService;
|
||||||
|
import android.hardware.usb.UsbDevice;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.os.*;
|
||||||
|
|
||||||
|
//import com.android.internal.util.HexDump;
|
||||||
|
|
||||||
|
import java.lang.Runnable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
|
||||||
|
|
||||||
|
private static final String TAG = "hidapi";
|
||||||
|
private HIDDeviceManager mManager;
|
||||||
|
private BluetoothDevice mDevice;
|
||||||
|
private int mDeviceId;
|
||||||
|
private BluetoothGatt mGatt;
|
||||||
|
private boolean mIsRegistered = false;
|
||||||
|
private boolean mIsConnected = false;
|
||||||
|
private boolean mIsChromebook = false;
|
||||||
|
private boolean mIsReconnecting = false;
|
||||||
|
private boolean mFrozen = false;
|
||||||
|
private LinkedList<GattOperation> mOperations;
|
||||||
|
GattOperation mCurrentOperation = null;
|
||||||
|
private Handler mHandler;
|
||||||
|
|
||||||
|
private static final int TRANSPORT_AUTO = 0;
|
||||||
|
private static final int TRANSPORT_BREDR = 1;
|
||||||
|
private static final int TRANSPORT_LE = 2;
|
||||||
|
|
||||||
|
private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
|
||||||
|
|
||||||
|
static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
|
||||||
|
static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
|
||||||
|
static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
|
||||||
|
static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
|
||||||
|
|
||||||
|
static class GattOperation {
|
||||||
|
private enum Operation {
|
||||||
|
CHR_READ,
|
||||||
|
CHR_WRITE,
|
||||||
|
ENABLE_NOTIFICATION
|
||||||
|
}
|
||||||
|
|
||||||
|
Operation mOp;
|
||||||
|
UUID mUuid;
|
||||||
|
byte[] mValue;
|
||||||
|
BluetoothGatt mGatt;
|
||||||
|
boolean mResult = true;
|
||||||
|
|
||||||
|
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
|
||||||
|
mGatt = gatt;
|
||||||
|
mOp = operation;
|
||||||
|
mUuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
|
||||||
|
mGatt = gatt;
|
||||||
|
mOp = operation;
|
||||||
|
mUuid = uuid;
|
||||||
|
mValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
// This is executed in main thread
|
||||||
|
BluetoothGattCharacteristic chr;
|
||||||
|
|
||||||
|
switch (mOp) {
|
||||||
|
case CHR_READ:
|
||||||
|
chr = getCharacteristic(mUuid);
|
||||||
|
//Log.v(TAG, "Reading characteristic " + chr.getUuid());
|
||||||
|
if (!mGatt.readCharacteristic(chr)) {
|
||||||
|
Log.e(TAG, "Unable to read characteristic " + mUuid.toString());
|
||||||
|
mResult = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mResult = true;
|
||||||
|
break;
|
||||||
|
case CHR_WRITE:
|
||||||
|
chr = getCharacteristic(mUuid);
|
||||||
|
//Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value));
|
||||||
|
chr.setValue(mValue);
|
||||||
|
if (!mGatt.writeCharacteristic(chr)) {
|
||||||
|
Log.e(TAG, "Unable to write characteristic " + mUuid.toString());
|
||||||
|
mResult = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mResult = true;
|
||||||
|
break;
|
||||||
|
case ENABLE_NOTIFICATION:
|
||||||
|
chr = getCharacteristic(mUuid);
|
||||||
|
//Log.v(TAG, "Writing descriptor of " + chr.getUuid());
|
||||||
|
if (chr != null) {
|
||||||
|
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
|
||||||
|
if (cccd != null) {
|
||||||
|
int properties = chr.getProperties();
|
||||||
|
byte[] value;
|
||||||
|
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
|
||||||
|
value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
|
||||||
|
} else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) {
|
||||||
|
value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Unable to start notifications on input characteristic");
|
||||||
|
mResult = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mGatt.setCharacteristicNotification(chr, true);
|
||||||
|
cccd.setValue(value);
|
||||||
|
if (!mGatt.writeDescriptor(cccd)) {
|
||||||
|
Log.e(TAG, "Unable to write descriptor " + mUuid.toString());
|
||||||
|
mResult = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mResult = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean finish() {
|
||||||
|
return mResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
|
||||||
|
BluetoothGattService valveService = mGatt.getService(steamControllerService);
|
||||||
|
if (valveService == null)
|
||||||
|
return null;
|
||||||
|
return valveService.getCharacteristic(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) {
|
||||||
|
return new GattOperation(gatt, Operation.CHR_READ, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) {
|
||||||
|
return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
|
||||||
|
return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
|
||||||
|
mManager = manager;
|
||||||
|
mDevice = device;
|
||||||
|
mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
|
||||||
|
mIsRegistered = false;
|
||||||
|
mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
|
||||||
|
mOperations = new LinkedList<GattOperation>();
|
||||||
|
mHandler = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
|
mGatt = connectGatt();
|
||||||
|
// final HIDDeviceBLESteamController finalThis = this;
|
||||||
|
// mHandler.postDelayed(new Runnable() {
|
||||||
|
// @Override
|
||||||
|
// public void run() {
|
||||||
|
// finalThis.checkConnectionForChromebookIssue();
|
||||||
|
// }
|
||||||
|
// }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentifier() {
|
||||||
|
return String.format("SteamController.%s", mDevice.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BluetoothGatt getGatt() {
|
||||||
|
return mGatt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
|
||||||
|
// of TRANSPORT_LE. Let's force ourselves to connect low energy.
|
||||||
|
private BluetoothGatt connectGatt(boolean managed) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
|
||||||
|
try {
|
||||||
|
return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return mDevice.connectGatt(mManager.getContext(), managed, this);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return mDevice.connectGatt(mManager.getContext(), managed, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BluetoothGatt connectGatt() {
|
||||||
|
return connectGatt(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getConnectionState() {
|
||||||
|
|
||||||
|
Context context = mManager.getContext();
|
||||||
|
if (context == null) {
|
||||||
|
// We are lacking any context to get our Bluetooth information. We'll just assume disconnected.
|
||||||
|
return BluetoothProfile.STATE_DISCONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||||
|
if (btManager == null) {
|
||||||
|
// This device doesn't support Bluetooth. We should never be here, because how did
|
||||||
|
// we instantiate a device to start with?
|
||||||
|
return BluetoothProfile.STATE_DISCONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reconnect() {
|
||||||
|
|
||||||
|
if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
|
||||||
|
mGatt.disconnect();
|
||||||
|
mGatt = connectGatt();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkConnectionForChromebookIssue() {
|
||||||
|
if (!mIsChromebook) {
|
||||||
|
// We only do this on Chromebooks, because otherwise it's really annoying to just attempt
|
||||||
|
// over and over.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int connectionState = getConnectionState();
|
||||||
|
|
||||||
|
switch (connectionState) {
|
||||||
|
case BluetoothProfile.STATE_CONNECTED:
|
||||||
|
if (!mIsConnected) {
|
||||||
|
// We are in the Bad Chromebook Place. We can force a disconnect
|
||||||
|
// to try to recover.
|
||||||
|
Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback. Forcing a reconnect.");
|
||||||
|
mIsReconnecting = true;
|
||||||
|
mGatt.disconnect();
|
||||||
|
mGatt = connectGatt(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (!isRegistered()) {
|
||||||
|
if (mGatt.getServices().size() > 0) {
|
||||||
|
Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration. Trying to recover.");
|
||||||
|
probeService(this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services. Trying to recover.");
|
||||||
|
mIsReconnecting = true;
|
||||||
|
mGatt.disconnect();
|
||||||
|
mGatt = connectGatt(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Log.v(TAG, "Chromebook: We are connected, and registered. Everything's good!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BluetoothProfile.STATE_DISCONNECTED:
|
||||||
|
Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us. Attempting a disconnect/reconnect, but we may not be able to recover.");
|
||||||
|
|
||||||
|
mIsReconnecting = true;
|
||||||
|
mGatt.disconnect();
|
||||||
|
mGatt = connectGatt(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BluetoothProfile.STATE_CONNECTING:
|
||||||
|
Log.v(TAG, "Chromebook: We're still trying to connect. Waiting a bit longer.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final HIDDeviceBLESteamController finalThis = this;
|
||||||
|
mHandler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
finalThis.checkConnectionForChromebookIssue();
|
||||||
|
}
|
||||||
|
}, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isRegistered() {
|
||||||
|
return mIsRegistered;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setRegistered() {
|
||||||
|
mIsRegistered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean probeService(HIDDeviceBLESteamController controller) {
|
||||||
|
|
||||||
|
if (isRegistered()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mIsConnected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.v(TAG, "probeService controller=" + controller);
|
||||||
|
|
||||||
|
for (BluetoothGattService service : mGatt.getServices()) {
|
||||||
|
if (service.getUuid().equals(steamControllerService)) {
|
||||||
|
Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
|
||||||
|
|
||||||
|
for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
|
||||||
|
if (chr.getUuid().equals(inputCharacteristic)) {
|
||||||
|
Log.v(TAG, "Found input characteristic");
|
||||||
|
// Start notifications
|
||||||
|
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
|
||||||
|
if (cccd != null) {
|
||||||
|
enableNotification(chr.getUuid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) {
|
||||||
|
Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us.");
|
||||||
|
mIsConnected = false;
|
||||||
|
mIsReconnecting = true;
|
||||||
|
mGatt.disconnect();
|
||||||
|
mGatt = connectGatt(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private void finishCurrentGattOperation() {
|
||||||
|
GattOperation op = null;
|
||||||
|
synchronized (mOperations) {
|
||||||
|
if (mCurrentOperation != null) {
|
||||||
|
op = mCurrentOperation;
|
||||||
|
mCurrentOperation = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (op != null) {
|
||||||
|
boolean result = op.finish(); // TODO: Maybe in main thread as well?
|
||||||
|
|
||||||
|
// Our operation failed, let's add it back to the beginning of our queue.
|
||||||
|
if (!result) {
|
||||||
|
mOperations.addFirst(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
executeNextGattOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeNextGattOperation() {
|
||||||
|
synchronized (mOperations) {
|
||||||
|
if (mCurrentOperation != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mOperations.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
mCurrentOperation = mOperations.removeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run in main thread
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (mOperations) {
|
||||||
|
if (mCurrentOperation == null) {
|
||||||
|
Log.e(TAG, "Current operation null in executor?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mCurrentOperation.run();
|
||||||
|
// now wait for the GATT callback and when it comes, finish this operation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void queueGattOperation(GattOperation op) {
|
||||||
|
synchronized (mOperations) {
|
||||||
|
mOperations.add(op);
|
||||||
|
}
|
||||||
|
executeNextGattOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enableNotification(UUID chrUuid) {
|
||||||
|
GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
|
||||||
|
queueGattOperation(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeCharacteristic(UUID uuid, byte[] value) {
|
||||||
|
GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
|
||||||
|
queueGattOperation(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readCharacteristic(UUID uuid) {
|
||||||
|
GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
|
||||||
|
queueGattOperation(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////// BluetoothGattCallback overridden methods
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
|
||||||
|
//Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
|
||||||
|
mIsReconnecting = false;
|
||||||
|
if (newState == 2) {
|
||||||
|
mIsConnected = true;
|
||||||
|
// Run directly, without GattOperation
|
||||||
|
if (!isRegistered()) {
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mGatt.discoverServices();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (newState == 0) {
|
||||||
|
mIsConnected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
|
||||||
|
//Log.v(TAG, "onServicesDiscovered status=" + status);
|
||||||
|
if (status == 0) {
|
||||||
|
if (gatt.getServices().size() == 0) {
|
||||||
|
Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack.");
|
||||||
|
mIsReconnecting = true;
|
||||||
|
mIsConnected = false;
|
||||||
|
gatt.disconnect();
|
||||||
|
mGatt = connectGatt(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
probeService(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||||
|
//Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
|
||||||
|
|
||||||
|
if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
|
||||||
|
mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
finishCurrentGattOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||||
|
//Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
|
||||||
|
|
||||||
|
if (characteristic.getUuid().equals(reportCharacteristic)) {
|
||||||
|
// Only register controller with the native side once it has been fully configured
|
||||||
|
if (!isRegistered()) {
|
||||||
|
Log.v(TAG, "Registering Steam Controller with ID: " + getId());
|
||||||
|
mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0);
|
||||||
|
setRegistered();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finishCurrentGattOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||||
|
// Enable this for verbose logging of controller input reports
|
||||||
|
//Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
|
||||||
|
|
||||||
|
if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) {
|
||||||
|
mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||||
|
//Log.v(TAG, "onDescriptorRead status=" + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||||
|
BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
|
||||||
|
//Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
|
||||||
|
|
||||||
|
if (chr.getUuid().equals(inputCharacteristic)) {
|
||||||
|
boolean hasWrittenInputDescriptor = true;
|
||||||
|
BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
|
||||||
|
if (reportChr != null) {
|
||||||
|
Log.v(TAG, "Writing report characteristic to enter valve mode");
|
||||||
|
reportChr.setValue(enterValveMode);
|
||||||
|
gatt.writeCharacteristic(reportChr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finishCurrentGattOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
|
||||||
|
//Log.v(TAG, "onReliableWriteCompleted status=" + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
|
||||||
|
//Log.v(TAG, "onReadRemoteRssi status=" + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
|
||||||
|
//Log.v(TAG, "onMtuChanged status=" + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////// Public API
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return mDeviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVendorId() {
|
||||||
|
// Valve Corporation
|
||||||
|
final int VALVE_USB_VID = 0x28DE;
|
||||||
|
return VALVE_USB_VID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getProductId() {
|
||||||
|
// We don't have an easy way to query from the Bluetooth device, but we know what it is
|
||||||
|
final int D0G_BLE2_PID = 0x1106;
|
||||||
|
return D0G_BLE2_PID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSerialNumber() {
|
||||||
|
// This will be read later via feature report by Steam
|
||||||
|
return "12345";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVersion() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getManufacturerName() {
|
||||||
|
return "Valve Corporation";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProductName() {
|
||||||
|
return "Steam Controller";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UsbDevice getDevice() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean open() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendFeatureReport(byte[] report) {
|
||||||
|
if (!isRegistered()) {
|
||||||
|
Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!");
|
||||||
|
if (mIsConnected) {
|
||||||
|
probeService(this);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to skip the first byte, as that doesn't go over the air
|
||||||
|
byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
|
||||||
|
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report));
|
||||||
|
writeCharacteristic(reportCharacteristic, actual_report);
|
||||||
|
return report.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendOutputReport(byte[] report) {
|
||||||
|
if (!isRegistered()) {
|
||||||
|
Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!");
|
||||||
|
if (mIsConnected) {
|
||||||
|
probeService(this);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report));
|
||||||
|
writeCharacteristic(reportCharacteristic, report);
|
||||||
|
return report.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getFeatureReport(byte[] report) {
|
||||||
|
if (!isRegistered()) {
|
||||||
|
Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!");
|
||||||
|
if (mIsConnected) {
|
||||||
|
probeService(this);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Log.v(TAG, "getFeatureReport");
|
||||||
|
readCharacteristic(reportCharacteristic);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFrozen(boolean frozen) {
|
||||||
|
mFrozen = frozen;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
close();
|
||||||
|
|
||||||
|
BluetoothGatt g = mGatt;
|
||||||
|
if (g != null) {
|
||||||
|
g.disconnect();
|
||||||
|
g.close();
|
||||||
|
mGatt = null;
|
||||||
|
}
|
||||||
|
mManager = null;
|
||||||
|
mIsRegistered = false;
|
||||||
|
mIsConnected = false;
|
||||||
|
mOperations.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
698
android/app/src/main/java/org/libsdl/app/HIDDeviceManager.java
Normal file
698
android/app/src/main/java/org/libsdl/app/HIDDeviceManager.java
Normal file
|
@ -0,0 +1,698 @@
|
||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothManager;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.hardware.usb.*;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HIDDeviceManager {
|
||||||
|
private static final String TAG = "hidapi";
|
||||||
|
private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
|
||||||
|
|
||||||
|
private static HIDDeviceManager sManager;
|
||||||
|
private static int sManagerRefCount = 0;
|
||||||
|
|
||||||
|
public static HIDDeviceManager acquire(Context context) {
|
||||||
|
if (sManagerRefCount == 0) {
|
||||||
|
sManager = new HIDDeviceManager(context);
|
||||||
|
}
|
||||||
|
++sManagerRefCount;
|
||||||
|
return sManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void release(HIDDeviceManager manager) {
|
||||||
|
if (manager == sManager) {
|
||||||
|
--sManagerRefCount;
|
||||||
|
if (sManagerRefCount == 0) {
|
||||||
|
sManager.close();
|
||||||
|
sManager = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private HashMap<Integer, HIDDevice> mDevicesById = new HashMap<Integer, HIDDevice>();
|
||||||
|
private HashMap<BluetoothDevice, HIDDeviceBLESteamController> mBluetoothDevices = new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
|
||||||
|
private int mNextDeviceId = 0;
|
||||||
|
private SharedPreferences mSharedPreferences = null;
|
||||||
|
private boolean mIsChromebook = false;
|
||||||
|
private UsbManager mUsbManager;
|
||||||
|
private Handler mHandler;
|
||||||
|
private BluetoothManager mBluetoothManager;
|
||||||
|
private List<BluetoothDevice> mLastBluetoothDevices;
|
||||||
|
|
||||||
|
private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
|
||||||
|
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||||
|
handleUsbDeviceAttached(usbDevice);
|
||||||
|
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
|
||||||
|
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||||
|
handleUsbDeviceDetached(usbDevice);
|
||||||
|
} else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
|
||||||
|
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||||
|
handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
// Bluetooth device was connected. If it was a Steam Controller, handle it
|
||||||
|
if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
|
||||||
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
|
Log.d(TAG, "Bluetooth device connected: " + device);
|
||||||
|
|
||||||
|
if (isSteamController(device)) {
|
||||||
|
connectBluetoothDevice(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bluetooth device was disconnected, remove from controller manager (if any)
|
||||||
|
if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
|
||||||
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
|
Log.d(TAG, "Bluetooth device disconnected: " + device);
|
||||||
|
|
||||||
|
disconnectBluetoothDevice(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private HIDDeviceManager(final Context context) {
|
||||||
|
mContext = context;
|
||||||
|
|
||||||
|
HIDDeviceRegisterCallback();
|
||||||
|
|
||||||
|
mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
|
||||||
|
mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
|
||||||
|
|
||||||
|
// if (shouldClear) {
|
||||||
|
// SharedPreferences.Editor spedit = mSharedPreferences.edit();
|
||||||
|
// spedit.clear();
|
||||||
|
// spedit.commit();
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
{
|
||||||
|
mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context getContext() {
|
||||||
|
return mContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeviceIDForIdentifier(String identifier) {
|
||||||
|
SharedPreferences.Editor spedit = mSharedPreferences.edit();
|
||||||
|
|
||||||
|
int result = mSharedPreferences.getInt(identifier, 0);
|
||||||
|
if (result == 0) {
|
||||||
|
result = mNextDeviceId++;
|
||||||
|
spedit.putInt("next_device_id", mNextDeviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
spedit.putInt(identifier, result);
|
||||||
|
spedit.commit();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeUSB() {
|
||||||
|
mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
|
||||||
|
if (mUsbManager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Logging
|
||||||
|
for (UsbDevice device : mUsbManager.getDeviceList().values()) {
|
||||||
|
Log.i(TAG,"Path: " + device.getDeviceName());
|
||||||
|
Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
|
||||||
|
Log.i(TAG,"Product: " + device.getProductName());
|
||||||
|
Log.i(TAG,"ID: " + device.getDeviceId());
|
||||||
|
Log.i(TAG,"Class: " + device.getDeviceClass());
|
||||||
|
Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
|
||||||
|
Log.i(TAG,"Vendor ID " + device.getVendorId());
|
||||||
|
Log.i(TAG,"Product ID: " + device.getProductId());
|
||||||
|
Log.i(TAG,"Interface count: " + device.getInterfaceCount());
|
||||||
|
Log.i(TAG,"---------------------------------------");
|
||||||
|
|
||||||
|
// Get interface details
|
||||||
|
for (int index = 0; index < device.getInterfaceCount(); index++) {
|
||||||
|
UsbInterface mUsbInterface = device.getInterface(index);
|
||||||
|
Log.i(TAG," ***** *****");
|
||||||
|
Log.i(TAG," Interface index: " + index);
|
||||||
|
Log.i(TAG," Interface ID: " + mUsbInterface.getId());
|
||||||
|
Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass());
|
||||||
|
Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass());
|
||||||
|
Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
|
||||||
|
Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
|
||||||
|
|
||||||
|
// Get endpoint details
|
||||||
|
for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
|
||||||
|
{
|
||||||
|
UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
|
||||||
|
Log.i(TAG," ++++ ++++ ++++");
|
||||||
|
Log.i(TAG," Endpoint index: " + epi);
|
||||||
|
Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
|
||||||
|
Log.i(TAG," Direction: " + mEndpoint.getDirection());
|
||||||
|
Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
|
||||||
|
Log.i(TAG," Interval: " + mEndpoint.getInterval());
|
||||||
|
Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
|
||||||
|
Log.i(TAG," Type: " + mEndpoint.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.i(TAG," No more devices connected.");
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Register for USB broadcasts and permission completions
|
||||||
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
|
||||||
|
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
|
||||||
|
filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
|
||||||
|
mContext.registerReceiver(mUsbBroadcast, filter);
|
||||||
|
|
||||||
|
for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
|
||||||
|
handleUsbDeviceAttached(usbDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UsbManager getUSBManager() {
|
||||||
|
return mUsbManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shutdownUSB() {
|
||||||
|
try {
|
||||||
|
mContext.unregisterReceiver(mUsbBroadcast);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We may not have registered, that's okay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isHIDDeviceInterface(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||||
|
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||||
|
final int XB360_IFACE_SUBCLASS = 93;
|
||||||
|
final int XB360_IFACE_PROTOCOL = 1; // Wired
|
||||||
|
final int XB360W_IFACE_PROTOCOL = 129; // Wireless
|
||||||
|
final int[] SUPPORTED_VENDORS = {
|
||||||
|
0x0079, // GPD Win 2
|
||||||
|
0x044f, // Thrustmaster
|
||||||
|
0x045e, // Microsoft
|
||||||
|
0x046d, // Logitech
|
||||||
|
0x056e, // Elecom
|
||||||
|
0x06a3, // Saitek
|
||||||
|
0x0738, // Mad Catz
|
||||||
|
0x07ff, // Mad Catz
|
||||||
|
0x0e6f, // PDP
|
||||||
|
0x0f0d, // Hori
|
||||||
|
0x1038, // SteelSeries
|
||||||
|
0x11c9, // Nacon
|
||||||
|
0x12ab, // Unknown
|
||||||
|
0x1430, // RedOctane
|
||||||
|
0x146b, // BigBen
|
||||||
|
0x1532, // Razer Sabertooth
|
||||||
|
0x15e4, // Numark
|
||||||
|
0x162e, // Joytech
|
||||||
|
0x1689, // Razer Onza
|
||||||
|
0x1949, // Lab126, Inc.
|
||||||
|
0x1bad, // Harmonix
|
||||||
|
0x20d6, // PowerA
|
||||||
|
0x24c6, // PowerA
|
||||||
|
0x2c22, // Qanba
|
||||||
|
0x2dc8, // 8BitDo
|
||||||
|
0x9886, // ASTRO Gaming
|
||||||
|
};
|
||||||
|
|
||||||
|
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
||||||
|
usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
|
||||||
|
(usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL ||
|
||||||
|
usbInterface.getInterfaceProtocol() == XB360W_IFACE_PROTOCOL)) {
|
||||||
|
int vendor_id = usbDevice.getVendorId();
|
||||||
|
for (int supportedVid : SUPPORTED_VENDORS) {
|
||||||
|
if (vendor_id == supportedVid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||||
|
final int XB1_IFACE_SUBCLASS = 71;
|
||||||
|
final int XB1_IFACE_PROTOCOL = 208;
|
||||||
|
final int[] SUPPORTED_VENDORS = {
|
||||||
|
0x03f0, // HP
|
||||||
|
0x044f, // Thrustmaster
|
||||||
|
0x045e, // Microsoft
|
||||||
|
0x0738, // Mad Catz
|
||||||
|
0x0b05, // ASUS
|
||||||
|
0x0e6f, // PDP
|
||||||
|
0x0f0d, // Hori
|
||||||
|
0x10f5, // Turtle Beach
|
||||||
|
0x1532, // Razer Wildcat
|
||||||
|
0x20d6, // PowerA
|
||||||
|
0x24c6, // PowerA
|
||||||
|
0x2dc8, // 8BitDo
|
||||||
|
0x2e24, // Hyperkin
|
||||||
|
0x3537, // GameSir
|
||||||
|
};
|
||||||
|
|
||||||
|
if (usbInterface.getId() == 0 &&
|
||||||
|
usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
||||||
|
usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
|
||||||
|
usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
|
||||||
|
int vendor_id = usbDevice.getVendorId();
|
||||||
|
for (int supportedVid : SUPPORTED_VENDORS) {
|
||||||
|
if (vendor_id == supportedVid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleUsbDeviceAttached(UsbDevice usbDevice) {
|
||||||
|
connectHIDDeviceUSB(usbDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleUsbDeviceDetached(UsbDevice usbDevice) {
|
||||||
|
List<Integer> devices = new ArrayList<Integer>();
|
||||||
|
for (HIDDevice device : mDevicesById.values()) {
|
||||||
|
if (usbDevice.equals(device.getDevice())) {
|
||||||
|
devices.add(device.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int id : devices) {
|
||||||
|
HIDDevice device = mDevicesById.get(id);
|
||||||
|
mDevicesById.remove(id);
|
||||||
|
device.shutdown();
|
||||||
|
HIDDeviceDisconnected(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
|
||||||
|
for (HIDDevice device : mDevicesById.values()) {
|
||||||
|
if (usbDevice.equals(device.getDevice())) {
|
||||||
|
boolean opened = false;
|
||||||
|
if (permission_granted) {
|
||||||
|
opened = device.open();
|
||||||
|
}
|
||||||
|
HIDDeviceOpenResult(device.getId(), opened);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connectHIDDeviceUSB(UsbDevice usbDevice) {
|
||||||
|
synchronized (this) {
|
||||||
|
int interface_mask = 0;
|
||||||
|
for (int interface_index = 0; interface_index < usbDevice.getInterfaceCount(); interface_index++) {
|
||||||
|
UsbInterface usbInterface = usbDevice.getInterface(interface_index);
|
||||||
|
if (isHIDDeviceInterface(usbDevice, usbInterface)) {
|
||||||
|
// Check to see if we've already added this interface
|
||||||
|
// This happens with the Xbox Series X controller which has a duplicate interface 0, which is inactive
|
||||||
|
int interface_id = usbInterface.getId();
|
||||||
|
if ((interface_mask & (1 << interface_id)) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
interface_mask |= (1 << interface_id);
|
||||||
|
|
||||||
|
HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index);
|
||||||
|
int id = device.getId();
|
||||||
|
mDevicesById.put(id, device);
|
||||||
|
HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeBluetooth() {
|
||||||
|
Log.d(TAG, "Initializing Bluetooth");
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 31 /* Android 12 */ &&
|
||||||
|
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH_CONNECT, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH_CONNECT");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT <= 30 /* Android 11.0 (R) */ &&
|
||||||
|
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18 /* Android 4.3 (JELLY_BEAN_MR2) */)) {
|
||||||
|
Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find bonded bluetooth controllers and create SteamControllers for them
|
||||||
|
mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||||
|
if (mBluetoothManager == null) {
|
||||||
|
// This device doesn't support Bluetooth.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
|
||||||
|
if (btAdapter == null) {
|
||||||
|
// This device has Bluetooth support in the codebase, but has no available adapters.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get our bonded devices.
|
||||||
|
for (BluetoothDevice device : btAdapter.getBondedDevices()) {
|
||||||
|
|
||||||
|
Log.d(TAG, "Bluetooth device available: " + device);
|
||||||
|
if (isSteamController(device)) {
|
||||||
|
connectBluetoothDevice(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: These don't work on Chromebooks, to my undying dismay.
|
||||||
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
|
||||||
|
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
|
||||||
|
mContext.registerReceiver(mBluetoothBroadcast, filter);
|
||||||
|
|
||||||
|
if (mIsChromebook) {
|
||||||
|
mHandler = new Handler(Looper.getMainLooper());
|
||||||
|
mLastBluetoothDevices = new ArrayList<BluetoothDevice>();
|
||||||
|
|
||||||
|
// final HIDDeviceManager finalThis = this;
|
||||||
|
// mHandler.postDelayed(new Runnable() {
|
||||||
|
// @Override
|
||||||
|
// public void run() {
|
||||||
|
// finalThis.chromebookConnectionHandler();
|
||||||
|
// }
|
||||||
|
// }, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shutdownBluetooth() {
|
||||||
|
try {
|
||||||
|
mContext.unregisterReceiver(mBluetoothBroadcast);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We may not have registered, that's okay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
|
||||||
|
// This function provides a sort of dummy version of that, watching for changes in the
|
||||||
|
// connected devices and attempting to add controllers as things change.
|
||||||
|
public void chromebookConnectionHandler() {
|
||||||
|
if (!mIsChromebook) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<BluetoothDevice> disconnected = new ArrayList<BluetoothDevice>();
|
||||||
|
ArrayList<BluetoothDevice> connected = new ArrayList<BluetoothDevice>();
|
||||||
|
|
||||||
|
List<BluetoothDevice> currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
|
||||||
|
|
||||||
|
for (BluetoothDevice bluetoothDevice : currentConnected) {
|
||||||
|
if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
|
||||||
|
connected.add(bluetoothDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
|
||||||
|
if (!currentConnected.contains(bluetoothDevice)) {
|
||||||
|
disconnected.add(bluetoothDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mLastBluetoothDevices = currentConnected;
|
||||||
|
|
||||||
|
for (BluetoothDevice bluetoothDevice : disconnected) {
|
||||||
|
disconnectBluetoothDevice(bluetoothDevice);
|
||||||
|
}
|
||||||
|
for (BluetoothDevice bluetoothDevice : connected) {
|
||||||
|
connectBluetoothDevice(bluetoothDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
final HIDDeviceManager finalThis = this;
|
||||||
|
mHandler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
finalThis.chromebookConnectionHandler();
|
||||||
|
}
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
|
||||||
|
Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
|
||||||
|
synchronized (this) {
|
||||||
|
if (mBluetoothDevices.containsKey(bluetoothDevice)) {
|
||||||
|
Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
|
||||||
|
|
||||||
|
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
|
||||||
|
device.reconnect();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
|
||||||
|
int id = device.getId();
|
||||||
|
mBluetoothDevices.put(bluetoothDevice, device);
|
||||||
|
mDevicesById.put(id, device);
|
||||||
|
|
||||||
|
// The Steam Controller will mark itself connected once initialization is complete
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
|
||||||
|
synchronized (this) {
|
||||||
|
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
|
||||||
|
if (device == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int id = device.getId();
|
||||||
|
mBluetoothDevices.remove(bluetoothDevice);
|
||||||
|
mDevicesById.remove(id);
|
||||||
|
device.shutdown();
|
||||||
|
HIDDeviceDisconnected(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSteamController(BluetoothDevice bluetoothDevice) {
|
||||||
|
// Sanity check. If you pass in a null device, by definition it is never a Steam Controller.
|
||||||
|
if (bluetoothDevice == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the device has no local name, we really don't want to try an equality check against it.
|
||||||
|
if (bluetoothDevice.getName() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void close() {
|
||||||
|
shutdownUSB();
|
||||||
|
shutdownBluetooth();
|
||||||
|
synchronized (this) {
|
||||||
|
for (HIDDevice device : mDevicesById.values()) {
|
||||||
|
device.shutdown();
|
||||||
|
}
|
||||||
|
mDevicesById.clear();
|
||||||
|
mBluetoothDevices.clear();
|
||||||
|
HIDDeviceReleaseCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFrozen(boolean frozen) {
|
||||||
|
synchronized (this) {
|
||||||
|
for (HIDDevice device : mDevicesById.values()) {
|
||||||
|
device.setFrozen(frozen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private HIDDevice getDevice(int id) {
|
||||||
|
synchronized (this) {
|
||||||
|
HIDDevice result = mDevicesById.get(id);
|
||||||
|
if (result == null) {
|
||||||
|
Log.v(TAG, "No device for id: " + id);
|
||||||
|
Log.v(TAG, "Available devices: " + mDevicesById.keySet());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////// JNI interface functions
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public boolean initialize(boolean usb, boolean bluetooth) {
|
||||||
|
Log.v(TAG, "initialize(" + usb + ", " + bluetooth + ")");
|
||||||
|
|
||||||
|
if (usb) {
|
||||||
|
initializeUSB();
|
||||||
|
}
|
||||||
|
if (bluetooth) {
|
||||||
|
initializeBluetooth();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean openDevice(int deviceID) {
|
||||||
|
Log.v(TAG, "openDevice deviceID=" + deviceID);
|
||||||
|
HIDDevice device = getDevice(deviceID);
|
||||||
|
if (device == null) {
|
||||||
|
HIDDeviceDisconnected(deviceID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look to see if this is a USB device and we have permission to access it
|
||||||
|
UsbDevice usbDevice = device.getDevice();
|
||||||
|
if (usbDevice != null && !mUsbManager.hasPermission(usbDevice)) {
|
||||||
|
HIDDeviceOpenPending(deviceID);
|
||||||
|
try {
|
||||||
|
final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31
|
||||||
|
int flags;
|
||||||
|
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
|
||||||
|
flags = FLAG_MUTABLE;
|
||||||
|
} else {
|
||||||
|
flags = 0;
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= 33 /* Android 14.0 (U) */) {
|
||||||
|
Intent intent = new Intent(HIDDeviceManager.ACTION_USB_PERMISSION);
|
||||||
|
intent.setPackage(mContext.getPackageName());
|
||||||
|
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, intent, flags));
|
||||||
|
} else {
|
||||||
|
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), flags));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
|
||||||
|
HIDDeviceOpenResult(deviceID, false);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return device.open();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int sendOutputReport(int deviceID, byte[] report) {
|
||||||
|
try {
|
||||||
|
//Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length);
|
||||||
|
HIDDevice device;
|
||||||
|
device = getDevice(deviceID);
|
||||||
|
if (device == null) {
|
||||||
|
HIDDeviceDisconnected(deviceID);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return device.sendOutputReport(report);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int sendFeatureReport(int deviceID, byte[] report) {
|
||||||
|
try {
|
||||||
|
//Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length);
|
||||||
|
HIDDevice device;
|
||||||
|
device = getDevice(deviceID);
|
||||||
|
if (device == null) {
|
||||||
|
HIDDeviceDisconnected(deviceID);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return device.sendFeatureReport(report);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getFeatureReport(int deviceID, byte[] report) {
|
||||||
|
try {
|
||||||
|
//Log.v(TAG, "getFeatureReport deviceID=" + deviceID);
|
||||||
|
HIDDevice device;
|
||||||
|
device = getDevice(deviceID);
|
||||||
|
if (device == null) {
|
||||||
|
HIDDeviceDisconnected(deviceID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return device.getFeatureReport(report);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeDevice(int deviceID) {
|
||||||
|
try {
|
||||||
|
Log.v(TAG, "closeDevice deviceID=" + deviceID);
|
||||||
|
HIDDevice device;
|
||||||
|
device = getDevice(deviceID);
|
||||||
|
if (device == null) {
|
||||||
|
HIDDeviceDisconnected(deviceID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
device.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/////////////// Native methods
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private native void HIDDeviceRegisterCallback();
|
||||||
|
private native void HIDDeviceReleaseCallback();
|
||||||
|
|
||||||
|
native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol);
|
||||||
|
native void HIDDeviceOpenPending(int deviceID);
|
||||||
|
native void HIDDeviceOpenResult(int deviceID, boolean opened);
|
||||||
|
native void HIDDeviceDisconnected(int deviceID);
|
||||||
|
|
||||||
|
native void HIDDeviceInputReport(int deviceID, byte[] report);
|
||||||
|
native void HIDDeviceFeatureReport(int deviceID, byte[] report);
|
||||||
|
}
|
309
android/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java
Normal file
309
android/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.hardware.usb.*;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
class HIDDeviceUSB implements HIDDevice {
|
||||||
|
|
||||||
|
private static final String TAG = "hidapi";
|
||||||
|
|
||||||
|
protected HIDDeviceManager mManager;
|
||||||
|
protected UsbDevice mDevice;
|
||||||
|
protected int mInterfaceIndex;
|
||||||
|
protected int mInterface;
|
||||||
|
protected int mDeviceId;
|
||||||
|
protected UsbDeviceConnection mConnection;
|
||||||
|
protected UsbEndpoint mInputEndpoint;
|
||||||
|
protected UsbEndpoint mOutputEndpoint;
|
||||||
|
protected InputThread mInputThread;
|
||||||
|
protected boolean mRunning;
|
||||||
|
protected boolean mFrozen;
|
||||||
|
|
||||||
|
public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) {
|
||||||
|
mManager = manager;
|
||||||
|
mDevice = usbDevice;
|
||||||
|
mInterfaceIndex = interface_index;
|
||||||
|
mInterface = mDevice.getInterface(mInterfaceIndex).getId();
|
||||||
|
mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
|
||||||
|
mRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentifier() {
|
||||||
|
return String.format("%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return mDeviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVendorId() {
|
||||||
|
return mDevice.getVendorId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getProductId() {
|
||||||
|
return mDevice.getProductId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSerialNumber() {
|
||||||
|
String result = null;
|
||||||
|
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||||
|
try {
|
||||||
|
result = mDevice.getSerialNumber();
|
||||||
|
}
|
||||||
|
catch (SecurityException exception) {
|
||||||
|
//Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result == null) {
|
||||||
|
result = "";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVersion() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getManufacturerName() {
|
||||||
|
String result = null;
|
||||||
|
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||||
|
result = mDevice.getManufacturerName();
|
||||||
|
}
|
||||||
|
if (result == null) {
|
||||||
|
result = String.format("%x", getVendorId());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProductName() {
|
||||||
|
String result = null;
|
||||||
|
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||||
|
result = mDevice.getProductName();
|
||||||
|
}
|
||||||
|
if (result == null) {
|
||||||
|
result = String.format("%x", getProductId());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UsbDevice getDevice() {
|
||||||
|
return mDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeviceName() {
|
||||||
|
return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean open() {
|
||||||
|
mConnection = mManager.getUSBManager().openDevice(mDevice);
|
||||||
|
if (mConnection == null) {
|
||||||
|
Log.w(TAG, "Unable to open USB device " + getDeviceName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force claim our interface
|
||||||
|
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
|
||||||
|
if (!mConnection.claimInterface(iface, true)) {
|
||||||
|
Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the endpoints
|
||||||
|
for (int j = 0; j < iface.getEndpointCount(); j++) {
|
||||||
|
UsbEndpoint endpt = iface.getEndpoint(j);
|
||||||
|
switch (endpt.getDirection()) {
|
||||||
|
case UsbConstants.USB_DIR_IN:
|
||||||
|
if (mInputEndpoint == null) {
|
||||||
|
mInputEndpoint = endpt;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case UsbConstants.USB_DIR_OUT:
|
||||||
|
if (mOutputEndpoint == null) {
|
||||||
|
mOutputEndpoint = endpt;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the required endpoints were present
|
||||||
|
if (mInputEndpoint == null || mOutputEndpoint == null) {
|
||||||
|
Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start listening for input
|
||||||
|
mRunning = true;
|
||||||
|
mInputThread = new InputThread();
|
||||||
|
mInputThread.start();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendFeatureReport(byte[] report) {
|
||||||
|
int res = -1;
|
||||||
|
int offset = 0;
|
||||||
|
int length = report.length;
|
||||||
|
boolean skipped_report_id = false;
|
||||||
|
byte report_number = report[0];
|
||||||
|
|
||||||
|
if (report_number == 0x0) {
|
||||||
|
++offset;
|
||||||
|
--length;
|
||||||
|
skipped_report_id = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = mConnection.controlTransfer(
|
||||||
|
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
|
||||||
|
0x09/*HID set_report*/,
|
||||||
|
(3/*HID feature*/ << 8) | report_number,
|
||||||
|
mInterface,
|
||||||
|
report, offset, length,
|
||||||
|
1000/*timeout millis*/);
|
||||||
|
|
||||||
|
if (res < 0) {
|
||||||
|
Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skipped_report_id) {
|
||||||
|
++length;
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendOutputReport(byte[] report) {
|
||||||
|
int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
|
||||||
|
if (r != report.length) {
|
||||||
|
Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getFeatureReport(byte[] report) {
|
||||||
|
int res = -1;
|
||||||
|
int offset = 0;
|
||||||
|
int length = report.length;
|
||||||
|
boolean skipped_report_id = false;
|
||||||
|
byte report_number = report[0];
|
||||||
|
|
||||||
|
if (report_number == 0x0) {
|
||||||
|
/* Offset the return buffer by 1, so that the report ID
|
||||||
|
will remain in byte 0. */
|
||||||
|
++offset;
|
||||||
|
--length;
|
||||||
|
skipped_report_id = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = mConnection.controlTransfer(
|
||||||
|
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
|
||||||
|
0x01/*HID get_report*/,
|
||||||
|
(3/*HID feature*/ << 8) | report_number,
|
||||||
|
mInterface,
|
||||||
|
report, offset, length,
|
||||||
|
1000/*timeout millis*/);
|
||||||
|
|
||||||
|
if (res < 0) {
|
||||||
|
Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skipped_report_id) {
|
||||||
|
++res;
|
||||||
|
++length;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data;
|
||||||
|
if (res == length) {
|
||||||
|
data = report;
|
||||||
|
} else {
|
||||||
|
data = Arrays.copyOfRange(report, 0, res);
|
||||||
|
}
|
||||||
|
mManager.HIDDeviceFeatureReport(mDeviceId, data);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
mRunning = false;
|
||||||
|
if (mInputThread != null) {
|
||||||
|
while (mInputThread.isAlive()) {
|
||||||
|
mInputThread.interrupt();
|
||||||
|
try {
|
||||||
|
mInputThread.join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// Keep trying until we're done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mInputThread = null;
|
||||||
|
}
|
||||||
|
if (mConnection != null) {
|
||||||
|
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
|
||||||
|
mConnection.releaseInterface(iface);
|
||||||
|
mConnection.close();
|
||||||
|
mConnection = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
close();
|
||||||
|
mManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFrozen(boolean frozen) {
|
||||||
|
mFrozen = frozen;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class InputThread extends Thread {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
int packetSize = mInputEndpoint.getMaxPacketSize();
|
||||||
|
byte[] packet = new byte[packetSize];
|
||||||
|
while (mRunning) {
|
||||||
|
int r;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (r < 0) {
|
||||||
|
// Could be a timeout or an I/O error
|
||||||
|
}
|
||||||
|
if (r > 0) {
|
||||||
|
byte[] data;
|
||||||
|
if (r == packetSize) {
|
||||||
|
data = packet;
|
||||||
|
} else {
|
||||||
|
data = Arrays.copyOfRange(packet, 0, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mFrozen) {
|
||||||
|
mManager.HIDDeviceInputReport(mDeviceId, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
android/app/src/main/java/org/libsdl/app/SDL.java
Normal file
90
android/app/src/main/java/org/libsdl/app/SDL.java
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import java.lang.Class;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
SDL library initialization
|
||||||
|
*/
|
||||||
|
public class SDL {
|
||||||
|
|
||||||
|
// This function should be called first and sets up the native code
|
||||||
|
// so it can call into the Java classes
|
||||||
|
public static void setupJNI() {
|
||||||
|
SDLActivity.nativeSetupJNI();
|
||||||
|
SDLAudioManager.nativeSetupJNI();
|
||||||
|
SDLControllerManager.nativeSetupJNI();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function should be called each time the activity is started
|
||||||
|
public static void initialize() {
|
||||||
|
setContext(null);
|
||||||
|
|
||||||
|
SDLActivity.initialize();
|
||||||
|
SDLAudioManager.initialize();
|
||||||
|
SDLControllerManager.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function stores the current activity (SDL or not)
|
||||||
|
public static void setContext(Context context) {
|
||||||
|
SDLAudioManager.setContext(context);
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Context getContext() {
|
||||||
|
return mContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
|
||||||
|
loadLibrary(libraryName, mContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadLibrary(String libraryName, Context context) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
|
||||||
|
|
||||||
|
if (libraryName == null) {
|
||||||
|
throw new NullPointerException("No library name provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Let's see if we have ReLinker available in the project. This is necessary for
|
||||||
|
// some projects that have huge numbers of local libraries bundled, and thus may
|
||||||
|
// trip a bug in Android's native library loader which ReLinker works around. (If
|
||||||
|
// loadLibrary works properly, ReLinker will simply use the normal Android method
|
||||||
|
// internally.)
|
||||||
|
//
|
||||||
|
// To use ReLinker, just add it as a dependency. For more information, see
|
||||||
|
// https://github.com/KeepSafe/ReLinker for ReLinker's repository.
|
||||||
|
//
|
||||||
|
Class<?> relinkClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker");
|
||||||
|
Class<?> relinkListenerClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener");
|
||||||
|
Class<?> contextClass = context.getClassLoader().loadClass("android.content.Context");
|
||||||
|
Class<?> stringClass = context.getClassLoader().loadClass("java.lang.String");
|
||||||
|
|
||||||
|
// Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if
|
||||||
|
// they've changed during updates.
|
||||||
|
Method forceMethod = relinkClass.getDeclaredMethod("force");
|
||||||
|
Object relinkInstance = forceMethod.invoke(null);
|
||||||
|
Class<?> relinkInstanceClass = relinkInstance.getClass();
|
||||||
|
|
||||||
|
// Actually load the library!
|
||||||
|
Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass);
|
||||||
|
loadMethod.invoke(relinkInstance, context, libraryName, null, null);
|
||||||
|
}
|
||||||
|
catch (final Throwable e) {
|
||||||
|
// Fall back
|
||||||
|
try {
|
||||||
|
System.loadLibrary(libraryName);
|
||||||
|
}
|
||||||
|
catch (final UnsatisfiedLinkError ule) {
|
||||||
|
throw ule;
|
||||||
|
}
|
||||||
|
catch (final SecurityException se) {
|
||||||
|
throw se;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Context mContext;
|
||||||
|
}
|
2125
android/app/src/main/java/org/libsdl/app/SDLActivity.java
Normal file
2125
android/app/src/main/java/org/libsdl/app/SDLActivity.java
Normal file
File diff suppressed because it is too large
Load diff
514
android/app/src/main/java/org/libsdl/app/SDLAudioManager.java
Normal file
514
android/app/src/main/java/org/libsdl/app/SDLAudioManager.java
Normal file
|
@ -0,0 +1,514 @@
|
||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.media.AudioDeviceCallback;
|
||||||
|
import android.media.AudioDeviceInfo;
|
||||||
|
import android.media.AudioFormat;
|
||||||
|
import android.media.AudioManager;
|
||||||
|
import android.media.AudioRecord;
|
||||||
|
import android.media.AudioTrack;
|
||||||
|
import android.media.MediaRecorder;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class SDLAudioManager {
|
||||||
|
protected static final String TAG = "SDLAudio";
|
||||||
|
|
||||||
|
protected static AudioTrack mAudioTrack;
|
||||||
|
protected static AudioRecord mAudioRecord;
|
||||||
|
protected static Context mContext;
|
||||||
|
|
||||||
|
private static final int[] NO_DEVICES = {};
|
||||||
|
|
||||||
|
private static AudioDeviceCallback mAudioDeviceCallback;
|
||||||
|
|
||||||
|
public static void initialize() {
|
||||||
|
mAudioTrack = null;
|
||||||
|
mAudioRecord = null;
|
||||||
|
mAudioDeviceCallback = null;
|
||||||
|
|
||||||
|
if(Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */)
|
||||||
|
{
|
||||||
|
mAudioDeviceCallback = new AudioDeviceCallback() {
|
||||||
|
@Override
|
||||||
|
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
|
||||||
|
Arrays.stream(addedDevices).forEach(deviceInfo -> addAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
|
||||||
|
Arrays.stream(removedDevices).forEach(deviceInfo -> removeAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setContext(Context context) {
|
||||||
|
mContext = context;
|
||||||
|
if (context != null) {
|
||||||
|
registerAudioDeviceCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void release(Context context) {
|
||||||
|
unregisterAudioDeviceCallback(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audio
|
||||||
|
|
||||||
|
protected static String getAudioFormatString(int audioFormat) {
|
||||||
|
switch (audioFormat) {
|
||||||
|
case AudioFormat.ENCODING_PCM_8BIT:
|
||||||
|
return "8-bit";
|
||||||
|
case AudioFormat.ENCODING_PCM_16BIT:
|
||||||
|
return "16-bit";
|
||||||
|
case AudioFormat.ENCODING_PCM_FLOAT:
|
||||||
|
return "float";
|
||||||
|
default:
|
||||||
|
return Integer.toString(audioFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
|
||||||
|
int channelConfig;
|
||||||
|
int sampleSize;
|
||||||
|
int frameSize;
|
||||||
|
|
||||||
|
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz");
|
||||||
|
|
||||||
|
/* On older devices let's use known good settings */
|
||||||
|
if (Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||||
|
if (desiredChannels > 2) {
|
||||||
|
desiredChannels = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AudioTrack has sample rate limitation of 48000 (fixed in 5.0.2) */
|
||||||
|
if (Build.VERSION.SDK_INT < 22 /* Android 5.1 (LOLLIPOP_MR1) */) {
|
||||||
|
if (sampleRate < 8000) {
|
||||||
|
sampleRate = 8000;
|
||||||
|
} else if (sampleRate > 48000) {
|
||||||
|
sampleRate = 48000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
|
||||||
|
int minSDKVersion = (isCapture ? 23 /* Android 6.0 (M) */ : 21 /* Android 5.0 (LOLLIPOP) */);
|
||||||
|
if (Build.VERSION.SDK_INT < minSDKVersion) {
|
||||||
|
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (audioFormat)
|
||||||
|
{
|
||||||
|
case AudioFormat.ENCODING_PCM_8BIT:
|
||||||
|
sampleSize = 1;
|
||||||
|
break;
|
||||||
|
case AudioFormat.ENCODING_PCM_16BIT:
|
||||||
|
sampleSize = 2;
|
||||||
|
break;
|
||||||
|
case AudioFormat.ENCODING_PCM_FLOAT:
|
||||||
|
sampleSize = 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.v(TAG, "Requested format " + audioFormat + ", getting ENCODING_PCM_16BIT");
|
||||||
|
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
|
||||||
|
sampleSize = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCapture) {
|
||||||
|
switch (desiredChannels) {
|
||||||
|
case 1:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_IN_MONO;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
|
||||||
|
desiredChannels = 2;
|
||||||
|
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (desiredChannels) {
|
||||||
|
case 1:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_MONO;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
|
||||||
|
} else {
|
||||||
|
Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround");
|
||||||
|
desiredChannels = 6;
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
|
||||||
|
desiredChannels = 2;
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Log.v(TAG, "Speaker configuration (and order of channels):");
|
||||||
|
|
||||||
|
if ((channelConfig & 0x00000004) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000008) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000010) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_CENTER");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000020) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_LOW_FREQUENCY");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000040) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_BACK_LEFT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000080) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_BACK_RIGHT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000100) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT_OF_CENTER");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000200) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT_OF_CENTER");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000400) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_BACK_CENTER");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000800) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_SIDE_LEFT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00001000) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_SIDE_RIGHT");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
frameSize = (sampleSize * desiredChannels);
|
||||||
|
|
||||||
|
// Let the user pick a larger buffer if they really want -- but ye
|
||||||
|
// gods they probably shouldn't, the minimums are horrifyingly high
|
||||||
|
// latency already
|
||||||
|
int minBufferSize;
|
||||||
|
if (isCapture) {
|
||||||
|
minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
|
||||||
|
} else {
|
||||||
|
minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
|
||||||
|
}
|
||||||
|
desiredFrames = Math.max(desiredFrames, (minBufferSize + frameSize - 1) / frameSize);
|
||||||
|
|
||||||
|
int[] results = new int[4];
|
||||||
|
|
||||||
|
if (isCapture) {
|
||||||
|
if (mAudioRecord == null) {
|
||||||
|
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
|
||||||
|
channelConfig, audioFormat, desiredFrames * frameSize);
|
||||||
|
|
||||||
|
// see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
|
||||||
|
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
|
||||||
|
Log.e(TAG, "Failed during initialization of AudioRecord");
|
||||||
|
mAudioRecord.release();
|
||||||
|
mAudioRecord = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
|
||||||
|
mAudioRecord.setPreferredDevice(getOutputAudioDeviceInfo(deviceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
mAudioRecord.startRecording();
|
||||||
|
}
|
||||||
|
|
||||||
|
results[0] = mAudioRecord.getSampleRate();
|
||||||
|
results[1] = mAudioRecord.getAudioFormat();
|
||||||
|
results[2] = mAudioRecord.getChannelCount();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (mAudioTrack == null) {
|
||||||
|
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
|
||||||
|
|
||||||
|
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
|
||||||
|
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
|
||||||
|
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
|
||||||
|
if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
|
||||||
|
/* Try again, with safer values */
|
||||||
|
|
||||||
|
Log.e(TAG, "Failed during initialization of Audio Track");
|
||||||
|
mAudioTrack.release();
|
||||||
|
mAudioTrack = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
|
||||||
|
mAudioTrack.setPreferredDevice(getInputAudioDeviceInfo(deviceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
mAudioTrack.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
results[0] = mAudioTrack.getSampleRate();
|
||||||
|
results[1] = mAudioTrack.getAudioFormat();
|
||||||
|
results[2] = mAudioTrack.getChannelCount();
|
||||||
|
}
|
||||||
|
results[3] = desiredFrames;
|
||||||
|
|
||||||
|
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", got " + results[3] + " frames of " + results[2] + " channel " + getAudioFormatString(results[1]) + " audio at " + results[0] + " Hz");
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AudioDeviceInfo getInputAudioDeviceInfo(int deviceId) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS))
|
||||||
|
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AudioDeviceInfo getOutputAudioDeviceInfo(int deviceId) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS))
|
||||||
|
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerAudioDeviceCallback() {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void unregisterAudioDeviceCallback(Context context) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static int[] getAudioOutputDevices() {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
|
||||||
|
} else {
|
||||||
|
return NO_DEVICES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static int[] getAudioInputDevices() {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
|
||||||
|
} else {
|
||||||
|
return NO_DEVICES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
|
||||||
|
return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void audioWriteFloatBuffer(float[] buffer) {
|
||||||
|
if (mAudioTrack == null) {
|
||||||
|
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (android.os.Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||||
|
Log.e(TAG, "Attempted to make an incompatible audio call with uninitialized audio! (floating-point output is supported since Android 5.0 Lollipop)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < buffer.length;) {
|
||||||
|
int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING);
|
||||||
|
if (result > 0) {
|
||||||
|
i += result;
|
||||||
|
} else if (result == 0) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1);
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
// Nom nom
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "SDL audio: error return from write(float)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void audioWriteShortBuffer(short[] buffer) {
|
||||||
|
if (mAudioTrack == null) {
|
||||||
|
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < buffer.length;) {
|
||||||
|
int result = mAudioTrack.write(buffer, i, buffer.length - i);
|
||||||
|
if (result > 0) {
|
||||||
|
i += result;
|
||||||
|
} else if (result == 0) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1);
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
// Nom nom
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "SDL audio: error return from write(short)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void audioWriteByteBuffer(byte[] buffer) {
|
||||||
|
if (mAudioTrack == null) {
|
||||||
|
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < buffer.length; ) {
|
||||||
|
int result = mAudioTrack.write(buffer, i, buffer.length - i);
|
||||||
|
if (result > 0) {
|
||||||
|
i += result;
|
||||||
|
} else if (result == 0) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1);
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
// Nom nom
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "SDL audio: error return from write(byte)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
|
||||||
|
return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static int captureReadFloatBuffer(float[] buffer, boolean blocking) {
|
||||||
|
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
|
||||||
|
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length);
|
||||||
|
} else {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
|
||||||
|
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length);
|
||||||
|
} else {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static void audioClose() {
|
||||||
|
if (mAudioTrack != null) {
|
||||||
|
mAudioTrack.stop();
|
||||||
|
mAudioTrack.release();
|
||||||
|
mAudioTrack = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static void captureClose() {
|
||||||
|
if (mAudioRecord != null) {
|
||||||
|
mAudioRecord.stop();
|
||||||
|
mAudioRecord.release();
|
||||||
|
mAudioRecord = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static void audioSetThreadPriority(boolean iscapture, int device_id) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
/* Set thread name */
|
||||||
|
if (iscapture) {
|
||||||
|
Thread.currentThread().setName("SDLAudioC" + device_id);
|
||||||
|
} else {
|
||||||
|
Thread.currentThread().setName("SDLAudioP" + device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set thread priority */
|
||||||
|
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.v(TAG, "modify thread properties failed " + e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static native int nativeSetupJNI();
|
||||||
|
|
||||||
|
public static native void removeAudioDevice(boolean isCapture, int deviceId);
|
||||||
|
|
||||||
|
public static native void addAudioDevice(boolean isCapture, int deviceId);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,856 @@
|
||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.VibrationEffect;
|
||||||
|
import android.os.Vibrator;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.InputDevice;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
|
||||||
|
public class SDLControllerManager
|
||||||
|
{
|
||||||
|
|
||||||
|
public static native int nativeSetupJNI();
|
||||||
|
|
||||||
|
public static native int nativeAddJoystick(int device_id, String name, String desc,
|
||||||
|
int vendor_id, int product_id,
|
||||||
|
boolean is_accelerometer, int button_mask,
|
||||||
|
int naxes, int axis_mask, int nhats, int nballs);
|
||||||
|
public static native int nativeRemoveJoystick(int device_id);
|
||||||
|
public static native int nativeAddHaptic(int device_id, String name);
|
||||||
|
public static native int nativeRemoveHaptic(int device_id);
|
||||||
|
public static native int onNativePadDown(int device_id, int keycode);
|
||||||
|
public static native int onNativePadUp(int device_id, int keycode);
|
||||||
|
public static native void onNativeJoy(int device_id, int axis,
|
||||||
|
float value);
|
||||||
|
public static native void onNativeHat(int device_id, int hat_id,
|
||||||
|
int x, int y);
|
||||||
|
|
||||||
|
protected static SDLJoystickHandler mJoystickHandler;
|
||||||
|
protected static SDLHapticHandler mHapticHandler;
|
||||||
|
|
||||||
|
private static final String TAG = "SDLControllerManager";
|
||||||
|
|
||||||
|
public static void initialize() {
|
||||||
|
if (mJoystickHandler == null) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) {
|
||||||
|
mJoystickHandler = new SDLJoystickHandler_API19();
|
||||||
|
} else {
|
||||||
|
mJoystickHandler = new SDLJoystickHandler_API16();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mHapticHandler == null) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
|
||||||
|
mHapticHandler = new SDLHapticHandler_API26();
|
||||||
|
} else {
|
||||||
|
mHapticHandler = new SDLHapticHandler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
|
||||||
|
public static boolean handleJoystickMotionEvent(MotionEvent event) {
|
||||||
|
return mJoystickHandler.handleMotionEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void pollInputDevices() {
|
||||||
|
mJoystickHandler.pollInputDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void pollHapticDevices() {
|
||||||
|
mHapticHandler.pollHapticDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void hapticRun(int device_id, float intensity, int length) {
|
||||||
|
mHapticHandler.run(device_id, intensity, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void hapticStop(int device_id)
|
||||||
|
{
|
||||||
|
mHapticHandler.stop(device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a given device is considered a possible SDL joystick
|
||||||
|
public static boolean isDeviceSDLJoystick(int deviceId) {
|
||||||
|
InputDevice device = InputDevice.getDevice(deviceId);
|
||||||
|
// We cannot use InputDevice.isVirtual before API 16, so let's accept
|
||||||
|
// only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
|
||||||
|
if ((device == null) || (deviceId < 0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int sources = device.getSources();
|
||||||
|
|
||||||
|
/* This is called for every button press, so let's not spam the logs */
|
||||||
|
/*
|
||||||
|
if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
|
||||||
|
Log.v(TAG, "Input device " + device.getName() + " has class joystick.");
|
||||||
|
}
|
||||||
|
if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
|
||||||
|
Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
|
||||||
|
}
|
||||||
|
if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
|
||||||
|
Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ||
|
||||||
|
((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
|
||||||
|
((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLJoystickHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles given MotionEvent.
|
||||||
|
* @param event the event to be handled.
|
||||||
|
* @return if given event was processed.
|
||||||
|
*/
|
||||||
|
public boolean handleMotionEvent(MotionEvent event) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles adding and removing of input devices.
|
||||||
|
*/
|
||||||
|
public void pollInputDevices() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actual joystick functionality available for API >= 12 devices */
|
||||||
|
class SDLJoystickHandler_API16 extends SDLJoystickHandler {
|
||||||
|
|
||||||
|
static class SDLJoystick {
|
||||||
|
public int device_id;
|
||||||
|
public String name;
|
||||||
|
public String desc;
|
||||||
|
public ArrayList<InputDevice.MotionRange> axes;
|
||||||
|
public ArrayList<InputDevice.MotionRange> hats;
|
||||||
|
}
|
||||||
|
static class RangeComparator implements Comparator<InputDevice.MotionRange> {
|
||||||
|
@Override
|
||||||
|
public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
|
||||||
|
// Some controllers, like the Moga Pro 2, return AXIS_GAS (22) for right trigger and AXIS_BRAKE (23) for left trigger - swap them so they're sorted in the right order for SDL
|
||||||
|
int arg0Axis = arg0.getAxis();
|
||||||
|
int arg1Axis = arg1.getAxis();
|
||||||
|
if (arg0Axis == MotionEvent.AXIS_GAS) {
|
||||||
|
arg0Axis = MotionEvent.AXIS_BRAKE;
|
||||||
|
} else if (arg0Axis == MotionEvent.AXIS_BRAKE) {
|
||||||
|
arg0Axis = MotionEvent.AXIS_GAS;
|
||||||
|
}
|
||||||
|
if (arg1Axis == MotionEvent.AXIS_GAS) {
|
||||||
|
arg1Axis = MotionEvent.AXIS_BRAKE;
|
||||||
|
} else if (arg1Axis == MotionEvent.AXIS_BRAKE) {
|
||||||
|
arg1Axis = MotionEvent.AXIS_GAS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the AXIS_Z is sorted between AXIS_RY and AXIS_RZ.
|
||||||
|
// This is because the usual pairing are:
|
||||||
|
// - AXIS_X + AXIS_Y (left stick).
|
||||||
|
// - AXIS_RX, AXIS_RY (sometimes the right stick, sometimes triggers).
|
||||||
|
// - AXIS_Z, AXIS_RZ (sometimes the right stick, sometimes triggers).
|
||||||
|
// This sorts the axes in the above order, which tends to be correct
|
||||||
|
// for Xbox-ish game pads that have the right stick on RX/RY and the
|
||||||
|
// triggers on Z/RZ.
|
||||||
|
//
|
||||||
|
// Gamepads that don't have AXIS_Z/AXIS_RZ but use
|
||||||
|
// AXIS_LTRIGGER/AXIS_RTRIGGER are unaffected by this.
|
||||||
|
//
|
||||||
|
// References:
|
||||||
|
// - https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input
|
||||||
|
// - https://www.kernel.org/doc/html/latest/input/gamepad.html
|
||||||
|
if (arg0Axis == MotionEvent.AXIS_Z) {
|
||||||
|
arg0Axis = MotionEvent.AXIS_RZ - 1;
|
||||||
|
} else if (arg0Axis > MotionEvent.AXIS_Z && arg0Axis < MotionEvent.AXIS_RZ) {
|
||||||
|
--arg0Axis;
|
||||||
|
}
|
||||||
|
if (arg1Axis == MotionEvent.AXIS_Z) {
|
||||||
|
arg1Axis = MotionEvent.AXIS_RZ - 1;
|
||||||
|
} else if (arg1Axis > MotionEvent.AXIS_Z && arg1Axis < MotionEvent.AXIS_RZ) {
|
||||||
|
--arg1Axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
return arg0Axis - arg1Axis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ArrayList<SDLJoystick> mJoysticks;
|
||||||
|
|
||||||
|
public SDLJoystickHandler_API16() {
|
||||||
|
|
||||||
|
mJoysticks = new ArrayList<SDLJoystick>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pollInputDevices() {
|
||||||
|
int[] deviceIds = InputDevice.getDeviceIds();
|
||||||
|
|
||||||
|
for (int device_id : deviceIds) {
|
||||||
|
if (SDLControllerManager.isDeviceSDLJoystick(device_id)) {
|
||||||
|
SDLJoystick joystick = getJoystick(device_id);
|
||||||
|
if (joystick == null) {
|
||||||
|
InputDevice joystickDevice = InputDevice.getDevice(device_id);
|
||||||
|
joystick = new SDLJoystick();
|
||||||
|
joystick.device_id = device_id;
|
||||||
|
joystick.name = joystickDevice.getName();
|
||||||
|
joystick.desc = getJoystickDescriptor(joystickDevice);
|
||||||
|
joystick.axes = new ArrayList<InputDevice.MotionRange>();
|
||||||
|
joystick.hats = new ArrayList<InputDevice.MotionRange>();
|
||||||
|
|
||||||
|
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
|
||||||
|
Collections.sort(ranges, new RangeComparator());
|
||||||
|
for (InputDevice.MotionRange range : ranges) {
|
||||||
|
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
|
||||||
|
if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
|
||||||
|
joystick.hats.add(range);
|
||||||
|
} else {
|
||||||
|
joystick.axes.add(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mJoysticks.add(joystick);
|
||||||
|
SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc,
|
||||||
|
getVendorId(joystickDevice), getProductId(joystickDevice), false,
|
||||||
|
getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check removed devices */
|
||||||
|
ArrayList<Integer> removedDevices = null;
|
||||||
|
for (SDLJoystick joystick : mJoysticks) {
|
||||||
|
int device_id = joystick.device_id;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < deviceIds.length; i++) {
|
||||||
|
if (device_id == deviceIds[i]) break;
|
||||||
|
}
|
||||||
|
if (i == deviceIds.length) {
|
||||||
|
if (removedDevices == null) {
|
||||||
|
removedDevices = new ArrayList<Integer>();
|
||||||
|
}
|
||||||
|
removedDevices.add(device_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removedDevices != null) {
|
||||||
|
for (int device_id : removedDevices) {
|
||||||
|
SDLControllerManager.nativeRemoveJoystick(device_id);
|
||||||
|
for (int i = 0; i < mJoysticks.size(); i++) {
|
||||||
|
if (mJoysticks.get(i).device_id == device_id) {
|
||||||
|
mJoysticks.remove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SDLJoystick getJoystick(int device_id) {
|
||||||
|
for (SDLJoystick joystick : mJoysticks) {
|
||||||
|
if (joystick.device_id == device_id) {
|
||||||
|
return joystick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleMotionEvent(MotionEvent event) {
|
||||||
|
int actionPointerIndex = event.getActionIndex();
|
||||||
|
int action = event.getActionMasked();
|
||||||
|
if (action == MotionEvent.ACTION_MOVE) {
|
||||||
|
SDLJoystick joystick = getJoystick(event.getDeviceId());
|
||||||
|
if (joystick != null) {
|
||||||
|
for (int i = 0; i < joystick.axes.size(); i++) {
|
||||||
|
InputDevice.MotionRange range = joystick.axes.get(i);
|
||||||
|
/* Normalize the value to -1...1 */
|
||||||
|
float value = (event.getAxisValue(range.getAxis(), actionPointerIndex) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
|
||||||
|
SDLControllerManager.onNativeJoy(joystick.device_id, i, value);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < joystick.hats.size() / 2; i++) {
|
||||||
|
int hatX = Math.round(event.getAxisValue(joystick.hats.get(2 * i).getAxis(), actionPointerIndex));
|
||||||
|
int hatY = Math.round(event.getAxisValue(joystick.hats.get(2 * i + 1).getAxis(), actionPointerIndex));
|
||||||
|
SDLControllerManager.onNativeHat(joystick.device_id, i, hatX, hatY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJoystickDescriptor(InputDevice joystickDevice) {
|
||||||
|
String desc = joystickDevice.getDescriptor();
|
||||||
|
|
||||||
|
if (desc != null && !desc.isEmpty()) {
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return joystickDevice.getName();
|
||||||
|
}
|
||||||
|
public int getProductId(InputDevice joystickDevice) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
public int getVendorId(InputDevice joystickDevice) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
public int getButtonMask(InputDevice joystickDevice) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getProductId(InputDevice joystickDevice) {
|
||||||
|
return joystickDevice.getProductId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVendorId(InputDevice joystickDevice) {
|
||||||
|
return joystickDevice.getVendorId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
|
||||||
|
// For compatibility, keep computing the axis mask like before,
|
||||||
|
// only really distinguishing 2, 4 and 6 axes.
|
||||||
|
int axis_mask = 0;
|
||||||
|
if (ranges.size() >= 2) {
|
||||||
|
// ((1 << SDL_GAMEPAD_AXIS_LEFTX) | (1 << SDL_GAMEPAD_AXIS_LEFTY))
|
||||||
|
axis_mask |= 0x0003;
|
||||||
|
}
|
||||||
|
if (ranges.size() >= 4) {
|
||||||
|
// ((1 << SDL_GAMEPAD_AXIS_RIGHTX) | (1 << SDL_GAMEPAD_AXIS_RIGHTY))
|
||||||
|
axis_mask |= 0x000c;
|
||||||
|
}
|
||||||
|
if (ranges.size() >= 6) {
|
||||||
|
// ((1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER))
|
||||||
|
axis_mask |= 0x0030;
|
||||||
|
}
|
||||||
|
// Also add an indicator bit for whether the sorting order has changed.
|
||||||
|
// This serves to disable outdated gamecontrollerdb.txt mappings.
|
||||||
|
boolean have_z = false;
|
||||||
|
boolean have_past_z_before_rz = false;
|
||||||
|
for (InputDevice.MotionRange range : ranges) {
|
||||||
|
int axis = range.getAxis();
|
||||||
|
if (axis == MotionEvent.AXIS_Z) {
|
||||||
|
have_z = true;
|
||||||
|
} else if (axis > MotionEvent.AXIS_Z && axis < MotionEvent.AXIS_RZ) {
|
||||||
|
have_past_z_before_rz = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (have_z && have_past_z_before_rz) {
|
||||||
|
// If both these exist, the compare() function changed sorting order.
|
||||||
|
// Set a bit to indicate this fact.
|
||||||
|
axis_mask |= 0x8000;
|
||||||
|
}
|
||||||
|
return axis_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getButtonMask(InputDevice joystickDevice) {
|
||||||
|
int button_mask = 0;
|
||||||
|
int[] keys = new int[] {
|
||||||
|
KeyEvent.KEYCODE_BUTTON_A,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_B,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_X,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_Y,
|
||||||
|
KeyEvent.KEYCODE_BACK,
|
||||||
|
KeyEvent.KEYCODE_MENU,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_MODE,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_START,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_THUMBL,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_THUMBR,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_L1,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_R1,
|
||||||
|
KeyEvent.KEYCODE_DPAD_UP,
|
||||||
|
KeyEvent.KEYCODE_DPAD_DOWN,
|
||||||
|
KeyEvent.KEYCODE_DPAD_LEFT,
|
||||||
|
KeyEvent.KEYCODE_DPAD_RIGHT,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_SELECT,
|
||||||
|
KeyEvent.KEYCODE_DPAD_CENTER,
|
||||||
|
|
||||||
|
// These don't map into any SDL controller buttons directly
|
||||||
|
KeyEvent.KEYCODE_BUTTON_L2,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_R2,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_C,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_Z,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_1,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_2,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_3,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_4,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_5,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_6,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_7,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_8,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_9,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_10,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_11,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_12,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_13,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_14,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_15,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_16,
|
||||||
|
};
|
||||||
|
int[] masks = new int[] {
|
||||||
|
(1 << 0), // A -> A
|
||||||
|
(1 << 1), // B -> B
|
||||||
|
(1 << 2), // X -> X
|
||||||
|
(1 << 3), // Y -> Y
|
||||||
|
(1 << 4), // BACK -> BACK
|
||||||
|
(1 << 6), // MENU -> START
|
||||||
|
(1 << 5), // MODE -> GUIDE
|
||||||
|
(1 << 6), // START -> START
|
||||||
|
(1 << 7), // THUMBL -> LEFTSTICK
|
||||||
|
(1 << 8), // THUMBR -> RIGHTSTICK
|
||||||
|
(1 << 9), // L1 -> LEFTSHOULDER
|
||||||
|
(1 << 10), // R1 -> RIGHTSHOULDER
|
||||||
|
(1 << 11), // DPAD_UP -> DPAD_UP
|
||||||
|
(1 << 12), // DPAD_DOWN -> DPAD_DOWN
|
||||||
|
(1 << 13), // DPAD_LEFT -> DPAD_LEFT
|
||||||
|
(1 << 14), // DPAD_RIGHT -> DPAD_RIGHT
|
||||||
|
(1 << 4), // SELECT -> BACK
|
||||||
|
(1 << 0), // DPAD_CENTER -> A
|
||||||
|
(1 << 15), // L2 -> ??
|
||||||
|
(1 << 16), // R2 -> ??
|
||||||
|
(1 << 17), // C -> ??
|
||||||
|
(1 << 18), // Z -> ??
|
||||||
|
(1 << 20), // 1 -> ??
|
||||||
|
(1 << 21), // 2 -> ??
|
||||||
|
(1 << 22), // 3 -> ??
|
||||||
|
(1 << 23), // 4 -> ??
|
||||||
|
(1 << 24), // 5 -> ??
|
||||||
|
(1 << 25), // 6 -> ??
|
||||||
|
(1 << 26), // 7 -> ??
|
||||||
|
(1 << 27), // 8 -> ??
|
||||||
|
(1 << 28), // 9 -> ??
|
||||||
|
(1 << 29), // 10 -> ??
|
||||||
|
(1 << 30), // 11 -> ??
|
||||||
|
(1 << 31), // 12 -> ??
|
||||||
|
// We're out of room...
|
||||||
|
0xFFFFFFFF, // 13 -> ??
|
||||||
|
0xFFFFFFFF, // 14 -> ??
|
||||||
|
0xFFFFFFFF, // 15 -> ??
|
||||||
|
0xFFFFFFFF, // 16 -> ??
|
||||||
|
};
|
||||||
|
boolean[] has_keys = joystickDevice.hasKeys(keys);
|
||||||
|
for (int i = 0; i < keys.length; ++i) {
|
||||||
|
if (has_keys[i]) {
|
||||||
|
button_mask |= masks[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return button_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLHapticHandler_API26 extends SDLHapticHandler {
|
||||||
|
@Override
|
||||||
|
public void run(int device_id, float intensity, int length) {
|
||||||
|
SDLHaptic haptic = getHaptic(device_id);
|
||||||
|
if (haptic != null) {
|
||||||
|
Log.d("SDL", "Rtest: Vibe with intensity " + intensity + " for " + length);
|
||||||
|
if (intensity == 0.0f) {
|
||||||
|
stop(device_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vibeValue = Math.round(intensity * 255);
|
||||||
|
|
||||||
|
if (vibeValue > 255) {
|
||||||
|
vibeValue = 255;
|
||||||
|
}
|
||||||
|
if (vibeValue < 1) {
|
||||||
|
stop(device_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
haptic.vib.vibrate(VibrationEffect.createOneShot(length, vibeValue));
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
// Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if
|
||||||
|
// something went horribly wrong with the Android 8.0 APIs.
|
||||||
|
haptic.vib.vibrate(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLHapticHandler {
|
||||||
|
|
||||||
|
static class SDLHaptic {
|
||||||
|
public int device_id;
|
||||||
|
public String name;
|
||||||
|
public Vibrator vib;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ArrayList<SDLHaptic> mHaptics;
|
||||||
|
|
||||||
|
public SDLHapticHandler() {
|
||||||
|
mHaptics = new ArrayList<SDLHaptic>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run(int device_id, float intensity, int length) {
|
||||||
|
SDLHaptic haptic = getHaptic(device_id);
|
||||||
|
if (haptic != null) {
|
||||||
|
haptic.vib.vibrate(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop(int device_id) {
|
||||||
|
SDLHaptic haptic = getHaptic(device_id);
|
||||||
|
if (haptic != null) {
|
||||||
|
haptic.vib.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pollHapticDevices() {
|
||||||
|
|
||||||
|
final int deviceId_VIBRATOR_SERVICE = 999999;
|
||||||
|
boolean hasVibratorService = false;
|
||||||
|
|
||||||
|
int[] deviceIds = InputDevice.getDeviceIds();
|
||||||
|
// It helps processing the device ids in reverse order
|
||||||
|
// For example, in the case of the XBox 360 wireless dongle,
|
||||||
|
// so the first controller seen by SDL matches what the receiver
|
||||||
|
// considers to be the first controller
|
||||||
|
|
||||||
|
for (int i = deviceIds.length - 1; i > -1; i--) {
|
||||||
|
SDLHaptic haptic = getHaptic(deviceIds[i]);
|
||||||
|
if (haptic == null) {
|
||||||
|
InputDevice device = InputDevice.getDevice(deviceIds[i]);
|
||||||
|
Vibrator vib = device.getVibrator();
|
||||||
|
if (vib != null) {
|
||||||
|
if (vib.hasVibrator()) {
|
||||||
|
haptic = new SDLHaptic();
|
||||||
|
haptic.device_id = deviceIds[i];
|
||||||
|
haptic.name = device.getName();
|
||||||
|
haptic.vib = vib;
|
||||||
|
mHaptics.add(haptic);
|
||||||
|
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check VIBRATOR_SERVICE */
|
||||||
|
Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
|
if (vib != null) {
|
||||||
|
hasVibratorService = vib.hasVibrator();
|
||||||
|
|
||||||
|
if (hasVibratorService) {
|
||||||
|
SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
|
||||||
|
if (haptic == null) {
|
||||||
|
haptic = new SDLHaptic();
|
||||||
|
haptic.device_id = deviceId_VIBRATOR_SERVICE;
|
||||||
|
haptic.name = "VIBRATOR_SERVICE";
|
||||||
|
haptic.vib = vib;
|
||||||
|
mHaptics.add(haptic);
|
||||||
|
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check removed devices */
|
||||||
|
ArrayList<Integer> removedDevices = null;
|
||||||
|
for (SDLHaptic haptic : mHaptics) {
|
||||||
|
int device_id = haptic.device_id;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < deviceIds.length; i++) {
|
||||||
|
if (device_id == deviceIds[i]) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_id != deviceId_VIBRATOR_SERVICE || !hasVibratorService) {
|
||||||
|
if (i == deviceIds.length) {
|
||||||
|
if (removedDevices == null) {
|
||||||
|
removedDevices = new ArrayList<Integer>();
|
||||||
|
}
|
||||||
|
removedDevices.add(device_id);
|
||||||
|
}
|
||||||
|
} // else: don't remove the vibrator if it is still present
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removedDevices != null) {
|
||||||
|
for (int device_id : removedDevices) {
|
||||||
|
SDLControllerManager.nativeRemoveHaptic(device_id);
|
||||||
|
for (int i = 0; i < mHaptics.size(); i++) {
|
||||||
|
if (mHaptics.get(i).device_id == device_id) {
|
||||||
|
mHaptics.remove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SDLHaptic getHaptic(int device_id) {
|
||||||
|
for (SDLHaptic haptic : mHaptics) {
|
||||||
|
if (haptic.device_id == device_id) {
|
||||||
|
return haptic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
|
||||||
|
// Generic Motion (mouse hover, joystick...) events go here
|
||||||
|
@Override
|
||||||
|
public boolean onGenericMotion(View v, MotionEvent event) {
|
||||||
|
float x, y;
|
||||||
|
int action;
|
||||||
|
|
||||||
|
switch ( event.getSource() ) {
|
||||||
|
case InputDevice.SOURCE_JOYSTICK:
|
||||||
|
return SDLControllerManager.handleJoystickMotionEvent(event);
|
||||||
|
|
||||||
|
case InputDevice.SOURCE_MOUSE:
|
||||||
|
action = event.getActionMasked();
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_SCROLL:
|
||||||
|
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||||
|
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_HOVER_MOVE:
|
||||||
|
x = event.getX(0);
|
||||||
|
y = event.getY(0);
|
||||||
|
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event was not managed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean supportsRelativeMouse() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean inRelativeMode() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setRelativeMouseEnabled(boolean enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reclaimRelativeMouseModeIfNeeded()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getEventX(MotionEvent event) {
|
||||||
|
return event.getX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getEventY(MotionEvent event) {
|
||||||
|
return event.getY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API12 {
|
||||||
|
// Generic Motion (mouse hover, joystick...) events go here
|
||||||
|
|
||||||
|
private boolean mRelativeModeEnabled;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onGenericMotion(View v, MotionEvent event) {
|
||||||
|
|
||||||
|
// Handle relative mouse mode
|
||||||
|
if (mRelativeModeEnabled) {
|
||||||
|
if (event.getSource() == InputDevice.SOURCE_MOUSE) {
|
||||||
|
int action = event.getActionMasked();
|
||||||
|
if (action == MotionEvent.ACTION_HOVER_MOVE) {
|
||||||
|
float x = event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
|
||||||
|
float y = event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event was not managed, call SDLGenericMotionListener_API12 method
|
||||||
|
return super.onGenericMotion(v, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRelativeMouse() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean inRelativeMode() {
|
||||||
|
return mRelativeModeEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setRelativeMouseEnabled(boolean enabled) {
|
||||||
|
mRelativeModeEnabled = enabled;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getEventX(MotionEvent event) {
|
||||||
|
if (mRelativeModeEnabled) {
|
||||||
|
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
|
||||||
|
} else {
|
||||||
|
return event.getX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getEventY(MotionEvent event) {
|
||||||
|
if (mRelativeModeEnabled) {
|
||||||
|
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
|
||||||
|
} else {
|
||||||
|
return event.getY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
|
||||||
|
// Generic Motion (mouse hover, joystick...) events go here
|
||||||
|
private boolean mRelativeModeEnabled;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onGenericMotion(View v, MotionEvent event) {
|
||||||
|
float x, y;
|
||||||
|
int action;
|
||||||
|
|
||||||
|
switch ( event.getSource() ) {
|
||||||
|
case InputDevice.SOURCE_JOYSTICK:
|
||||||
|
return SDLControllerManager.handleJoystickMotionEvent(event);
|
||||||
|
|
||||||
|
case InputDevice.SOURCE_MOUSE:
|
||||||
|
// DeX desktop mouse cursor is a separate non-standard input type.
|
||||||
|
case InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN:
|
||||||
|
action = event.getActionMasked();
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_SCROLL:
|
||||||
|
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||||
|
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_HOVER_MOVE:
|
||||||
|
x = event.getX(0);
|
||||||
|
y = event.getY(0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InputDevice.SOURCE_MOUSE_RELATIVE:
|
||||||
|
action = event.getActionMasked();
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_SCROLL:
|
||||||
|
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||||
|
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_HOVER_MOVE:
|
||||||
|
x = event.getX(0);
|
||||||
|
y = event.getY(0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event was not managed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRelativeMouse() {
|
||||||
|
return (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean inRelativeMode() {
|
||||||
|
return mRelativeModeEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setRelativeMouseEnabled(boolean enabled) {
|
||||||
|
if (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */) {
|
||||||
|
if (enabled) {
|
||||||
|
SDLActivity.getContentView().requestPointerCapture();
|
||||||
|
} else {
|
||||||
|
SDLActivity.getContentView().releasePointerCapture();
|
||||||
|
}
|
||||||
|
mRelativeModeEnabled = enabled;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reclaimRelativeMouseModeIfNeeded()
|
||||||
|
{
|
||||||
|
if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) {
|
||||||
|
SDLActivity.getContentView().requestPointerCapture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getEventX(MotionEvent event) {
|
||||||
|
// Relative mouse in capture mode will only have relative for X/Y
|
||||||
|
return event.getX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getEventY(MotionEvent event) {
|
||||||
|
// Relative mouse in capture mode will only have relative for X/Y
|
||||||
|
return event.getY(0);
|
||||||
|
}
|
||||||
|
}
|
405
android/app/src/main/java/org/libsdl/app/SDLSurface.java
Normal file
405
android/app/src/main/java/org/libsdl/app/SDLSurface.java
Normal file
|
@ -0,0 +1,405 @@
|
||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.hardware.Sensor;
|
||||||
|
import android.hardware.SensorEvent;
|
||||||
|
import android.hardware.SensorEventListener;
|
||||||
|
import android.hardware.SensorManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Display;
|
||||||
|
import android.view.InputDevice;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.Surface;
|
||||||
|
import android.view.SurfaceHolder;
|
||||||
|
import android.view.SurfaceView;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
SDLSurface. This is what we draw on, so we need to know when it's created
|
||||||
|
in order to do anything useful.
|
||||||
|
|
||||||
|
Because of this, that's where we set up the SDL thread
|
||||||
|
*/
|
||||||
|
public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
||||||
|
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
|
||||||
|
|
||||||
|
// Sensors
|
||||||
|
protected SensorManager mSensorManager;
|
||||||
|
protected Display mDisplay;
|
||||||
|
|
||||||
|
// Keep track of the surface size to normalize touch events
|
||||||
|
protected float mWidth, mHeight;
|
||||||
|
|
||||||
|
// Is SurfaceView ready for rendering
|
||||||
|
public boolean mIsSurfaceReady;
|
||||||
|
|
||||||
|
// Startup
|
||||||
|
public SDLSurface(Context context) {
|
||||||
|
super(context);
|
||||||
|
getHolder().addCallback(this);
|
||||||
|
|
||||||
|
setFocusable(true);
|
||||||
|
setFocusableInTouchMode(true);
|
||||||
|
requestFocus();
|
||||||
|
setOnKeyListener(this);
|
||||||
|
setOnTouchListener(this);
|
||||||
|
|
||||||
|
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||||
|
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
|
||||||
|
|
||||||
|
setOnGenericMotionListener(SDLActivity.getMotionListener());
|
||||||
|
|
||||||
|
// Some arbitrary defaults to avoid a potential division by zero
|
||||||
|
mWidth = 1.0f;
|
||||||
|
mHeight = 1.0f;
|
||||||
|
|
||||||
|
mIsSurfaceReady = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handlePause() {
|
||||||
|
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleResume() {
|
||||||
|
setFocusable(true);
|
||||||
|
setFocusableInTouchMode(true);
|
||||||
|
requestFocus();
|
||||||
|
setOnKeyListener(this);
|
||||||
|
setOnTouchListener(this);
|
||||||
|
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Surface getNativeSurface() {
|
||||||
|
return getHolder().getSurface();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when we have a valid drawing surface
|
||||||
|
@Override
|
||||||
|
public void surfaceCreated(SurfaceHolder holder) {
|
||||||
|
Log.v("SDL", "surfaceCreated()");
|
||||||
|
SDLActivity.onNativeSurfaceCreated();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when we lose the surface
|
||||||
|
@Override
|
||||||
|
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||||
|
Log.v("SDL", "surfaceDestroyed()");
|
||||||
|
|
||||||
|
// Transition to pause, if needed
|
||||||
|
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
|
||||||
|
SDLActivity.handleNativeState();
|
||||||
|
|
||||||
|
mIsSurfaceReady = false;
|
||||||
|
SDLActivity.onNativeSurfaceDestroyed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the surface is resized
|
||||||
|
@Override
|
||||||
|
public void surfaceChanged(SurfaceHolder holder,
|
||||||
|
int format, int width, int height) {
|
||||||
|
Log.v("SDL", "surfaceChanged()");
|
||||||
|
|
||||||
|
if (SDLActivity.mSingleton == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mWidth = width;
|
||||||
|
mHeight = height;
|
||||||
|
int nDeviceWidth = width;
|
||||||
|
int nDeviceHeight = height;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SDK_INT >= 17 /* Android 4.2 (JELLY_BEAN_MR1) */) {
|
||||||
|
DisplayMetrics realMetrics = new DisplayMetrics();
|
||||||
|
mDisplay.getRealMetrics( realMetrics );
|
||||||
|
nDeviceWidth = realMetrics.widthPixels;
|
||||||
|
nDeviceHeight = realMetrics.heightPixels;
|
||||||
|
}
|
||||||
|
} catch(Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized(SDLActivity.getContext()) {
|
||||||
|
// In case we're waiting on a size change after going fullscreen, send a notification.
|
||||||
|
SDLActivity.getContext().notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.v("SDL", "Window size: " + width + "x" + height);
|
||||||
|
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
|
||||||
|
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
|
||||||
|
SDLActivity.onNativeResize();
|
||||||
|
|
||||||
|
// Prevent a screen distortion glitch,
|
||||||
|
// for instance when the device is in Landscape and a Portrait App is resumed.
|
||||||
|
boolean skip = false;
|
||||||
|
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
|
||||||
|
|
||||||
|
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
|
||||||
|
if (mWidth > mHeight) {
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
|
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
|
||||||
|
if (mWidth < mHeight) {
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special Patch for Square Resolution: Black Berry Passport
|
||||||
|
if (skip) {
|
||||||
|
double min = Math.min(mWidth, mHeight);
|
||||||
|
double max = Math.max(mWidth, mHeight);
|
||||||
|
|
||||||
|
if (max / min < 1.20) {
|
||||||
|
Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
|
||||||
|
skip = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't skip in MultiWindow.
|
||||||
|
if (skip) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
if (SDLActivity.mSingleton.isInMultiWindowMode()) {
|
||||||
|
Log.v("SDL", "Don't skip in Multi-Window");
|
||||||
|
skip = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skip) {
|
||||||
|
Log.v("SDL", "Skip .. Surface is not ready.");
|
||||||
|
mIsSurfaceReady = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
|
||||||
|
SDLActivity.onNativeSurfaceChanged();
|
||||||
|
|
||||||
|
/* Surface is ready */
|
||||||
|
mIsSurfaceReady = true;
|
||||||
|
|
||||||
|
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
|
||||||
|
SDLActivity.handleNativeState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key events
|
||||||
|
@Override
|
||||||
|
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||||
|
return SDLActivity.handleKeyEvent(v, keyCode, event, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Touch events
|
||||||
|
@Override
|
||||||
|
public boolean onTouch(View v, MotionEvent event) {
|
||||||
|
/* Ref: http://developer.android.com/training/gestures/multi.html */
|
||||||
|
int touchDevId = event.getDeviceId();
|
||||||
|
final int pointerCount = event.getPointerCount();
|
||||||
|
int action = event.getActionMasked();
|
||||||
|
int pointerFingerId;
|
||||||
|
int i = -1;
|
||||||
|
float x,y,p;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevent id to be -1, since it's used in SDL internal for synthetic events
|
||||||
|
* Appears when using Android emulator, eg:
|
||||||
|
* adb shell input mouse tap 100 100
|
||||||
|
* adb shell input touchscreen tap 100 100
|
||||||
|
*/
|
||||||
|
if (touchDevId < 0) {
|
||||||
|
touchDevId -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12290 = Samsung DeX mode desktop mouse
|
||||||
|
// 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
|
||||||
|
// 0x2 = SOURCE_CLASS_POINTER
|
||||||
|
if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
|
||||||
|
int mouseButton = 1;
|
||||||
|
try {
|
||||||
|
Object object = event.getClass().getMethod("getButtonState").invoke(event);
|
||||||
|
if (object != null) {
|
||||||
|
mouseButton = (Integer) object;
|
||||||
|
}
|
||||||
|
} catch(Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
|
||||||
|
// if we are. We'll leverage our existing mouse motion listener
|
||||||
|
SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
|
||||||
|
x = motionListener.getEventX(event);
|
||||||
|
y = motionListener.getEventY(event);
|
||||||
|
|
||||||
|
SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
|
||||||
|
} else {
|
||||||
|
switch(action) {
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
for (i = 0; i < pointerCount; i++) {
|
||||||
|
pointerFingerId = event.getPointerId(i);
|
||||||
|
x = event.getX(i) / mWidth;
|
||||||
|
y = event.getY(i) / mHeight;
|
||||||
|
p = event.getPressure(i);
|
||||||
|
if (p > 1.0f) {
|
||||||
|
// may be larger than 1.0f on some devices
|
||||||
|
// see the documentation of getPressure(i)
|
||||||
|
p = 1.0f;
|
||||||
|
}
|
||||||
|
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
// Primary pointer up/down, the index is always zero
|
||||||
|
i = 0;
|
||||||
|
/* fallthrough */
|
||||||
|
case MotionEvent.ACTION_POINTER_UP:
|
||||||
|
case MotionEvent.ACTION_POINTER_DOWN:
|
||||||
|
// Non primary pointer up/down
|
||||||
|
if (i == -1) {
|
||||||
|
i = event.getActionIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
pointerFingerId = event.getPointerId(i);
|
||||||
|
x = event.getX(i) / mWidth;
|
||||||
|
y = event.getY(i) / mHeight;
|
||||||
|
p = event.getPressure(i);
|
||||||
|
if (p > 1.0f) {
|
||||||
|
// may be larger than 1.0f on some devices
|
||||||
|
// see the documentation of getPressure(i)
|
||||||
|
p = 1.0f;
|
||||||
|
}
|
||||||
|
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_CANCEL:
|
||||||
|
for (i = 0; i < pointerCount; i++) {
|
||||||
|
pointerFingerId = event.getPointerId(i);
|
||||||
|
x = event.getX(i) / mWidth;
|
||||||
|
y = event.getY(i) / mHeight;
|
||||||
|
p = event.getPressure(i);
|
||||||
|
if (p > 1.0f) {
|
||||||
|
// may be larger than 1.0f on some devices
|
||||||
|
// see the documentation of getPressure(i)
|
||||||
|
p = 1.0f;
|
||||||
|
}
|
||||||
|
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sensor events
|
||||||
|
public void enableSensor(int sensortype, boolean enabled) {
|
||||||
|
// TODO: This uses getDefaultSensor - what if we have >1 accels?
|
||||||
|
if (enabled) {
|
||||||
|
mSensorManager.registerListener(this,
|
||||||
|
mSensorManager.getDefaultSensor(sensortype),
|
||||||
|
SensorManager.SENSOR_DELAY_GAME, null);
|
||||||
|
} else {
|
||||||
|
mSensorManager.unregisterListener(this,
|
||||||
|
mSensorManager.getDefaultSensor(sensortype));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSensorChanged(SensorEvent event) {
|
||||||
|
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
||||||
|
|
||||||
|
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
|
||||||
|
// We thus should check here.
|
||||||
|
int newOrientation;
|
||||||
|
|
||||||
|
float x, y;
|
||||||
|
switch (mDisplay.getRotation()) {
|
||||||
|
case Surface.ROTATION_90:
|
||||||
|
x = -event.values[1];
|
||||||
|
y = event.values[0];
|
||||||
|
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
|
||||||
|
break;
|
||||||
|
case Surface.ROTATION_270:
|
||||||
|
x = event.values[1];
|
||||||
|
y = -event.values[0];
|
||||||
|
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
|
||||||
|
break;
|
||||||
|
case Surface.ROTATION_180:
|
||||||
|
x = -event.values[0];
|
||||||
|
y = -event.values[1];
|
||||||
|
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
|
||||||
|
break;
|
||||||
|
case Surface.ROTATION_0:
|
||||||
|
default:
|
||||||
|
x = event.values[0];
|
||||||
|
y = event.values[1];
|
||||||
|
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newOrientation != SDLActivity.mCurrentOrientation) {
|
||||||
|
SDLActivity.mCurrentOrientation = newOrientation;
|
||||||
|
SDLActivity.onNativeOrientationChanged(newOrientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
|
||||||
|
y / SensorManager.GRAVITY_EARTH,
|
||||||
|
event.values[2] / SensorManager.GRAVITY_EARTH);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Captured pointer events for API 26.
|
||||||
|
public boolean onCapturedPointerEvent(MotionEvent event)
|
||||||
|
{
|
||||||
|
int action = event.getActionMasked();
|
||||||
|
|
||||||
|
float x, y;
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_SCROLL:
|
||||||
|
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||||
|
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_HOVER_MOVE:
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
x = event.getX(0);
|
||||||
|
y = event.getY(0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_BUTTON_PRESS:
|
||||||
|
case MotionEvent.ACTION_BUTTON_RELEASE:
|
||||||
|
|
||||||
|
// Change our action value to what SDL's code expects.
|
||||||
|
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
|
||||||
|
action = MotionEvent.ACTION_DOWN;
|
||||||
|
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
|
||||||
|
action = MotionEvent.ACTION_UP;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = event.getX(0);
|
||||||
|
y = event.getY(0);
|
||||||
|
int button = event.getButtonState();
|
||||||
|
|
||||||
|
SDLActivity.onNativeMouse(button, action, x, y, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
11
android/app/src/main/res/values-de/strings.xml
Normal file
11
android/app/src/main/res/values-de/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="loading">Lädt…</string>
|
||||||
|
<string name="unzip_notification_title">Luanti lädt</string>
|
||||||
|
<string name="unzip_notification_description">Weniger als 1 Minute…</string>
|
||||||
|
<string name="ime_dialog_done">Fertig</string>
|
||||||
|
<string name="no_web_browser">Kein Web-Browser gefunden</string>
|
||||||
|
<string name="notification_channel_name">Allgemeine Benachrichtigung</string>
|
||||||
|
<string name="notification_channel_description">Benachrichtigungen von Luanti</string>
|
||||||
|
</resources>
|
11
android/app/src/main/res/values-es-rUS/strings.xml
Normal file
11
android/app/src/main/res/values-es-rUS/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="unzip_notification_description">Menos de 1 minuto…</string>
|
||||||
|
<string name="ime_dialog_done">Hecho</string>
|
||||||
|
<string name="no_web_browser">No se encontró ningún navegador web</string>
|
||||||
|
<string name="loading">Cargando…</string>
|
||||||
|
<string name="notification_channel_name">Notificación General</string>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="notification_channel_description">Notificaciones de Luanti</string>
|
||||||
|
<string name="unzip_notification_title">Cargando Luanti</string>
|
||||||
|
</resources>
|
11
android/app/src/main/res/values-es/strings.xml
Normal file
11
android/app/src/main/res/values-es/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="no_web_browser">No se encontró ningún navegador web</string>
|
||||||
|
<string name="loading">Cargando…</string>
|
||||||
|
<string name="notification_channel_description">Notificaciones de Luanti</string>
|
||||||
|
<string name="unzip_notification_description">Menos de 1 minuto…</string>
|
||||||
|
<string name="ime_dialog_done">Hecho</string>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="unzip_notification_title">Cargando Luanti</string>
|
||||||
|
<string name="notification_channel_name">Notificación General</string>
|
||||||
|
</resources>
|
11
android/app/src/main/res/values-fa/strings.xml
Normal file
11
android/app/src/main/res/values-fa/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="unzip_notification_description">کمتر از 1 دقیقه…</string>
|
||||||
|
<string name="loading">در حال بارگذاری…</string>
|
||||||
|
<string name="ime_dialog_done">انجام شد</string>
|
||||||
|
<string name="label">لوآنتی</string>
|
||||||
|
<string name="unzip_notification_title">در حال بارگذاری لوآنتی</string>
|
||||||
|
<string name="notification_channel_name">نوتیفیکیشن عمومی</string>
|
||||||
|
<string name="notification_channel_description">نوتیفیکیشن از لوآنتی</string>
|
||||||
|
<string name="no_web_browser">هیچ مرورگری یافت نشد</string>
|
||||||
|
</resources>
|
11
android/app/src/main/res/values-fr/strings.xml
Normal file
11
android/app/src/main/res/values-fr/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="loading">Chargement…</string>
|
||||||
|
<string name="notification_channel_name">Notification générale</string>
|
||||||
|
<string name="notification_channel_description">Notifications de Luanti</string>
|
||||||
|
<string name="unzip_notification_title">Chargement de Luanti</string>
|
||||||
|
<string name="unzip_notification_description">Moins d\'une minute…</string>
|
||||||
|
<string name="ime_dialog_done">Terminé</string>
|
||||||
|
<string name="no_web_browser">Aucun navigateur web trouvé</string>
|
||||||
|
</resources>
|
11
android/app/src/main/res/values-gl/strings.xml
Normal file
11
android/app/src/main/res/values-gl/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="loading">Cargando…</string>
|
||||||
|
<string name="notification_channel_name">Notificación xeral</string>
|
||||||
|
<string name="notification_channel_description">Notificacións de Luanti</string>
|
||||||
|
<string name="unzip_notification_title">Cargando Luanti</string>
|
||||||
|
<string name="unzip_notification_description">Menos de 1 minuto…</string>
|
||||||
|
<string name="ime_dialog_done">Feito</string>
|
||||||
|
<string name="no_web_browser">Non se atopou ningún navegador web</string>
|
||||||
|
</resources>
|
11
android/app/src/main/res/values-hu/strings.xml
Normal file
11
android/app/src/main/res/values-hu/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="loading">Betöltés…</string>
|
||||||
|
<string name="notification_channel_name">Általános értesítés</string>
|
||||||
|
<string name="notification_channel_description">Értesítések a Luanti-től</string>
|
||||||
|
<string name="unzip_notification_title">Luanti betöltése…</string>
|
||||||
|
<string name="ime_dialog_done">Kész</string>
|
||||||
|
<string name="no_web_browser">Nem található webböngésző</string>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="unzip_notification_description">Kevesebb, mint 1 perc…</string>
|
||||||
|
</resources>
|
11
android/app/src/main/res/values-in/strings.xml
Normal file
11
android/app/src/main/res/values-in/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="no_web_browser">Tidak ditemukan peramban web</string>
|
||||||
|
<string name="ime_dialog_done">Selesai</string>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="loading">Memuat…</string>
|
||||||
|
<string name="notification_channel_name">Pemberitahuan umum</string>
|
||||||
|
<string name="unzip_notification_description">Kurang dari 1 menit…</string>
|
||||||
|
<string name="notification_channel_description">Pemberitahuan dari Luanti</string>
|
||||||
|
<string name="unzip_notification_title">Memuat Luanti…</string>
|
||||||
|
</resources>
|
11
android/app/src/main/res/values-ms/strings.xml
Normal file
11
android/app/src/main/res/values-ms/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="notification_channel_description">Pemberitahuan dari Luanti</string>
|
||||||
|
<string name="unzip_notification_title">Memuatkan Luanti…</string>
|
||||||
|
<string name="unzip_notification_description">Kurang dari 1 minit…</string>
|
||||||
|
<string name="no_web_browser">Tiada pelayar sesawang dijumpai</string>
|
||||||
|
<string name="loading">Memuatkan…</string>
|
||||||
|
<string name="notification_channel_name">Pemberitahuan umum</string>
|
||||||
|
<string name="ime_dialog_done">Selesai</string>
|
||||||
|
</resources>
|
11
android/app/src/main/res/values-nb-rNO/strings.xml
Normal file
11
android/app/src/main/res/values-nb-rNO/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="loading">Laster inn …</string>
|
||||||
|
<string name="notification_channel_name">Generell merknad</string>
|
||||||
|
<string name="notification_channel_description">Merknader fra Luanti</string>
|
||||||
|
<string name="unzip_notification_description">Mindre enn ett minutt …</string>
|
||||||
|
<string name="ime_dialog_done">Ferdig</string>
|
||||||
|
<string name="no_web_browser">Fant ingen nettleser</string>
|
||||||
|
<string name="unzip_notification_title">Laster inn Luanti …</string>
|
||||||
|
</resources>
|
11
android/app/src/main/res/values-ru/strings.xml
Normal file
11
android/app/src/main/res/values-ru/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="unzip_notification_title">Загрузка Luanti</string>
|
||||||
|
<string name="unzip_notification_description">Меньше чам за 1 минуту…</string>
|
||||||
|
<string name="ime_dialog_done">Готово</string>
|
||||||
|
<string name="label">Luаnti</string>
|
||||||
|
<string name="notification_channel_description">Уведомления от Luanti</string>
|
||||||
|
<string name="notification_channel_name">Основные уведомления</string>
|
||||||
|
<string name="loading">Загрузка…</string>
|
||||||
|
<string name="no_web_browser">Не найдено веб-браузера</string>
|
||||||
|
</resources>
|
11
android/app/src/main/res/values-sv/strings.xml
Normal file
11
android/app/src/main/res/values-sv/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="loading">Laddar…</string>
|
||||||
|
<string name="unzip_notification_description">Mindre än 1 minut…</string>
|
||||||
|
<string name="ime_dialog_done">Färdig</string>
|
||||||
|
<string name="no_web_browser">Ingen webbläsare kunde hittas</string>
|
||||||
|
<string name="notification_channel_name">Generell notis</string>
|
||||||
|
<string name="notification_channel_description">Notiser från Luanti</string>
|
||||||
|
<string name="unzip_notification_title">Laddar Luanti</string>
|
||||||
|
</resources>
|
2
android/app/src/main/res/values-ta/strings.xml
Normal file
2
android/app/src/main/res/values-ta/strings.xml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
11
android/app/src/main/res/values-uk/strings.xml
Normal file
11
android/app/src/main/res/values-uk/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="no_web_browser">Браузерів не знайдено</string>
|
||||||
|
<string name="loading">Завантаження…</string>
|
||||||
|
<string name="notification_channel_name">Загальні сповіщення</string>
|
||||||
|
<string name="unzip_notification_title">Luanti завантажується</string>
|
||||||
|
<string name="unzip_notification_description">Менше за 1 хвилину…</string>
|
||||||
|
<string name="ime_dialog_done">Готово</string>
|
||||||
|
<string name="notification_channel_description">Сповіщення від Luanti</string>
|
||||||
|
</resources>
|
11
android/app/src/main/res/values-zh-rCN/strings.xml
Normal file
11
android/app/src/main/res/values-zh-rCN/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="loading">加载中…</string>
|
||||||
|
<string name="notification_channel_name">一般通知</string>
|
||||||
|
<string name="notification_channel_description">Luanti 的通知</string>
|
||||||
|
<string name="unzip_notification_title">加载 Luanti 中</string>
|
||||||
|
<string name="unzip_notification_description">不到1分钟…</string>
|
||||||
|
<string name="ime_dialog_done">完成</string>
|
||||||
|
<string name="no_web_browser">未找到网页浏览器</string>
|
||||||
|
</resources>
|
11
android/app/src/main/res/values-zh-rTW/strings.xml
Normal file
11
android/app/src/main/res/values-zh-rTW/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="loading">載入中…</string>
|
||||||
|
<string name="notification_channel_name">一般通知</string>
|
||||||
|
<string name="notification_channel_description">Luanti 的通知</string>
|
||||||
|
<string name="unzip_notification_description">不到1分鐘…</string>
|
||||||
|
<string name="ime_dialog_done">完畢</string>
|
||||||
|
<string name="no_web_browser">未找到任何網頁瀏覽器</string>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="unzip_notification_title">載入Luanti中</string>
|
||||||
|
</resources>
|
|
@ -1,13 +1,11 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
<string name="label">Minetest</string>
|
|
||||||
<string name="loading">Loading…</string>
|
<string name="loading">Loading…</string>
|
||||||
<string name="migrating">Migrating save data from old install… (this may take a while)</string>
|
<string name="notification_channel_name">General notification</string>
|
||||||
<string name="not_granted">Required permission wasn\'t granted, Minetest can\'t run without it</string>
|
<string name="notification_channel_description">Notifications from Luanti</string>
|
||||||
<string name="notification_title">Loading Minetest</string>
|
<string name="unzip_notification_title">Loading Luanti</string>
|
||||||
<string name="notification_description">Less than 1 minute…</string>
|
<string name="unzip_notification_description">Less than 1 minute…</string>
|
||||||
<string name="ime_dialog_done">Done</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>
|
<string name="no_web_browser">No web browser found</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,23 +1,21 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// 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("versionMajor", 5) // Version Major
|
||||||
project.ext.set("versionMinor", 6) // Version Minor
|
project.ext.set("versionMinor", 12) // Version Minor
|
||||||
project.ext.set("versionPatch", 0) // Version Patch
|
project.ext.set("versionPatch", 0) // Version Patch
|
||||||
project.ext.set("versionExtra", "") // Version Extra
|
// ^ keep in sync with cmake
|
||||||
project.ext.set("versionCode", 40) // Android Version Code
|
|
||||||
project.ext.set("developmentBuild", 0) // Whether it is a development build, or a release
|
project.ext.set("versionBuild", 0) // Version Build
|
||||||
// NOTE: +2 after each release!
|
// ^ fourth version number to allow releasing Android-only fixes and beta versions
|
||||||
// +1 for ARM and +1 for ARM64 APK's, because
|
|
||||||
// each APK must have a larger `versionCode` than the previous
|
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.ndk_version = '23.2.8568313'
|
ext.ndk_version = '27.2.12479018'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.0.3'
|
classpath 'com.android.tools.build:gradle:8.5.1'
|
||||||
classpath 'de.undercouch:gradle-download-task:4.1.1'
|
classpath 'de.undercouch:gradle-download-task:4.1.1'
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
|
@ -27,11 +25,10 @@ buildscript {
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task clean(type: Delete) {
|
task clean(type: Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
delete 'native/deps'
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
<#if isLowMemory>
|
<#if isLowMemory>
|
||||||
org.gradle.jvmargs=-Xmx4G -XX:MaxPermSize=2G -XX:+HeapDumpOnOutOfMemoryError
|
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
|
||||||
<#else>
|
<#else>
|
||||||
org.gradle.jvmargs=-Xmx16G -XX:MaxPermSize=8G -XX:+HeapDumpOnOutOfMemoryError
|
org.gradle.jvmargs=-Xmx16G -XX:+HeapDumpOnOutOfMemoryError
|
||||||
</#if>
|
</#if>
|
||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
org.gradle.parallel.threads=8
|
org.gradle.parallel.threads=8
|
||||||
org.gradle.configureondemand=true
|
org.gradle.configureondemand=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=false
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
|
android.nonTransitiveRClass=false
|
||||||
|
android.nonFinalResIds=false
|
||||||
|
|
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
|
@ -1,5 +1,7 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
|
||||||
|
|
295
android/gradlew
vendored
295
android/gradlew
vendored
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env sh
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright 2015 the original author or authors.
|
# Copyright © 2015-2021 the original authors.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -17,78 +17,111 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
#
|
||||||
## Gradle start up script for UN*X
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
##
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
PRG="$0"
|
app_path=$0
|
||||||
# Need this for relative symlinks.
|
|
||||||
while [ -h "$PRG" ] ; do
|
# Need this for daisy-chained symlinks.
|
||||||
ls=`ls -ld "$PRG"`
|
while
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
[ -h "$app_path" ]
|
||||||
PRG="$link"
|
do
|
||||||
else
|
ls=$( ls -ld "$app_path" )
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
link=${ls#*' -> '}
|
||||||
fi
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
SAVED="`pwd`"
|
|
||||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >/dev/null
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
# This is normally unused
|
||||||
APP_BASE_NAME=`basename "$0"`
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD=maximum
|
||||||
|
|
||||||
warn () {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
die () {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
cygwin=false
|
cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
nonstop=false
|
nonstop=false
|
||||||
case "`uname`" in
|
case "$( uname )" in #(
|
||||||
CYGWIN* )
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
cygwin=true
|
Darwin* ) darwin=true ;; #(
|
||||||
;;
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
Darwin* )
|
NONSTOP* ) nonstop=true ;;
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
NONSTOP* )
|
|
||||||
nonstop=true
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
else
|
else
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
fi
|
fi
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
@ -97,92 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD="java"
|
JAVACMD=java
|
||||||
command -v java >/dev/null || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
case $MAX_FD in #(
|
||||||
if [ $? -eq 0 ] ; then
|
max*)
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
# shellcheck disable=SC2039,SC3045
|
||||||
fi
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
ulimit -n $MAX_FD
|
warn "Could not query maximum file descriptor limit"
|
||||||
if [ $? -ne 0 ] ; then
|
esac
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
case $MAX_FD in #(
|
||||||
fi
|
'' | soft) :;; #(
|
||||||
else
|
*)
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
fi
|
# shellcheck disable=SC2039,SC3045
|
||||||
fi
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
# For Darwin, add options to specify how the application appears in the dock
|
|
||||||
if $darwin; then
|
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
|
||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
|
||||||
SEP=""
|
|
||||||
for dir in $ROOTDIRSRAW ; do
|
|
||||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
|
||||||
SEP="|"
|
|
||||||
done
|
|
||||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
|
||||||
# Add a user-defined pattern to the cygpath arguments
|
|
||||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
|
||||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
|
||||||
fi
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
i=0
|
|
||||||
for arg in "$@" ; do
|
|
||||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
|
||||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
|
||||||
|
|
||||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
|
||||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
|
||||||
else
|
|
||||||
eval `echo args$i`="\"$arg\""
|
|
||||||
fi
|
|
||||||
i=$((i+1))
|
|
||||||
done
|
|
||||||
case $i in
|
|
||||||
(0) set -- ;;
|
|
||||||
(1) set -- "$args0" ;;
|
|
||||||
(2) set -- "$args0" "$args1" ;;
|
|
||||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
|
||||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
|
||||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
|
||||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
|
||||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
|
||||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
|
||||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
save () {
|
# * args from the command line
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
# * the main class name
|
||||||
echo " "
|
# * -classpath
|
||||||
}
|
# * -D...appname settings
|
||||||
APP_ARGS=$(save "$@")
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
|
||||||
cd "$(dirname "$0")"
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|
100
android/gradlew.bat
vendored
100
android/gradlew.bat
vendored
|
@ -1,100 +0,0 @@
|
||||||
@rem
|
|
||||||
@rem Copyright 2015 the original author or authors.
|
|
||||||
@rem
|
|
||||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
@rem you may not use this file except in compliance with the License.
|
|
||||||
@rem You may obtain a copy of the License at
|
|
||||||
@rem
|
|
||||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
@rem
|
|
||||||
@rem Unless required by applicable law or agreed to in writing, software
|
|
||||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
@rem See the License for the specific language governing permissions and
|
|
||||||
@rem limitations under the License.
|
|
||||||
@rem
|
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
|
||||||
@rem ##########################################################################
|
|
||||||
@rem
|
|
||||||
@rem Gradle startup script for Windows
|
|
||||||
@rem
|
|
||||||
@rem ##########################################################################
|
|
||||||
|
|
||||||
@rem Set local scope for the variables with windows NT shell
|
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
|
||||||
set APP_BASE_NAME=%~n0
|
|
||||||
set APP_HOME=%DIRNAME%
|
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
||||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
|
||||||
|
|
||||||
@rem Find java.exe
|
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
|
||||||
|
|
||||||
set JAVA_EXE=java.exe
|
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
|
||||||
if "%ERRORLEVEL%" == "0" goto init
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
|
||||||
echo.
|
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
echo location of your Java installation.
|
|
||||||
|
|
||||||
goto fail
|
|
||||||
|
|
||||||
:findJavaFromJavaHome
|
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto init
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
|
||||||
echo.
|
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
echo location of your Java installation.
|
|
||||||
|
|
||||||
goto fail
|
|
||||||
|
|
||||||
:init
|
|
||||||
@rem Get command-line arguments, handling Windows variants
|
|
||||||
|
|
||||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
|
||||||
|
|
||||||
:win9xME_args
|
|
||||||
@rem Slurp the command line arguments.
|
|
||||||
set CMD_LINE_ARGS=
|
|
||||||
set _SKIP=2
|
|
||||||
|
|
||||||
:win9xME_args_slurp
|
|
||||||
if "x%~1" == "x" goto execute
|
|
||||||
|
|
||||||
set CMD_LINE_ARGS=%*
|
|
||||||
|
|
||||||
:execute
|
|
||||||
@rem Setup the command line
|
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
|
||||||
|
|
||||||
:end
|
|
||||||
@rem End local scope for the variables with windows NT shell
|
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
|
||||||
|
|
||||||
:fail
|
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
|
||||||
rem the _cmd.exe /c_ return code!
|
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
|
||||||
exit /b 1
|
|
||||||
|
|
||||||
:mainEnd
|
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
|
||||||
|
|
||||||
:omega
|
|
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 |
|
@ -1,194 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="512"
|
|
||||||
height="512"
|
|
||||||
viewBox="0 0 135.46666 135.46667"
|
|
||||||
version="1.1"
|
|
||||||
id="svg8"
|
|
||||||
inkscape:version="0.92.1 r15371"
|
|
||||||
sodipodi:docname="gear_icon.svg"
|
|
||||||
inkscape:export-filename="/home/stu/Desktop/icons/png/gear_icon.png"
|
|
||||||
inkscape:export-xdpi="24"
|
|
||||||
inkscape:export-ydpi="24">
|
|
||||||
<defs
|
|
||||||
id="defs2">
|
|
||||||
<marker
|
|
||||||
style="overflow:visible"
|
|
||||||
refY="0.0"
|
|
||||||
refX="0.0"
|
|
||||||
orient="auto"
|
|
||||||
id="DistanceX">
|
|
||||||
<path
|
|
||||||
id="path4687"
|
|
||||||
style="stroke:#000000; stroke-width:0.5"
|
|
||||||
d="M 3,-3 L -3,3 M 0,-5 L 0,5" />
|
|
||||||
</marker>
|
|
||||||
<pattern
|
|
||||||
y="0"
|
|
||||||
x="0"
|
|
||||||
width="8"
|
|
||||||
patternUnits="userSpaceOnUse"
|
|
||||||
id="Hatch"
|
|
||||||
height="8">
|
|
||||||
<path
|
|
||||||
id="path4690"
|
|
||||||
stroke-width="0.25"
|
|
||||||
stroke="#000000"
|
|
||||||
linecap="square"
|
|
||||||
d="M8 4 l-4,4" />
|
|
||||||
<path
|
|
||||||
id="path4692"
|
|
||||||
stroke-width="0.25"
|
|
||||||
stroke="#000000"
|
|
||||||
linecap="square"
|
|
||||||
d="M6 2 l-4,4" />
|
|
||||||
<path
|
|
||||||
id="path4694"
|
|
||||||
stroke-width="0.25"
|
|
||||||
stroke="#000000"
|
|
||||||
linecap="square"
|
|
||||||
d="M4 0 l-4,4" />
|
|
||||||
</pattern>
|
|
||||||
<marker
|
|
||||||
style="overflow:visible"
|
|
||||||
refY="0.0"
|
|
||||||
refX="0.0"
|
|
||||||
orient="auto"
|
|
||||||
id="DistanceX-3">
|
|
||||||
<path
|
|
||||||
id="path4756"
|
|
||||||
style="stroke:#000000; stroke-width:0.5"
|
|
||||||
d="M 3,-3 L -3,3 M 0,-5 L 0,5" />
|
|
||||||
</marker>
|
|
||||||
<pattern
|
|
||||||
y="0"
|
|
||||||
x="0"
|
|
||||||
width="8"
|
|
||||||
patternUnits="userSpaceOnUse"
|
|
||||||
id="Hatch-6"
|
|
||||||
height="8">
|
|
||||||
<path
|
|
||||||
id="path4759"
|
|
||||||
stroke-width="0.25"
|
|
||||||
stroke="#000000"
|
|
||||||
linecap="square"
|
|
||||||
d="M8 4 l-4,4" />
|
|
||||||
<path
|
|
||||||
id="path4761"
|
|
||||||
stroke-width="0.25"
|
|
||||||
stroke="#000000"
|
|
||||||
linecap="square"
|
|
||||||
d="M6 2 l-4,4" />
|
|
||||||
<path
|
|
||||||
id="path4763"
|
|
||||||
stroke-width="0.25"
|
|
||||||
stroke="#000000"
|
|
||||||
linecap="square"
|
|
||||||
d="M4 0 l-4,4" />
|
|
||||||
</pattern>
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#404040"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="0.5"
|
|
||||||
inkscape:cx="-308.644"
|
|
||||||
inkscape:cy="171.10144"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="true"
|
|
||||||
units="px"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1023"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="34"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:pagecheckerboard="false"
|
|
||||||
inkscape:snap-page="true"
|
|
||||||
inkscape:snap-grids="true"
|
|
||||||
inkscape:snap-bbox="true"
|
|
||||||
inkscape:snap-bbox-midpoints="false"
|
|
||||||
inkscape:snap-object-midpoints="true"
|
|
||||||
inkscape:snap-to-guides="false"
|
|
||||||
inkscape:showpageshadow="false"
|
|
||||||
inkscape:snap-smooth-nodes="true"
|
|
||||||
inkscape:object-nodes="false">
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid16"
|
|
||||||
spacingx="2.1166666"
|
|
||||||
spacingy="2.1166666"
|
|
||||||
empspacing="2"
|
|
||||||
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" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
<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:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(0,-161.53332)">
|
|
||||||
<g
|
|
||||||
id="g4792"
|
|
||||||
transform="matrix(0.68725287,0,0,0.65623884,67.477909,-509.24679)"
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.74041259;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
|
|
||||||
<g
|
|
||||||
id="g4772"
|
|
||||||
inkscape:label="OpenJsCad"
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.74041259;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="m 256,80.943359 -8.28125,0.72461 -3.63477,5.410156 -5.61328,12.685547 -2.28906,9.259768 -0.35156,5.1875 0.0937,0.86133 0.70703,2.44726 0.60547,9.80664 -2.66602,5.47461 -21.5957,5.78711 -5.04492,-3.40625 -4.37696,-8.79687 -0.61132,-2.47071 -0.35157,-0.79297 -2.89648,-4.31836 -6.60938,-6.87304 -11.20507,-8.17969 -5.84961,-2.86719 -7.53516,3.51367 -6.80859,4.76954 -0.44336,6.50195 1.48047,13.79297 2.64453,9.16406 2.28906,4.66992 0.51172,0.69922 1.83398,1.76563 5.42774,8.18945 0.42773,6.07422 -15.81054,15.81445 -6.07032,-0.42773 -8.18945,-5.42969 -1.76367,-1.83399 -0.69922,-0.51171 -4.66992,-2.28907 -9.15821,-2.64843 -13.79297,-1.47852 -6.5,0.44141 -4.76757,6.8125 -3.51367,7.53515 2.86914,5.85157 8.17382,11.20703 6.87305,6.61132 4.31641,2.90039 0.79297,0.34961 2.4707,0.61133 8.79492,4.37696 3.4043,5.04687 -5.78516,21.60156 -5.47265,2.66602 -9.80469,-0.60547 -2.44727,-0.70703 -0.85937,-0.0918 -5.1875,0.35156 -9.257816,2.28907 -12.68164,5.61523 -5.408203,3.63281 -0.72461,8.28516 0.72461,8.28516 5.408203,3.63281 12.68164,5.61523 9.257816,2.28907 5.1875,0.35156 0.85937,-0.0918 2.44727,-0.70703 9.80469,-0.60547 5.47265,2.66602 5.78516,21.60156 -3.4043,5.04687 -8.79492,4.37696 -2.4707,0.61133 -0.79297,0.34961 -4.31641,2.90039 -6.87305,6.61132 -8.17382,11.20703 -2.86914,5.85157 3.51367,7.53515 4.76757,6.8125 6.5,0.44141 13.79297,-1.47852 9.15821,-2.64843 4.66992,-2.28907 0.69922,-0.50976 1.76367,-1.83594 8.18945,-5.42969 6.07032,-0.42773 15.81054,15.81445 -0.42773,6.07422 -5.42774,8.18945 -1.83398,1.76563 -0.51172,0.69922 -2.28906,4.66992 -2.64453,9.16406 -1.48047,13.79297 0.44336,6.50195 6.80859,4.76758 7.53516,3.51758 5.84961,-2.86914 11.20507,-8.17969 6.60938,-6.87304 2.89648,-4.31836 0.35157,-0.79297 0.61132,-2.47071 4.37696,-8.79687 5.04492,-3.40625 21.5957,5.78711 2.66602,5.47461 -0.60547,9.80664 -0.70703,2.44726 -0.0937,0.85938 0.35156,5.18945 2.28906,9.25977 5.61328,12.68555 3.63477,5.41015 8.28125,0.72461 8.28125,-0.72461 3.63477,-5.41015 5.61328,-12.68555 2.28906,-9.25977 0.35156,-5.18945 -0.0937,-0.85938 -0.70703,-2.44726 -0.60547,-9.80664 2.66602,-5.47461 21.5957,-5.78711 5.04492,3.40625 4.37696,8.79687 0.61132,2.47071 0.35157,0.79297 2.89648,4.31836 6.60938,6.87304 11.20507,8.17969 5.84961,2.86914 7.53516,-3.51758 6.80859,-4.76758 0.44336,-6.50195 -1.48047,-13.79297 -2.64453,-9.16406 -2.28906,-4.66992 -0.51172,-0.69922 -1.83398,-1.76563 -5.42774,-8.18945 -0.42773,-6.07422 15.81054,-15.81445 6.07032,0.42773 8.18945,5.42969 1.76367,1.83594 0.69922,0.50976 4.66992,2.28907 9.15821,2.64843 13.79297,1.47852 6.5,-0.44141 v -0.002 l 4.76757,-6.81055 3.51367,-7.53711 -2.86914,-5.85156 -8.17382,-11.20508 -6.87305,-6.61328 -4.31641,-2.89843 -0.79297,-0.34961 -2.4707,-0.61133 -8.79492,-4.37891 -3.4043,-5.04492 5.78516,-21.60352 5.47265,-2.66797 9.80469,0.60938 2.44727,0.70703 0.85937,0.0918 5.1875,-0.35156 9.25782,-2.28907 12.68164,-5.61718 5.4082,-3.63282 0.72461,-8.28515 -0.72461,-8.28321 -5.4082,-3.63476 -12.68164,-5.61524 -9.25782,-2.28711 -5.1875,-0.35351 -0.85937,0.0937 -2.44727,0.70508 -9.80469,0.60937 -5.47265,-2.66797 -5.78516,-21.59961 3.4043,-5.04882 8.79492,-4.37696 2.4707,-0.61133 0.79297,-0.35156 4.31641,-2.89844 6.87305,-6.61132 8.17382,-11.20703 2.86914,-5.85157 -3.51367,-7.53711 -4.76757,-6.81054 -6.5,-0.44336 -13.79297,1.48047 -9.15821,2.64648 -4.66992,2.28906 -0.69922,0.51172 -1.76367,1.83594 -8.18945,5.42773 -6.07032,0.42774 -15.81054,-15.81446 0.42773,-6.07226 5.42774,-8.18945 1.83398,-1.76563 0.51172,-0.69922 2.28906,-4.67187 2.64453,-9.16016 1.48047,-13.79492 -0.44336,-6.50195 -6.80859,-4.76954 -7.53516,-3.51562 -5.84961,2.87109 -11.20507,8.17578 -6.60938,6.875 -2.89648,4.31836 -0.35157,0.79102 -0.61132,2.47266 -4.37696,8.79687 -5.04492,3.4082 -21.5957,-5.79101 -2.66602,-5.47266 0.60547,-9.80664 0.70703,-2.44726 0.0937,-0.85938 -0.35156,-5.19141 -2.28906,-9.259761 -5.61328,-12.683594 -3.63477,-5.412109 z m 0,97.111331 A 77.946197,77.946197 0 0 1 333.94531,256 77.946197,77.946197 0 0 1 256,333.94531 77.946197,77.946197 0 0 1 178.05469,256 77.946197,77.946197 0 0 1 256,178.05469 Z"
|
|
||||||
transform="matrix(0.38495268,0,0,0.40318156,-98.176247,1022.1341)"
|
|
||||||
id="path4768"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g4774"
|
|
||||||
inkscape:label="0"
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.74041259;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 9.7 KiB |
|
@ -2,23 +2,23 @@
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
width="512"
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="256"
|
|
||||||
height="512"
|
height="512"
|
||||||
viewBox="0 0 67.73333 135.46667"
|
viewBox="0 0 135.46666 135.46667"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="svg8"
|
id="svg8"
|
||||||
inkscape:version="0.92.1 r15371"
|
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
||||||
sodipodi:docname="rare_controls.svg"
|
sodipodi:docname="overflow_btn.svg"
|
||||||
inkscape:export-filename="/home/stu/Desktop/icons/png/rare_controls.png"
|
inkscape:export-filename="../../textures/base/pack/overflow_btn.png"
|
||||||
inkscape:export-xdpi="24.000002"
|
inkscape:export-xdpi="24.000002"
|
||||||
inkscape:export-ydpi="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
|
<defs
|
||||||
id="defs2">
|
id="defs2">
|
||||||
<filter
|
<filter
|
||||||
|
@ -397,22 +397,24 @@
|
||||||
borderopacity="1.0"
|
borderopacity="1.0"
|
||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:zoom="0.7"
|
inkscape:zoom="0.98994949"
|
||||||
inkscape:cx="-59.862018"
|
inkscape:cx="41.921331"
|
||||||
inkscape:cy="260.34663"
|
inkscape:cy="163.13964"
|
||||||
inkscape:document-units="mm"
|
inkscape:document-units="mm"
|
||||||
inkscape:current-layer="layer2"
|
inkscape:current-layer="layer2"
|
||||||
showgrid="true"
|
showgrid="true"
|
||||||
units="px"
|
units="px"
|
||||||
inkscape:window-width="1920"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="1023"
|
inkscape:window-height="1011"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="34"
|
inkscape:window-y="32"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:pagecheckerboard="false"
|
inkscape:pagecheckerboard="false"
|
||||||
inkscape:snap-grids="true"
|
inkscape:snap-grids="true"
|
||||||
inkscape:snap-page="true"
|
inkscape:snap-page="true"
|
||||||
showguides="false">
|
showguides="false"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:deskcolor="#d1d1d1">
|
||||||
<inkscape:grid
|
<inkscape:grid
|
||||||
type="xygrid"
|
type="xygrid"
|
||||||
id="grid16"
|
id="grid16"
|
||||||
|
@ -422,7 +424,11 @@
|
||||||
color="#40ff40"
|
color="#40ff40"
|
||||||
opacity="0.1254902"
|
opacity="0.1254902"
|
||||||
empcolor="#40ff40"
|
empcolor="#40ff40"
|
||||||
empopacity="0.25098039" />
|
empopacity="0.25098039"
|
||||||
|
originx="0"
|
||||||
|
originy="0"
|
||||||
|
units="px"
|
||||||
|
visible="true" />
|
||||||
</sodipodi:namedview>
|
</sodipodi:namedview>
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata5">
|
id="metadata5">
|
||||||
|
@ -432,7 +438,6 @@
|
||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title></dc:title>
|
|
||||||
<cc:license
|
<cc:license
|
||||||
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
|
@ -496,26 +501,27 @@
|
||||||
x="264.65997"
|
x="264.65997"
|
||||||
y="124.10143"
|
y="124.10143"
|
||||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1" /></flowRegion><flowPara
|
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1" /></flowRegion><flowPara
|
||||||
id="flowPara4724" /></flowRoot> <rect
|
id="flowPara4724" /></flowRoot>
|
||||||
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="rect5231-9"
|
|
||||||
width="25.4"
|
|
||||||
height="25.400003"
|
|
||||||
x="21.166666"
|
|
||||||
y="101.6" />
|
|
||||||
<rect
|
<rect
|
||||||
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:5.38756;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
id="rect5231-9-7"
|
|
||||||
width="25.4"
|
|
||||||
height="25.400003"
|
|
||||||
x="21.166666"
|
|
||||||
y="55.033333" />
|
|
||||||
<rect
|
|
||||||
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="rect5231-9-5"
|
id="rect5231-9-5"
|
||||||
width="25.4"
|
width="96.212502"
|
||||||
height="25.400003"
|
height="0.61243516"
|
||||||
x="21.166664"
|
x="19.62678"
|
||||||
y="8.4666681" />
|
y="33.575779" />
|
||||||
|
<rect
|
||||||
|
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:5.38755;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect5231-9-5-7"
|
||||||
|
width="96.212448"
|
||||||
|
height="0.6124543"
|
||||||
|
x="19.627108"
|
||||||
|
y="67.442772" />
|
||||||
|
<rect
|
||||||
|
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:5.38755;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect5231-9-5-7-5"
|
||||||
|
width="96.212448"
|
||||||
|
height="0.6124543"
|
||||||
|
x="19.627108"
|
||||||
|
y="101.30978" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
@ -2,27 +2,24 @@ apply plugin: 'com.android.library'
|
||||||
apply plugin: 'de.undercouch.download'
|
apply plugin: 'de.undercouch.download'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 30
|
|
||||||
buildToolsVersion '30.0.3'
|
|
||||||
ndkVersion "$ndk_version"
|
ndkVersion "$ndk_version"
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 21
|
||||||
targetSdkVersion 30
|
compileSdk 34
|
||||||
|
targetSdkVersion 34
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
ndkBuild {
|
cmake {
|
||||||
arguments '-j' + Runtime.getRuntime().availableProcessors(),
|
arguments "-DANDROID_STL=c++_shared",
|
||||||
"versionMajor=${versionMajor}",
|
"-DENABLE_CURL=1", "-DENABLE_SOUND=1",
|
||||||
"versionMinor=${versionMinor}",
|
"-DENABLE_GETTEXT=1",
|
||||||
"versionPatch=${versionPatch}",
|
"-DBUILD_UNITTESTS=0", "-DENABLE_UPDATE_CHECKER=0"
|
||||||
"versionExtra=${versionExtra}",
|
|
||||||
"developmentBuild=${developmentBuild}"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
ndkBuild {
|
cmake {
|
||||||
path file('jni/Android.mk')
|
path file("../../CMakeLists.txt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,39 +28,45 @@ android {
|
||||||
abi {
|
abi {
|
||||||
enable true
|
enable true
|
||||||
reset()
|
reset()
|
||||||
include 'armeabi-v7a', 'arm64-v8a'//, 'x86'
|
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
externalNativeBuild {
|
|
||||||
ndkBuild {
|
|
||||||
arguments 'NDEBUG=1'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ndk {
|
ndk {
|
||||||
debugSymbolLevel 'SYMBOL_TABLE'
|
debugSymbolLevel 'FULL'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
namespace 'net.minetest'
|
||||||
|
}
|
||||||
|
|
||||||
|
// get precompiled deps
|
||||||
|
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/luanti-org/luanti_android_deps/releases/download/latest/deps-lite.zip'
|
||||||
|
dest depsZip
|
||||||
|
overwrite false
|
||||||
|
|
||||||
|
task getDeps(dependsOn: downloadDeps, type: Copy) {
|
||||||
|
depsDir.mkdir()
|
||||||
|
from zipTree(depsZip)
|
||||||
|
into depsDir
|
||||||
|
doFirst { logger.lifecycle('Extracting to {}', depsDir) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get precompiled deps
|
|
||||||
task downloadDeps(type: Download) {
|
|
||||||
src 'https://github.com/minetest/minetest_android_deps/releases/download/latest/deps.zip'
|
|
||||||
dest new File(buildDir, 'deps.zip')
|
|
||||||
overwrite false
|
|
||||||
}
|
|
||||||
|
|
||||||
task getDeps(dependsOn: downloadDeps, type: Copy) {
|
|
||||||
def deps = new File(buildDir.parent, 'deps')
|
|
||||||
if (!deps.exists()) {
|
|
||||||
deps.mkdir()
|
|
||||||
from zipTree(downloadDeps.dest)
|
|
||||||
into deps
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
preBuild.dependsOn getDeps
|
preBuild.dependsOn getDeps
|
||||||
|
|
||||||
|
clean {
|
||||||
|
delete new File(buildDir.parent, 'deps')
|
||||||
|
}
|
||||||
|
|
|
@ -1,291 +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)
|
|
||||||
|
|
||||||
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/log.cpp \
|
|
||||||
../../src/main.cpp \
|
|
||||||
../../src/map.cpp \
|
|
||||||
../../src/map_settings_manager.cpp \
|
|
||||||
../../src/mapblock.cpp \
|
|
||||||
../../src/mapnode.cpp \
|
|
||||||
../../src/mapsector.cpp \
|
|
||||||
../../src/metadata.cpp \
|
|
||||||
../../src/modchannels.cpp \
|
|
||||||
../../src/nameidmapping.cpp \
|
|
||||||
../../src/nodedef.cpp \
|
|
||||||
../../src/nodemetadata.cpp \
|
|
||||||
../../src/nodetimer.cpp \
|
|
||||||
../../src/noise.cpp \
|
|
||||||
../../src/objdef.cpp \
|
|
||||||
../../src/object_properties.cpp \
|
|
||||||
../../src/particles.cpp \
|
|
||||||
../../src/pathfinder.cpp \
|
|
||||||
../../src/player.cpp \
|
|
||||||
../../src/porting.cpp \
|
|
||||||
../../src/porting_android.cpp \
|
|
||||||
../../src/profiler.cpp \
|
|
||||||
../../src/raycast.cpp \
|
|
||||||
../../src/reflowscan.cpp \
|
|
||||||
../../src/remoteplayer.cpp \
|
|
||||||
../../src/rollback.cpp \
|
|
||||||
../../src/rollback_interface.cpp \
|
|
||||||
../../src/serialization.cpp \
|
|
||||||
../../src/server.cpp \
|
|
||||||
../../src/serverenvironment.cpp \
|
|
||||||
../../src/serverlist.cpp \
|
|
||||||
../../src/settings.cpp \
|
|
||||||
../../src/staticobject.cpp \
|
|
||||||
../../src/texture_override.cpp \
|
|
||||||
../../src/tileanimation.cpp \
|
|
||||||
../../src/tool.cpp \
|
|
||||||
../../src/translation.cpp \
|
|
||||||
../../src/version.cpp \
|
|
||||||
../../src/voxel.cpp \
|
|
||||||
../../src/voxelalgorithms.cpp
|
|
||||||
|
|
||||||
# Built-in Lua
|
|
||||||
ifdef USE_BUILTIN_LUA
|
|
||||||
LOCAL_SRC_FILES += \
|
|
||||||
../../lib/lua/src/lapi.c \
|
|
||||||
../../lib/lua/src/lauxlib.c \
|
|
||||||
../../lib/lua/src/lbaselib.c \
|
|
||||||
../../lib/lua/src/lcode.c \
|
|
||||||
../../lib/lua/src/ldblib.c \
|
|
||||||
../../lib/lua/src/ldebug.c \
|
|
||||||
../../lib/lua/src/ldo.c \
|
|
||||||
../../lib/lua/src/ldump.c \
|
|
||||||
../../lib/lua/src/lfunc.c \
|
|
||||||
../../lib/lua/src/lgc.c \
|
|
||||||
../../lib/lua/src/linit.c \
|
|
||||||
../../lib/lua/src/liolib.c \
|
|
||||||
../../lib/lua/src/llex.c \
|
|
||||||
../../lib/lua/src/lmathlib.c \
|
|
||||||
../../lib/lua/src/lmem.c \
|
|
||||||
../../lib/lua/src/loadlib.c \
|
|
||||||
../../lib/lua/src/lobject.c \
|
|
||||||
../../lib/lua/src/lopcodes.c \
|
|
||||||
../../lib/lua/src/loslib.c \
|
|
||||||
../../lib/lua/src/lparser.c \
|
|
||||||
../../lib/lua/src/lstate.c \
|
|
||||||
../../lib/lua/src/lstring.c \
|
|
||||||
../../lib/lua/src/lstrlib.c \
|
|
||||||
../../lib/lua/src/ltable.c \
|
|
||||||
../../lib/lua/src/ltablib.c \
|
|
||||||
../../lib/lua/src/ltm.c \
|
|
||||||
../../lib/lua/src/lundump.c \
|
|
||||||
../../lib/lua/src/lvm.c \
|
|
||||||
../../lib/lua/src/lzio.c \
|
|
||||||
../../lib/bitop/bit.c
|
|
||||||
endif
|
|
||||||
|
|
||||||
# GMP
|
|
||||||
LOCAL_SRC_FILES += ../../lib/gmp/mini-gmp.c
|
|
||||||
|
|
||||||
# JSONCPP
|
|
||||||
LOCAL_SRC_FILES += ../../lib/jsoncpp/jsoncpp.cpp
|
|
||||||
|
|
||||||
LOCAL_STATIC_LIBRARIES += \
|
|
||||||
Curl libmbedcrypto libmbedtls libmbedx509 \
|
|
||||||
Freetype \
|
|
||||||
Iconv libcharset \
|
|
||||||
Irrlicht \
|
|
||||||
OpenAL \
|
|
||||||
Gettext \
|
|
||||||
SQLite3 \
|
|
||||||
Vorbis libvorbisfile libogg \
|
|
||||||
Zstd
|
|
||||||
ifndef USE_BUILTIN_LUA
|
|
||||||
LOCAL_STATIC_LIBRARIES += LuaJIT
|
|
||||||
endif
|
|
||||||
LOCAL_STATIC_LIBRARIES += android_native_app_glue $(PROFILER_LIBS)
|
|
||||||
|
|
||||||
LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES
|
|
||||||
|
|
||||||
include $(BUILD_SHARED_LIBRARY)
|
|
||||||
|
|
||||||
ifdef GPROF
|
|
||||||
$(call import-module,android-ndk-profiler)
|
|
||||||
endif
|
|
||||||
$(call import-module,android/native_app_glue)
|
|
|
@ -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
|
|
|
@ -1 +1 @@
|
||||||
<manifest package="net.minetest" />
|
<manifest />
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
rootProject.name = "Minetest"
|
rootProject.name = "Luanti"
|
||||||
include ':app', ':native'
|
include ':app', ':native'
|
||||||
|
|
|
@ -13,9 +13,12 @@ end
|
||||||
|
|
||||||
-- Import a bunch of individual files from builtin/game/
|
-- Import a bunch of individual files from builtin/game/
|
||||||
local gamepath = core.get_builtin_path() .. "game" .. DIR_DELIM
|
local gamepath = core.get_builtin_path() .. "game" .. DIR_DELIM
|
||||||
|
local commonpath = core.get_builtin_path() .. "common" .. DIR_DELIM
|
||||||
|
|
||||||
|
local builtin_shared = {}
|
||||||
|
|
||||||
dofile(gamepath .. "constants.lua")
|
dofile(gamepath .. "constants.lua")
|
||||||
dofile(gamepath .. "item_s.lua")
|
assert(loadfile(commonpath .. "item_s.lua"))(builtin_shared)
|
||||||
dofile(gamepath .. "misc_s.lua")
|
dofile(gamepath .. "misc_s.lua")
|
||||||
dofile(gamepath .. "features.lua")
|
dofile(gamepath .. "features.lua")
|
||||||
dofile(gamepath .. "voxelarea.lua")
|
dofile(gamepath .. "voxelarea.lua")
|
||||||
|
@ -33,11 +36,16 @@ do
|
||||||
setmetatable(v, {__newindex = {}})
|
setmetatable(v, {__newindex = {}})
|
||||||
-- Reassemble the other tables
|
-- Reassemble the other tables
|
||||||
if v.type == "node" then
|
if v.type == "node" then
|
||||||
|
getmetatable(v).__index = all.nodedef_default
|
||||||
all.registered_nodes[k] = v
|
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
|
all.registered_craftitems[k] = v
|
||||||
elseif v.type == "tool" then
|
elseif v.type == "tool" then
|
||||||
|
getmetatable(v).__index = all.tooldef_default
|
||||||
all.registered_tools[k] = v
|
all.registered_tools[k] = v
|
||||||
|
else
|
||||||
|
getmetatable(v).__index = all.noneitemdef_default
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -57,3 +65,5 @@ setmetatable(core.registered_items, alias_metatable)
|
||||||
setmetatable(core.registered_nodes, alias_metatable)
|
setmetatable(core.registered_nodes, alias_metatable)
|
||||||
setmetatable(core.registered_craftitems, alias_metatable)
|
setmetatable(core.registered_craftitems, alias_metatable)
|
||||||
setmetatable(core.registered_tools, alias_metatable)
|
setmetatable(core.registered_tools, alias_metatable)
|
||||||
|
|
||||||
|
builtin_shared.cache_content_ids()
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
-- Minetest: builtin/client/chatcommands.lua
|
|
||||||
|
|
||||||
core.register_on_sending_chat_message(function(message)
|
core.register_on_sending_chat_message(function(message)
|
||||||
if message:sub(1,2) == ".." then
|
if message:sub(1,2) == ".." then
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
-- CSM death formspec. Only used when clientside modding is enabled, otherwise
|
|
||||||
-- handled by the engine.
|
|
||||||
|
|
||||||
core.register_on_death(function()
|
|
||||||
local formspec = "size[11,5.5]bgcolor[#320000b4;true]" ..
|
|
||||||
"label[4.85,1.35;" .. fgettext("You died") ..
|
|
||||||
"]button_exit[4,3;3,0.5;btn_respawn;".. fgettext("Respawn") .."]"
|
|
||||||
core.show_formspec("bultin:death", formspec)
|
|
||||||
end)
|
|
||||||
|
|
||||||
core.register_on_formspec_input(function(formname, fields)
|
|
||||||
if formname == "bultin:death" then
|
|
||||||
core.send_respawn()
|
|
||||||
end
|
|
||||||
end)
|
|
|
@ -1,12 +1,15 @@
|
||||||
-- Minetest: builtin/client/init.lua
|
|
||||||
local scriptpath = core.get_builtin_path()
|
local scriptpath = core.get_builtin_path()
|
||||||
local clientpath = scriptpath.."client"..DIR_DELIM
|
local clientpath = scriptpath.."client"..DIR_DELIM
|
||||||
local commonpath = scriptpath.."common"..DIR_DELIM
|
local commonpath = scriptpath.."common"..DIR_DELIM
|
||||||
|
|
||||||
dofile(clientpath .. "register.lua")
|
local builtin_shared = {}
|
||||||
|
|
||||||
|
assert(loadfile(commonpath .. "register.lua"))(builtin_shared)
|
||||||
|
assert(loadfile(clientpath .. "register.lua"))(builtin_shared)
|
||||||
dofile(commonpath .. "after.lua")
|
dofile(commonpath .. "after.lua")
|
||||||
dofile(commonpath .. "mod_storage.lua")
|
dofile(commonpath .. "mod_storage.lua")
|
||||||
dofile(commonpath .. "chatcommands.lua")
|
dofile(commonpath .. "chatcommands.lua")
|
||||||
|
dofile(commonpath .. "information_formspecs.lua")
|
||||||
dofile(clientpath .. "chatcommands.lua")
|
dofile(clientpath .. "chatcommands.lua")
|
||||||
dofile(clientpath .. "death_formspec.lua")
|
|
||||||
dofile(clientpath .. "misc.lua")
|
dofile(clientpath .. "misc.lua")
|
||||||
|
assert(loadfile(commonpath .. "item_s.lua"))({}) -- Just for push/read node functions
|
||||||
|
|
|
@ -5,3 +5,14 @@ function core.setting_get_pos(name)
|
||||||
end
|
end
|
||||||
return core.string_to_pos(value)
|
return core.string_to_pos(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- old non-method sound functions
|
||||||
|
|
||||||
|
function core.sound_stop(handle, ...)
|
||||||
|
return handle:stop(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function core.sound_fade(handle, ...)
|
||||||
|
return handle:fade(...)
|
||||||
|
end
|
||||||
|
|
|
@ -1,68 +1,6 @@
|
||||||
core.callback_origins = {}
|
local builtin_shared = ...
|
||||||
|
|
||||||
local getinfo = debug.getinfo
|
local make_registration = builtin_shared.make_registration
|
||||||
debug.getinfo = nil
|
|
||||||
|
|
||||||
--- Runs given callbacks.
|
|
||||||
--
|
|
||||||
-- Note: this function is also called from C++
|
|
||||||
-- @tparam table callbacks a table with registered callbacks, like `core.registered_on_*`
|
|
||||||
-- @tparam number mode a RunCallbacksMode, as defined in src/script/common/c_internal.h
|
|
||||||
-- @param ... arguments for the callback
|
|
||||||
-- @return depends on mode
|
|
||||||
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
|
|
||||||
for i = 1, cb_len do
|
|
||||||
local cb_ret = callbacks[i](...)
|
|
||||||
|
|
||||||
if mode == 0 and i == 1 or 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
|
|
||||||
|
|
||||||
--
|
|
||||||
-- 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 = 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
|
|
||||||
|
|
||||||
core.registered_globalsteps, core.register_globalstep = make_registration()
|
core.registered_globalsteps, core.register_globalstep = make_registration()
|
||||||
core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration()
|
core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration()
|
||||||
|
|
|
@ -1,4 +1,116 @@
|
||||||
local jobs = {}
|
-- This is an implementation of a job sheduling mechanism. It guarantees that
|
||||||
|
-- coexisting jobs will execute primarily in order of least expiry, and
|
||||||
|
-- secondarily in order of first registration.
|
||||||
|
|
||||||
|
-- These functions implement an intrusive singly linked list of one or more
|
||||||
|
-- elements where the first element has a pointer to the last. The next pointer
|
||||||
|
-- is stored with key list_next. The pointer to the last is with key list_end.
|
||||||
|
|
||||||
|
local function list_init(first)
|
||||||
|
first.list_end = first
|
||||||
|
end
|
||||||
|
|
||||||
|
local function list_append(first, append)
|
||||||
|
first.list_end.list_next = append
|
||||||
|
first.list_end = append
|
||||||
|
end
|
||||||
|
|
||||||
|
local function list_append_list(first, first_append)
|
||||||
|
first.list_end.list_next = first_append
|
||||||
|
first.list_end = first_append.list_end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- The jobs are stored in a map from expiration times to linked lists of jobs
|
||||||
|
-- as above. The expiration times are also stored in an array representing a
|
||||||
|
-- binary min heap, which is a particular arrangement of binary tree. A parent
|
||||||
|
-- at index i has children at indices i*2 and i*2+1. Out-of-bounds indices
|
||||||
|
-- represent nonexistent children. A parent is never greater than its children.
|
||||||
|
-- This structure means that, if there is at least one job, the next expiration
|
||||||
|
-- time is the first item in the array.
|
||||||
|
|
||||||
|
-- Push element on a binary min-heap,
|
||||||
|
-- "bubbling up" the element by swapping with larger parents.
|
||||||
|
local function heap_push(heap, element)
|
||||||
|
local index = #heap + 1
|
||||||
|
while index > 1 do
|
||||||
|
local parent_index = math.floor(index / 2)
|
||||||
|
local parent = heap[parent_index]
|
||||||
|
if element < parent then
|
||||||
|
heap[index] = parent
|
||||||
|
index = parent_index
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
heap[index] = element
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Pop smallest element from the heap,
|
||||||
|
-- "sinking down" the last leaf on the last layer of the heap
|
||||||
|
-- by swapping with the smaller child.
|
||||||
|
local function heap_pop(heap)
|
||||||
|
local removed_element = heap[1]
|
||||||
|
local length = #heap
|
||||||
|
local element = heap[length]
|
||||||
|
heap[length] = nil
|
||||||
|
length = length - 1
|
||||||
|
if length > 0 then
|
||||||
|
local index = 1
|
||||||
|
while true do
|
||||||
|
local old_index = index
|
||||||
|
local smaller_element = element
|
||||||
|
local left_index = index * 2
|
||||||
|
local right_index = index * 2 + 1
|
||||||
|
if left_index <= length then
|
||||||
|
local left_element = heap[left_index]
|
||||||
|
if left_element < smaller_element then
|
||||||
|
index = left_index
|
||||||
|
smaller_element = left_element
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if right_index <= length then
|
||||||
|
if heap[right_index] < smaller_element then
|
||||||
|
index = right_index
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if old_index ~= index then
|
||||||
|
heap[old_index] = heap[index]
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
heap[index] = element
|
||||||
|
end
|
||||||
|
return removed_element
|
||||||
|
end
|
||||||
|
|
||||||
|
local job_map = {}
|
||||||
|
local expiries = {}
|
||||||
|
|
||||||
|
-- Adds an individual job with the given expiry.
|
||||||
|
-- The worst-case complexity is O(log n), where n is the number of distinct
|
||||||
|
-- expiration times.
|
||||||
|
local function add_job(expiry, job)
|
||||||
|
local list = job_map[expiry]
|
||||||
|
if list then
|
||||||
|
list_append(list, job)
|
||||||
|
else
|
||||||
|
list_init(job)
|
||||||
|
job_map[expiry] = job
|
||||||
|
heap_push(expiries, expiry)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Removes the next expiring jobs and returns the linked list of them.
|
||||||
|
-- The worst-case complexity is O(log n), where n is the number of distinct
|
||||||
|
-- expiration times.
|
||||||
|
local function remove_first_jobs()
|
||||||
|
local removed_expiry = heap_pop(expiries)
|
||||||
|
local removed = job_map[removed_expiry]
|
||||||
|
job_map[removed_expiry] = nil
|
||||||
|
return removed
|
||||||
|
end
|
||||||
|
|
||||||
local time = 0.0
|
local time = 0.0
|
||||||
local time_next = math.huge
|
local time_next = math.huge
|
||||||
|
|
||||||
|
@ -9,42 +121,54 @@ core.register_globalstep(function(dtime)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
time_next = math.huge
|
-- Remove the expired jobs.
|
||||||
|
local expired = remove_first_jobs()
|
||||||
|
|
||||||
-- Iterate backwards so that we miss any new timers added by
|
-- Remove other expired jobs and append them to the list.
|
||||||
-- a timer callback.
|
while true do
|
||||||
for i = #jobs, 1, -1 do
|
time_next = expiries[1] or math.huge
|
||||||
local job = jobs[i]
|
if time_next > time then
|
||||||
if time >= job.expire then
|
break
|
||||||
core.set_last_run_mod(job.mod_origin)
|
|
||||||
job.func(unpack(job.arg))
|
|
||||||
local jobs_l = #jobs
|
|
||||||
jobs[i] = jobs[jobs_l]
|
|
||||||
jobs[jobs_l] = nil
|
|
||||||
elseif job.expire < time_next then
|
|
||||||
time_next = job.expire
|
|
||||||
end
|
end
|
||||||
|
list_append_list(expired, remove_first_jobs())
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Run the callbacks afterward to prevent infinite loops with core.after(0, ...).
|
||||||
|
local last_expired = expired.list_end
|
||||||
|
while true do
|
||||||
|
core.set_last_run_mod(expired.mod_origin)
|
||||||
|
expired.func(unpack(expired.args, 1, expired.args.n))
|
||||||
|
if expired == last_expired then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
expired = expired.list_next
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
function core.after(after, func, ...)
|
local job_metatable = {__index = {}}
|
||||||
assert(tonumber(after) and type(func) == "function",
|
|
||||||
"Invalid minetest.after invocation")
|
|
||||||
local expire = time + after
|
|
||||||
local new_job = {
|
|
||||||
func = func,
|
|
||||||
expire = expire,
|
|
||||||
arg = {...},
|
|
||||||
mod_origin = core.get_last_run_mod(),
|
|
||||||
}
|
|
||||||
|
|
||||||
jobs[#jobs + 1] = new_job
|
local function dummy_func() end
|
||||||
time_next = math.min(time_next, expire)
|
function job_metatable.__index:cancel()
|
||||||
|
self.func = dummy_func
|
||||||
return {
|
self.args = {n = 0}
|
||||||
cancel = function()
|
end
|
||||||
new_job.func = function() end
|
|
||||||
new_job.args = {}
|
function core.after(after, func, ...)
|
||||||
end
|
assert(tonumber(after) and not core.is_nan(after) and type(func) == "function",
|
||||||
}
|
"Invalid core.after invocation")
|
||||||
|
|
||||||
|
local new_job = {
|
||||||
|
mod_origin = core.get_last_run_mod(),
|
||||||
|
func = func,
|
||||||
|
args = {
|
||||||
|
n = select("#", ...),
|
||||||
|
...
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
local expiry = time + after
|
||||||
|
add_job(expiry, new_job)
|
||||||
|
time_next = math.min(time_next, expiry)
|
||||||
|
|
||||||
|
return setmetatable(new_job, job_metatable)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
-- Minetest: builtin/common/chatcommands.lua
|
|
||||||
|
|
||||||
-- For server-side translations (if INIT == "game")
|
-- For server-side translations (if INIT == "game")
|
||||||
-- Otherwise, use core.gettext
|
-- Otherwise, use core.gettext
|
||||||
local S = core.get_translator("__builtin")
|
local S = core.get_translator("__builtin")
|
||||||
|
@ -89,7 +87,7 @@ local function do_help_cmd(name, param)
|
||||||
if #args > 1 then
|
if #args > 1 then
|
||||||
return false, S("Too many arguments, try using just /help <command>")
|
return false, S("Too many arguments, try using just /help <command>")
|
||||||
end
|
end
|
||||||
local use_gui = INIT ~= "client" and core.get_player_by_name(name)
|
local use_gui = INIT == "client" or core.get_player_by_name(name)
|
||||||
use_gui = use_gui and not opts:find("t")
|
use_gui = use_gui and not opts:find("t")
|
||||||
|
|
||||||
if #args == 0 and not use_gui then
|
if #args == 0 and not use_gui then
|
||||||
|
@ -163,8 +161,8 @@ end
|
||||||
|
|
||||||
if INIT == "client" then
|
if INIT == "client" then
|
||||||
core.register_chatcommand("help", {
|
core.register_chatcommand("help", {
|
||||||
params = core.gettext("[all | <cmd>]"),
|
params = core.gettext("[all | <cmd>] [-t]"),
|
||||||
description = core.gettext("Get help for commands"),
|
description = core.gettext("Get help for commands (-t: output in chat)"),
|
||||||
func = function(param)
|
func = function(param)
|
||||||
return do_help_cmd(nil, param)
|
return do_help_cmd(nil, param)
|
||||||
end,
|
end,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
--Minetest
|
--Luanti
|
||||||
--Copyright (C) 2013 sapier
|
--Copyright (C) 2013 sapier
|
||||||
--
|
--
|
||||||
--This program is free software; you can redistribute it and/or modify
|
--This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -20,8 +20,8 @@
|
||||||
-- TODO code cleanup --
|
-- TODO code cleanup --
|
||||||
-- Generic implementation of a filter/sortable list --
|
-- Generic implementation of a filter/sortable list --
|
||||||
-- Usage: --
|
-- Usage: --
|
||||||
-- Filterlist needs to be initialized on creation. To achieve this you need to --
|
-- Filterlist needs to be initialized on creation. To achieve this you need --
|
||||||
-- pass following functions: --
|
-- to pass following functions: --
|
||||||
-- raw_fct() (mandatory): --
|
-- raw_fct() (mandatory): --
|
||||||
-- function returning a table containing the elements to be filtered --
|
-- function returning a table containing the elements to be filtered --
|
||||||
-- compare_fct(element1,element2) (mandatory): --
|
-- compare_fct(element1,element2) (mandatory): --
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
-- filter_fct(element,filtercriteria) (optional) --
|
-- filter_fct(element,filtercriteria) (optional) --
|
||||||
-- function returning true/false if filtercriteria met to element --
|
-- function returning true/false if filtercriteria met to element --
|
||||||
-- fetch_param (optional) --
|
-- fetch_param (optional) --
|
||||||
-- parameter passed to raw_fct to aquire correct raw data --
|
-- parameter passed to raw_fct to acquire correct raw data --
|
||||||
-- --
|
-- --
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
filterlist = {}
|
filterlist = {}
|
||||||
|
|
|
@ -22,6 +22,7 @@ local LIST_FORMSPEC_DESCRIPTION = [[
|
||||||
|
|
||||||
local F = core.formspec_escape
|
local F = core.formspec_escape
|
||||||
local S = core.get_translator("__builtin")
|
local S = core.get_translator("__builtin")
|
||||||
|
local check_player_privs = core.check_player_privs
|
||||||
|
|
||||||
|
|
||||||
-- CHAT COMMANDS FORMSPEC
|
-- CHAT COMMANDS FORMSPEC
|
||||||
|
@ -57,19 +58,23 @@ local function build_chatcommands_formspec(name, sel, copy)
|
||||||
.. "any entry in the list.").. "\n" ..
|
.. "any entry in the list.").. "\n" ..
|
||||||
S("Double-click to copy the entry to the chat history.")
|
S("Double-click to copy the entry to the chat history.")
|
||||||
|
|
||||||
local privs = core.get_player_privs(name)
|
|
||||||
for i, data in ipairs(mod_cmds) do
|
for i, data in ipairs(mod_cmds) do
|
||||||
rows[#rows + 1] = COLOR_BLUE .. ",0," .. F(data[1]) .. ","
|
rows[#rows + 1] = COLOR_BLUE .. ",0," .. F(data[1]) .. ","
|
||||||
for j, cmds in ipairs(data[2]) do
|
for j, cmds in ipairs(data[2]) do
|
||||||
local has_priv = privs[cmds[2].privs]
|
local has_priv = INIT == "client" or check_player_privs(name, cmds[2].privs)
|
||||||
rows[#rows + 1] = ("%s,1,%s,%s"):format(
|
rows[#rows + 1] = ("%s,1,%s,%s"):format(
|
||||||
has_priv and COLOR_GREEN or COLOR_GRAY,
|
has_priv and COLOR_GREEN or COLOR_GRAY,
|
||||||
cmds[1], F(cmds[2].params))
|
cmds[1], F(cmds[2].params))
|
||||||
if sel == #rows then
|
if sel == #rows then
|
||||||
description = cmds[2].description
|
description = cmds[2].description
|
||||||
if copy then
|
if copy then
|
||||||
core.chat_send_player(name, S("Command: @1 @2",
|
local msg = S("Command: @1 @2",
|
||||||
core.colorize("#0FF", "/" .. cmds[1]), cmds[2].params))
|
core.colorize("#0FF", (INIT == "client" and "." or "/") .. cmds[1]), cmds[2].params)
|
||||||
|
if INIT == "client" then
|
||||||
|
core.display_chat_message(msg)
|
||||||
|
else
|
||||||
|
core.chat_send_player(name, msg)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -111,26 +116,46 @@ end
|
||||||
|
|
||||||
|
|
||||||
-- DETAILED CHAT COMMAND INFORMATION
|
-- DETAILED CHAT COMMAND INFORMATION
|
||||||
|
if INIT == "client" then
|
||||||
|
core.register_on_formspec_input(function(formname, fields)
|
||||||
|
if formname ~= "__builtin:help_cmds" or fields.quit then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
core.register_on_player_receive_fields(function(player, formname, fields)
|
local event = core.explode_table_event(fields.list)
|
||||||
if formname ~= "__builtin:help_cmds" or fields.quit then
|
if event.type ~= "INV" then
|
||||||
return
|
core.show_formspec("__builtin:help_cmds",
|
||||||
end
|
build_chatcommands_formspec(nil, event.row, event.type == "DCL"))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
core.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
|
if formname ~= "__builtin:help_cmds" or fields.quit then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local event = core.explode_table_event(fields.list)
|
local event = core.explode_table_event(fields.list)
|
||||||
if event.type ~= "INV" then
|
if event.type ~= "INV" then
|
||||||
local name = player:get_player_name()
|
local name = player:get_player_name()
|
||||||
core.show_formspec(name, "__builtin:help_cmds",
|
core.show_formspec(name, "__builtin:help_cmds",
|
||||||
build_chatcommands_formspec(name, event.row, event.type == "DCL"))
|
build_chatcommands_formspec(name, event.row, event.type == "DCL"))
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
function core.show_general_help_formspec(name)
|
function core.show_general_help_formspec(name)
|
||||||
core.show_formspec(name, "__builtin:help_cmds",
|
if INIT == "client" then
|
||||||
build_chatcommands_formspec(name))
|
core.show_formspec("__builtin:help_cmds",
|
||||||
|
build_chatcommands_formspec(name))
|
||||||
|
else
|
||||||
|
core.show_formspec(name, "__builtin:help_cmds",
|
||||||
|
build_chatcommands_formspec(name))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function core.show_privs_help_formspec(name)
|
if INIT ~= "client" then
|
||||||
core.show_formspec(name, "__builtin:help_privs",
|
function core.show_privs_help_formspec(name)
|
||||||
build_privs_formspec(name))
|
core.show_formspec(name, "__builtin:help_privs",
|
||||||
|
build_privs_formspec(name))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
-- Minetest: builtin/item_s.lua
|
|
||||||
-- The distinction of what goes here is a bit tricky, basically it's everything
|
-- The distinction of what goes here is a bit tricky, basically it's everything
|
||||||
-- that does not (directly or indirectly) need access to ServerEnvironment,
|
-- that does not (directly or indirectly) need access to ServerEnvironment,
|
||||||
-- Server or writable access to IGameDef on the engine side.
|
-- Server or writable access to IGameDef on the engine side.
|
||||||
-- (The '_s' stands for standalone.)
|
-- (The '_s' stands for standalone.)
|
||||||
|
|
||||||
|
local builtin_shared = ...
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Item definition helpers
|
-- Item definition helpers
|
||||||
--
|
--
|
||||||
|
@ -89,7 +90,27 @@ local facedir_to_dir_map = {
|
||||||
1, 4, 3, 2,
|
1, 4, 3, 2,
|
||||||
}
|
}
|
||||||
function core.facedir_to_dir(facedir)
|
function core.facedir_to_dir(facedir)
|
||||||
return facedir_to_dir[facedir_to_dir_map[facedir % 32]]
|
return vector.copy(facedir_to_dir[facedir_to_dir_map[facedir % 32]])
|
||||||
|
end
|
||||||
|
|
||||||
|
function core.dir_to_fourdir(dir)
|
||||||
|
if math.abs(dir.x) > math.abs(dir.z) then
|
||||||
|
if dir.x < 0 then
|
||||||
|
return 3
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if dir.z < 0 then
|
||||||
|
return 2
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function core.fourdir_to_dir(fourdir)
|
||||||
|
return vector.copy(facedir_to_dir[facedir_to_dir_map[fourdir % 4]])
|
||||||
end
|
end
|
||||||
|
|
||||||
function core.dir_to_wallmounted(dir)
|
function core.dir_to_wallmounted(dir)
|
||||||
|
@ -122,9 +143,11 @@ local wallmounted_to_dir = {
|
||||||
vector.new(-1, 0, 0),
|
vector.new(-1, 0, 0),
|
||||||
vector.new( 0, 0, 1),
|
vector.new( 0, 0, 1),
|
||||||
vector.new( 0, 0, -1),
|
vector.new( 0, 0, -1),
|
||||||
|
vector.new( 0, 1, 0),
|
||||||
|
vector.new( 0, -1, 0),
|
||||||
}
|
}
|
||||||
function core.wallmounted_to_dir(wallmounted)
|
function core.wallmounted_to_dir(wallmounted)
|
||||||
return wallmounted_to_dir[wallmounted % 8]
|
return vector.copy(wallmounted_to_dir[wallmounted % 8])
|
||||||
end
|
end
|
||||||
|
|
||||||
function core.dir_to_yaw(dir)
|
function core.dir_to_yaw(dir)
|
||||||
|
@ -137,20 +160,80 @@ end
|
||||||
|
|
||||||
function core.is_colored_paramtype(ptype)
|
function core.is_colored_paramtype(ptype)
|
||||||
return (ptype == "color") or (ptype == "colorfacedir") or
|
return (ptype == "color") or (ptype == "colorfacedir") or
|
||||||
(ptype == "colorwallmounted") or (ptype == "colordegrotate")
|
(ptype == "color4dir") or (ptype == "colorwallmounted") or
|
||||||
|
(ptype == "colordegrotate")
|
||||||
end
|
end
|
||||||
|
|
||||||
function core.strip_param2_color(param2, paramtype2)
|
function core.strip_param2_color(param2, paramtype2)
|
||||||
if not core.is_colored_paramtype(paramtype2) then
|
if paramtype2 == "color" then
|
||||||
|
return param2
|
||||||
|
elseif paramtype2 == "colorfacedir" then
|
||||||
|
return math.floor(param2 / 32) * 32
|
||||||
|
elseif paramtype2 == "color4dir" then
|
||||||
|
return math.floor(param2 / 4) * 4
|
||||||
|
elseif paramtype2 == "colorwallmounted" then
|
||||||
|
return math.floor(param2 / 8) * 8
|
||||||
|
elseif paramtype2 == "colordegrotate" then
|
||||||
|
return math.floor(param2 / 32) * 32
|
||||||
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
if paramtype2 == "colorfacedir" then
|
end
|
||||||
param2 = math.floor(param2 / 32) * 32
|
|
||||||
elseif paramtype2 == "colorwallmounted" then
|
-- Content ID caching
|
||||||
param2 = math.floor(param2 / 8) * 8
|
|
||||||
elseif paramtype2 == "colordegrotate" then
|
local old_get_content_id = core.get_content_id
|
||||||
param2 = math.floor(param2 / 32) * 32
|
local old_get_name_from_content_id = core.get_name_from_content_id
|
||||||
end
|
|
||||||
-- paramtype2 == "color" requires no modification.
|
local name2content = setmetatable({}, {
|
||||||
return param2
|
__index = function(self, name)
|
||||||
|
return old_get_content_id(name)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
local content2name = setmetatable({}, {
|
||||||
|
__index = function(self, id)
|
||||||
|
return old_get_name_from_content_id(id)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
function core.get_content_id(name)
|
||||||
|
return name2content[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
function core.get_name_from_content_id(id)
|
||||||
|
return content2name[id]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cache content IDs after they have stopped changing.
|
||||||
|
function builtin_shared.cache_content_ids()
|
||||||
|
for name in pairs(core.registered_nodes) do
|
||||||
|
local id = old_get_content_id(name)
|
||||||
|
name2content[name] = id
|
||||||
|
content2name[id] = name
|
||||||
|
end
|
||||||
|
-- unknown is not in the registered node list.
|
||||||
|
local unknown_name = old_get_name_from_content_id(core.CONTENT_UNKNOWN)
|
||||||
|
name2content[unknown_name] = core.CONTENT_UNKNOWN
|
||||||
|
content2name[core.CONTENT_UNKNOWN] = unknown_name
|
||||||
|
|
||||||
|
for name in pairs(core.registered_aliases) do
|
||||||
|
if core.registered_nodes[name] then
|
||||||
|
name2content[name] = old_get_content_id(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if core.set_read_node and core.set_push_node then
|
||||||
|
local function read_node(node)
|
||||||
|
return name2content[node.name], node.param1, node.param2
|
||||||
|
end
|
||||||
|
core.set_read_node(read_node)
|
||||||
|
core.set_read_node = nil
|
||||||
|
|
||||||
|
local function push_node(content, param1, param2)
|
||||||
|
return {name = content2name[content], param1 = param1, param2 = param2}
|
||||||
|
end
|
||||||
|
core.set_push_node(push_node)
|
||||||
|
core.set_push_node = nil
|
||||||
end
|
end
|
41
builtin/common/math.lua
Normal file
41
builtin/common/math.lua
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
--[[
|
||||||
|
Math utils.
|
||||||
|
--]]
|
||||||
|
|
||||||
|
function math.hypot(x, y)
|
||||||
|
return math.sqrt(x * x + y * y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function math.sign(x, tolerance)
|
||||||
|
tolerance = tolerance or 0
|
||||||
|
if x > tolerance then
|
||||||
|
return 1
|
||||||
|
elseif x < -tolerance then
|
||||||
|
return -1
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function math.factorial(x)
|
||||||
|
assert(x % 1 == 0 and x >= 0, "factorial expects a non-negative integer")
|
||||||
|
if x >= 171 then
|
||||||
|
-- 171! is greater than the biggest double, no need to calculate
|
||||||
|
return math.huge
|
||||||
|
end
|
||||||
|
local v = 1
|
||||||
|
for k = 2, x do
|
||||||
|
v = v * k
|
||||||
|
end
|
||||||
|
return v
|
||||||
|
end
|
||||||
|
|
||||||
|
function math.round(x)
|
||||||
|
if x < 0 then
|
||||||
|
local int = math.ceil(x)
|
||||||
|
local frac = x - int
|
||||||
|
return int - ((frac <= -0.5) and 1 or 0)
|
||||||
|
end
|
||||||
|
local int = math.floor(x)
|
||||||
|
local frac = x - int
|
||||||
|
return int + ((frac >= 0.5) and 1 or 0)
|
||||||
|
end
|
19
builtin/common/metatable.lua
Normal file
19
builtin/common/metatable.lua
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
-- Registered metatables, used by the C++ packer
|
||||||
|
local known_metatables = {}
|
||||||
|
function core.register_portable_metatable(name, mt)
|
||||||
|
assert(type(name) == "string", ("attempt to use %s value as metatable name"):format(type(name)))
|
||||||
|
assert(type(mt) == "table", ("attempt to register a %s value as metatable"):format(type(mt)))
|
||||||
|
assert(known_metatables[name] == nil or known_metatables[name] == mt,
|
||||||
|
("attempt to override metatable %s"):format(name))
|
||||||
|
known_metatables[name] = mt
|
||||||
|
known_metatables[mt] = name
|
||||||
|
end
|
||||||
|
core.known_metatables = known_metatables
|
||||||
|
|
||||||
|
function core.register_async_metatable(...)
|
||||||
|
core.log("deprecated", "core.register_async_metatable is deprecated. " ..
|
||||||
|
"Use core.register_portable_metatable instead.")
|
||||||
|
return core.register_portable_metatable(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
core.register_portable_metatable("__builtin:vector", vector.metatable)
|
|
@ -1,8 +1,7 @@
|
||||||
-- Minetest: builtin/misc_helpers.lua
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Localize functions to avoid table lookups (better performance).
|
-- Localize functions to avoid table lookups (better performance).
|
||||||
local string_sub, string_find = string.sub, string.find
|
local string_sub, string_find = string.sub, string.find
|
||||||
|
local math = math
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
local function basic_dump(o)
|
local function basic_dump(o)
|
||||||
|
@ -170,6 +169,9 @@ end
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function string.split(str, delim, include_empty, max_splits, sep_is_pattern)
|
function string.split(str, delim, include_empty, max_splits, sep_is_pattern)
|
||||||
delim = delim or ","
|
delim = delim or ","
|
||||||
|
if delim == "" then
|
||||||
|
error("string.split separator is empty", 2)
|
||||||
|
end
|
||||||
max_splits = max_splits or -2
|
max_splits = max_splits or -2
|
||||||
local items = {}
|
local items = {}
|
||||||
local pos, len = 1, #str
|
local pos, len = 1, #str
|
||||||
|
@ -202,59 +204,42 @@ function table.indexof(list, val)
|
||||||
return -1
|
return -1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
function table.keyof(tb, val)
|
||||||
|
for k, v in pairs(tb) do
|
||||||
|
if v == val then
|
||||||
|
return k
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function string:trim()
|
function string:trim()
|
||||||
return self:match("^%s*(.-)%s*$")
|
return self:match("^%s*(.-)%s*$")
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
function math.hypot(x, y)
|
|
||||||
return math.sqrt(x * x + y * y)
|
|
||||||
end
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
function math.sign(x, tolerance)
|
|
||||||
tolerance = tolerance or 0
|
|
||||||
if x > tolerance then
|
|
||||||
return 1
|
|
||||||
elseif x < -tolerance then
|
|
||||||
return -1
|
|
||||||
end
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
function math.factorial(x)
|
|
||||||
assert(x % 1 == 0 and x >= 0, "factorial expects a non-negative integer")
|
|
||||||
if x >= 171 then
|
|
||||||
-- 171! is greater than the biggest double, no need to calculate
|
|
||||||
return math.huge
|
|
||||||
end
|
|
||||||
local v = 1
|
|
||||||
for k = 2, x do
|
|
||||||
v = v * k
|
|
||||||
end
|
|
||||||
return v
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function math.round(x)
|
|
||||||
if x >= 0 then
|
|
||||||
return math.floor(x + 0.5)
|
|
||||||
end
|
|
||||||
return math.ceil(x - 0.5)
|
|
||||||
end
|
|
||||||
|
|
||||||
local formspec_escapes = {
|
local formspec_escapes = {
|
||||||
["\\"] = "\\\\",
|
["\\"] = "\\\\",
|
||||||
["["] = "\\[",
|
["["] = "\\[",
|
||||||
["]"] = "\\]",
|
["]"] = "\\]",
|
||||||
[";"] = "\\;",
|
[";"] = "\\;",
|
||||||
[","] = "\\,"
|
[","] = "\\,",
|
||||||
|
["$"] = "\\$",
|
||||||
}
|
}
|
||||||
function core.formspec_escape(text)
|
function core.formspec_escape(text)
|
||||||
-- Use explicit character set instead of dot here because it doubles the performance
|
-- Use explicit character set instead of dot here because it doubles the performance
|
||||||
return text and string.gsub(text, "[\\%[%];,]", formspec_escapes)
|
return text and string.gsub(text, "[\\%[%];,$]", formspec_escapes)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local hypertext_escapes = {
|
||||||
|
["\\"] = "\\\\",
|
||||||
|
["<"] = "\\<",
|
||||||
|
[">"] = "\\>",
|
||||||
|
}
|
||||||
|
function core.hypertext_escape(text)
|
||||||
|
return text and text:gsub("[\\<>]", hypertext_escapes)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -486,6 +471,9 @@ end
|
||||||
|
|
||||||
|
|
||||||
function table.insert_all(t, other)
|
function table.insert_all(t, other)
|
||||||
|
if table.move then -- LuaJIT
|
||||||
|
return table.move(other, 1, #other, #t + 1, t)
|
||||||
|
end
|
||||||
for i=1, #other do
|
for i=1, #other do
|
||||||
t[#t + 1] = other[i]
|
t[#t + 1] = other[i]
|
||||||
end
|
end
|
||||||
|
@ -519,18 +507,6 @@ end
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- mainmenu only functions
|
-- mainmenu only functions
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
if INIT == "mainmenu" then
|
|
||||||
function core.get_game(index)
|
|
||||||
local games = core.get_games()
|
|
||||||
|
|
||||||
if index > 0 and index <= #games then
|
|
||||||
return games[index]
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if core.gettext then -- for client and mainmenu
|
if core.gettext then -- for client and mainmenu
|
||||||
function fgettext_ne(text, ...)
|
function fgettext_ne(text, ...)
|
||||||
text = core.gettext(text)
|
text = core.gettext(text)
|
||||||
|
@ -596,12 +572,14 @@ function core.strip_colors(str)
|
||||||
return (str:gsub(ESCAPE_CHAR .. "%([bc]@[^)]+%)", ""))
|
return (str:gsub(ESCAPE_CHAR .. "%([bc]@[^)]+%)", ""))
|
||||||
end
|
end
|
||||||
|
|
||||||
function core.translate(textdomain, str, ...)
|
local function translate(textdomain, str, num, ...)
|
||||||
local start_seq
|
local start_seq
|
||||||
if textdomain == "" then
|
if textdomain == "" and num == "" then
|
||||||
start_seq = ESCAPE_CHAR .. "T"
|
start_seq = ESCAPE_CHAR .. "T"
|
||||||
else
|
elseif num == "" then
|
||||||
start_seq = ESCAPE_CHAR .. "(T@" .. textdomain .. ")"
|
start_seq = ESCAPE_CHAR .. "(T@" .. textdomain .. ")"
|
||||||
|
else
|
||||||
|
start_seq = ESCAPE_CHAR .. "(T@" .. textdomain .. "@" .. num .. ")"
|
||||||
end
|
end
|
||||||
local arg = {n=select('#', ...), ...}
|
local arg = {n=select('#', ...), ...}
|
||||||
local end_seq = ESCAPE_CHAR .. "E"
|
local end_seq = ESCAPE_CHAR .. "E"
|
||||||
|
@ -632,8 +610,31 @@ function core.translate(textdomain, str, ...)
|
||||||
return start_seq .. translated .. end_seq
|
return start_seq .. translated .. end_seq
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function core.translate(textdomain, str, ...)
|
||||||
|
return translate(textdomain, str, "", ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function core.translate_n(textdomain, str, str_plural, n, ...)
|
||||||
|
assert (type(n) == "number")
|
||||||
|
assert (n >= 0)
|
||||||
|
assert (math.floor(n) == n)
|
||||||
|
|
||||||
|
-- Truncate n if too large
|
||||||
|
local max = 1000000
|
||||||
|
if n >= 2 * max then
|
||||||
|
n = n % max + max
|
||||||
|
end
|
||||||
|
if n == 1 then
|
||||||
|
return translate(textdomain, str, "1", ...)
|
||||||
|
else
|
||||||
|
return translate(textdomain, str_plural, tostring(n), ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function core.get_translator(textdomain)
|
function core.get_translator(textdomain)
|
||||||
return function(str, ...) return core.translate(textdomain or "", str, ...) end
|
return
|
||||||
|
(function(str, ...) return core.translate(textdomain or "", str, ...) end),
|
||||||
|
(function(str, str_plural, n, ...) return core.translate_n(textdomain or "", str, str_plural, n, ...) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
@ -694,6 +695,7 @@ function core.privs_to_string(privs, delim)
|
||||||
list[#list + 1] = priv
|
list[#list + 1] = priv
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
table.sort(list)
|
||||||
return table.concat(list, delim)
|
return table.concat(list, delim)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
76
builtin/common/register.lua
Normal file
76
builtin/common/register.lua
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
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] = {
|
||||||
|
-- may be nil or return nil
|
||||||
|
mod = core.get_current_modname and 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] = {
|
||||||
|
-- may be nil or return nil
|
||||||
|
mod = core.get_current_modname and core.get_current_modname() or "??",
|
||||||
|
name = debug.getinfo(1, "n").name or "??"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
return t, registerfunc
|
||||||
|
end
|
|
@ -8,7 +8,7 @@ local next, rawget, pairs, pcall, error, type, setfenv, loadstring
|
||||||
local table_concat, string_dump, string_format, string_match, math_huge
|
local table_concat, string_dump, string_format, string_match, math_huge
|
||||||
= table.concat, string.dump, string.format, string.match, math.huge
|
= table.concat, string.dump, string.format, string.match, math.huge
|
||||||
|
|
||||||
-- Recursively counts occurences of objects (non-primitives including strings) in a table.
|
-- Recursively counts occurrences of objects (non-primitives including strings) in a table.
|
||||||
local function count_objects(value)
|
local function count_objects(value)
|
||||||
local counts = {}
|
local counts = {}
|
||||||
if value == nil then
|
if value == nil then
|
||||||
|
@ -61,17 +61,21 @@ end
|
||||||
-- Serializes Lua nil, booleans, numbers, strings, tables and even functions
|
-- Serializes Lua nil, booleans, numbers, strings, tables and even functions
|
||||||
-- Tables are referenced by reference, strings are referenced by value. Supports circular tables.
|
-- Tables are referenced by reference, strings are referenced by value. Supports circular tables.
|
||||||
local function serialize(value, write)
|
local function serialize(value, write)
|
||||||
local reference, refnum = "r1", 1
|
local reference, refnum = "1", 1
|
||||||
-- [object] = reference string
|
-- [object] = reference
|
||||||
local references = {}
|
local references = {}
|
||||||
-- Circular tables that must be filled using `table[key] = value` statements
|
-- Circular tables that must be filled using `table[key] = value` statements
|
||||||
local to_fill = {}
|
local to_fill = {}
|
||||||
for object, count in pairs(count_objects(value)) do
|
for object, count in pairs(count_objects(value)) do
|
||||||
local type_ = type(object)
|
local type_ = type(object)
|
||||||
-- Object must appear more than once. If it is a string, the reference has to be shorter than the string.
|
-- Object must appear more than once. If it is a string, the reference has to be shorter than the string.
|
||||||
if count >= 2 and (type_ ~= "string" or #reference + 2 < #object) then
|
if count >= 2 and (type_ ~= "string" or #reference + 5 < #object) then
|
||||||
|
if refnum == 1 then
|
||||||
|
write"local _={};" -- initialize reference table
|
||||||
|
end
|
||||||
|
write"_["
|
||||||
write(reference)
|
write(reference)
|
||||||
write("=")
|
write("]=")
|
||||||
if type_ == "table" then
|
if type_ == "table" then
|
||||||
write("{}")
|
write("{}")
|
||||||
elseif type_ == "function" then
|
elseif type_ == "function" then
|
||||||
|
@ -85,7 +89,7 @@ local function serialize(value, write)
|
||||||
to_fill[object] = reference
|
to_fill[object] = reference
|
||||||
end
|
end
|
||||||
refnum = refnum + 1
|
refnum = refnum + 1
|
||||||
reference = ("r%X"):format(refnum)
|
reference = ("%d"):format(refnum)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Used to decide whether we should do "key=..."
|
-- Used to decide whether we should do "key=..."
|
||||||
|
@ -105,12 +109,22 @@ local function serialize(value, write)
|
||||||
end
|
end
|
||||||
local type_ = type(value)
|
local type_ = type(value)
|
||||||
if type_ == "number" then
|
if type_ == "number" then
|
||||||
return write(string_format("%.17g", value))
|
if value ~= value then -- nan
|
||||||
|
return write"0/0"
|
||||||
|
elseif value == math_huge then
|
||||||
|
return write"1/0"
|
||||||
|
elseif value == -math_huge then
|
||||||
|
return write"-1/0"
|
||||||
|
else
|
||||||
|
return write(string_format("%.17g", value))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
-- Reference types: table, function and string
|
-- Reference types: table, function and string
|
||||||
local ref = references[value]
|
local ref = references[value]
|
||||||
if ref then
|
if ref then
|
||||||
return write(ref)
|
write"_["
|
||||||
|
write(ref)
|
||||||
|
return write"]"
|
||||||
end
|
end
|
||||||
if type_ == "string" then
|
if type_ == "string" then
|
||||||
return write(quote(value))
|
return write(quote(value))
|
||||||
|
@ -156,7 +170,9 @@ local function serialize(value, write)
|
||||||
-- Write the statements to fill circular tables
|
-- Write the statements to fill circular tables
|
||||||
for table, ref in pairs(to_fill) do
|
for table, ref in pairs(to_fill) do
|
||||||
for k, v in pairs(table) do
|
for k, v in pairs(table) do
|
||||||
|
write("_[")
|
||||||
write(ref)
|
write(ref)
|
||||||
|
write("]")
|
||||||
if use_short_key(k) then
|
if use_short_key(k) then
|
||||||
write(".")
|
write(".")
|
||||||
write(k)
|
write(k)
|
||||||
|
@ -185,24 +201,22 @@ end
|
||||||
|
|
||||||
local function dummy_func() end
|
local function dummy_func() end
|
||||||
|
|
||||||
local nan = (0/0)^1 -- +nan
|
|
||||||
|
|
||||||
function core.deserialize(str, safe)
|
function core.deserialize(str, safe)
|
||||||
-- Backwards compatibility
|
-- Backwards compatibility
|
||||||
if str == nil then
|
if str == nil then
|
||||||
core.log("deprecated", "minetest.deserialize called with nil (expected string).")
|
core.log("deprecated", "core.deserialize called with nil (expected string).")
|
||||||
return nil, "Invalid type: Expected a string, got nil"
|
return nil, "Invalid type: Expected a string, got nil"
|
||||||
end
|
end
|
||||||
local t = type(str)
|
local t = type(str)
|
||||||
if t ~= "string" then
|
if t ~= "string" then
|
||||||
error(("minetest.deserialize called with %s (expected string)."):format(t))
|
error(("core.deserialize called with %s (expected string)."):format(t))
|
||||||
end
|
end
|
||||||
|
|
||||||
local func, err = loadstring(str)
|
local func, err = loadstring(str)
|
||||||
if not func then return nil, err end
|
if not func then return nil, err end
|
||||||
|
|
||||||
-- math.huge is serialized to inf, NaNs are serialized to nan by Lua
|
-- math.huge was serialized to inf and NaNs to nan by Lua in engine version 5.6, so we have to support this here
|
||||||
local env = {inf = math_huge, nan = nan}
|
local env = {inf = math_huge, nan = 0/0}
|
||||||
if safe then
|
if safe then
|
||||||
env.loadstring = dummy_func
|
env.loadstring = dummy_func
|
||||||
else
|
else
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue