Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a00e23e690 | ||
|
|
ebbee02308 | ||
|
|
649f316f9f | ||
|
|
ed579e5ca4 | ||
|
|
f4ba7bd23a | ||
|
|
204e7280db | ||
|
|
222d71f4d2 | ||
|
|
dd4a2b689f | ||
|
|
bed1a35d07 | ||
|
|
d76f3d4aeb | ||
|
|
a234c8ec9d | ||
|
|
7761d36cb8 | ||
|
|
c1e3724d9f | ||
|
|
0cac59ec8c | ||
|
|
348113c23d | ||
|
|
a378c35fb6 | ||
|
|
f688c81b9d | ||
|
|
34a23f2ae9 | ||
|
|
39bc5c0f81 | ||
|
|
0f2f962ac4 | ||
|
|
a021e362ee | ||
|
|
467cd51d5b | ||
|
|
7f6827d219 | ||
|
|
9dfd0ed49a | ||
|
|
6ef7c1509d | ||
|
|
0e09fbbc2c | ||
|
|
65ade81fb6 | ||
|
|
601ce4d35f | ||
|
|
b87f1d226b | ||
|
|
33a2d0a59e | ||
|
|
c1081205d6 | ||
|
|
fe5016c6fd | ||
|
|
ed017975cc | ||
|
|
2c1ee3a564 | ||
|
|
b90793c2fc | ||
|
|
606e88d683 | ||
|
|
294aeaad46 | ||
|
|
8e519a0e24 | ||
|
|
fc9b6067af | ||
|
|
24721e4aa0 | ||
|
|
b63b494e49 | ||
|
|
9863d3985d | ||
|
|
69ed90bb9d | ||
|
|
7e825d4c30 | ||
|
|
b1fff2ad15 | ||
|
|
e337452d46 | ||
|
|
5809c4486b | ||
|
|
c8f4ec05f3 | ||
|
|
9f70386466 | ||
|
|
e7de4e1a10 | ||
|
|
1ce95dc9c9 | ||
|
|
30e3d1438c | ||
|
|
b09de7f881 | ||
|
|
e39fd3d43a | ||
|
|
f7736b953c | ||
|
|
1e3a9638ab | ||
|
|
c9f149d7c7 | ||
|
|
4e0a20d28d | ||
|
|
47e4603391 | ||
|
|
5d75db97f5 | ||
|
|
5a01cfefe4 | ||
|
|
74d28ddc5d | ||
|
|
1d1608da56 | ||
|
|
4bddd5f23b | ||
|
|
15317122be | ||
|
|
221b4a840b | ||
|
|
2fb6d2503e | ||
|
|
724bdc661a | ||
|
|
9caa1df777 | ||
|
|
3769737d33 | ||
|
|
9780b09705 | ||
|
|
de480b9c18 | ||
|
|
0d3888343f | ||
|
|
74c31b272e | ||
|
|
f93a746061 | ||
|
|
4cc15b84f6 | ||
|
|
f478112fd8 | ||
|
|
a49ad98445 | ||
|
|
71799611bf | ||
|
|
ea38db6d06 | ||
|
|
61156bf527 | ||
|
|
4b27c6ece4 | ||
|
|
8d4b4b4851 | ||
|
|
88dc7efbe2 | ||
|
|
b25ffe9516 | ||
|
|
fedb8b8366 | ||
|
|
fb29be30fa | ||
|
|
4985dee0ad | ||
|
|
f3bfa418a5 | ||
|
|
0a7afa3e7c | ||
|
|
813d47895d | ||
|
|
43936c0150 | ||
|
|
4b525a8a84 | ||
|
|
79eaa03abd | ||
|
|
2d9010dc2d | ||
|
|
1641cde4b7 | ||
|
|
ac31e065ab | ||
|
|
f29d00ee0a | ||
|
|
c35c1b37b3 | ||
|
|
7fb87c6d08 | ||
|
|
174791cdd4 | ||
|
|
305cf07c75 | ||
|
|
22c63a3ffa | ||
|
|
663b0b8737 | ||
|
|
3bd96cb383 | ||
|
|
765f4842eb | ||
|
|
5c19b87425 | ||
|
|
0645f6f82d | ||
|
|
871f3256cb | ||
|
|
e30eaa3001 | ||
|
|
b2b541c880 | ||
|
|
624df89739 | ||
|
|
34772a337e | ||
|
|
29bd7c877a | ||
|
|
28fd4b2663 | ||
|
|
a71c473d01 | ||
|
|
d0e9c2767a | ||
|
|
0a316ff99e | ||
|
|
b1f269a5fc | ||
|
|
a4ce362e4b | ||
|
|
992db3ed7b | ||
|
|
cbd19fb4fa | ||
|
|
7fe820f7fb | ||
|
|
378e20478d | ||
|
|
fddac9c821 | ||
|
|
0faddee3c8 | ||
|
|
733db5035b | ||
|
|
0011a1503a | ||
|
|
8396d5c9fa | ||
|
|
318ca1cc8e | ||
|
|
13597bc402 | ||
|
|
6a9b816f84 | ||
|
|
9e06c7f88f | ||
|
|
da62ba6e2a | ||
|
|
0dffe0ee2b | ||
|
|
db57f0cf91 | ||
|
|
d7d0506e3f | ||
|
|
162083a0d5 | ||
|
|
65da207421 | ||
|
|
4969d0423f | ||
|
|
59d11e56a5 | ||
|
|
c92cd927d1 | ||
|
|
979ba53b56 | ||
|
|
081409e687 | ||
|
|
9aca7ec86d | ||
|
|
3a54813513 | ||
|
|
8b6fe795d2 | ||
|
|
9426d4c670 | ||
|
|
4dcadcf3cf | ||
|
|
3e43d88374 | ||
|
|
580f8f5217 | ||
|
|
067c720a88 | ||
|
|
2541682bee | ||
|
|
78921a064e | ||
|
|
92d53b01fc | ||
|
|
8f1a4c5541 | ||
|
|
e2ccb6ea96 | ||
|
|
2f3a3979a4 | ||
|
|
9f065cea59 | ||
|
|
0650d5ae41 | ||
|
|
d2c5e09413 | ||
|
|
1cc5d54fde | ||
|
|
4ef2a6d81e | ||
|
|
be14e4173e | ||
|
|
c58aac5f67 | ||
|
|
cada4b2543 | ||
|
|
ccc5bebc3b | ||
|
|
8b80186320 | ||
|
|
1e8317fbac | ||
|
|
6500b5b6de | ||
|
|
eff677e112 | ||
|
|
543dae701c | ||
|
|
ec2bc54339 | ||
|
|
2b2c3f8d30 | ||
|
|
772bf60a3c | ||
|
|
ca2c51896d | ||
|
|
c640ae21ad | ||
|
|
b8f392a116 | ||
|
|
bfa31bf184 | ||
|
|
088486c1ad | ||
|
|
acf891e419 | ||
|
|
ca90af5f56 | ||
|
|
fc1761d6fe | ||
|
|
45b82b9fa2 | ||
|
|
22cf431170 | ||
|
|
b513d92456 | ||
|
|
b3be63437e | ||
|
|
c801b625ac | ||
|
|
0bbc4c6777 | ||
|
|
4eed142ccc | ||
|
|
42ec5c9685 | ||
|
|
ca9876544e | ||
|
|
73d5865908 | ||
|
|
f474eab926 | ||
|
|
0bcc96b5a2 | ||
|
|
b76147ec50 | ||
|
|
22ca0ce33f | ||
|
|
4368c6e60e | ||
|
|
7591c51b60 | ||
|
|
fb4bddd163 | ||
|
|
5c7cacaafe | ||
|
|
9c7b0981a6 | ||
|
|
93c88fea8c | ||
|
|
9de4deed37 | ||
|
|
254fd3e28d | ||
|
|
9a886aa582 | ||
|
|
a51047a80d | ||
|
|
55dba0c156 | ||
|
|
a98a7ba227 | ||
|
|
56348b3e31 | ||
|
|
89527f879b | ||
|
|
57221d4a9f | ||
|
|
7f982b303b | ||
|
|
78646f512e | ||
|
|
fd4eac0f81 | ||
|
|
60cc6a1839 | ||
|
|
51561bcb85 | ||
|
|
3d56fefb51 | ||
|
|
9b7556a078 | ||
|
|
cf2f3f82b5 | ||
|
|
3be43b1a54 | ||
|
|
a5c178ae5a | ||
|
|
06163708bd | ||
|
|
1ee9684d96 | ||
|
|
137c6201c0 | ||
|
|
a341cff83f | ||
|
|
7501697f02 | ||
|
|
776214f7bb | ||
|
|
6df25c8308 | ||
|
|
ab51304dd6 | ||
|
|
e751e240db | ||
|
|
f490ce4a82 | ||
|
|
b0a3e52aad | ||
|
|
0a4d1a7fe7 | ||
|
|
fd11c61292 | ||
|
|
240d7667f6 | ||
|
|
1a34b786a7 | ||
|
|
b7f1681157 | ||
|
|
e675535cb7 | ||
|
|
84cdf2bbfe | ||
|
|
6f4914d5cb | ||
|
|
c7945610ee | ||
|
|
c6512c0b00 | ||
|
|
cc5a63109a | ||
|
|
43aa0d95f5 | ||
|
|
42ad617bbd | ||
|
|
e433d5f334 | ||
|
|
0ac15152d8 | ||
|
|
4af4824ad0 | ||
|
|
83b93eafbb | ||
|
|
7f8f08d2c5 | ||
|
|
85e6db44c1 | ||
|
|
b430eaa239 | ||
|
|
889c581d73 | ||
|
|
6163278161 | ||
|
|
ae9bb643e4 | ||
|
|
2af44b5f5e | ||
|
|
00ee86a1b7 | ||
|
|
9e4d2e67d2 | ||
|
|
34fa2bc52a | ||
|
|
da4be8f848 | ||
|
|
acd8682f40 | ||
|
|
0e3ca1d2c6 | ||
|
|
7c5f29e6fb | ||
|
|
760fcb2e23 | ||
|
|
ded8eb762f | ||
|
|
ef5d3cb26b | ||
|
|
b3317f3d4d | ||
|
|
63b2069e96 | ||
|
|
52232dc2ee | ||
|
|
961e8b6e60 | ||
|
|
d0048f32de | ||
|
|
867dd29ba8 | ||
|
|
64bc6d3ac0 | ||
|
|
124dcc67aa | ||
|
|
2276f471f2 | ||
|
|
2837f2cad2 | ||
|
|
6407a48973 | ||
|
|
f02d919b64 | ||
|
|
5566674c29 | ||
|
|
80a99eb031 | ||
|
|
cfe01850aa | ||
|
|
48158883c1 | ||
|
|
d71be5ecb1 | ||
|
|
1ba71a14f7 | ||
|
|
12157feeca | ||
|
|
200f4d42be | ||
|
|
201f606383 | ||
|
|
cf8b1c6b34 | ||
|
|
04452e6ba8 | ||
|
|
9ca00b5935 | ||
|
|
05999c9759 | ||
|
|
d23f113806 | ||
|
|
cc36b51bad | ||
|
|
3787b6308c | ||
|
|
7e2d57247d | ||
|
|
80a2eef239 | ||
|
|
16ee499f75 | ||
|
|
a9ff32df12 | ||
|
|
37c1b6ac1e | ||
|
|
54e5f3bcf6 | ||
|
|
2290685999 | ||
|
|
f3cbb54af5 | ||
|
|
85446f3284 | ||
|
|
ba93ccfc3e | ||
|
|
a1e7aad19d | ||
|
|
9d07ba06ab | ||
|
|
5a0c4bfc41 | ||
|
|
58ed13fc96 | ||
|
|
1f6c9eac65 | ||
|
|
bf3d400ca5 | ||
|
|
e37c2f1ec2 | ||
|
|
d95a3ef6aa | ||
|
|
c2f7bddb0f | ||
|
|
ec656a63eb | ||
|
|
06f2dc7032 | ||
|
|
0e5189e1b7 | ||
|
|
c477c767c5 | ||
|
|
c81d0eb025 | ||
|
|
7494e9a1a8 | ||
|
|
ccf6b81ed6 | ||
|
|
171899596f | ||
|
|
953c58fc49 | ||
|
|
ec4fe559f5 | ||
|
|
8079274531 | ||
|
|
ca3c0f30b8 | ||
|
|
a7955970ff | ||
|
|
dbbd03f4cd | ||
|
|
788ead37fb | ||
|
|
83dd62f2ad | ||
|
|
5ee6debb2a | ||
|
|
07a080931d | ||
|
|
0c44604a0f | ||
|
|
a9dfed098a | ||
|
|
71aa9230bf | ||
|
|
666978f9e1 | ||
|
|
9fe9344dfd | ||
|
|
f2e1e2ad4f | ||
|
|
ca6b369b85 | ||
|
|
ef65b1f5b3 | ||
|
|
962a3d859f | ||
|
|
daf36fc2a2 | ||
|
|
52a10f6898 | ||
|
|
a8b1dda1a1 | ||
|
|
dbd527dbdf | ||
|
|
ddbdfbb447 | ||
|
|
fda8db5562 | ||
|
|
b515773fbf | ||
|
|
a5dc717ff9 | ||
|
|
13bc8aa881 | ||
|
|
f958406e5e | ||
|
|
3ebd0c9f4f | ||
|
|
66e47fdc57 | ||
|
|
33bc4c9e6f | ||
|
|
a360416946 | ||
|
|
95f600e87a | ||
|
|
c492376c87 | ||
|
|
53b9d15993 | ||
|
|
115551b4f4 | ||
|
|
9e95a4bbc4 | ||
|
|
fa59008448 | ||
|
|
a298ccfd45 | ||
|
|
ee6c9ae1ab | ||
|
|
8238b9d2c1 | ||
|
|
49dbd44604 | ||
|
|
553c2beeea | ||
|
|
7c0f19a520 | ||
|
|
cbf0025712 | ||
|
|
5af84b4ad5 | ||
|
|
0bb475a54d | ||
|
|
db8d9669c2 | ||
|
|
f7ededb3a1 | ||
|
|
69257a2eb4 | ||
|
|
2b3fee983c | ||
|
|
e4045fb8a5 | ||
|
|
6ef175cb29 | ||
|
|
63e839c67c | ||
|
|
ccae400bcf | ||
|
|
dc75f996b0 | ||
|
|
e567777179 | ||
|
|
667907009d | ||
|
|
b9189a5570 | ||
|
|
fac33d08d7 | ||
|
|
182db13909 | ||
|
|
d2ee7cdf42 | ||
|
|
cf41adda2b | ||
|
|
5720f8b9b6 | ||
|
|
b8917ff608 | ||
|
|
b3e8aa9294 | ||
|
|
69cb000395 | ||
|
|
e115a4a0a0 | ||
|
|
01fcc9d674 | ||
|
|
664b7a3e45 | ||
|
|
aeba3e1f63 | ||
|
|
27d7a2917e | ||
|
|
023a4ff1ae | ||
|
|
7509cb1375 | ||
|
|
bc3a701b93 | ||
|
|
6be1d664b0 | ||
|
|
aa929b9c61 | ||
|
|
2504e6a93c | ||
|
|
0d5c83f928 | ||
|
|
79c314a983 | ||
|
|
0352ae9bf3 | ||
|
|
ae291be0a1 | ||
|
|
7e80948b58 | ||
|
|
dcc54faa93 | ||
|
|
1f5fe1ae13 | ||
|
|
6deaea5356 | ||
|
|
7f009df50f | ||
|
|
e69d827f72 | ||
|
|
b4cd02137d | ||
|
|
f7f002b786 | ||
|
|
2564190ae3 | ||
|
|
6732c14779 | ||
|
|
76877127b8 | ||
|
|
6cccf21960 | ||
|
|
b934bfc4f1 | ||
|
|
25fe0cc9f9 | ||
|
|
06d527a7ca | ||
|
|
e386a2c85b | ||
|
|
42ce901da2 | ||
|
|
616cc98098 | ||
|
|
87c5e27f50 | ||
|
|
3271d9d72e | ||
|
|
87d27b3070 | ||
|
|
df06d44c19 | ||
|
|
8b965dd872 | ||
|
|
01849ef1b3 | ||
|
|
0ac6fcca16 | ||
|
|
c48b239de0 | ||
|
|
da3b3358cd | ||
|
|
8538190e80 | ||
|
|
4df53b8e04 | ||
|
|
06dd6d98b2 | ||
|
|
e87793968a | ||
|
|
0dcad1261c | ||
|
|
d6fd7a0977 | ||
|
|
97a438cab3 | ||
|
|
48e56fcd7b | ||
|
|
81e18df994 | ||
|
|
12d7492708 | ||
|
|
efa856226d | ||
|
|
3b0548d42e | ||
|
|
cf4a69cb33 | ||
|
|
37bf2d8c2d | ||
|
|
66d80f8b8c | ||
|
|
c3c231ff6c | ||
|
|
0de5cb7e6b | ||
|
|
124640e23a | ||
|
|
e46deae1b1 | ||
|
|
19322f91f5 | ||
|
|
168dcb1dd2 | ||
|
|
48978acc47 | ||
|
|
eb4d7f1de6 | ||
|
|
da16465961 | ||
|
|
25b42157a9 | ||
|
|
2fa2fc9bf1 | ||
|
|
b452ac48f7 | ||
|
|
0e956a1f8e | ||
|
|
460debfa0c | ||
|
|
adbb931d93 | ||
|
|
f4307e8db3 | ||
|
|
60c35280f1 | ||
|
|
b843a50c4b | ||
|
|
19544d1e51 | ||
|
|
8b4cf3e3be | ||
|
|
8824d36dd2 | ||
|
|
ea7a69c637 | ||
|
|
cb3fd27cce | ||
|
|
6e19c7e6e5 | ||
|
|
be25b94242 | ||
|
|
d16ec09793 | ||
|
|
8041a9de3f | ||
|
|
eeeec99215 | ||
|
|
854a71eb32 | ||
|
|
22f0b9a5c7 | ||
|
|
4db3cd713c | ||
|
|
8553bb7ecc | ||
|
|
12a22933dc | ||
|
|
9d49b58571 | ||
|
|
f29235ebf3 | ||
|
|
f761cdc79a | ||
|
|
a750252c02 | ||
|
|
456adc6a4f | ||
|
|
89587e378b | ||
|
|
19186c677c | ||
|
|
c1c4781f22 | ||
|
|
db54e4f350 | ||
|
|
4be3f27d5c | ||
|
|
a1975a40b6 | ||
|
|
5112fb9a0e | ||
|
|
e2cecffec3 | ||
|
|
3f65bdd383 | ||
|
|
98cec2df46 | ||
|
|
e6b66673a3 | ||
|
|
756439ce66 | ||
|
|
84b3c3d809 | ||
|
|
332cd6d118 | ||
|
|
77e2ce7274 | ||
|
|
6ed7a905a4 | ||
|
|
f92db080ed | ||
|
|
02dea62c26 | ||
|
|
dc32943612 | ||
|
|
e981ce8faf | ||
|
|
0699ae305e | ||
|
|
2ebd548170 | ||
|
|
b05d9a11f1 | ||
|
|
e488a79096 | ||
|
|
428785494d | ||
|
|
7efe3b2ea7 | ||
|
|
25bc99c68b | ||
|
|
2b9937c3be | ||
|
|
9f0bd7f579 | ||
|
|
3267830fc9 | ||
|
|
309f56b108 | ||
|
|
684c840080 | ||
|
|
72d5df4c75 | ||
|
|
85654ee544 | ||
|
|
894bab4ecd | ||
|
|
d69229a333 | ||
|
|
7ecd2f4249 | ||
|
|
17f3d89300 | ||
|
|
82ac6a98a5 | ||
|
|
5da3bbd414 | ||
|
|
3f40c250d3 | ||
|
|
27ba9d7cd2 | ||
|
|
0194398a81 | ||
|
|
a4974ec8e3 | ||
|
|
3086ea6ecf | ||
|
|
872e967a12 | ||
|
|
727dacf9bb | ||
|
|
d5218ad160 | ||
|
|
7517d89964 | ||
|
|
f5d06edefc | ||
|
|
6ce1a323ab | ||
|
|
31436ac146 | ||
|
|
d827f2e218 | ||
|
|
0d8eaa1a1f | ||
|
|
5184b5e6e1 | ||
|
|
ce6e4f02e0 | ||
|
|
e130c34a61 | ||
|
|
71f4ec957f | ||
|
|
9d6379dbf8 | ||
|
|
809cf8baad | ||
|
|
da911f3877 | ||
|
|
ff580d8a06 | ||
|
|
d38ec52faa | ||
|
|
abebad2f5f | ||
|
|
1befd890cc | ||
|
|
56485c39f4 | ||
|
|
292552618a | ||
|
|
e26205a4f7 | ||
|
|
e8bf80a1d2 | ||
|
|
f695ad7e60 | ||
|
|
58d1c3265e | ||
|
|
774b2b64d3 | ||
|
|
099971d8f6 | ||
|
|
275d357b6c | ||
|
|
11d79aceec | ||
|
|
f6fa8fb326 | ||
|
|
6b849a4017 | ||
|
|
8ac7087adf | ||
|
|
9cb67b15da | ||
|
|
313e80448a | ||
|
|
be90286451 | ||
|
|
0f444a8ad7 | ||
|
|
c5d6814c50 | ||
|
|
491cd90af8 | ||
|
|
fb1cde88d8 | ||
|
|
a1fe1f7a9e | ||
|
|
d27480312a | ||
|
|
43d3aa6460 | ||
|
|
a819f80520 | ||
|
|
5c7e462e9c | ||
|
|
92219de5b7 | ||
|
|
02e789687d | ||
|
|
c5d67a56fd | ||
|
|
807e9a4db5 | ||
|
|
3f3c0d4911 | ||
|
|
340b2192b9 | ||
|
|
ab0cc53ae7 | ||
|
|
e7d214d43b | ||
|
|
a5c6b9d240 | ||
|
|
ec02e87d94 | ||
|
|
90eb03cf78 | ||
|
|
4aa0c64567 | ||
|
|
c800574c4c | ||
|
|
9ebf3b966e | ||
|
|
1c4245d070 | ||
|
|
59ca250593 | ||
|
|
269a3f5841 | ||
|
|
9302dc1f44 | ||
|
|
694e722ce8 | ||
|
|
9fc5b187bf | ||
|
|
beb45688b2 | ||
|
|
9dd19c7559 | ||
|
|
c018a6feb0 | ||
|
|
1d8da1531d | ||
|
|
38a921558d | ||
|
|
f0d29df1d8 | ||
|
|
40951ad576 | ||
|
|
be15bdee46 | ||
|
|
d499f67839 | ||
|
|
53dba45410 | ||
|
|
26e3452f7c | ||
|
|
52d23725a7 | ||
|
|
b88591c51b | ||
|
|
49f5e584eb | ||
|
|
15641b7f2d | ||
|
|
bd75e7bb27 | ||
|
|
6b431ece1b | ||
|
|
dbecd74428 | ||
|
|
e58c886d60 | ||
|
|
70f92077b5 | ||
|
|
5c60109a88 | ||
|
|
04bb82d622 | ||
|
|
0b13529061 | ||
|
|
3ffdba4ee1 | ||
|
|
257a28e4d7 | ||
|
|
220b965d3c | ||
|
|
9eea0969cd | ||
|
|
e7c4e44530 | ||
|
|
424b4ba8a7 | ||
|
|
5a9603cf48 | ||
|
|
54105ce586 | ||
|
|
96cd2e20fe | ||
|
|
fb16df23af | ||
|
|
c27cd836d8 | ||
|
|
68bfae2cf3 | ||
|
|
491e2d923b | ||
|
|
3b658e0fc5 | ||
|
|
9b357dbe8c | ||
|
|
5f77b10bf9 | ||
|
|
3d7a930dcd | ||
|
|
d7baddecdd | ||
|
|
2724337aad | ||
|
|
42b02ec944 | ||
|
|
10968885c6 | ||
|
|
cf7dbf1090 | ||
|
|
116c2f9300 | ||
|
|
e38d49399b | ||
|
|
1e14d37387 | ||
|
|
83f8d89bf3 | ||
|
|
d7714654f1 | ||
|
|
eae749bbf1 | ||
|
|
20eee370ff | ||
|
|
cbf3ca6650 | ||
|
|
8718814bff | ||
|
|
fc0113c887 | ||
|
|
82951f6855 | ||
|
|
467f61583b | ||
|
|
97b9afb85a | ||
|
|
16c9fbe148 | ||
|
|
87b1bc1ad6 | ||
|
|
21ea978455 | ||
|
|
170cf7f8c0 | ||
|
|
7ebfe8990d | ||
|
|
390dfad960 | ||
|
|
c197382955 | ||
|
|
b6a2725c77 | ||
|
|
5b8f7d5c93 | ||
|
|
355ea97b50 | ||
|
|
0e4883c228 | ||
|
|
2fd3ae7d88 | ||
|
|
addf1ddf6d | ||
|
|
75515288cd | ||
|
|
5daa268748 | ||
|
|
ff427fbffb | ||
|
|
81d2043d06 | ||
|
|
9809cb149a | ||
|
|
1fdb6ce3db | ||
|
|
65e932039b | ||
|
|
a2ac8bf0d2 | ||
|
|
ea8cad4f2a | ||
|
|
4a89218e69 | ||
|
|
8109637349 | ||
|
|
9ae1ccebf1 | ||
|
|
b41f40e6ff | ||
|
|
43025b373a | ||
|
|
8fad301da8 | ||
|
|
cf1b2e38c2 | ||
|
|
f9ab9e6f96 | ||
|
|
c9fe48f6bb | ||
|
|
574506507f | ||
|
|
d91028a376 | ||
|
|
b7148edba5 | ||
|
|
b2876d5916 | ||
|
|
ad2bae37f1 | ||
|
|
5330e8d45d | ||
|
|
3f59a53024 | ||
|
|
6e5b3e519b | ||
|
|
e962ed5a25 | ||
|
|
caa6801ec4 | ||
|
|
6c7bf7511a | ||
|
|
6f3e4d7f25 | ||
|
|
2ca5d373d5 | ||
|
|
fb3fff9894 | ||
|
|
e5e417265d | ||
|
|
94453c127c | ||
|
|
0408389fda | ||
|
|
6ab65f735d | ||
|
|
296d6cbdb9 | ||
|
|
474f486980 | ||
|
|
3af897de1d | ||
|
|
2a72d5e3bf | ||
|
|
cb6c5b1c5d | ||
|
|
33b7f32eb5 | ||
|
|
02785f49e8 | ||
|
|
20da6a38ab | ||
|
|
0e2f071d63 | ||
|
|
37194ce6ab | ||
|
|
fa2b202dcf | ||
|
|
d07016eea9 | ||
|
|
71c15e996f | ||
|
|
e56227b2b0 | ||
|
|
6f2af6013c | ||
|
|
384c8d576b | ||
|
|
96393067bf | ||
|
|
8ce8423758 | ||
|
|
d3a50f6951 | ||
|
|
048ea6e56f | ||
|
|
4e85807aa9 | ||
|
|
c407e38516 | ||
|
|
8a1b3c291c | ||
|
|
ef294666ae | ||
|
|
d2c35d7b4b | ||
|
|
330ddc1c23 | ||
|
|
3434c35334 | ||
|
|
bae93299c7 | ||
|
|
62b8bdd7e3 | ||
|
|
729a4f50ba | ||
|
|
9af6cc0c6a | ||
|
|
b91800d821 | ||
|
|
5cbdd3ef0c | ||
|
|
de1584ece7 | ||
|
|
21b98b809f | ||
|
|
2235e35939 | ||
|
|
b0f574071a | ||
|
|
0fb3c6183e | ||
|
|
7f484c34a4 | ||
|
|
efb218653b | ||
|
|
a7f53421e3 | ||
|
|
55908f9112 | ||
|
|
a7053b9c1c | ||
|
|
8a183e72d4 | ||
|
|
8b672526e5 | ||
|
|
4d39837cf3 | ||
|
|
ae274d35c7 | ||
|
|
2fd3ab74a0 | ||
|
|
62dbbe39d1 | ||
|
|
6425f37e68 | ||
|
|
d120f1d730 | ||
|
|
21290057f0 | ||
|
|
5558211c4f | ||
|
|
feab7aa908 | ||
|
|
7e416f4251 | ||
|
|
d23f072922 | ||
|
|
3ecdd5ff2a | ||
|
|
c9686eb5d0 | ||
|
|
509cafe386 | ||
|
|
58642efa83 | ||
|
|
4bddbfa9ca | ||
|
|
8713c41741 | ||
|
|
1ada6b9fdd | ||
|
|
6209a63bba | ||
|
|
a39660ec88 | ||
|
|
81655bbc99 | ||
|
|
5779583a65 | ||
|
|
db14d9f086 | ||
|
|
366e54784a | ||
|
|
fc8d70b12f | ||
|
|
7af49d733d | ||
|
|
fb34738d60 | ||
|
|
2e74261566 | ||
|
|
09a45ee2fb | ||
|
|
aa7c39636c | ||
|
|
786194b0b9 | ||
|
|
23fb20abe3 | ||
|
|
5e426a60c1 | ||
|
|
91c3e50771 | ||
|
|
cef447356b | ||
|
|
965550ab0d | ||
|
|
8761f0f810 | ||
|
|
3d2521a06c | ||
|
|
1924e1fb0e | ||
|
|
98afd42aac | ||
|
|
4133f3fa48 | ||
|
|
75e0f20964 | ||
|
|
c0dd4d09f9 | ||
|
|
d5b8726c8c | ||
|
|
6c09e9f09a | ||
|
|
fbf4482c73 | ||
|
|
167be4e4a5 | ||
|
|
21bc2a108b | ||
|
|
3820baa885 | ||
|
|
665e458409 | ||
|
|
39a056e09f | ||
|
|
823a61670a | ||
|
|
8a60e96f0e | ||
|
|
1ee1d4398a | ||
|
|
3aff70b948 | ||
|
|
bf3316b110 | ||
|
|
f26fe0e821 | ||
|
|
4e3e232a30 | ||
|
|
e60474c7d0 | ||
|
|
08dbcde924 | ||
|
|
993737a5c5 | ||
|
|
56efff6a0a | ||
|
|
3580c2b63d | ||
|
|
e93430019c | ||
|
|
dab3f8ef16 | ||
|
|
53d602c294 | ||
|
|
abf52f737a | ||
|
|
6d51b2e0a0 | ||
|
|
3a527c1292 | ||
|
|
e1fde4fcfd | ||
|
|
282f21e903 | ||
|
|
0e87cdafd5 | ||
|
|
3b29a4877f | ||
|
|
aef729eef6 | ||
|
|
c10cdbe1fc | ||
|
|
c5853eade6 | ||
|
|
f90829457a | ||
|
|
4a825a31ae | ||
|
|
207cfba2bd | ||
|
|
2c066e9c8c | ||
|
|
9cf425164e | ||
|
|
56defd8e02 | ||
|
|
c124414aba | ||
|
|
cc86657f62 | ||
|
|
28d1b99444 | ||
|
|
d4305f0287 | ||
|
|
7241bdece6 | ||
|
|
dc5e877af6 | ||
|
|
0b3300397b | ||
|
|
668c8dfd93 | ||
|
|
6e4903f8c6 | ||
|
|
0d92ef53ec | ||
|
|
8a1775ab3f | ||
|
|
907b4edbf0 | ||
|
|
7ac0ce5bf5 | ||
|
|
58118440b9 | ||
|
|
ebfaf13bed | ||
|
|
1de3e80525 | ||
|
|
664e3b96f8 | ||
|
|
fcfc42e057 | ||
|
|
4a41b50065 | ||
|
|
d1baaad955 | ||
|
|
3cd72e9e34 | ||
|
|
c39e2384ec | ||
|
|
57309b63a9 | ||
|
|
ff1bcdef35 | ||
|
|
1a3605761c | ||
|
|
b195a4a8e5 | ||
|
|
6f11fd4269 | ||
|
|
48556228df | ||
|
|
81d1554166 | ||
|
|
58c942116b | ||
|
|
439799ce8f | ||
|
|
b5b6b55f51 | ||
|
|
b48cd24118 | ||
|
|
05aa271ba0 | ||
|
|
069f495746 | ||
|
|
9b0b96e1cc | ||
|
|
0ba4359e55 | ||
|
|
65705cf86e | ||
|
|
38fd53b544 | ||
|
|
5e47aa3bb2 | ||
|
|
f194df5f80 | ||
|
|
25a5263c21 | ||
|
|
86e63beb14 | ||
|
|
d329a7bed9 | ||
|
|
613d5b44e2 | ||
|
|
179a74ce49 | ||
|
|
f9f9369082 | ||
|
|
4c2d9d791d | ||
|
|
335306f478 | ||
|
|
b8984db2f7 | ||
|
|
856a2c5967 | ||
|
|
856332c93f | ||
|
|
90464c55cf | ||
|
|
be890fcf56 | ||
|
|
a19fa7f871 | ||
|
|
114db448dd | ||
|
|
6398fa4e1b | ||
|
|
e34d3d29e7 | ||
|
|
11e88fe009 | ||
|
|
36ca01a1a3 | ||
|
|
ad4cd7c54a | ||
|
|
93260d4662 | ||
|
|
fc2f39f44c | ||
|
|
a018d53496 | ||
|
|
9cadceb7b7 | ||
|
|
1ddc69bb60 | ||
|
|
544f63e101 | ||
|
|
92fe219e78 | ||
|
|
6d9601b579 | ||
|
|
567434fe99 | ||
|
|
094726e916 | ||
|
|
e0996e29f4 | ||
|
|
e25b728089 | ||
|
|
5d23c9b3af | ||
|
|
8051a5c8d4 | ||
|
|
8b3d9504c9 | ||
|
|
f8e804d3cb | ||
|
|
0c96b3f7ed | ||
|
|
7af809496e | ||
|
|
b9331331a7 | ||
|
|
5e78db1a11 | ||
|
|
72f947634e | ||
|
|
ed33b04bfc | ||
|
|
9e648dd34f | ||
|
|
1dfdc28c66 | ||
|
|
44dee9b285 | ||
|
|
80b18bf793 | ||
|
|
f00e7c4d85 | ||
|
|
308d38bd65 | ||
|
|
be7f811761 | ||
|
|
0b3ba6f94a | ||
|
|
cb3ee2112f | ||
|
|
4d72af7d30 | ||
|
|
9704bf37cc | ||
|
|
8d5e02dfbe | ||
|
|
8318b5cb71 | ||
|
|
014f23c7a0 | ||
|
|
910a4e7b00 | ||
|
|
aef8998058 | ||
|
|
4ae6053ace | ||
|
|
112ec4bdff | ||
|
|
85aa1f5e34 | ||
|
|
4e92cd8b5c | ||
|
|
717aa69293 | ||
|
|
97fce0735d | ||
|
|
8b64aebe27 | ||
|
|
a9258f99d4 | ||
|
|
2fb28e589a | ||
|
|
dddce86ec7 | ||
|
|
a64aae1d78 | ||
|
|
028f1036fb | ||
|
|
0c2e006cc8 | ||
|
|
f42b519f20 | ||
|
|
41c6f389c7 | ||
|
|
db02b63be6 | ||
|
|
4805c77eac | ||
|
|
71b1428fbe | ||
|
|
cbd3bea792 | ||
|
|
e24d63c971 | ||
|
|
bdc4ed311c | ||
|
|
05c3c856a9 | ||
|
|
e58782c838 | ||
|
|
6ad776ca7a | ||
|
|
3c2004a2e7 | ||
|
|
e3a877c948 | ||
|
|
33814d2339 | ||
|
|
c756faddac | ||
|
|
eed063af2f | ||
|
|
382ca559eb | ||
|
|
e0ed93ed8a | ||
|
|
bb62a3163a | ||
|
|
cc850dd75e | ||
|
|
5ad9a65a15 | ||
|
|
c2103247ed | ||
|
|
6a6ed90473 | ||
|
|
478e3a2109 | ||
|
|
cf1c08c66e | ||
|
|
689d8c9e48 | ||
|
|
3ff15ff7e9 | ||
|
|
6d855fef24 | ||
|
|
7b8347a9c2 | ||
|
|
713e796da6 | ||
|
|
780449edee | ||
|
|
2736ec96fb | ||
|
|
4691696afe | ||
|
|
66d0019063 | ||
|
|
86683d60cd | ||
|
|
7b9f564f74 | ||
|
|
9e2851aa0b | ||
|
|
9823981222 | ||
|
|
69f7fbfbb2 | ||
|
|
1af06f58ce | ||
|
|
03abf9dc0d | ||
|
|
4a2e76c415 | ||
|
|
12509e6225 | ||
|
|
58e21f32b4 | ||
|
|
735cb143ae | ||
|
|
7b7c1190cf | ||
|
|
f9fdbaae9b | ||
|
|
bcb080aa42 | ||
|
|
81b217dadc | ||
|
|
e48790de77 | ||
|
|
a4178e4c18 | ||
|
|
5ae986a6dd | ||
|
|
a07ded343c | ||
|
|
c990822abb | ||
|
|
dca67a8996 | ||
|
|
dc2020f328 | ||
|
|
7370173f63 | ||
|
|
c423d24834 | ||
|
|
002541aa27 | ||
|
|
beaed55c08 | ||
|
|
2e3491cd39 | ||
|
|
3492446a73 | ||
|
|
aa9f39f82f | ||
|
|
0df2b4487f | ||
|
|
0a9d5c5005 | ||
|
|
d581160603 | ||
|
|
364f5a8d9b | ||
|
|
04c1aab69c | ||
|
|
12290b4d53 | ||
|
|
a8f6222f0c | ||
|
|
b58bddbd03 | ||
|
|
2361f9a630 | ||
|
|
4334710289 | ||
|
|
c764999d75 | ||
|
|
6b416f7187 | ||
|
|
b3b59a5f6a | ||
|
|
5d252515e2 | ||
|
|
37ac3c9e70 | ||
|
|
32e95a69a9 | ||
|
|
c054ffeb2b | ||
|
|
027ea766e6 | ||
|
|
4e8a7d98af | ||
|
|
b50716362b | ||
|
|
4e57366960 | ||
|
|
52822246e8 | ||
|
|
25c7067664 | ||
|
|
6b89eabfee | ||
|
|
0571faa350 | ||
|
|
c3785ed4fc | ||
|
|
10b478b1bd | ||
|
|
e3da9fed6e | ||
|
|
c74cfb63a6 | ||
|
|
90a6bf2508 | ||
|
|
b1480d4270 | ||
|
|
9b13d85b46 | ||
|
|
25a542759d | ||
|
|
6d625167a5 | ||
|
|
e453f3ccc7 | ||
|
|
2d1852470e | ||
|
|
5ef7c8bce8 | ||
|
|
1afa757f3b | ||
|
|
692c4c65e4 | ||
|
|
de5c858903 | ||
|
|
2effc25ad0 | ||
|
|
ca7f9662e2 | ||
|
|
b56d235233 | ||
|
|
60b4e9a0d7 | ||
|
|
0e5d6d1252 | ||
|
|
4da35f91d9 | ||
|
|
e6b17adf3a | ||
|
|
d22a2995a3 | ||
|
|
32dd187421 | ||
|
|
2bdedfed25 | ||
|
|
9d967277cd | ||
|
|
d925a0a1b2 | ||
|
|
b1135371ac | ||
|
|
40bf89df32 | ||
|
|
84d23af453 | ||
|
|
4e28dbbf09 | ||
|
|
5ce1b5acb3 | ||
|
|
0c31df6630 | ||
|
|
709ab337f1 | ||
|
|
9a71b203af | ||
|
|
88b2f6e94e | ||
|
|
601034adfa | ||
|
|
919ffd4430 | ||
|
|
f681ce9036 | ||
|
|
b194fcbe2c | ||
|
|
b3f9fd9a71 | ||
|
|
3e84b28154 | ||
|
|
61c96dbc67 | ||
|
|
9147b69c3a | ||
|
|
2ecc7ff340 | ||
|
|
991310be92 | ||
|
|
7359e05412 | ||
|
|
f5133aa8e3 | ||
|
|
35306423b5 | ||
|
|
6283c10cd7 | ||
|
|
69f95ee396 | ||
|
|
559b64b3ac | ||
|
|
4747ee35be | ||
|
|
ff84208703 | ||
|
|
1f1df02356 | ||
|
|
143585bd39 | ||
|
|
59347a3de1 | ||
|
|
ac25174a95 | ||
|
|
b6b42bad9d | ||
|
|
ffffd5751d | ||
|
|
fc867d38e6 | ||
|
|
9c98602f9a | ||
|
|
cc278a3a6e | ||
|
|
2dd86456cf | ||
|
|
e43d2db0d6 | ||
|
|
33a8406bd3 | ||
|
|
66be97ee47 | ||
|
|
45e44fd8bc | ||
|
|
479b3854ba | ||
|
|
0293c2a3a8 | ||
|
|
a10f4455f7 | ||
|
|
a586a34f36 | ||
|
|
fcf3f83737 | ||
|
|
24adf22c53 | ||
|
|
0e4d0f6216 | ||
|
|
3e4de7df52 | ||
|
|
ed130b1bac | ||
|
|
96e73c89d8 | ||
|
|
b8fc1a70ed | ||
|
|
6175104b0e | ||
|
|
d6400c573a | ||
|
|
fe7f458a2f | ||
|
|
a96611a68c | ||
|
|
aa82a26e1d | ||
|
|
dfcfabec66 | ||
|
|
04e74cba01 | ||
|
|
7542587558 | ||
|
|
5a83175d96 | ||
|
|
e4ceff01d4 | ||
|
|
4f43020509 | ||
|
|
e4d4b51962 | ||
|
|
ff30daa79a | ||
|
|
3d21e11360 | ||
|
|
4856af4916 | ||
|
|
345750ac61 | ||
|
|
81841ad35a | ||
|
|
82d1df7445 | ||
|
|
13fecec653 | ||
|
|
a4c87caa04 | ||
|
|
c397c797ed | ||
|
|
8eacc17ce4 | ||
|
|
475746e75d | ||
|
|
5c7e7074ba | ||
|
|
d225eaf624 | ||
|
|
1314db7295 | ||
|
|
b7b8c61d47 | ||
|
|
0672e7f37e | ||
|
|
7f67b1dbce | ||
|
|
368e787f89 | ||
|
|
de2cba940d | ||
|
|
bdc7a62c39 | ||
|
|
d9d904a5c8 | ||
|
|
7ee15b1ffd | ||
|
|
7f7117187e | ||
|
|
3b4c78ca4a | ||
|
|
0d62cbd7c5 | ||
|
|
21d6494923 | ||
|
|
a5d076decc | ||
|
|
02ce0bc8a5 | ||
|
|
fe5ca2614e | ||
|
|
066e1b39ab | ||
|
|
e1e9f886a3 | ||
|
|
66ea037add | ||
|
|
24c38894bb | ||
|
|
dc1e36bcd2 | ||
|
|
48d3fa1c11 | ||
|
|
88e487303d | ||
|
|
a6b29b854c | ||
|
|
e5e19f0a44 | ||
|
|
6e43281ee4 | ||
|
|
394f6b8170 | ||
|
|
b8e9683c81 | ||
|
|
ca3b71d2b8 | ||
|
|
5dfd76427b | ||
|
|
e421b6f896 | ||
|
|
bb144185a1 | ||
|
|
947375ba52 | ||
|
|
c02fba3719 | ||
|
|
30d02787a6 | ||
|
|
2a360a8113 | ||
|
|
e87687ea04 | ||
|
|
9cafff14c6 | ||
|
|
debae7c6a1 | ||
|
|
11e3f522b3 | ||
|
|
5a3304d006 | ||
|
|
515a3073d5 | ||
|
|
9d8168dbf5 |
@@ -1,5 +1,5 @@
|
||||
name: Bug report
|
||||
description: The type of device you were playing on
|
||||
description: An issue with the game.
|
||||
labels: ["bug"]
|
||||
|
||||
body:
|
||||
@@ -43,17 +43,17 @@ body:
|
||||
id: mods
|
||||
attributes:
|
||||
label: Mods used
|
||||
description: The mod repositories or zip files that are related to the issue, if applicable.
|
||||
description: The mod repositories or zip files that are related to the issue, if applicable. If you are positive that you have none enabled, write 'none'.
|
||||
validations:
|
||||
required: false
|
||||
required: true
|
||||
- type: textarea
|
||||
id: save-file
|
||||
attributes:
|
||||
label: Save file
|
||||
description: The (zipped) save file you were playing on when the bug happened. If this happened in the campaign, specify the sector, and attach the file you get from Settings -> Game Data -> Export Data. For custom games, attach the .msav file exported from the save dialog, zipped.
|
||||
placeholder: THIS IS REQUIRED FOR ANY ISSUE HAPPENING IN-GAME OR IN MULTIPLAYER, REGARDLESS OF WHETHER YOU THINK IT HAPPENS EVERYWHERE. DO NOT OMIT THIS LINE UNLESS YOU ARE SURE THAT THE ISSUE DOES NOT HAPPEN IN-GAME. IF YOU DO NOT HAVE A SAVE, DON'T WASTE TIME OPENING THIS ISSUE.
|
||||
placeholder: THIS IS REQUIRED FOR ANY IN-GAME ISSUE. IF YOU DO NOT HAVE A SAVE, DO NOT WASTE TIME OPENING THIS ISSUE.
|
||||
validations:
|
||||
required: false
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
@@ -72,5 +72,9 @@ body:
|
||||
required: true
|
||||
- label: I have searched the closed and open issues to make sure that this problem has not already been reported.
|
||||
required: true
|
||||
- label: "I am not using Foo's Client, and have made sure the bug is not caused by mods I have installed."
|
||||
- label: "I am not using Foo's Client."
|
||||
required: true
|
||||
- label: "This issue occurs with no mods enabled."
|
||||
required: true
|
||||
- label: "I have attached a valid save file."
|
||||
required: true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
If your pull request is **not** translation or serverlist-related, read the list of requirements below and check each box:
|
||||
If your pull request is **not** translation-related, read the list of requirements below and check each box:
|
||||
|
||||
- [ ] I have read the [contribution guidelines](https://github.com/Anuken/Mindustry/blob/master/CONTRIBUTING.md).
|
||||
- [ ] I have ensured that my code compiles, if applicable.
|
||||
- [ ] I have ensured that any new features in this PR function correctly in-game, if applicable.
|
||||
- [ ] I have ensured that any new features or fixes in this PR function correctly in-game, if applicable.
|
||||
- [ ] I affirm that this code follows Mindustry's [AI policy](https://github.com/Anuken/Mindustry/blob/master/CONTRIBUTING.md#ai-policy).
|
||||
@@ -0,0 +1,8 @@
|
||||
# Container image that runs your code
|
||||
FROM ubuntu:18.04
|
||||
|
||||
# Copies your code file from your action repository to the filesystem path `/` of the container
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
|
||||
# Code file to execute when the docker container starts up (`entrypoint.sh`)
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
@@ -0,0 +1,5 @@
|
||||
name: 'Build Linux Natives in Docker Container'
|
||||
description: 'Build Linux Natives in Ubuntu 18.04 docker container'
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
@@ -0,0 +1,41 @@
|
||||
#!/bin/sh -l
|
||||
|
||||
# ubuntu dockerfile is very minimal (only 122 packages are installed)
|
||||
# need to install updated git (from official git ppa)
|
||||
apt-get -q update
|
||||
apt-get -yq install software-properties-common
|
||||
add-apt-repository ppa:git-core/ppa -y
|
||||
# install dependencies expected by other steps
|
||||
apt-get -q update
|
||||
apt-get -yq install git \
|
||||
curl \
|
||||
ca-certificates \
|
||||
wget \
|
||||
bzip2 \
|
||||
zip \
|
||||
unzip \
|
||||
xz-utils \
|
||||
sudo gnupg locales
|
||||
|
||||
# set Locale to en_US.UTF-8 (avoids hang during compilation)
|
||||
locale-gen en_US.UTF-8
|
||||
export LANG=en_US.UTF-8
|
||||
export LANGUAGE=en_US.UTF-8
|
||||
export LC_ALL=en_US.UTF-8
|
||||
|
||||
# add zulu apt repository - https://docs.azul.com/core/install/debian
|
||||
curl -s https://repos.azul.com/azul-repo.key | gpg --dearmor -o /usr/share/keyrings/azul.gpg
|
||||
echo "deb [signed-by=/usr/share/keyrings/azul.gpg] https://repos.azul.com/zulu/deb stable main" | tee /etc/apt/sources.list.d/zulu.list
|
||||
apt-get -q update
|
||||
# install zulu JDK and Java build tools
|
||||
apt-get -yq install zulu17-jdk-headless
|
||||
|
||||
# Install cross-compilation toolchains
|
||||
apt-get -yq --force-yes install gcc g++
|
||||
apt-get -yq --force-yes install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||
apt-get -yq --force-yes install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf libc6-dev-armhf-cross
|
||||
apt-get -yq --force-yes install gcc-riscv64-linux-gnu g++-riscv64-linux-gnu libc6-dev-riscv64-cross
|
||||
|
||||
# Build Linux natives
|
||||
cd arc
|
||||
./gradlew arc-core:jnigenBuildAllLinux --no-daemon
|
||||
@@ -14,40 +14,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v7
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v1
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Set env
|
||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
- name: Add Arc release
|
||||
run: |
|
||||
git config --global user.email "actions@github.com"
|
||||
git config --global user.name "Github Actions"
|
||||
git clone --depth=1 --branch=master https://github.com/Anuken/Arc ../Arc
|
||||
cd ../Arc
|
||||
git tag ${RELEASE_VERSION}
|
||||
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/Arc ${RELEASE_VERSION};
|
||||
cd ../Mindustry
|
||||
- name: Update JITpack repo
|
||||
run: |
|
||||
cd ../
|
||||
cp -r ./Mindustry ./MindustryJitpack
|
||||
cd MindustryJitpack
|
||||
git config --global user.name "Github Actions"
|
||||
git config --global user.email "actions@github.com"
|
||||
git clone --depth 1 https://github.com/Anuken/MindustryJitpack.git
|
||||
rm -rf .git
|
||||
cp -r ./MindustryJitpack/.git ./.git
|
||||
rm -rf MindustryJitpack
|
||||
rm -rf .github
|
||||
rm README.md
|
||||
git add .
|
||||
git commit --allow-empty -m "Updating"
|
||||
git tag ${RELEASE_VERSION}
|
||||
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryJitpack ${RELEASE_VERSION}
|
||||
cd ../Mindustry
|
||||
- name: Create artifacts
|
||||
run: |
|
||||
./gradlew desktop:dist server:dist core:depsJar core:assetsJar core:mergedJavadoc -Pbuildversion=${RELEASE_VERSION:1}
|
||||
|
||||
@@ -6,5 +6,5 @@ jobs:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/wrapper-validation-action@v2
|
||||
- uses: actions/checkout@v7
|
||||
- uses: gradle/actions/wrapper-validation@v3
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
name: Build Natives
|
||||
|
||||
on: [workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
natives-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Build Linux Natives in Docker Container
|
||||
uses: ./.github/workflows/build-linux-natives
|
||||
id: docker
|
||||
|
||||
- name: Upload natives
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: Linux natives
|
||||
path: |
|
||||
arc/arc-core/build/natives
|
||||
|
||||
natives-windows-android:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Install dependencies
|
||||
run: sudo apt install g++-mingw-w64 g++-mingw-w64-i686 g++-mingw-w64-x86-64 gcc-mingw-w64 gcc-mingw-w64-i686 gcc-mingw-w64-x86-64
|
||||
- name: Set env
|
||||
run: echo "NDK_HOME=$ANDROID_NDK_HOME" >> $GITHUB_ENV
|
||||
- name: Build natives
|
||||
working-directory: arc
|
||||
run: |
|
||||
./gradlew arc-core:jnigenBuildAllWindows arc-core:jnigenBuildAllAndroid
|
||||
- name: Upload arc-core natives
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: Windows and Android natives
|
||||
path: |
|
||||
arc/arc-core/build/natives
|
||||
natives-mac:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Build natives
|
||||
working-directory: arc
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: '10.9'
|
||||
run: |
|
||||
./gradlew arc-core:jnigenBuildAllMacOsX
|
||||
- name: Upload natives
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: MacOS natives
|
||||
path: |
|
||||
arc/arc-core/build/natives
|
||||
@@ -10,21 +10,25 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v7
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v1
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Fetch latest Arc
|
||||
run: git clone --depth=1 --branch=master https://github.com/Anuken/Arc ../Arc
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
- name: Install client test dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y xvfb libgl1-mesa-dri libglx-mesa0
|
||||
- name: Run unit tests
|
||||
run: ./gradlew tests:test --stacktrace --rerun
|
||||
- name: Run unit tests and build JAR
|
||||
- name: Run client tests
|
||||
run: ./gradlew pack tests:clientTest
|
||||
- name: Build JAR
|
||||
run: ./gradlew desktop:dist
|
||||
- name: Upload desktop JAR for testing
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: Desktop JAR (zipped)
|
||||
name: Desktop JAR
|
||||
archive: false
|
||||
path: desktop/build/libs/Mindustry.jar
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v7
|
||||
- name: Trigger BE build
|
||||
if: ${{ github.repository == 'Anuken/Mindustry' }}
|
||||
run: |
|
||||
@@ -22,17 +22,16 @@ jobs:
|
||||
git config --global user.name "Github Actions"
|
||||
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryBuilds ${BNUM}
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v1
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Fetch latest Arc
|
||||
run: git clone --depth=1 --branch=master https://github.com/Anuken/Arc ../Arc
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
- name: Update bundles
|
||||
if: ${{ github.repository == 'Anuken/Mindustry' }}
|
||||
run: |
|
||||
./gradlew updateBundles
|
||||
./gradlew -DcompileMethodParams updateBundles
|
||||
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
git config --global user.name "Github Actions"
|
||||
@@ -41,35 +40,22 @@ jobs:
|
||||
git commit -m "Automatic bundle update"
|
||||
git push
|
||||
fi
|
||||
- name: Update scripts and class mappings
|
||||
- name: Update scripts, class mappings and map names
|
||||
if: ${{ github.repository == 'Anuken/Mindustry' }}
|
||||
run: |
|
||||
./gradlew updateScripts
|
||||
./gradlew -DcompileMethodParams updateScripts fixMaps
|
||||
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
git config --global user.name "Github Actions"
|
||||
git config --global user.email "actions@github.com"
|
||||
git add core/assets/scripts/* core/src/mindustry/mod/ClassMap.java
|
||||
git commit -m "Automatic class mapping update"
|
||||
git add core/assets/maps/*
|
||||
git commit -m "Automatic class mapping and map name update"
|
||||
git push
|
||||
fi
|
||||
- name: Update JITpack repo
|
||||
if: ${{ github.repository == 'Anuken/Mindustry' }}
|
||||
run: |
|
||||
git config --global user.name "Github Actions"
|
||||
git config --global user.email "actions@github.com"
|
||||
cd ../
|
||||
cp -r ./Mindustry ./MindustryJitpack
|
||||
cd MindustryJitpack
|
||||
git clone --depth 1 https://github.com/Anuken/MindustryJitpack.git
|
||||
rm -rf .git
|
||||
cp -r ./MindustryJitpack/.git ./.git
|
||||
rm -rf MindustryJitpack
|
||||
rm -rf .github
|
||||
rm README.md
|
||||
git add .
|
||||
git commit --allow-empty -m "Updating"
|
||||
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryJitpack
|
||||
cd ../Mindustry
|
||||
- name: Run unit tests
|
||||
run: ./gradlew tests:test --rerun --stacktrace
|
||||
- name: Install client test dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y xvfb libgl1-mesa-dri libglx-mesa0
|
||||
- name: Run client tests
|
||||
run: ./gradlew pack tests:clientTest
|
||||
|
||||
@@ -4,9 +4,6 @@ This is for code contributions. For translations, see [TRANSLATING](TRANSLATING.
|
||||
|
||||
## Basic Guidelines
|
||||
|
||||
### Use an IDE.
|
||||
Specifically, IntelliJ IDEA. Download the (free) Community Edition of it [here](https://www.jetbrains.com/idea/download/). Some people use other tools, like VS Code, but I would personally not recommend them for Java development.
|
||||
|
||||
### Always test your changes.
|
||||
Do not submit something without at least running the game to see if it compiles.
|
||||
If you are submitting a new block, make sure it has a name and description, and that it works correctly in-game. If you are changing existing block mechanics, test them out first.
|
||||
@@ -20,11 +17,19 @@ Yes, there are occurrences of trailing spaces, extra newlines, empty indents, an
|
||||
|
||||
I **especially** do not want to see PRs that apply any kind of automated analysis to the source code to "optimize" anything - my IDE can do that already. If the PR doesn't actually change anything useful, I'm not going to review or merge it.
|
||||
|
||||
### Do not make AI "contributions".
|
||||
## AI Policy
|
||||
|
||||
If I see a PR with significant amounts of code that's obviously written by AI, I will reject your PR, and you will be blocked. Don't waste my time with slop.
|
||||
Mindustry's AI contribution policies are similar to that of [the Rust language](https://forge.rust-lang.org/policies/llm-usage.html), which are summarized as follows:
|
||||
|
||||
Asking AI questions, and using that information to help you write code? Fine. Using it to actually write code? No.
|
||||
- ✔️ You **are** allowed use AI to review your own code, summarize, inform, and answer questions.
|
||||
- ✔️ You **are** allowed to use AI to translate your *human-written* messages into English, if necessary.
|
||||
|
||||
- ❌ You are **not** allowed to submit PRs that are generated by AI.
|
||||
- ❌ You are **not** allowed to use AI to *generate* any text posted (PRs, issues, commit messages, etc.)
|
||||
|
||||
- ⚠️ If you post *anything* that is based on AI output (potential solution to a bug, crash diagnosis...), it must be clearly marked as being sourced from AI.
|
||||
|
||||
Repeated (or particularly egregious) violations of these rules will get you banned from this repository.
|
||||
|
||||
## Style Guidelines
|
||||
|
||||
@@ -34,7 +39,7 @@ This means:
|
||||
- Same-line braces.
|
||||
- 4 spaces indentation
|
||||
- `camelCase`, **even for constants or enums**. Why? Because `SCREAMING_CASE` is ugly, annoying to type and does not achieve anything useful. Constants are *less* dangerous than variables, not more. Any reasonable IDE should highlight them for you anyway.
|
||||
- No underscores for anything. (Yes, I know `Bindings` violates this principle, but that's for legacy reasons and really should be cleaned up some day)
|
||||
- No underscores for anything.
|
||||
- Do not use braceless `if/else` statements. `if(x) statement else statement2` should **never** be done. In very specific situations, having braceless if-statements on one line is allowed: `if(cond) return;` would be valid.
|
||||
- Prefer single-line javadoc `/** @return for example */` instead of multiline javadoc whenever possible
|
||||
- Short method/variable names (multipleLongWords should be avoided if it's possible to do so reasonably, especially for variables)
|
||||
@@ -67,7 +72,6 @@ What you'll usually need to change:
|
||||
### Avoid boxed types (Integer, Boolean)
|
||||
Never create variables or collections with boxed types `Seq<Integer>` or `ObjectMap<Integer, ...>`. Use the collections specialized for this task, e.g. `IntSeq` and `IntMap`.
|
||||
|
||||
|
||||
### Do not allocate anything if possible.
|
||||
Never allocate `new` objects in the main loop. If you absolutely require new objects, use `Pools` to obtain and free object instances.
|
||||
Otherwise, use the `Tmp` variables for things like vector/shape operations, or create `static` variables for re-use.
|
||||
@@ -77,9 +81,13 @@ If using a list, make it a static variable and clear it every time it is used. R
|
||||
This is situational, but in essence, what it means is to avoid using any sort of getters and setters unless absolutely necessary. Public or protected fields should suffice for most things.
|
||||
If something needs to be encapsulated in the future, IntelliJ can handle it with a few clicks.
|
||||
|
||||
|
||||
### Do not create methods unless necessary.
|
||||
Unless a block of code is very large or used in more than 1-2 places, don't split it up into a separate method. Making unnecessary methods only creates confusion, and may slightly decrease performance.
|
||||
|
||||
## Other Notes
|
||||
If you would like your name to appear in the game's credits, add it to the [list of contributors](https://github.com/Anuken/Mindustry/blob/master/core/assets/contributors) as part of your PR.
|
||||
|
||||
- If you would like your name to appear in the game's credits, add it to the [list of contributors](https://github.com/Anuken/Mindustry/blob/master/core/assets/contributors) as part of your PR.
|
||||
|
||||
- Using an IDE is highly recommended, but not required.
|
||||
- I use IntelliJ; download it [here](https://www.jetbrains.com/idea/download/).
|
||||
- Some people use other tools, like VS Code, but I haven't used them for Java myself, and cannot guarantee that they will work well for this codebase.
|
||||
@@ -1,3 +0,0 @@
|
||||
# Note: The v7 server list is frozen. No new servers will be accepted. All v8 server PRs should be made [here](https://github.com/Anuken/MindustryServerList).
|
||||
|
||||
*PRs to edit addresses of existing servers will still be accepted, although very infrequently.*
|
||||
@@ -1,13 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
|
||||
<uses-feature android:glEsVersion="0x00030000" android:required="true"/>
|
||||
<uses-feature android:name="android.hardware.type.pc" android:required="false" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<application
|
||||
android:enableOnBackInvokedCallback="false"
|
||||
android:resizeableActivity="true"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
@@ -24,13 +25,40 @@
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="user"
|
||||
android:exported="true"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout">
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="uiMode|keyboard|keyboardHidden|orientation|screenSize|screenLayout">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="mindustry"/>
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="content" />
|
||||
<data android:mimeType="application/octet-stream" />
|
||||
<data android:mimeType="application/x-msav" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="file" />
|
||||
<data android:host="*" />
|
||||
<data android:mimeType="*/*" />
|
||||
<data android:pathPattern=".*\\.msav" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
buildscript{
|
||||
repositories{
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
|
||||
dependencies{
|
||||
classpath 'com.android.tools.build:gradle:8.2.2'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: "com.android.application"
|
||||
|
||||
configurations{ natives }
|
||||
|
||||
repositories{
|
||||
mavenCentral()
|
||||
maven{ url = "https://maven.google.com" }
|
||||
}
|
||||
|
||||
task deploy(type: Copy){
|
||||
dependsOn "assembleRelease"
|
||||
|
||||
from "build/outputs/apk/release/android-release.apk"
|
||||
into "../deploy/"
|
||||
rename("android-release.apk", "${generateDeployName('android')}.apk")
|
||||
}
|
||||
|
||||
android{
|
||||
namespace = "io.anuke.mindustry"
|
||||
buildToolsVersion = '36.0.0'
|
||||
compileSdk = 36
|
||||
sourceSets{
|
||||
main{
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = ['src']
|
||||
aidl.srcDirs = ['src']
|
||||
renderscript.srcDirs = ['src']
|
||||
res.srcDirs = ['res']
|
||||
assets.srcDirs = ['assets', 'src/main/assets', '../core/assets/']
|
||||
|
||||
if(localArc){
|
||||
jniLibs.srcDirs += ['../../Arc/natives/natives-android/libs', '../../Arc/natives/natives-freetype-android/libs']
|
||||
}else{
|
||||
jniLibs.srcDirs = ['libs']
|
||||
}
|
||||
}
|
||||
|
||||
androidTest.setRoot('tests')
|
||||
}
|
||||
|
||||
packagingOptions{
|
||||
exclude 'META-INF/robovm/ios/robovm.xml'
|
||||
}
|
||||
|
||||
defaultConfig{
|
||||
Properties props = loadVersionProps()
|
||||
Integer vcode = props['androidBuildCode']?.toInteger() ?: 1
|
||||
def versionNameResult = "$versionNumber-$versionType-${getBuildVersion().replace(" ", "-")}"
|
||||
|
||||
applicationId "io.anuke.mindustry"
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 36
|
||||
|
||||
versionName versionNameResult
|
||||
versionCode = vcode
|
||||
|
||||
if(project.hasProperty("release")){
|
||||
props['androidBuildCode'] = (vcode + 1).toString()
|
||||
}
|
||||
props.store(file('../core/assets/version.properties').newWriter(), null)
|
||||
|
||||
multiDexEnabled = true
|
||||
}
|
||||
|
||||
compileOptions{
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
flavorDimensions = ["google"]
|
||||
|
||||
signingConfigs{
|
||||
release{
|
||||
if(project.hasProperty("RELEASE_STORE_FILE")){
|
||||
storeFile file(RELEASE_STORE_FILE)
|
||||
storePassword RELEASE_STORE_PASSWORD
|
||||
keyAlias RELEASE_KEY_ALIAS
|
||||
keyPassword RELEASE_KEY_PASSWORD
|
||||
}else if(System.getenv("CI") == "true"){
|
||||
storeFile = file("../../bekeystore.jks")
|
||||
storePassword = System.getenv("keystore_password")
|
||||
keyAlias = System.getenv("keystore_alias")
|
||||
keyPassword = System.getenv("keystore_alias_password")
|
||||
}else{
|
||||
println("No keystore property found. Releases will be unsigned.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes{
|
||||
all{
|
||||
//TODO without these lines (r8 enabled), Mindustry crashes with missing default interface method errors.
|
||||
//WHY THE HELL ARE DEFAULT INTERFACES NOT BEING DESUGARED? WHY DID UPDATING AGP MAKE THIS HAPPEN?
|
||||
//When I ENABLE shrinking, r8 goes and REMOVES ALL DEFAULT INTERFACE CLASSES, which breaks mods. Why?
|
||||
//-keep class mindustry.** { *; } should *keep the classes* - WHY IS R8 REMOVING THEM?
|
||||
minifyEnabled = true
|
||||
shrinkResources = true
|
||||
proguardFiles("proguard-rules.pro")
|
||||
}
|
||||
}
|
||||
|
||||
if(project.hasProperty("RELEASE_STORE_FILE") || System.getenv("CI") == "true"){
|
||||
buildTypes{
|
||||
release{
|
||||
signingConfig = signingConfigs.release
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies{
|
||||
implementation project(":core")
|
||||
|
||||
implementation arcModule("backends:backend-android")
|
||||
implementation 'com.jakewharton.android.repackaged:dalvik-dx:9.0.0_r3'
|
||||
|
||||
if(!localArc){
|
||||
natives "com.github.Anuken.Arc:natives-android:$arcHash"
|
||||
natives "com.github.Anuken.Arc:natives-freetype-android:$arcHash"
|
||||
}
|
||||
|
||||
def version, highestVersion
|
||||
new File((String)findSdkDir(), "/platforms").eachFileMatch ~/android-\d+/, {
|
||||
version = it.name.find(/\d+/).toInteger()
|
||||
highestVersion = version > highestVersion ? version : highestVersion
|
||||
}
|
||||
|
||||
def sdkFile = new File((String)findSdkDir(), "/platforms/android-${highestVersion}/android.jar")
|
||||
if(sdkFile.exists()) compileOnly files(sdkFile.absolutePath)
|
||||
}
|
||||
|
||||
task copyAndroidNatives(){
|
||||
if(!localArc){
|
||||
configurations.natives.files.each{ jar ->
|
||||
copy{
|
||||
from zipTree(jar)
|
||||
into file("libs/")
|
||||
include "**"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task run(type: Exec){
|
||||
commandLine "${findSdkDir()}/platform-tools/adb", 'shell', 'am', 'start', '-n', 'io.anuke.mindustry/mindustry.android.AndroidLauncher'
|
||||
}
|
||||
|
||||
if(!project.ext.hasSprites()){
|
||||
tasks.whenTaskAdded{ task ->
|
||||
if(task.name == 'assembleDebug' || task.name == 'assembleRelease'){
|
||||
task.dependsOn ":tools:pack"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import java.util.*
|
||||
|
||||
plugins{
|
||||
id("com.android.application")
|
||||
}
|
||||
|
||||
evaluationDependsOn(":core")
|
||||
|
||||
val versionNumber = providers.gradleProperty("versionNumber").get()
|
||||
val versionType = providers.gradleProperty("versionType").get()
|
||||
val buildVersion = rootProject.extra["buildVersion"] as String
|
||||
val hasSprites = rootProject.extra["hasSprites"] as Boolean
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val deployName = rootProject.extra["deployName"] as (String) -> String
|
||||
|
||||
val versionPropsFile = rootProject.file("core/assets/version.properties")
|
||||
|
||||
fun loadVersionProps(): Properties =
|
||||
Properties().also{ props -> versionPropsFile.bufferedReader().use{ props.load(it) } }
|
||||
|
||||
fun findSdkDir(): String?{
|
||||
System.getenv("ANDROID_HOME")?.let{ return it }
|
||||
val props = Properties().also{ p -> File(rootDir, "local.properties").bufferedReader().use{ p.load(it) } }
|
||||
return props.getProperty("sdk.dir")
|
||||
}
|
||||
|
||||
repositories{
|
||||
google()
|
||||
}
|
||||
|
||||
fun MutableCollection<String>.only(vararg paths: String){
|
||||
clear()
|
||||
addAll(paths)
|
||||
}
|
||||
|
||||
tasks.register<Copy>("deploy"){
|
||||
dependsOn("assembleRelease")
|
||||
|
||||
from("build/outputs/apk/release/android-release.apk")
|
||||
into("../deploy/")
|
||||
rename("android-release.apk", "${deployName("android")}.apk")
|
||||
}
|
||||
|
||||
android{
|
||||
namespace = "io.anuke.mindustry"
|
||||
compileSdk = 36
|
||||
|
||||
sourceSets{
|
||||
getByName("main"){
|
||||
manifest.srcFile("AndroidManifest.xml")
|
||||
java.directories.only("src")
|
||||
aidl.directories.only("src")
|
||||
renderscript.directories.only("src")
|
||||
res.directories.only("res")
|
||||
assets.directories.only("assets", "src/main/assets", "../core/assets/")
|
||||
jniLibs.directories.only("../arc/natives/natives-android/libs")
|
||||
}
|
||||
|
||||
getByName("androidTest").setRoot("tests")
|
||||
}
|
||||
|
||||
packaging{
|
||||
resources{
|
||||
excludes += "META-INF/robovm/ios/robovm.xml"
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig{
|
||||
val props = loadVersionProps()
|
||||
val vcode = props.getProperty("androidBuildCode")?.toInt() ?: 1
|
||||
val versionNameResult = "$versionNumber-$versionType-${buildVersion.replace(" ", "-")}"
|
||||
|
||||
applicationId = "io.anuke.mindustry"
|
||||
minSdk = 21
|
||||
targetSdk = 36
|
||||
|
||||
versionName = versionNameResult
|
||||
versionCode = vcode
|
||||
|
||||
if(project.hasProperty("release")){
|
||||
props["androidBuildCode"] = (vcode + 1).toString()
|
||||
}
|
||||
versionPropsFile.bufferedWriter().use{ props.store(it, null) }
|
||||
|
||||
multiDexEnabled = true
|
||||
}
|
||||
|
||||
compileOptions{
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
signingConfigs{
|
||||
create("release"){
|
||||
if(project.hasProperty("RELEASE_STORE_FILE")){
|
||||
storeFile = file(project.property("RELEASE_STORE_FILE") as String)
|
||||
storePassword = project.property("RELEASE_STORE_PASSWORD") as String
|
||||
keyAlias = project.property("RELEASE_KEY_ALIAS") as String
|
||||
keyPassword = project.property("RELEASE_KEY_PASSWORD") as String
|
||||
}else if(System.getenv("CI") == "true"){
|
||||
storeFile = file("../../bekeystore.jks")
|
||||
storePassword = System.getenv("keystore_password")
|
||||
keyAlias = System.getenv("keystore_alias")
|
||||
keyPassword = System.getenv("keystore_alias_password")
|
||||
}else{
|
||||
println("No keystore property found. Releases will be unsigned.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes{
|
||||
all{
|
||||
//TODO without these lines (r8 enabled), Mindustry crashes with missing default interface method errors.
|
||||
//WHY THE HELL ARE DEFAULT INTERFACES NOT BEING DESUGARED? WHY DID UPDATING AGP MAKE THIS HAPPEN?
|
||||
//When I ENABLE shrinking, r8 goes and REMOVES ALL DEFAULT INTERFACE CLASSES, which breaks mods. Why?
|
||||
//-keep class mindustry.** { *; } should *keep the classes* - WHY IS R8 REMOVING THEM?
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles("proguard-rules.pro")
|
||||
}
|
||||
|
||||
if(project.hasProperty("RELEASE_STORE_FILE") || System.getenv("CI") == "true"){
|
||||
getByName("release"){
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies{
|
||||
implementation(project(":core"))
|
||||
|
||||
implementation("com.github.Anuken:backend-android:frog")
|
||||
implementation("com.jakewharton.android.repackaged:dalvik-dx:9.0.0_r3")
|
||||
}
|
||||
|
||||
tasks.register<Exec>("run"){
|
||||
commandLine("${findSdkDir()}/platform-tools/adb", "shell", "am", "start", "-n", "io.anuke.mindustry/mindustry.android.AndroidLauncher")
|
||||
}
|
||||
|
||||
if(!hasSprites){
|
||||
tasks.configureEach{
|
||||
if(name == "assembleDebug" || name == "assembleRelease"){
|
||||
dependsOn(":tools:pack")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 28 KiB |
@@ -2,4 +2,4 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
</adaptive-icon>
|
||||
@@ -2,4 +2,4 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -16,8 +16,7 @@ import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import dalvik.system.*;
|
||||
import mindustry.*;
|
||||
import mindustry.game.Saves.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.net.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.FileChooser.*;
|
||||
@@ -173,6 +172,15 @@ public class AndroidLauncher extends AndroidApplication{
|
||||
throw new ArcRuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Writer writer(boolean append, String charset){
|
||||
try{
|
||||
return new OutputStreamWriter(write(append), charset);
|
||||
}catch(IOException ex){
|
||||
throw new ArcRuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}).toArray(Fi.class);
|
||||
|
||||
Core.app.post(() -> Core.app.post(() -> params.handleChooseResult(files)));
|
||||
@@ -211,9 +219,10 @@ public class AndroidLauncher extends AndroidApplication{
|
||||
}, new AndroidApplicationConfiguration(){{
|
||||
useImmersiveMode = true;
|
||||
hideStatusBar = true;
|
||||
useGL30 = true;
|
||||
}});
|
||||
checkFiles(getIntent());
|
||||
|
||||
var intent = getIntent();
|
||||
Events.on(ClientLoadEvent.class, u -> handleIntent(intent));
|
||||
|
||||
try{
|
||||
//new external folder
|
||||
@@ -271,54 +280,45 @@ public class AndroidLauncher extends AndroidApplication{
|
||||
}
|
||||
}
|
||||
|
||||
private void checkFiles(Intent intent){
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent){
|
||||
super.onNewIntent(intent);
|
||||
|
||||
handleIntent(intent);
|
||||
}
|
||||
|
||||
private void handleIntent(Intent intent){
|
||||
if(intent == null) return;
|
||||
|
||||
try{
|
||||
Uri uri = intent.getData();
|
||||
if(uri != null){
|
||||
File myFile = null;
|
||||
String scheme = uri.getScheme();
|
||||
if(scheme.equals("file")){
|
||||
String fileName = uri.getEncodedPath();
|
||||
myFile = new File(fileName);
|
||||
}else if(!scheme.equals("content")){
|
||||
//error
|
||||
return;
|
||||
}
|
||||
boolean save = uri.getPath().endsWith(saveExtension);
|
||||
boolean map = uri.getPath().endsWith(mapExtension);
|
||||
InputStream inStream;
|
||||
if(myFile != null) inStream = new FileInputStream(myFile);
|
||||
else inStream = getContentResolver().openInputStream(uri);
|
||||
Core.app.post(() -> Core.app.post(() -> {
|
||||
if(save){ //open save
|
||||
System.out.println("Opening save.");
|
||||
Fi file = Core.files.local("temp-save." + saveExtension);
|
||||
file.write(inStream, false);
|
||||
if(SaveIO.isSaveValid(file)){
|
||||
try{
|
||||
SaveSlot slot = control.saves.importSave(file);
|
||||
ui.load.runLoadSave(slot);
|
||||
}catch(IOException e){
|
||||
ui.showException("@save.import.fail", e);
|
||||
}
|
||||
}else{
|
||||
ui.showErrorMessage("@save.import.invalid");
|
||||
}
|
||||
}else if(map){ //open map
|
||||
Fi file = Core.files.local("temp-map." + mapExtension);
|
||||
file.write(inStream, false);
|
||||
|
||||
//clear data (not sure if necessary?)
|
||||
intent.setAction(Intent.ACTION_MAIN);
|
||||
intent.setData(null);
|
||||
setIntent(intent);
|
||||
|
||||
if("mindustry".equalsIgnoreCase(scheme)){ //open a server URL
|
||||
|
||||
String host = uri.getHost();
|
||||
int port = uri.getPort();
|
||||
|
||||
if(host != null && !host.isEmpty()){
|
||||
Core.app.post(() -> {
|
||||
System.out.println("Opening map.");
|
||||
if(!ui.editor.isShown()){
|
||||
ui.editor.show();
|
||||
}
|
||||
ui.editor.beginEditMap(file);
|
||||
ui.showConfirm(Core.bundle.format("servers.connect.confirm", host), () -> ui.join.connect(host, port != -1 ? port : 6567));
|
||||
});
|
||||
}
|
||||
}));
|
||||
}else{ //open a save file
|
||||
Fi file = Core.files.cache("imported");
|
||||
file.write(getContentResolver().openInputStream(uri), false);
|
||||
|
||||
ClientLauncher.handleFileImport(file);
|
||||
}
|
||||
}
|
||||
}catch(IOException e){
|
||||
e.printStackTrace();
|
||||
}catch(Throwable e){
|
||||
Log.err(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,7 @@ package mindustry.android;
|
||||
|
||||
import android.annotation.*;
|
||||
import android.os.*;
|
||||
import android.os.Build.*;
|
||||
import arc.*;
|
||||
import arc.backend.android.*;
|
||||
import com.android.dex.*;
|
||||
import com.android.dx.cf.direct.*;
|
||||
import com.android.dx.command.dexer.*;
|
||||
@@ -13,7 +11,6 @@ import com.android.dx.dex.cf.*;
|
||||
import com.android.dx.dex.file.DexFile;
|
||||
import com.android.dx.merge.*;
|
||||
import dalvik.system.*;
|
||||
import mindustry.mod.*;
|
||||
import rhino.*;
|
||||
|
||||
import java.io.*;
|
||||
@@ -159,8 +156,9 @@ public class AndroidRhinoContext{
|
||||
}catch(IOException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
android.content.Context context = (android.content.Context)((AndroidApplication)Core.app);
|
||||
return new DexClassLoader(dexFile.getPath(), VERSION.SDK_INT >= 21 ? context.getCodeCacheDir().getPath() : context.getCacheDir().getAbsolutePath(), null, getParent()).loadClass(name);
|
||||
|
||||
var context = (android.content.Context)Core.app;
|
||||
return new DexClassLoader(dexFile.getPath(), context.getCodeCacheDir().getPath(), null, getParent()).loadClass(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
sourceSets.main.java.srcDirs = ["src/main/java/"]
|
||||
sourceSets.main.resources.srcDirs = ["src/main/resources/"]
|
||||
@@ -0,0 +1,35 @@
|
||||
plugins{
|
||||
`java-library`
|
||||
}
|
||||
|
||||
sourceSets.main{
|
||||
java.setSrcDirs(listOf("src/main/java/"))
|
||||
resources.setSrcDirs(listOf("src/main/resources/"))
|
||||
}
|
||||
|
||||
dependencies{
|
||||
implementation("com.squareup:javapoet:1.13.0")
|
||||
implementation("com.github.Anuken:arc-core:frog")
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach{
|
||||
options.isFork = true
|
||||
|
||||
options.compilerArgs.addAll(
|
||||
listOf(
|
||||
"--add-exports", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
|
||||
"--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
|
||||
"--add-exports", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
|
||||
"--add-exports", "jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
|
||||
"--add-exports", "jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
|
||||
"--add-exports", "jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
|
||||
"--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
|
||||
"--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
|
||||
"--add-exports", "jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
|
||||
"--add-exports", "jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
|
||||
"--add-exports", "jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED",
|
||||
"--add-exports", "jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
|
||||
"--add-exports", "java.base/sun.reflect.annotation=ALL-UNNAMED"
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -85,6 +85,7 @@ public class Annotations{
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface GroupDef{
|
||||
Class[] value();
|
||||
Class[] exclude() default {};
|
||||
boolean collide() default false;
|
||||
boolean spatial() default false;
|
||||
boolean mapping() default false;
|
||||
|
||||
@@ -211,10 +211,7 @@ public abstract class BaseProcessor extends AbstractProcessor{
|
||||
if(round++ >= rounds) return false; //only process 1 round
|
||||
if(rootDirectory == null){
|
||||
try{
|
||||
String path = Fi.get(filer.getResource(StandardLocation.CLASS_OUTPUT, "no", "no")
|
||||
.toUri().toURL().toString().substring(OS.isWindows ? 6 : "file:".length()))
|
||||
.parent().parent().parent().parent().parent().parent().parent().toString().replace("%20", " ");
|
||||
rootDirectory = Fi.get(path).parent();
|
||||
rootDirectory = new Fi(new File(filer.getResource(StandardLocation.CLASS_OUTPUT, "no", "no").toUri())).parent().parent().parent().parent().parent().parent().parent().parent();
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -40,8 +40,6 @@ public class EntityIO{
|
||||
this.serializer = serializer;
|
||||
this.name = name;
|
||||
|
||||
json.setIgnoreUnknownFields(true);
|
||||
|
||||
directory.mkdirs();
|
||||
|
||||
//load old revisions
|
||||
@@ -236,11 +234,11 @@ public class EntityIO{
|
||||
|
||||
if(BaseProcessor.isPrimitive(type)){
|
||||
s(type.equals("boolean") ? "bool" : type.charAt(0) + "", field);
|
||||
}else if(instanceOf(type, "mindustry.ctype.Content") && !type.equals("mindustry.ai.UnitStance") && !type.equals("mindustry.ai.UnitCommand")){
|
||||
}else if(instanceOf(type, "mindustry.type.Content") && !type.equals("mindustry.ai.UnitStance") && !type.equals("mindustry.ai.UnitCommand")){
|
||||
if(write){
|
||||
s("s", field + " == null ? -1 : " + field + ".id");
|
||||
}else{
|
||||
st(field + "mindustry.Vars.content.getByID(mindustry.ctype.ContentType.$L, read.s())", BaseProcessor.simpleName(type).toLowerCase().replace("type", ""));
|
||||
st(field + "mindustry.Vars.content.getByID(mindustry.type.ContentType.$L, read.s())", BaseProcessor.simpleName(type).toLowerCase().replace("type", ""));
|
||||
}
|
||||
}else if((serializer.writers.containsKey(type) || (network && serializer.netWriters.containsKey(type))) && write){
|
||||
st("$L(write, $L)", network ? serializer.getNetWriter(type, null) : serializer.writers.get(type), field);
|
||||
|
||||
@@ -20,6 +20,7 @@ import javax.lang.model.element.*;
|
||||
import javax.lang.model.type.*;
|
||||
import java.lang.annotation.*;
|
||||
import java.util.*;
|
||||
import java.util.regex.*;
|
||||
|
||||
@SupportedAnnotationTypes({
|
||||
"mindustry.annotations.Annotations.EntityDef",
|
||||
@@ -29,6 +30,11 @@ import java.util.*;
|
||||
"mindustry.annotations.Annotations.TypeIOHandler"
|
||||
})
|
||||
public class EntityProcess extends BaseProcessor{
|
||||
static final Pattern selfParamPattern = Pattern.compile("this\\.<(.*)>self\\(\\)");
|
||||
static final Pattern selfPattern = Pattern.compile("self\\(\\)");
|
||||
static final Pattern yieldPattern = Pattern.compile(" yield ");
|
||||
static final Pattern missingPattern = Pattern.compile("\\/\\*missing\\*\\/");
|
||||
|
||||
Seq<EntityDefinition> definitions = new Seq<>();
|
||||
Seq<GroupDefinition> groupDefs = new Seq<>();
|
||||
Seq<Stype> baseComponents;
|
||||
@@ -77,12 +83,12 @@ public class EntityProcess extends BaseProcessor{
|
||||
for(Smethod elem : component.methods()){
|
||||
if(elem.is(Modifier.ABSTRACT) || elem.is(Modifier.NATIVE)) continue;
|
||||
//get all statements in the method, store them
|
||||
methodBlocks.put(elem.descString(), elem.tree().getBody().toString()
|
||||
.replaceAll("this\\.<(.*)>self\\(\\)", "this") //fix parameterized self() calls
|
||||
.replaceAll("self\\(\\)", "this") //fix self() calls
|
||||
.replaceAll(" yield ", "") //fix enchanced switch
|
||||
.replaceAll("\\/\\*missing\\*\\/", "var") //fix vars
|
||||
);
|
||||
String body = elem.tree().getBody().toString();
|
||||
body = selfParamPattern.matcher(body).replaceAll("this"); //fix parameterized self() calls
|
||||
body = selfPattern.matcher(body).replaceAll("this"); //fix self() calls
|
||||
body = yieldPattern.matcher(body).replaceAll(""); //fix enhanced switch
|
||||
body = missingPattern.matcher(body).replaceAll("var"); //fix vars
|
||||
methodBlocks.put(elem.descString(), body);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,15 +240,21 @@ public class EntityProcess extends BaseProcessor{
|
||||
return result;
|
||||
});
|
||||
|
||||
Seq<Stype> exclusions = types(an, GroupDef::exclude).map(stype -> {
|
||||
Stype result = interfaceToComp(stype);
|
||||
if(result == null) throw new IllegalArgumentException("Interface " + stype + " does not have an associated component!");
|
||||
return result;
|
||||
});
|
||||
|
||||
//representative component type
|
||||
Stype repr = types.first();
|
||||
String groupType = repr.annotation(Component.class).base() ? baseName(repr) : interfaceName(repr);
|
||||
|
||||
String name = group.name().startsWith("g") ? group.name().substring(1) : group.name();
|
||||
String name = group.name();
|
||||
|
||||
boolean collides = an.collide();
|
||||
groupDefs.add(new GroupDefinition(name,
|
||||
ClassName.bestGuess(packageName + "." + groupType), types, an.spatial(), an.mapping(), collides, an.update()));
|
||||
ClassName.bestGuess(packageName + "." + groupType), types, exclusions, an.spatial(), an.mapping(), collides, an.update()));
|
||||
|
||||
TypeSpec.Builder accessor = TypeSpec.interfaceBuilder("IndexableEntity__" + name);
|
||||
accessor.addMethod(MethodSpec.methodBuilder("setIndex__" + name).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC).addParameter(int.class, "index").returns(void.class).build());
|
||||
@@ -258,7 +270,9 @@ public class EntityProcess extends BaseProcessor{
|
||||
|
||||
//all component classes (not interfaces)
|
||||
Seq<Stype> components = allComponents(type);
|
||||
Seq<GroupDefinition> groups = groupDefs.select(g -> (!g.components.isEmpty() && !Structs.contains(ann.excludeGroups(), g.name) && !g.components.contains(s -> !components.contains(s))) || g.manualInclusions.contains(type));
|
||||
Seq<GroupDefinition> groups = groupDefs.select(g -> (!g.components.isEmpty() && !Structs.contains(ann.excludeGroups(), g.name)
|
||||
&& !g.components.contains(s -> !components.contains(s)) && !g.excludedComponents.contains(s -> components.contains(s))));
|
||||
|
||||
ObjectMap<String, Seq<Smethod>> methods = new ObjectMap<>();
|
||||
ObjectMap<FieldSpec, Svar> specVariables = new ObjectMap<>();
|
||||
ObjectSet<String> usedFields = new ObjectSet<>();
|
||||
@@ -489,10 +503,10 @@ public class EntityProcess extends BaseProcessor{
|
||||
for(GroupDefinition def : groups){
|
||||
if(first.name().equals("add")){
|
||||
//remove/add from each group, assume imported
|
||||
mbuilder.addStatement("index__$L = Groups.$L.addIndex(this)", def.name, def.name);
|
||||
mbuilder.addStatement("index__$L = mindustry.Vars.state.entities.$L.addIndex(this)", def.name, def.name);
|
||||
}else{
|
||||
//remove/add from each group, assume imported
|
||||
mbuilder.addStatement("Groups.$L.removeIndex(this, index__$L);", def.name, def.name);
|
||||
mbuilder.addStatement("mindustry.Vars.state.entities.$L.removeIndex(this, index__$L);", def.name, def.name);
|
||||
|
||||
mbuilder.addStatement("index__$L = -1", def.name);
|
||||
}
|
||||
@@ -558,7 +572,15 @@ public class EntityProcess extends BaseProcessor{
|
||||
String blockName = elem.up().getSimpleName().toString().toLowerCase().replace("comp", "");
|
||||
|
||||
//skip empty blocks
|
||||
if(str.replace("{", "").replace("\n", "").replace("}", "").replace("\t", "").replace(" ", "").isEmpty()){
|
||||
boolean empty = true;
|
||||
for(int i = 0; i < str.length(); i++){
|
||||
char c = str.charAt(i);
|
||||
if(c != '{' && c != '}' && c != '\n' && c != '\t' && c != ' '){
|
||||
empty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(empty){
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -582,7 +604,7 @@ public class EntityProcess extends BaseProcessor{
|
||||
//add free code to remove methods - always at the end
|
||||
//this only gets called next frame.
|
||||
if(first.name().equals("remove") && ann.pooled()){
|
||||
mbuilder.addStatement("mindustry.gen.Groups.queueFree(($T)this)", Poolable.class);
|
||||
mbuilder.addStatement("mindustry.Vars.state.entities.queueFree(($T)this)", Poolable.class);
|
||||
}
|
||||
|
||||
if(!legacy || specialIO){
|
||||
@@ -638,17 +660,17 @@ public class EntityProcess extends BaseProcessor{
|
||||
definitions.add(new EntityDefinition(packageName + "." + name, builder, type, typeIsBase ? null : baseClass, components, groups, allFieldSpecs, legacy));
|
||||
}
|
||||
|
||||
//generate groups
|
||||
TypeSpec.Builder groupsBuilder = TypeSpec.classBuilder("Groups").addModifiers(Modifier.PUBLIC);
|
||||
MethodSpec.Builder groupInit = MethodSpec.methodBuilder("init").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
//generate entities, the per-state container of all groups
|
||||
TypeSpec.Builder groupsBuilder = TypeSpec.classBuilder("Entities").addModifiers(Modifier.PUBLIC);
|
||||
MethodSpec.Builder groupInit = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC);
|
||||
for(GroupDefinition group : groupDefs){
|
||||
//class names for interface/group
|
||||
ClassName itype = group.baseType;
|
||||
ClassName groupc = ClassName.bestGuess("mindustry.entities.EntityGroup");
|
||||
TypeName groupType = ParameterizedTypeName.get(groupc, itype);
|
||||
|
||||
//add field...
|
||||
groupsBuilder.addField(ParameterizedTypeName.get(
|
||||
ClassName.bestGuess("mindustry.entities.EntityGroup"), itype), group.name, Modifier.PUBLIC, Modifier.STATIC);
|
||||
groupsBuilder.addField(groupType, group.name, Modifier.PUBLIC);
|
||||
|
||||
groupInit.addStatement("$L = new $T<>($L.class, $L, $L, (e, pos) -> { if(e instanceof $L.IndexableEntity__$L ix) ix.setIndex__$L(pos); })", group.name, groupc, itype, group.spatial, group.mapping, packageName, group.name, group.name);
|
||||
}
|
||||
@@ -656,9 +678,9 @@ public class EntityProcess extends BaseProcessor{
|
||||
//write the groups
|
||||
groupsBuilder.addMethod(groupInit.build());
|
||||
|
||||
groupsBuilder.addField(boolean.class, "isClearing", Modifier.PUBLIC, Modifier.STATIC);
|
||||
groupsBuilder.addField(boolean.class, "isClearing", Modifier.PUBLIC);
|
||||
|
||||
MethodSpec.Builder groupClear = MethodSpec.methodBuilder("clear").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
MethodSpec.Builder groupClear = MethodSpec.methodBuilder("clear").addModifiers(Modifier.PUBLIC);
|
||||
groupClear.addStatement("isClearing = true");
|
||||
for(GroupDefinition group : groupDefs){
|
||||
groupClear.addStatement("$L.clear()", group.name);
|
||||
@@ -669,11 +691,11 @@ public class EntityProcess extends BaseProcessor{
|
||||
groupsBuilder.addMethod(groupClear.build());
|
||||
|
||||
//add method for pool storage
|
||||
groupsBuilder.addField(FieldSpec.builder(ParameterizedTypeName.get(Seq.class, Poolable.class), "freeQueue", Modifier.PRIVATE, Modifier.STATIC).initializer("new Seq<>()").build());
|
||||
groupsBuilder.addField(FieldSpec.builder(ParameterizedTypeName.get(Seq.class, Poolable.class), "freeQueue", Modifier.PRIVATE).initializer("new Seq<>()").build());
|
||||
|
||||
//method for freeing things
|
||||
MethodSpec.Builder groupFreeQueue = MethodSpec.methodBuilder("queueFree")
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addParameter(Poolable.class, "obj")
|
||||
.addStatement("freeQueue.add(obj)");
|
||||
|
||||
@@ -682,16 +704,21 @@ public class EntityProcess extends BaseProcessor{
|
||||
//add method for resizing all necessary groups
|
||||
MethodSpec.Builder groupResize = MethodSpec.methodBuilder("resize")
|
||||
.addParameter(TypeName.FLOAT, "x").addParameter(TypeName.FLOAT, "y").addParameter(TypeName.FLOAT, "w").addParameter(TypeName.FLOAT, "h")
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
.addModifiers(Modifier.PUBLIC);
|
||||
|
||||
MethodSpec.Builder groupUpdate = MethodSpec.methodBuilder("update")
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
.addModifiers(Modifier.PUBLIC);
|
||||
|
||||
MethodSpec.Builder groupPoolUpdate = MethodSpec.methodBuilder("updatePooling")
|
||||
.addModifiers(Modifier.PUBLIC);
|
||||
|
||||
//free everything pooled at the start of each updaet
|
||||
groupUpdate
|
||||
groupPoolUpdate
|
||||
.addStatement("for($T p : freeQueue) $T.free(p)", Poolable.class, Pools.class)
|
||||
.addStatement("freeQueue.clear()");
|
||||
|
||||
groupUpdate.addStatement("updatePooling()");
|
||||
|
||||
//method resize
|
||||
for(GroupDefinition group : groupDefs){
|
||||
if(group.spatial){
|
||||
@@ -713,6 +740,7 @@ public class EntityProcess extends BaseProcessor{
|
||||
}
|
||||
|
||||
groupsBuilder.addMethod(groupResize.build());
|
||||
groupsBuilder.addMethod(groupPoolUpdate.build());
|
||||
groupsBuilder.addMethod(groupUpdate.build());
|
||||
|
||||
write(groupsBuilder);
|
||||
@@ -988,13 +1016,13 @@ public class EntityProcess extends BaseProcessor{
|
||||
class GroupDefinition{
|
||||
final String name;
|
||||
final ClassName baseType;
|
||||
final Seq<Stype> components;
|
||||
final Seq<Stype> components, excludedComponents;
|
||||
final boolean spatial, mapping, collides, updates;
|
||||
final ObjectSet<Selement> manualInclusions = new ObjectSet<>();
|
||||
|
||||
public GroupDefinition(String name, ClassName bestType, Seq<Stype> components, boolean spatial, boolean mapping, boolean collides, boolean updates){
|
||||
public GroupDefinition(String name, ClassName bestType, Seq<Stype> components, Seq<Stype> excludedComponents, boolean spatial, boolean mapping, boolean collides, boolean updates){
|
||||
this.baseType = bestType;
|
||||
this.components = components;
|
||||
this.excludedComponents = excludedComponents;
|
||||
this.name = name;
|
||||
this.spatial = spatial;
|
||||
this.mapping = mapping;
|
||||
|
||||
@@ -8,6 +8,7 @@ import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import arc.util.serialization.*;
|
||||
import arc.util.serialization.Jval.*;
|
||||
import com.squareup.javapoet.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.annotations.*;
|
||||
@@ -36,7 +37,7 @@ public class AssetsProcess extends BaseProcessor{
|
||||
MethodSpec.Builder icload = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
CodeBlock.Builder ichinit = CodeBlock.builder();
|
||||
String resources = rootDirectory + "/core/assets-raw/sprites/ui";
|
||||
Jval icons = Jval.read(Fi.get(rootDirectory + "/core/assets-raw/fontgen/config.json").readString());
|
||||
JsonArray icons = Jval.read(Fi.get(rootDirectory + "/core/assets-raw/icons/codepoints.json").readString()).asArray();
|
||||
|
||||
ObjectMap<String, String> texIcons = new OrderedMap<>();
|
||||
PropertiesUtils.load(texIcons, Fi.get(rootDirectory + "/core/assets/icons/icons.properties").reader());
|
||||
@@ -62,10 +63,10 @@ public class AssetsProcess extends BaseProcessor{
|
||||
|
||||
ObjectSet<String> used = new ObjectSet<>();
|
||||
|
||||
for(Jval val : icons.get("glyphs").asArray()){
|
||||
String name = capitalize(val.getString("css", ""));
|
||||
for(Jval val : icons){
|
||||
String name = capitalize(val.getString("name", ""));
|
||||
|
||||
if(!val.getBool("selected", true) || !used.add(name)) continue;
|
||||
if(!used.add(name)) continue;
|
||||
|
||||
int code = val.getInt("code", 0);
|
||||
iconcAll.append((char)code);
|
||||
|
||||
@@ -20,7 +20,7 @@ public class LoadRegionProcessor extends BaseProcessor{
|
||||
.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "\"deprecation\"").build())
|
||||
.addModifiers(Modifier.PUBLIC);
|
||||
MethodSpec.Builder method = MethodSpec.methodBuilder("loadRegions")
|
||||
.addParameter(tname("mindustry.ctype.MappableContent"), "content")
|
||||
.addParameter(tname("mindustry.type.MappableContent"), "content")
|
||||
.addModifiers(Modifier.STATIC, Modifier.PUBLIC);
|
||||
|
||||
ObjectMap<Stype, Seq<Svar>> fieldMap = new ObjectMap<>();
|
||||
|
||||
@@ -25,7 +25,7 @@ public class LogicStatementProcessor extends BaseProcessor{
|
||||
|
||||
MethodSpec.Builder reader = MethodSpec.methodBuilder("read")
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.returns(tname("mindustry.logic.LStatement"))
|
||||
.returns(tname("mindustry.logic.LogicStatement"))
|
||||
.addParameter(String[].class, "tokens")
|
||||
.addParameter(int.class, "length");
|
||||
|
||||
@@ -35,8 +35,8 @@ public class LogicStatementProcessor extends BaseProcessor{
|
||||
ParameterizedTypeName.get(
|
||||
ClassName.get(Seq.class),
|
||||
ParameterizedTypeName.get(ClassName.get(Prov.class),
|
||||
tname("mindustry.logic.LStatement"))), "allStatements", Modifier.PUBLIC, Modifier.STATIC)
|
||||
.initializer("Seq.with(" + types.toString(", ", t -> "" + t.toString() + "::new") + ")").build());
|
||||
tname("mindustry.logic.LogicStatement"))), "allStatements", Modifier.PUBLIC, Modifier.STATIC)
|
||||
.initializer("Seq.with(" + types.toString(", ", t -> t.toString() + "::new") + ")").build());
|
||||
|
||||
boolean beganWrite = false, beganRead = false;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ atrax=1
|
||||
beta=30
|
||||
block=2
|
||||
corvus=24
|
||||
dummy=49
|
||||
elude=45
|
||||
flare=3
|
||||
gamma=31
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:building,type:Building},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
|
||||
@@ -0,0 +1,65 @@
|
||||
# Temporary Files
|
||||
*~
|
||||
.*.swp
|
||||
.DS_STORE
|
||||
robovm-build/
|
||||
project.xcworkspace/
|
||||
xcuserdata/
|
||||
xcode/native/
|
||||
.gradle/
|
||||
build/
|
||||
bin/
|
||||
target/
|
||||
obj/
|
||||
out/
|
||||
classes/
|
||||
war/
|
||||
gen/
|
||||
gen-external-apklibs/
|
||||
Debug/
|
||||
Release/
|
||||
robovm-build/
|
||||
ipch/
|
||||
/dist
|
||||
|
||||
#for now
|
||||
extensions/physic/
|
||||
|
||||
#maven sucks
|
||||
pom.xml
|
||||
|
||||
*.dll.mdb
|
||||
*.userprefs
|
||||
*.sdf
|
||||
*.class
|
||||
hs_err_pid*
|
||||
Thumbs.db
|
||||
|
||||
arc-*.zip
|
||||
arc-*.zip.MD5
|
||||
|
||||
*.iml
|
||||
/.idea/
|
||||
|
||||
#core & extension libs/ folders that have no 3rd party dependencies in them
|
||||
/backends/arc-backend-robovm/libs/
|
||||
/extensions/freetype/libs/
|
||||
/extensions/freetype/jni/
|
||||
/extensions/freetype/Users/
|
||||
/extensions/filedialogs/jni/
|
||||
/extensions/filedialogs/libs/
|
||||
/backends/backend-sdl/jni
|
||||
/backends/backend-robovm/res/META-INF/robovm/ios/libs/
|
||||
/arc-core/libs/
|
||||
/arc-core/jni/
|
||||
/arc-core/obj/
|
||||
/arc-core/Users/
|
||||
/arc-core/csrc/stb_image.h
|
||||
/arc-core/csrc/soloud
|
||||
/arc-core/csrc/freetype
|
||||
/arc-core/csrc/freetype.tar.gz
|
||||
#directories created by fetch.xml
|
||||
/tmp/
|
||||
|
||||
#gradle wrapper
|
||||
/local.properties
|
||||
@@ -0,0 +1,202 @@
|
||||
import com.badlogic.gdx.jnigen.gradle.JnigenExtension.*
|
||||
import java.nio.file.*
|
||||
|
||||
plugins{
|
||||
id("com.badlogicgames.jnigen.jnigen-gradle")
|
||||
}
|
||||
|
||||
sourceSets{
|
||||
main{
|
||||
java.setSrcDirs(listOf("src"))
|
||||
}
|
||||
test{
|
||||
java.setSrcDirs(listOf("test"))
|
||||
resources.setSrcDirs(listOf("test/resources"))
|
||||
}
|
||||
}
|
||||
|
||||
val extraLibs = configurations.create("extraLibs")
|
||||
|
||||
dependencies{
|
||||
testImplementation("junit:junit:4.11")
|
||||
testImplementation(project(":natives:natives-desktop"))
|
||||
testImplementation(files("unsafe/unsafe.jar"))
|
||||
//file generated from UnsafeBuffers.java
|
||||
compileOnly(files("unsafe/unsafe.jar"))
|
||||
extraLibs(files("unsafe/unsafe.jar"))
|
||||
}
|
||||
|
||||
tasks.jar{
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
from(provider{ extraLibs.map{ if(it.isDirectory) it else zipTree(it) } })
|
||||
}
|
||||
|
||||
//now, you may ask: why don't I make this a new module? why do I include JARs in the repository? why don't I make this a separate build step?
|
||||
//the reason is: I don't want to deal with compiler arguments, --add-opens, or whatever else Java throws at you these days
|
||||
//this just works and I don't want to touch it, it's a dependency that has already been compiled and won't explode as much as source compilation might
|
||||
//"why are you using unsafe, why aren't you using the new memory access APIs?" because they don't work on Android or iOS
|
||||
//and no, I cannot work with buffer put() because of Java's idiotic design making it so that you need to put()/limit()/position() to write memory, breaking multithreading
|
||||
//this makes Unsafe the only viable option - any "state independent" methods are Java-16 specific, and iOS/Android don't support those
|
||||
|
||||
tasks.register<Exec>("compileBuffersUnsafe"){
|
||||
workingDir = file("unsafe")
|
||||
commandLine("javac --target 8 --source 8 -d . UnsafeBuffers.java".split(" "))
|
||||
}
|
||||
|
||||
tasks.register<Exec>("compileBuffersSafe"){
|
||||
workingDir = file("unsafe")
|
||||
commandLine("javac --target 16 --source 16 -d . Java16Buffers.java".split(" "))
|
||||
}
|
||||
|
||||
tasks.register<Exec>("recompileUnsafe"){
|
||||
dependsOn("compileBuffersUnsafe", "compileBuffersSafe")
|
||||
workingDir = file("unsafe")
|
||||
commandLine("jar cvf unsafe.jar arc".split(" "))
|
||||
doLast{
|
||||
delete("unsafe/arc")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test{
|
||||
testLogging{
|
||||
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||
showStandardStreams = true
|
||||
}
|
||||
}
|
||||
|
||||
jnigen{
|
||||
sharedLibName = "arc"
|
||||
libsDir = "build/natives"
|
||||
val root = "csrc"
|
||||
val fdir = "$root/freetype"
|
||||
|
||||
all{
|
||||
headerDirs = arrayOf(
|
||||
"$root/soloud/include", "$root/soloud/src/audiosource/wav", root,
|
||||
"$fdir/include", "$fdir/include/freetype", "$fdir/include/freetype/config",
|
||||
"$fdir/include/freetype/internal", "$fdir/include/freetype/internal/services"
|
||||
)
|
||||
cppIncludes = arrayOf("$root/*.cpp", "$root/soloud/src/core/**.cpp", "$root/soloud/src/audiosource/wav/**.cpp", "$root/soloud/src/audiosource/wav/**.c", "$root/soloud/src/filter/**.cpp")
|
||||
cIncludes = arrayOf(
|
||||
"$fdir/src/base/ftsystem.c", "$fdir/src/base/ftinit.c", "$fdir/src/base/ftdebug.c",
|
||||
"$fdir/src/base/ftbase.c", "$fdir/src/base/ftbbox.c", "$fdir/src/base/ftglyph.c",
|
||||
"$fdir/src/base/ftbitmap.c", "$fdir/src/base/ftstroke.c", "$fdir/src/base/ftmm.c",
|
||||
"$fdir/src/truetype/truetype.c", "$fdir/src/sfnt/sfnt.c",
|
||||
"$fdir/src/smooth/smooth.c",
|
||||
"$fdir/src/autofit/autofit.c", "$fdir/src/gzip/ftgzip.c", "$fdir/src/psnames/psnames.c"
|
||||
)
|
||||
cppFlags += "-DSOLOUD_MAX_VOICE_COUNT=100"
|
||||
cFlags += "-DFT2_BUILD_LIBRARY"
|
||||
cppFlags += "-DFT2_BUILD_LIBRARY"
|
||||
cFlags += "-flto"
|
||||
cppFlags += "-flto"
|
||||
linkerFlags += "-flto"
|
||||
}
|
||||
addLinux(x64, x86)
|
||||
addLinux(x64, ARM)
|
||||
each({ it.os == Linux }){
|
||||
headerDirs += "$root/soloud/src/backend/miniaudio/"
|
||||
cppIncludes += "$root/soloud/src/backend/miniaudio/*.cpp"
|
||||
cppFlags += "-DWITH_MINIAUDIO"
|
||||
libraries += "-lpthread -lrt -lm -ldl".split(" ")
|
||||
}
|
||||
addWindows(x64, x86){
|
||||
headerDirs += "$root/soloud/src/backend/miniaudio/"
|
||||
cppIncludes += "$root/soloud/src/backend/miniaudio/*.cpp"
|
||||
cppFlags += arrayOf("-msse", "-DWITH_MINIAUDIO")
|
||||
}
|
||||
addAndroid{
|
||||
linkerFlags += "-llog -lOpenSLES".split(" ")
|
||||
cppIncludes += "$root/soloud/src/backend/opensles/*.cpp"
|
||||
cppFlags += "-DWITH_OPENSLES"
|
||||
androidApplicationMk += "APP_STL := c++_static"
|
||||
}
|
||||
addMac(x64, ARM)
|
||||
each({ it.os == MacOsX }){
|
||||
cppIncludes += "$root/soloud/src/backend/coreaudio/*.mm"
|
||||
cppFlags += arrayOf("-std=c++11", "-DWITH_COREAUDIO")
|
||||
libraries += "-Wl,-framework,CoreAudio -Wl,-framework,AudioToolbox".split(" ")
|
||||
}
|
||||
addIOS{
|
||||
headerDirs += "$root/iosgl"
|
||||
cppIncludes += arrayOf("$root/soloud/src/backend/coreaudio/*.mm", "$root/iosgl/**.cpp")
|
||||
cppFlags += arrayOf("-stdlib=libc++", "-std=c++11", "-DWITH_COREAUDIO")
|
||||
libraries += "-Wl,-framework,CoreAudio -Wl,-framework,AudioToolbox -Wl,-framework,AVFoundation -Wl,-framework,Foundation".split(" ")
|
||||
linkerFlags += arrayOf("-undefined", "dynamic_lookup")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("copyUnsafeStuff"){
|
||||
doLast{
|
||||
copy{
|
||||
from(zipTree("unsafe/unsafe.jar"))
|
||||
into("build/classes/java/main")
|
||||
include("**/*.class")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("cleanNatives"){
|
||||
doLast{
|
||||
delete("$rootDir/arc-core/csrc/soloud")
|
||||
delete("$rootDir/arc-core/csrc/stb_image.h")
|
||||
}
|
||||
}
|
||||
|
||||
val soloudVersion = "2026.09.04"
|
||||
|
||||
fun runQuietly(vararg command: String, workingDir: File? = null){
|
||||
providers.exec{
|
||||
commandLine(*command)
|
||||
if(workingDir != null) this.workingDir = workingDir
|
||||
isIgnoreExitValue = true
|
||||
}.result.get()
|
||||
}
|
||||
|
||||
tasks.register("preJni"){
|
||||
doFirst{
|
||||
if(!file("csrc/stb_image.h").exists()){
|
||||
println("Fetching stb_image source...")
|
||||
//currently locked to a specific commit
|
||||
runQuietly("curl", "-o", "$rootDir/arc-core/csrc/stb_image.h", "https://raw.githubusercontent.com/nothings/stb/013ac3beddff3dbffafd5177e7972067cd2b5083/stb_image.h")
|
||||
}
|
||||
|
||||
if(!file("csrc/soloud").exists()){
|
||||
println("Fetching soloud source...")
|
||||
runQuietly("git", "clone", "--depth", "1", "--branch", soloudVersion, "https://github.com/Anuken/soloud.git", "$rootDir/arc-core/csrc/soloud")
|
||||
}
|
||||
|
||||
val freetypeRoot = file("csrc")
|
||||
val freetypeDir = file("csrc/freetype")
|
||||
if(!freetypeDir.exists()){
|
||||
val extractedFreetypeDir = file("csrc/freetype-2.14.3")
|
||||
if(!extractedFreetypeDir.exists()){
|
||||
println("Fetching freetype source...")
|
||||
freetypeRoot.mkdirs()
|
||||
providers.exec{
|
||||
workingDir = freetypeRoot
|
||||
commandLine("wget", "-c", "https://sourceforge.net/projects/freetype/files/freetype2/2.14.3/freetype-2.14.3.tar.gz/download", "-O", "freetype.tar.gz")
|
||||
}.result.get()
|
||||
providers.exec{
|
||||
workingDir = freetypeRoot
|
||||
commandLine("tar", "-xf", "freetype.tar.gz")
|
||||
}.result.get()
|
||||
}
|
||||
Files.move(extractedFreetypeDir.toPath(), freetypeDir.toPath())
|
||||
}
|
||||
|
||||
file("csrc/freetype/include/freetype/config/ftmodule.h").writeText(
|
||||
"""
|
||||
FT_USE_MODULE( FT_Module_Class, autofit_module_class )
|
||||
FT_USE_MODULE( FT_Driver_ClassRec, tt_driver_class )
|
||||
FT_USE_MODULE( FT_Module_Class, psnames_module_class )
|
||||
FT_USE_MODULE( FT_Module_Class, sfnt_module_class )
|
||||
FT_USE_MODULE( FT_Renderer_Class, ft_smooth_renderer_class )
|
||||
""".trimIndent() + "\n"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
getTasksByName("jnigen", true).forEach{
|
||||
it.dependsOn("copyUnsafeStuff", "preJni")
|
||||
}
|
||||
@@ -0,0 +1,756 @@
|
||||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
/* Header for class arc_backend_robovm_IOSGLES30 */
|
||||
|
||||
#ifndef _Included_arc_backend_robovm_IOSGLES30
|
||||
#define _Included_arc_backend_robovm_IOSGLES30
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: init
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_init
|
||||
(JNIEnv *, jclass);
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glReadBuffer
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glReadBuffer
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDrawRangeElements
|
||||
* Signature: (IIIIILjava/nio/Buffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDrawRangeElements__IIIIILjava_nio_Buffer_2
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDrawRangeElements
|
||||
* Signature: (IIIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDrawRangeElements__IIIIII
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glTexImage3D
|
||||
* Signature: (IIIIIIIIILjava/nio/Buffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glTexImage3D__IIIIIIIIILjava_nio_Buffer_2
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint, jint, jint, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glTexImage3D
|
||||
* Signature: (IIIIIIIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glTexImage3D__IIIIIIIIII
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glTexSubImage3D
|
||||
* Signature: (IIIIIIIIIILjava/nio/Buffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glTexSubImage3D__IIIIIIIIIILjava_nio_Buffer_2
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint, jint, jint, jint, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glTexSubImage3D
|
||||
* Signature: (IIIIIIIIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glTexSubImage3D__IIIIIIIIIII
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint, jint, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glCopyTexSubImage3D
|
||||
* Signature: (IIIIIIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glCopyTexSubImage3D
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGenQueries
|
||||
* Signature: (I[II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGenQueries__I_3II
|
||||
(JNIEnv *, jobject, jint, jintArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGenQueries
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGenQueries__ILjava_nio_IntBuffer_2
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDeleteQueries
|
||||
* Signature: (I[II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDeleteQueries__I_3II
|
||||
(JNIEnv *, jobject, jint, jintArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDeleteQueries
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDeleteQueries__ILjava_nio_IntBuffer_2
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glIsQuery
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_arc_backend_robovm_IOSGLES30_glIsQuery
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glBeginQuery
|
||||
* Signature: (II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glBeginQuery
|
||||
(JNIEnv *, jobject, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glEndQuery
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glEndQuery
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetQueryiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetQueryiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetQueryObjectuiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetQueryObjectuiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUnmapBuffer
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_arc_backend_robovm_IOSGLES30_glUnmapBuffer
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetBufferPointerv
|
||||
* Signature: (II)Ljava/nio/Buffer;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_arc_backend_robovm_IOSGLES30_glGetBufferPointerv
|
||||
(JNIEnv *, jobject, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDrawBuffers
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDrawBuffers
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniformMatrix2x3fv
|
||||
* Signature: (IIZLjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniformMatrix2x3fv
|
||||
(JNIEnv *, jobject, jint, jint, jboolean, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniformMatrix3x2fv
|
||||
* Signature: (IIZLjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniformMatrix3x2fv
|
||||
(JNIEnv *, jobject, jint, jint, jboolean, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniformMatrix2x4fv
|
||||
* Signature: (IIZLjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniformMatrix2x4fv
|
||||
(JNIEnv *, jobject, jint, jint, jboolean, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniformMatrix4x2fv
|
||||
* Signature: (IIZLjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniformMatrix4x2fv
|
||||
(JNIEnv *, jobject, jint, jint, jboolean, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniformMatrix3x4fv
|
||||
* Signature: (IIZLjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniformMatrix3x4fv
|
||||
(JNIEnv *, jobject, jint, jint, jboolean, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniformMatrix4x3fv
|
||||
* Signature: (IIZLjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniformMatrix4x3fv
|
||||
(JNIEnv *, jobject, jint, jint, jboolean, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glBlitFramebuffer
|
||||
* Signature: (IIIIIIIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glBlitFramebuffer
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glRenderbufferStorageMultisample
|
||||
* Signature: (IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glRenderbufferStorageMultisample
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glFramebufferTextureLayer
|
||||
* Signature: (IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glFramebufferTextureLayer
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glFlushMappedBufferRange
|
||||
* Signature: (III)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glFlushMappedBufferRange
|
||||
(JNIEnv *, jobject, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glBindVertexArray
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glBindVertexArray
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDeleteVertexArrays
|
||||
* Signature: (I[II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDeleteVertexArrays__I_3II
|
||||
(JNIEnv *, jobject, jint, jintArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDeleteVertexArrays
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDeleteVertexArrays__ILjava_nio_IntBuffer_2
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGenVertexArrays
|
||||
* Signature: (I[II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGenVertexArrays__I_3II
|
||||
(JNIEnv *, jobject, jint, jintArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGenVertexArrays
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGenVertexArrays__ILjava_nio_IntBuffer_2
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glIsVertexArray
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_arc_backend_robovm_IOSGLES30_glIsVertexArray
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glBeginTransformFeedback
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glBeginTransformFeedback
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glEndTransformFeedback
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glEndTransformFeedback
|
||||
(JNIEnv *, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glBindBufferRange
|
||||
* Signature: (IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glBindBufferRange
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glBindBufferBase
|
||||
* Signature: (III)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glBindBufferBase
|
||||
(JNIEnv *, jobject, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glTransformFeedbackVaryings
|
||||
* Signature: (I[Ljava/lang/String;I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glTransformFeedbackVaryings
|
||||
(JNIEnv *, jobject, jint, jobjectArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glVertexAttribIPointer
|
||||
* Signature: (IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glVertexAttribIPointer
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetVertexAttribIiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetVertexAttribIiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetVertexAttribIuiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetVertexAttribIuiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glVertexAttribI4i
|
||||
* Signature: (IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glVertexAttribI4i
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glVertexAttribI4ui
|
||||
* Signature: (IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glVertexAttribI4ui
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetUniformuiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetUniformuiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetFragDataLocation
|
||||
* Signature: (ILjava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_arc_backend_robovm_IOSGLES30_glGetFragDataLocation
|
||||
(JNIEnv *, jobject, jint, jstring);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniform1uiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniform1uiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniform3uiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniform3uiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniform4uiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniform4uiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glClearBufferiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glClearBufferiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glClearBufferuiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glClearBufferuiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glClearBufferfv
|
||||
* Signature: (IILjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glClearBufferfv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glClearBufferfi
|
||||
* Signature: (IIFI)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glClearBufferfi
|
||||
(JNIEnv *, jobject, jint, jint, jfloat, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetStringi
|
||||
* Signature: (II)Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jstring JNICALL Java_arc_backend_robovm_IOSGLES30_glGetStringi
|
||||
(JNIEnv *, jobject, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glCopyBufferSubData
|
||||
* Signature: (IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glCopyBufferSubData
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetUniformIndices
|
||||
* Signature: (I[Ljava/lang/String;Ljava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetUniformIndices
|
||||
(JNIEnv *, jobject, jint, jobjectArray, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetActiveUniformsiv
|
||||
* Signature: (IILjava/nio/IntBuffer;ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetActiveUniformsiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetUniformBlockIndex
|
||||
* Signature: (ILjava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_arc_backend_robovm_IOSGLES30_glGetUniformBlockIndex
|
||||
(JNIEnv *, jobject, jint, jstring);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetActiveUniformBlockiv
|
||||
* Signature: (IIILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetActiveUniformBlockiv
|
||||
(JNIEnv *, jobject, jint, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetActiveUniformBlockName
|
||||
* Signature: (IILjava/nio/Buffer;Ljava/nio/Buffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetActiveUniformBlockName__IILjava_nio_Buffer_2Ljava_nio_Buffer_2
|
||||
(JNIEnv *, jobject, jint, jint, jobject, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetActiveUniformBlockName
|
||||
* Signature: (II)Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jstring JNICALL Java_arc_backend_robovm_IOSGLES30_glGetActiveUniformBlockName__II
|
||||
(JNIEnv *, jobject, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniformBlockBinding
|
||||
* Signature: (III)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniformBlockBinding
|
||||
(JNIEnv *, jobject, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDrawArraysInstanced
|
||||
* Signature: (IIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDrawArraysInstanced
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDrawElementsInstanced
|
||||
* Signature: (IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDrawElementsInstanced
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetInteger64v
|
||||
* Signature: (ILjava/nio/LongBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetInteger64v
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetBufferParameteri64v
|
||||
* Signature: (IILjava/nio/LongBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetBufferParameteri64v
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGenSamplers
|
||||
* Signature: (I[II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGenSamplers__I_3II
|
||||
(JNIEnv *, jobject, jint, jintArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGenSamplers
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGenSamplers__ILjava_nio_IntBuffer_2
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDeleteSamplers
|
||||
* Signature: (I[II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDeleteSamplers__I_3II
|
||||
(JNIEnv *, jobject, jint, jintArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDeleteSamplers
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDeleteSamplers__ILjava_nio_IntBuffer_2
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glIsSampler
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_arc_backend_robovm_IOSGLES30_glIsSampler
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glBindSampler
|
||||
* Signature: (II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glBindSampler
|
||||
(JNIEnv *, jobject, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glSamplerParameteri
|
||||
* Signature: (III)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glSamplerParameteri
|
||||
(JNIEnv *, jobject, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glSamplerParameteriv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glSamplerParameteriv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glSamplerParameterf
|
||||
* Signature: (IIF)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glSamplerParameterf
|
||||
(JNIEnv *, jobject, jint, jint, jfloat);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glSamplerParameterfv
|
||||
* Signature: (IILjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glSamplerParameterfv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetSamplerParameteriv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetSamplerParameteriv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetSamplerParameterfv
|
||||
* Signature: (IILjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetSamplerParameterfv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glVertexAttribDivisor
|
||||
* Signature: (II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glVertexAttribDivisor
|
||||
(JNIEnv *, jobject, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glBindTransformFeedback
|
||||
* Signature: (II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glBindTransformFeedback
|
||||
(JNIEnv *, jobject, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDeleteTransformFeedbacks
|
||||
* Signature: (I[II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDeleteTransformFeedbacks__I_3II
|
||||
(JNIEnv *, jobject, jint, jintArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDeleteTransformFeedbacks
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDeleteTransformFeedbacks__ILjava_nio_IntBuffer_2
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGenTransformFeedbacks
|
||||
* Signature: (I[II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGenTransformFeedbacks__I_3II
|
||||
(JNIEnv *, jobject, jint, jintArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGenTransformFeedbacks
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGenTransformFeedbacks__ILjava_nio_IntBuffer_2
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glIsTransformFeedback
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_arc_backend_robovm_IOSGLES30_glIsTransformFeedback
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glPauseTransformFeedback
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glPauseTransformFeedback
|
||||
(JNIEnv *, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glResumeTransformFeedback
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glResumeTransformFeedback
|
||||
(JNIEnv *, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glProgramParameteri
|
||||
* Signature: (III)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glProgramParameteri
|
||||
(JNIEnv *, jobject, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glInvalidateFramebuffer
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glInvalidateFramebuffer
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glInvalidateSubFramebuffer
|
||||
* Signature: (IILjava/nio/IntBuffer;IIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glInvalidateSubFramebuffer
|
||||
(JNIEnv *, jobject, jint, jint, jobject, jint, jint, jint, jint);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,148 @@
|
||||
package arc;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
|
||||
import java.net.*;
|
||||
|
||||
public interface Application extends Disposable{
|
||||
|
||||
/** Returns a list of all the application listeners used. */
|
||||
Seq<ApplicationListener> getListeners();
|
||||
|
||||
/** Adds a new application listener. */
|
||||
default void addListener(ApplicationListener listener){
|
||||
synchronized(getListeners()){
|
||||
getListeners().add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes an application listener. */
|
||||
default void removeListener(ApplicationListener listener){
|
||||
post(() -> {
|
||||
synchronized(getListeners()){
|
||||
getListeners().remove(listener);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Call this before update() in each backend. */
|
||||
default void defaultUpdate(){
|
||||
Core.settings.autosave();
|
||||
Time.updateGlobal();
|
||||
}
|
||||
|
||||
/** @return what {@link ApplicationType} this application has, e.g. Android or Desktop */
|
||||
ApplicationType getType();
|
||||
|
||||
default boolean isDesktop(){
|
||||
return getType() == ApplicationType.desktop;
|
||||
}
|
||||
|
||||
default boolean isHeadless(){
|
||||
return getType() == ApplicationType.headless;
|
||||
}
|
||||
|
||||
default boolean isAndroid(){
|
||||
return getType() == ApplicationType.android;
|
||||
}
|
||||
|
||||
default boolean isIOS(){
|
||||
return getType() == ApplicationType.iOS;
|
||||
}
|
||||
|
||||
default boolean isMobile(){
|
||||
return isAndroid() || isIOS();
|
||||
}
|
||||
|
||||
default boolean isWeb(){
|
||||
return getType() == ApplicationType.web;
|
||||
}
|
||||
|
||||
/** @return the Android API level on Android, the major OS version on iOS (5, 6, 7, ..), or 0 on the desktop. */
|
||||
default int getVersion(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @return the Java heap memory use in bytes. */
|
||||
default long getJavaHeap(){
|
||||
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
|
||||
}
|
||||
|
||||
/** @return the Native heap memory use in bytes. Only valid on Android. */
|
||||
default long getNativeHeap(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @return the main graphics thread, upon which all ApplicationListener methods are called. May return null if not initialized. */
|
||||
default @Nullable Thread getMainThread(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the currently executing thread is the main thread.
|
||||
* If the main thread is not initialized, returns true by default. This is used for error checking purposes.
|
||||
* */
|
||||
default boolean isOnMainThread(){
|
||||
Thread thread = getMainThread();
|
||||
return thread == null || Thread.currentThread() == thread;
|
||||
}
|
||||
|
||||
@Nullable String getClipboardText();
|
||||
|
||||
void setClipboardText(String text);
|
||||
|
||||
default void setClipboardImage(Pixmap pixmap){
|
||||
//no-op
|
||||
}
|
||||
|
||||
/** Open a folder in the system's file browser.
|
||||
* @return whether this operation was successful. */
|
||||
default boolean openFolder(String file){
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches the default browser to display a URI. If the default browser is not able to handle the specified URI, the
|
||||
* application registered for handling URIs of the specified type is invoked. The application is determined from the protocol
|
||||
* and path of the URI. A best effort is made to open the given URI; however, since external applications are involved, no guarantee
|
||||
* can be made as to whether the URI was actually opened. If it is known that the URI was not opened, false will be returned;
|
||||
* otherwise, true will be returned.
|
||||
* @param URI the URI to be opened.
|
||||
* @return false if it is known the uri was not opened, true otherwise.
|
||||
*/
|
||||
default boolean openURI(String URI){
|
||||
return false;
|
||||
}
|
||||
|
||||
default void getDnsServers(Seq<InetSocketAddress> out){}
|
||||
|
||||
/** Posts a runnable on the main loop thread.*/
|
||||
void post(Runnable runnable);
|
||||
|
||||
/**
|
||||
* Schedule an exit from the application. On android, this will cause a call to pause() and dispose() some time in the future,
|
||||
* it will not immediately finish your application.
|
||||
* On iOS this should be avoided in production as it breaks Apples guidelines.
|
||||
*/
|
||||
void exit();
|
||||
|
||||
/** Disposes of core resources. */
|
||||
@Override
|
||||
default void dispose(){
|
||||
//flush any changes to settings upon dispose
|
||||
if(Core.settings != null){
|
||||
Core.settings.autosave();
|
||||
}
|
||||
|
||||
if(Core.audio != null){
|
||||
Core.audio.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/** Enumeration of possible {@link Application} types */
|
||||
enum ApplicationType{
|
||||
android, desktop, headless, web, iOS
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package arc;
|
||||
|
||||
import arc.files.Fi;
|
||||
|
||||
public abstract class ApplicationCore implements ApplicationListener{
|
||||
protected ApplicationListener[] modules = {};
|
||||
|
||||
public void add(ApplicationListener module){
|
||||
//use an array instead of a seq/list, for faster iteration; modules do not get added often, so a resize each time is acceptable
|
||||
ApplicationListener[] news = new ApplicationListener[modules.length + 1];
|
||||
news[news.length - 1] = module;
|
||||
System.arraycopy(modules, 0, news, 0, modules.length);
|
||||
modules = news;
|
||||
}
|
||||
|
||||
public abstract void setup();
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
setup();
|
||||
|
||||
for(ApplicationListener listener : modules){
|
||||
listener.init();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height){
|
||||
for(ApplicationListener listener : modules){
|
||||
listener.resize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
for(ApplicationListener listener : modules){
|
||||
listener.update();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause(){
|
||||
for(ApplicationListener listener : modules){
|
||||
listener.pause();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume(){
|
||||
for(ApplicationListener listener : modules){
|
||||
listener.resume();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
for(ApplicationListener listener : modules){
|
||||
listener.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileDropped(Fi file){
|
||||
for(ApplicationListener listener : modules){
|
||||
listener.fileDropped(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package arc;
|
||||
|
||||
import arc.files.Fi;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An <code>ApplicationListener</code> is called when the {@link Application} is created, resumed, rendering, paused or destroyed.
|
||||
* All methods are called in a thread that has the OpenGL context current. You can thus safely create and manipulate graphics
|
||||
* resources.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The <code>ApplicationListener</code> interface follows the standard Android activity life-cycle and is emulated on the desktop
|
||||
* accordingly.
|
||||
* </p>
|
||||
* @author mzechner
|
||||
*/
|
||||
public interface ApplicationListener{
|
||||
/**
|
||||
* Called when the {@link Application} is first created.
|
||||
* Only gets called if the application is created before the listener is added.
|
||||
*/
|
||||
default void init(){
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the {@link Application} is resized. This can happen at any point during a non-paused state but will never happen
|
||||
* before a call to {@link #init()}.
|
||||
* @param width the new width in pixels
|
||||
* @param height the new height in pixels
|
||||
*/
|
||||
default void resize(int width, int height){
|
||||
}
|
||||
|
||||
/** Called when the {@link Application} should update itself. */
|
||||
default void update(){
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the {@link Application} is paused, usually when it's not active or visible on screen. An Application is also
|
||||
* paused before it is destroyed.
|
||||
*/
|
||||
default void pause(){
|
||||
}
|
||||
|
||||
/** Called when the {@link Application} is resumed from a paused state, usually when it regains focus. */
|
||||
default void resume(){
|
||||
}
|
||||
|
||||
/** Called when the {@link Application} is destroyed. Preceded by a call to {@link #pause()}. */
|
||||
default void dispose(){
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the applications exits gracefully, either through `Core.app.exit()` or through a window closing.
|
||||
* Never called after a crash, unlike dispose().
|
||||
* */
|
||||
default void exit(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an external file is dropped into the window, e.g from the desktop.
|
||||
*/
|
||||
default void fileDropped(Fi file){
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package arc;
|
||||
|
||||
import arc.assets.*;
|
||||
import arc.audio.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.graphics.gl.*;
|
||||
import arc.scene.*;
|
||||
import arc.util.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/** Global references to all of Arc's core modules. */
|
||||
public class Core{
|
||||
public static Application app;
|
||||
public static Graphics graphics;
|
||||
public static Audio audio;
|
||||
public static Input input;
|
||||
public static Files files;
|
||||
public static Settings settings;
|
||||
|
||||
public static I18NBundle bundle = I18NBundle.createEmptyBundle();
|
||||
public static Camera camera;
|
||||
public static Batch batch;
|
||||
public static Scene scene;
|
||||
public static AssetManager assets;
|
||||
public static TextureAtlas atlas;
|
||||
public static ExecutorService executor = Threads.executor("Main Executor", OS.cores);
|
||||
|
||||
/** This class should never be used directly - use {@link Gl} instead. */
|
||||
public static GLProvider glProvider;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package arc;
|
||||
|
||||
|
||||
import arc.struct.Seq;
|
||||
import arc.struct.ObjectMap;
|
||||
import arc.func.Cons;
|
||||
|
||||
/** Simple global event listener system. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public class Events{
|
||||
private static final ObjectMap<Object, Seq<Cons<?>>> events = new ObjectMap<>();
|
||||
|
||||
/** Handle an event by class. */
|
||||
public static <T> void on(Class<T> type, Cons<T> listener){
|
||||
events.get(type, () -> new Seq<>(Cons.class)).add(listener);
|
||||
}
|
||||
|
||||
/** Handle an event by enum trigger. */
|
||||
public static void run(Object type, Runnable listener){
|
||||
events.get(type, () -> new Seq<>(Cons.class)).add(e -> listener.run());
|
||||
}
|
||||
|
||||
/** Only use this method if you have the reference to the exact listener object that was used. */
|
||||
public static <T> boolean remove(Class<T> type, Cons<T> listener){
|
||||
return events.get(type, () -> new Seq<>(Cons.class)).remove(listener);
|
||||
}
|
||||
|
||||
/** Fires an enum trigger. */
|
||||
public static <T extends Enum<T>> void fire(Enum<T> type){
|
||||
Seq<Cons<?>> listeners = events.get(type);
|
||||
|
||||
if(listeners != null){
|
||||
int len = listeners.size;
|
||||
Cons[] items = listeners.items;
|
||||
for(int i = 0; i < len; i++){
|
||||
items[i].get(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Fires a non-enum event by class. */
|
||||
public static <T> void fire(T type){
|
||||
fire(type.getClass(), type);
|
||||
}
|
||||
|
||||
public static <T> void fire(Class<?> ctype, T type){
|
||||
Seq<Cons<?>> listeners = events.get(ctype);
|
||||
|
||||
if(listeners != null){
|
||||
int len = listeners.size;
|
||||
Cons[] items = listeners.items;
|
||||
for(int i = 0; i < len; i++){
|
||||
items[i].get(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't do this. */
|
||||
public static void clear(){
|
||||
events.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package arc;
|
||||
|
||||
import arc.audio.Audio;
|
||||
import arc.files.Fi;
|
||||
import arc.util.ArcRuntimeException;
|
||||
|
||||
/**
|
||||
* Provides standard access to the filesystem, classpath, Android SD card, and Android assets directory.
|
||||
* @author mzechner
|
||||
* @author Nathan Sweet
|
||||
*/
|
||||
public interface Files{
|
||||
/**
|
||||
* Returns a handle representing a file or directory.
|
||||
* @param type Determines how the path is resolved.
|
||||
* @throws ArcRuntimeException if the type is classpath or internal and the file does not exist.
|
||||
* @see FileType
|
||||
*/
|
||||
Fi get(String path, FileType type);
|
||||
|
||||
/** Convenience method that returns a {@link FileType#classpath} file handle. */
|
||||
default Fi classpath(String path){
|
||||
return get(path, FileType.classpath);
|
||||
}
|
||||
|
||||
/** Convenience method that returns a {@link FileType#internal} file handle. */
|
||||
default Fi internal(String path){
|
||||
return get(path, FileType.internal);
|
||||
}
|
||||
|
||||
/** Convenience method that returns a {@link FileType#external} file handle. */
|
||||
default Fi external(String path){
|
||||
return get(path, FileType.external);
|
||||
}
|
||||
|
||||
/** Convenience method that returns a {@link FileType#absolute} file handle. */
|
||||
default Fi absolute(String path){
|
||||
return get(path, FileType.absolute);
|
||||
}
|
||||
|
||||
/** Convenience method that returns a {@link FileType#local} file handle. */
|
||||
default Fi local(String path){
|
||||
return get(path, FileType.local);
|
||||
}
|
||||
|
||||
/** Convenience method that returns a cache file handle. */
|
||||
default Fi cache(String path){
|
||||
return get(getCachePath(), FileType.absolute).child(path);
|
||||
}
|
||||
|
||||
/** @return absolute path to cache directory. */
|
||||
default String getCachePath(){
|
||||
return local("cache").absolutePath();
|
||||
}
|
||||
|
||||
/** @return on IOS, the internal path to the assets directory. Not used on other platforms. */
|
||||
default String getInternalStoragePath(){
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the external storage path directory. This is the SD card on Android and the home directory of the current user on
|
||||
* the desktop.
|
||||
*/
|
||||
String getExternalStoragePath();
|
||||
|
||||
/**
|
||||
* @return true if the external storage is ready for file IO. Eg, on Android, the SD card is not available when mounted for use
|
||||
* with a PC.
|
||||
*/
|
||||
boolean isExternalStorageAvailable();
|
||||
|
||||
/**
|
||||
* @return the local storage path directory. This is the private files directory on Android and the directory of the jar on the
|
||||
* desktop.
|
||||
*/
|
||||
String getLocalStoragePath();
|
||||
|
||||
/** @return true if the local storage is ready for file IO. */
|
||||
boolean isLocalStorageAvailable();
|
||||
|
||||
/**
|
||||
* Indicates how to resolve a path to a file.
|
||||
* @author mzechner
|
||||
* @author Nathan Sweet
|
||||
*/
|
||||
enum FileType{
|
||||
/**
|
||||
* Path relative to the root of the classpath. Classpath files are always readonly. Note that classpath files are not
|
||||
* compatible with some functionality on Android, such as {@link arc.audio.Audio#newSound(Fi)} and
|
||||
* {@link Audio#newMusic(Fi)}.
|
||||
*/
|
||||
classpath,
|
||||
|
||||
/**
|
||||
* Path relative to the asset directory on Android and to the application's root directory on the desktop. On the desktop,
|
||||
* if the file is not found, then the classpath is checked. This enables files to be found when using JWS or applets.
|
||||
* Internal files are always readonly.
|
||||
*/
|
||||
internal,
|
||||
|
||||
/** Path relative to the root of the SD card on Android and to the home directory of the current user on the desktop. */
|
||||
external,
|
||||
|
||||
/**
|
||||
* Path that is a fully qualified, absolute filesystem path. To ensure portability across platforms use absolute files only
|
||||
* when absolutely (heh) necessary.
|
||||
*/
|
||||
absolute,
|
||||
|
||||
/** Path relative to the private files directory on Android and to the application's root directory on the desktop. */
|
||||
local
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
package arc;
|
||||
|
||||
import arc.Graphics.Cursor.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.gl.*;
|
||||
import arc.graphics.gl.GLVersion.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
|
||||
/**
|
||||
* This interface encapsulates communication with the graphics processor. Depending on the available hardware and the current
|
||||
* {@link Application} configuration, access to {@link GLProvider} are provided here.
|
||||
* <p>
|
||||
* If supported by the backend, this interface lets you query the available display modes (graphics resolution and color depth)
|
||||
* and change it.
|
||||
* <p>
|
||||
* This interface can be used to switch between continuous and non-continuous rendering (see
|
||||
* {@link #setContinuousRendering(boolean)}), and to explicitly {@link #requestRendering()}.
|
||||
* <p>
|
||||
* @author mzechner
|
||||
*/
|
||||
public abstract class Graphics implements Disposable{
|
||||
/** The last cursor used. Can be Cursor or SystemCursor.*/
|
||||
private Object lastCursor;
|
||||
|
||||
/** @return whether instancing with glVertexAttribDivisor and glDrawElementsInstanced is available on this platform. */
|
||||
public boolean supportsInstancing(){
|
||||
//instancing via glDrawElementsInstanced + glVertexAttribDivisor is available on GL ES 3.0; otherwise, only >= Gl 3.3 has glVertexAttribDivisor on desktop
|
||||
return getGLVersion().type == GlType.GLES || getGLVersion().atLeast(3, 3);
|
||||
}
|
||||
|
||||
/** Clears the color buffer using the specified color. */
|
||||
public void clear(float r, float g, float b, float a){
|
||||
Gl.clearColor(r, g, b, a);
|
||||
Gl.clear(Gl.colorBufferBit);
|
||||
}
|
||||
|
||||
/** Clears the color buffer using the specified color. */
|
||||
public void clear(Color color){
|
||||
clear(color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
/** Returns whether this application is probably in portrait mode, e.g. if width < height.*/
|
||||
public boolean isPortrait(){
|
||||
return getWidth() < getHeight();
|
||||
}
|
||||
|
||||
/** @return the width of the client area in logical pixels. */
|
||||
public abstract int getWidth();
|
||||
|
||||
/** @return the height of the client area in logical pixels */
|
||||
public abstract int getHeight();
|
||||
|
||||
/** @return the aspect ratio, e.g. width/height */
|
||||
public float getAspect(){
|
||||
return (float)getWidth() / getHeight();
|
||||
}
|
||||
|
||||
/** @return whether the window is 'hidden', e.g. whether width or height of the window is less than 2.
|
||||
* This is due to Windows reporting window size as 0 or 1 when minimized. This causes framebuffers to crash if resized.
|
||||
*/
|
||||
public boolean isHidden(){
|
||||
return getWidth() < 2 || getHeight() < 2;
|
||||
}
|
||||
|
||||
/** @return the width of the framebuffer in physical pixels */
|
||||
public abstract int getBackBufferWidth();
|
||||
|
||||
/** @return the height of the framebuffer in physical pixels */
|
||||
public abstract int getBackBufferHeight();
|
||||
|
||||
/** @return the safe area insets, in the order left-right-top-bottom. */
|
||||
public int[] getSafeInsets(){
|
||||
return new int[4];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the current frame. The general contract of this method is that the id is incremented only when the
|
||||
* application is in the running state right before calling the {@link ApplicationListener#update()} method. Also, the id of
|
||||
* the first frame is 0; the id of subsequent frames is guaranteed to take increasing values for 2<sup>63</sup>-1 rendering
|
||||
* cycles.
|
||||
* @return the id of the current frame
|
||||
*/
|
||||
public abstract long getFrameId();
|
||||
|
||||
/** @return the time span between the current frame and the last frame in seconds. Might be smoothed over n frames. */
|
||||
public abstract float getDeltaTime();
|
||||
|
||||
/** @return the average number of frames per second */
|
||||
public abstract int getFramesPerSecond();
|
||||
|
||||
/** @return the {@link GLVersion} of this Graphics instance */
|
||||
public abstract GLVersion getGLVersion();
|
||||
|
||||
/** @return the pixels per inch on the x-axis */
|
||||
public abstract float getPpiX();
|
||||
|
||||
/** @return the pixels per inch on the y-axis */
|
||||
public abstract float getPpiY();
|
||||
|
||||
/** @return the pixels per centimeter on the x-axis */
|
||||
public abstract float getPpcX();
|
||||
|
||||
/** @return the pixels per centimeter on the y-axis. */
|
||||
public abstract float getPpcY();
|
||||
|
||||
/**
|
||||
* This is a scaling factor for the Density Independent Pixel unit, following the same conventions as
|
||||
* android.util.DisplayMetrics#density, where one DIP is one pixel on an approximately 160 dpi screen. Thus on a 160dpi screen
|
||||
* this density value will be 1; on a 120 dpi screen it would be .75; etc.
|
||||
* @return the logical density of the Display.
|
||||
*/
|
||||
public abstract float getDensity();
|
||||
|
||||
/**
|
||||
* Sets the window to full-screen mode.
|
||||
* @return whether the operation succeeded.
|
||||
*/
|
||||
public boolean setFullscreen(boolean fullscreen){
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the window position on its current monitor.
|
||||
*/
|
||||
public void setWindowPosition(int x, int y){}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the window size in pixels.
|
||||
*/
|
||||
public void setWindowSize(int width, int height){}
|
||||
|
||||
/**
|
||||
* Sets the title of the window. Ignored on Android.
|
||||
* @param title the title.
|
||||
*/
|
||||
public abstract void setTitle(String title);
|
||||
|
||||
/**
|
||||
* Enable/Disable vsynching. This is a best-effort attempt which might not work on all platforms.
|
||||
* @param vsync vsync enabled or not.
|
||||
*/
|
||||
public abstract void setVSync(boolean vsync);
|
||||
|
||||
/** @return the format of the color, depth and stencil buffer in a {@link BufferFormat} instance */
|
||||
public abstract BufferFormat getBufferFormat();
|
||||
|
||||
/**
|
||||
* @param extension the extension name
|
||||
* @return whether the extension is supported
|
||||
*/
|
||||
public abstract boolean supportsExtension(String extension);
|
||||
|
||||
/** On iOS, sets the preferred frame rate. A value of 0 sets the maximum supported value for this device. */
|
||||
public void setPreferredFPS(int fps){}
|
||||
|
||||
/** @return whether rendering is continuous. */
|
||||
public abstract boolean isContinuousRendering();
|
||||
|
||||
/**
|
||||
* Sets whether to render continuously. In case rendering is performed non-continuously, the following events will trigger a
|
||||
* redraw:
|
||||
*
|
||||
* <ul>
|
||||
* <li>A call to {@link #requestRendering()}</li>
|
||||
* <li>Input events from the touch screen/mouse or keyboard</li>
|
||||
* <li>A {@link Runnable} is posted to the rendering thread via {@link Application#post(Runnable)}. In the case
|
||||
* of a multi-window app, all windows will request rendering if a runnable is posted to the application. To avoid this,
|
||||
* post a runnable to the window instead. </li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Life-cycle events will also be reported as usual, see {@link ApplicationListener}. This method can be called from any
|
||||
* thread.
|
||||
* @param isContinuous whether the rendering should be continuous or not.
|
||||
*/
|
||||
public abstract void setContinuousRendering(boolean isContinuous);
|
||||
|
||||
/** Requests a new frame to be rendered if the rendering mode is non-continuous. This method can be called from any thread. */
|
||||
public abstract void requestRendering();
|
||||
|
||||
/** Whether the app is fullscreen or not */
|
||||
public abstract boolean isFullscreen();
|
||||
|
||||
/**
|
||||
* Create a new cursor represented by the {@link arc.graphics.Pixmap}. The Pixmap must be in RGBA8888 format,
|
||||
* width & height must be powers-of-two greater than zero (not necessarily equal) and of a certain minimum size (32x32 is a safe bet),
|
||||
* and alpha transparency must be single-bit (i.e., 0x00 or 0xFF only). This function returns a Cursor object that can be set as the
|
||||
* system cursor by calling {@link #setCursor(Cursor)} .
|
||||
* @param pixmap the mouse cursor image as a {@link arc.graphics.Pixmap}
|
||||
* @param xHotspot the x location of the hotspot pixel within the cursor image (origin top-left corner)
|
||||
* @param yHotspot the y location of the hotspot pixel within the cursor image (origin top-left corner)
|
||||
* @return a cursor object that can be used by calling {@link #setCursor(Cursor)} or null if not supported
|
||||
*/
|
||||
public abstract Cursor newCursor(Pixmap pixmap, int xHotspot, int yHotspot);
|
||||
|
||||
/**
|
||||
* Creates a new cursor by scaling a pixmap and adding an outline.
|
||||
* @param pixmap The base pixmap. Unscaled.
|
||||
* @param scaling The factor by which to scale the base pixmap.
|
||||
* @param outlineColor The color of the cursor's outline.
|
||||
*/
|
||||
public Cursor newCursor(Pixmap pixmap, int scaling, Color outlineColor, int outlineThickness){
|
||||
Pixmap out = Pixmaps.outline(pixmap, outlineColor, outlineThickness);
|
||||
Pixmap out2 = Pixmaps.scale(out, scaling);
|
||||
|
||||
if(!Mathf.isPowerOfTwo(out2.width)){
|
||||
Pixmap old = out2;
|
||||
out2 = Pixmaps.resize(out2, Mathf.nextPowerOfTwo(out2.width), Mathf.nextPowerOfTwo(out2.width));
|
||||
old.dispose();
|
||||
}
|
||||
|
||||
out.dispose();
|
||||
pixmap.dispose();
|
||||
|
||||
Cursor cur = newCursor(out2, out2.width / 2, out2.height / 2);
|
||||
out2.dispose();
|
||||
return cur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new cursor by file name.
|
||||
* @param filename the name of the cursor .png file, found in the internal file "cursors/{name}.png"
|
||||
*/
|
||||
public Cursor newCursor(String filename, int scale){
|
||||
if(scale == 1 || OS.isAndroid || OS.isIos) return newCursor(filename);
|
||||
Pixmap base = new Pixmap(Core.files.internal("cursors/" + filename + ".png"));
|
||||
Pixmap result = Pixmaps.scale(base, base.width * scale, base.height * scale);
|
||||
base.dispose();
|
||||
Cursor cur = newCursor(result, result.width /2, result.height /2);
|
||||
result.dispose();
|
||||
return cur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new cursor by file name.
|
||||
* @param filename the name of the cursor .png file, found in the internal file "cursors/{name}.png"
|
||||
*/
|
||||
public Cursor newCursor(String filename){
|
||||
Pixmap p = new Pixmap(Core.files.internal("cursors/" + filename + ".png"));
|
||||
Cursor result = newCursor(p, p.width /2, p.height /2);
|
||||
p.dispose();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new cursor by file name.
|
||||
* @param filename the name of the cursor .png file, found in the internal file "cursors/{name}.png"
|
||||
*/
|
||||
public Cursor newCursor(String filename, int scaling, Color outlineColor, int outlineScaling){
|
||||
return newCursor(new Pixmap(Core.files.internal("cursors/" + filename + ".png")), scaling, outlineColor, outlineScaling);
|
||||
}
|
||||
|
||||
/**Sets the cursor to the default value, e.g. {@link SystemCursor#arrow}.*/
|
||||
public void restoreCursor(){
|
||||
cursor(SystemCursor.arrow);
|
||||
}
|
||||
|
||||
/**Sets the display cursor.*/
|
||||
public void cursor(Cursor cursor){
|
||||
if(lastCursor == cursor) return;
|
||||
|
||||
if(cursor instanceof SystemCursor){
|
||||
if(((SystemCursor)cursor).cursor != null){
|
||||
setCursor(((SystemCursor)cursor).cursor);
|
||||
}else{
|
||||
setSystemCursor((SystemCursor)cursor);
|
||||
}
|
||||
}else{
|
||||
setCursor(cursor);
|
||||
}
|
||||
|
||||
lastCursor = cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only viable on desktop and web. Browsers that support cursor:url() and support the png format (the
|
||||
* pixmap is converted to a data-url of type image/png) should also support custom cursors. Will set the mouse cursor image to
|
||||
* the image represented by the {@link Cursor}. It is recommended to call this function in the main render thread, and maximum one time per frame.
|
||||
* Internal use only!
|
||||
* @param cursor the mouse cursor as a {@link Cursor}
|
||||
*/
|
||||
protected abstract void setCursor(Cursor cursor);
|
||||
|
||||
/**Sets one of the predefined {@link SystemCursor}s.
|
||||
* Internal use only!*/
|
||||
protected abstract void setSystemCursor(SystemCursor systemCursor);
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
for(SystemCursor cursor : SystemCursor.values()){
|
||||
cursor.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/** Class describing the bits per pixel, depth buffer precision, stencil precision and number of MSAA samples. */
|
||||
public static class BufferFormat{
|
||||
/* number of bits per color channel */
|
||||
public final int r, g, b, a;
|
||||
/* number of bits for depth and stencil buffer */
|
||||
public final int depth, stencil;
|
||||
/** number of samples for multi-sample anti-aliasing (MSAA) **/
|
||||
public final int samples;
|
||||
/** whether coverage sampling anti-aliasing is used. in that case you have to clear the coverage buffer as well! */
|
||||
public final boolean coverageSampling;
|
||||
|
||||
public BufferFormat(int r, int g, int b, int a, int depth, int stencil, int samples, boolean coverageSampling){
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
this.depth = depth;
|
||||
this.stencil = stencil;
|
||||
this.samples = samples;
|
||||
this.coverageSampling = coverageSampling;
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return "r: " + r + ", g: " + g + ", b: " + b + ", a: " + a + ", depth: " + depth + ", stencil: " + stencil
|
||||
+ ", num samples: " + samples + ", coverage sampling: " + coverageSampling;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Represents a mouse cursor. Create a cursor via
|
||||
* {@link Graphics#newCursor(Pixmap, int, int)}. To
|
||||
* set the cursor use {@link Graphics#setCursor(Cursor)}.
|
||||
* To use one of the system cursors, call Graphics#setSystemCursor
|
||||
* </p>
|
||||
**/
|
||||
public interface Cursor extends arc.util.Disposable{
|
||||
|
||||
enum SystemCursor implements Cursor{
|
||||
arrow,
|
||||
ibeam,
|
||||
crosshair,
|
||||
hand,
|
||||
horizontalResize,
|
||||
verticalResize;
|
||||
|
||||
/** The override cursor to use when setting this cursor.*/
|
||||
@Nullable Cursor cursor;
|
||||
|
||||
/** Sets the alias for this cursor. */
|
||||
public void set(Cursor cursor){
|
||||
this.cursor = cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
if(cursor != null && !(cursor instanceof SystemCursor)){
|
||||
cursor.dispose();
|
||||
cursor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
package arc;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.input.*;
|
||||
import arc.input.KeyBind.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Interface to the input facilities. This allows polling the state of the keyboard, the touch screen and the accelerometer. On
|
||||
* some backends (desktop) the touch screen is replaced by mouse input. The accelerometer is of course not available on
|
||||
* all backends.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The class also offers methods to use (and test for the presence of) other input systems like vibration, compass, on-screen
|
||||
* keyboards, and cursor capture. Support for simple input dialogs is also provided.
|
||||
* </p>
|
||||
* @author mzechner
|
||||
*/
|
||||
public abstract class Input{
|
||||
/** The default input device (keyboard) */
|
||||
protected KeyboardDevice keyboard = new KeyboardDevice();
|
||||
/** An input multiplexer to handle events. */
|
||||
protected InputMultiplexer inputMultiplexer = new InputMultiplexer(keyboard);
|
||||
/** List of caught keys for Android. */
|
||||
protected IntSet caughtKeys = new IntSet();
|
||||
/** Return Vec2 value for various functions. */
|
||||
protected Vec2 mouseReturn = new Vec2();
|
||||
/** Whether to use keyboard controls on Android. */
|
||||
protected boolean useKeyboard;
|
||||
|
||||
/**Returns the unprojected mouse position (screen -> world).*/
|
||||
public Vec2 mouseWorld(float x, float y){
|
||||
return Core.camera.unproject(mouseReturn.set(x, y));
|
||||
}
|
||||
|
||||
/**Returns the projected mouse position (world -> screen).*/
|
||||
public Vec2 mouseScreen(float x, float y){
|
||||
return Core.camera.project(mouseReturn.set(x, y));
|
||||
}
|
||||
|
||||
/** @return the unprojected mouse position in the world.*/
|
||||
public float mouseWorldX(){
|
||||
return Core.camera.unproject(mouse()).x;
|
||||
}
|
||||
|
||||
/** @return the unprojected mouse position in the world.*/
|
||||
public float mouseWorldY(){
|
||||
return Core.camera.unproject(mouse()).y;
|
||||
}
|
||||
|
||||
/**Returns the unprojected mouse position in the world.*/
|
||||
public Vec2 mouseWorld(){
|
||||
return Core.camera.unproject(mouse());
|
||||
}
|
||||
|
||||
/**Returns the mouse position as a Vec2.*/
|
||||
public Vec2 mouse(){
|
||||
return mouseReturn.set(mouseX(), mouseY());
|
||||
}
|
||||
|
||||
public void setUseKeyboard(boolean useKeyboard){
|
||||
this.useKeyboard = useKeyboard;
|
||||
}
|
||||
|
||||
/** @return whether the keyboard should be preferred for mobile devices - used in text fields. */
|
||||
public boolean useKeyboard(){
|
||||
return useKeyboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The x coordinate of the last touch on touch screen devices and the current mouse position on desktop for the first
|
||||
* pointer in screen coordinates. The screen origin is the top left corner.
|
||||
*/
|
||||
public abstract int mouseX();
|
||||
|
||||
/**
|
||||
* Returns the x coordinate in screen coordinates of the given pointer. Pointers are indexed from 0 to n. The pointer id
|
||||
* identifies the order in which the fingers went down on the screen, e.g. 0 is the first finger, 1 is the second and so on.
|
||||
* When two fingers are touched down and the first one is lifted the second one keeps its index. If another finger is placed on
|
||||
* the touch screen the first free index will be used.
|
||||
* @param pointer the pointer id.
|
||||
* @return the x coordinate
|
||||
*/
|
||||
public abstract int mouseX(int pointer);
|
||||
|
||||
/** @return the different between the current pointer location and the last pointer location on the x-axis. */
|
||||
public abstract int deltaX();
|
||||
|
||||
/** @return the different between the current pointer location and the last pointer location on the x-axis. */
|
||||
public abstract int deltaX(int pointer);
|
||||
|
||||
/**
|
||||
* @return The y coordinate of the last touch on touch screen devices and the current mouse position on desktop for the first
|
||||
* pointer in screen coordinates. The screen origin is the bottom left corner.
|
||||
*/
|
||||
public abstract int mouseY();
|
||||
|
||||
/**
|
||||
* Returns the y coordinate in screen coordinates of the given pointer. Pointers are indexed from 0 to n. The pointer id
|
||||
* identifies the order in which the fingers went down on the screen, e.g. 0 is the first finger, 1 is the second and so on.
|
||||
* When two fingers are touched down and the first one is lifted the second one keeps its index. If another finger is placed on
|
||||
* the touch screen the first free index will be used.
|
||||
* @param pointer the pointer id.
|
||||
* @return the y coordinate
|
||||
*/
|
||||
public abstract int mouseY(int pointer);
|
||||
|
||||
/** @return the different between the current pointer location and the last pointer location on the y-axis. */
|
||||
public abstract int deltaY();
|
||||
|
||||
/** @return the different between the current pointer location and the last pointer location on the y-axis. */
|
||||
public abstract int deltaY(int pointer);
|
||||
|
||||
/** @return whether the screen is currently touched. */
|
||||
public abstract boolean isTouched();
|
||||
|
||||
/** @return whether a new touch down event just occurred. */
|
||||
public abstract boolean justTouched();
|
||||
|
||||
public int getTouches(){
|
||||
int sum = 0;
|
||||
for(int i = 0; i < 10; i++){
|
||||
if(isTouched(i)) sum ++;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the screen is currently touched by the pointer with the given index. Pointers are indexed from 0 to n. The pointer
|
||||
* id identifies the order in which the fingers went down on the screen, e.g. 0 is the first finger, 1 is the second and so on.
|
||||
* When two fingers are touched down and the first one is lifted the second one keeps its index. If another finger is placed on
|
||||
* the touch screen the first free index will be used.
|
||||
* @param pointer the pointer
|
||||
* @return whether the screen is touched by the pointer
|
||||
*/
|
||||
public abstract boolean isTouched(int pointer);
|
||||
|
||||
/** @return the pressure of the first pointer */
|
||||
public float getPressure(){
|
||||
return getPressure(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pressure of the given pointer, where 0 is untouched. On Android it should be
|
||||
* up to 1.0, but it can go above that slightly and its not consistent between devices. On iOS 1.0 is the normal touch
|
||||
* and significantly more of hard touch. Check relevant manufacturer documentation for details.
|
||||
* Check availability with {@link Input#isPeripheralAvailable(Peripheral)}. If not supported, returns 1.0 when touched.
|
||||
* @param pointer the pointer id.
|
||||
* @return the pressure
|
||||
*/
|
||||
public float getPressure(int pointer){
|
||||
return isTouched(pointer) ? 1f : 0f;
|
||||
}
|
||||
|
||||
/** Returns whether one of the two shift keys is currently pressed.*/
|
||||
public boolean shift(){
|
||||
return keyDown(KeyCode.shiftLeft) || keyDown(KeyCode.shiftRight);
|
||||
}
|
||||
|
||||
/** Returns whether one of the two control keys is currently pressed - or, on Macs, the cmd key.*/
|
||||
public boolean ctrl(){
|
||||
return OS.isMac ? keyDown(KeyCode.sym) : keyDown(KeyCode.controlLeft) || keyDown(KeyCode.controlRight);
|
||||
}
|
||||
|
||||
/** Returns whether one of the two alt keys is pressed.*/
|
||||
public boolean alt(){
|
||||
return keyDown(KeyCode.altLeft) || keyDown(KeyCode.altRight);
|
||||
}
|
||||
|
||||
/** Returns whether the key is pressed. */
|
||||
public boolean keyDown(KeyCode key){
|
||||
return keyboard.isPressed(key);
|
||||
}
|
||||
|
||||
/** Returns whether the key has just been pressed. */
|
||||
public boolean keyTap(KeyCode key){
|
||||
return keyboard.isTapped(key);
|
||||
}
|
||||
|
||||
/** Returns whether the key has just been released. */
|
||||
public boolean keyRelease(KeyCode key){
|
||||
return keyboard.isReleased(key);
|
||||
}
|
||||
|
||||
/** Returns the [-1, 1] axis value of a key. */
|
||||
public float axis(KeyCode key){
|
||||
return keyboard.getAxis(key);
|
||||
}
|
||||
|
||||
/** Returns whether the keybind is pressed. */
|
||||
public boolean keyDown(KeyBind key){
|
||||
return key.value.key != null && keyboard.isPressed(key.value.key);
|
||||
}
|
||||
|
||||
/** Returns whether the key has just been pressed. */
|
||||
public boolean keyTap(KeyBind key){
|
||||
return key.value.key != null && keyboard.isTapped(key.value.key);
|
||||
}
|
||||
|
||||
/** Returns whether the key has just been released. */
|
||||
public boolean keyRelease(KeyBind key){
|
||||
return key.value.key != null && keyboard.isReleased(key.value.key);
|
||||
}
|
||||
|
||||
/** Returns the [-1, 1] axis value of a key. */
|
||||
public float axis(KeyBind key){
|
||||
Axis axis = key.value;
|
||||
if(axis.key != null){
|
||||
return keyboard.getAxis(axis.key);
|
||||
}else{
|
||||
return keyboard.isPressed(axis.min) && keyboard.isPressed(axis.max) ? 0 :
|
||||
keyboard.isPressed(axis.min) ? -1 : keyboard.isPressed(axis.max) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the [-1, 1] axis value of a key.
|
||||
* In the case of keyboard-based axes, this will only return a value if one of the axes was just pressed. */
|
||||
public float axisTap(KeyBind key){
|
||||
Axis axis = key.value;
|
||||
if(axis.key != null){
|
||||
return keyboard.getAxis(axis.key);
|
||||
}else{
|
||||
return keyboard.isTapped(axis.min) ? -1 : keyboard.isTapped(axis.max) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* System dependent method to input a string of text. A dialog box will be created with the given title and the given text as a
|
||||
* message for the user. Once the dialog has been closed the consumer be called on the rendering thread.
|
||||
*/
|
||||
public void getTextInput(TextInput input){
|
||||
}
|
||||
|
||||
/** @return on mobile, whether text input is currently being fetched. Not implemented on other platforms. */
|
||||
public boolean isShowingTextInput(){
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the on-screen keyboard visible if available. Only applicable on mobile.
|
||||
* @param visible visible or not
|
||||
*/
|
||||
public void setOnscreenKeyboardVisible(boolean visible){
|
||||
}
|
||||
|
||||
/**
|
||||
* Vibrates for the given amount of time. Note that you'll need the permission
|
||||
* <code> <uses-permission android:name="android.permission.VIBRATE" /></code> in your manifest file in order for this to work.
|
||||
* @param milliseconds the number of milliseconds to vibrate.
|
||||
*/
|
||||
public void vibrate(int milliseconds){
|
||||
}
|
||||
|
||||
/**
|
||||
* Vibrate with a given pattern. Pass in an array of ints that are the times at which to turn on or off the vibrator. The first
|
||||
* one is how long to wait before turning it on, and then after that it alternates. If you want to repeat, pass the index into
|
||||
* the pattern at which to start the repeat.
|
||||
* @param pattern an array of longs of times to turn the vibrator on or off.
|
||||
* @param repeat the index into pattern at which to repeat, or -1 if you don't want to repeat.
|
||||
*/
|
||||
public void vibrate(long[] pattern, int repeat){
|
||||
}
|
||||
|
||||
/** Stops the vibrator */
|
||||
public void cancelVibrate(){
|
||||
}
|
||||
|
||||
/** @return The acceleration force in m/s^2 applied to the device, including the force of gravity */
|
||||
public Vec3 getAccelerometer(){
|
||||
return Vec3.Zero;
|
||||
}
|
||||
|
||||
/** @return The rate of rotation in rad/s. */
|
||||
public Vec3 getGyroscope(){
|
||||
return Vec3.Zero;
|
||||
}
|
||||
|
||||
/** @return the device's orientation in degrees in the format (pitch, roll, azimuth) corresponding to x,y,z. */
|
||||
public Vec3 getOrientation(){
|
||||
return Vec3.Zero;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rotation matrix describing the devices rotation as per <a href=
|
||||
* "http://developer.android.com/reference/android/hardware/SensorManager.html#getRotationMatrix(float[], float[], float[], float[])"
|
||||
* >SensorManager#getRotationMatrix(float[], float[], float[], float[])</a>. Does not manipulate the matrix if the platform
|
||||
* does not have an accelerometer.
|
||||
*/
|
||||
public void getRotationMatrix(float[] matrix){
|
||||
}
|
||||
|
||||
/** @return the time of the event currently reported to the {@link InputProcessor}. */
|
||||
public abstract long getCurrentEventTime();
|
||||
|
||||
/**
|
||||
* Sets whether the specified button on Android should be caught. This will prevent the app from processing the key. Will have no effect
|
||||
* on the desktop.
|
||||
* @param c whether to catch the button
|
||||
*/
|
||||
public void setCatch(KeyCode code, boolean c){
|
||||
if(c){
|
||||
caughtKeys.add(code.ordinal());
|
||||
}else{
|
||||
caughtKeys.remove(code.ordinal());
|
||||
}
|
||||
}
|
||||
|
||||
/** @return whether the back button is currently being caught */
|
||||
public boolean isCatch(KeyCode code){
|
||||
return caughtKeys.contains(code.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link InputProcessor} that will receive all touch and key input events. It will be called before the
|
||||
* {@link ApplicationListener#update()} method each frame.
|
||||
* @param processor the InputProcessor
|
||||
* */
|
||||
public void addProcessor(InputProcessor processor){
|
||||
inputMultiplexer.addProcessor(processor);
|
||||
}
|
||||
|
||||
/**Removes a {@link InputProcessor} from the chain.*/
|
||||
public void removeProcessor(InputProcessor processor){
|
||||
inputMultiplexer.removeProcessor(processor);
|
||||
}
|
||||
|
||||
/** @return the currently set {@link InputProcessor} or null. */
|
||||
public Seq<InputProcessor> getInputProcessors(){
|
||||
return inputMultiplexer.getProcessors();
|
||||
}
|
||||
|
||||
public InputMultiplexer getInputMultiplexer(){
|
||||
return inputMultiplexer;
|
||||
}
|
||||
|
||||
/** Returns the default input device (keyboard). */
|
||||
public KeyboardDevice getKeyboard(){
|
||||
return keyboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries whether a {@link Peripheral} is currently available. In case of Android and the {@link Peripheral#hardwareKeyboard}
|
||||
* this returns the whether the keyboard is currently slid out or not.
|
||||
* @param peripheral the {@link Peripheral}
|
||||
* @return whether the peripheral is available or not.
|
||||
*/
|
||||
public boolean isPeripheralAvailable(Peripheral peripheral){
|
||||
return peripheral == Peripheral.hardwareKeyboard;
|
||||
}
|
||||
|
||||
/** @return the rotation of the device with respect to its native orientation. */
|
||||
public int getRotation(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @return the native orientation of the device. */
|
||||
public Orientation getNativeOrientation(){
|
||||
return Orientation.landscape;
|
||||
}
|
||||
|
||||
/** Internal method for looking up a key's name based on keyboard layout. Internal use only - use {@link KeyCode#getName()} instead. */
|
||||
public String getKeyName(KeyCode code){
|
||||
return code.value;
|
||||
}
|
||||
|
||||
public enum Orientation{
|
||||
landscape, portrait
|
||||
}
|
||||
|
||||
/** Enumeration of potentially available peripherals. Use with {@link Input#isPeripheralAvailable(Peripheral)}. */
|
||||
public enum Peripheral{
|
||||
hardwareKeyboard, onscreenKeyboard, multitouchScreen, accelerometer, compass, vibrator, gyroscope, rotationVector, pressure
|
||||
}
|
||||
|
||||
/** Parameters for text input. */
|
||||
public static class TextInput{
|
||||
public boolean multiline = false;
|
||||
public boolean allowEmpty = true;
|
||||
public String title = "";
|
||||
public String text = "";
|
||||
public String message = "";
|
||||
public boolean numeric;
|
||||
public Cons<String> accepted = s -> { };
|
||||
public Runnable canceled = () -> { };
|
||||
public int maxLength = -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,463 @@
|
||||
package arc;
|
||||
|
||||
import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.struct.*;
|
||||
import arc.struct.ObjectMap.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import arc.util.serialization.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
public class Settings{
|
||||
protected final static byte typeBool = 0, typeInt = 1, typeLong = 2, typeFloat = 3, typeString = 4, typeBinary = 5;
|
||||
protected final static int maxBackups = 10, minBackupIntervalMs = 1000 * 60 * 2;
|
||||
|
||||
//general state data
|
||||
protected Fi dataDirectory;
|
||||
protected String appName = "app";
|
||||
protected ObjectMap<String, Object> defaults = new ObjectMap<>();
|
||||
protected HashMap<String, Object> values = new HashMap<>();
|
||||
protected boolean modified;
|
||||
protected Cons<Throwable> errorHandler;
|
||||
protected boolean hasErrored;
|
||||
protected boolean shouldAutosave = true;
|
||||
protected boolean loaded = false;
|
||||
protected boolean writeCompressed = false;
|
||||
private long lastBackupTime;
|
||||
protected ExecutorService executor = Threads.executor("Settings Backup", 1);
|
||||
|
||||
//IO utility objects
|
||||
protected ByteArrayOutputStream byteStream = new ByteArrayOutputStream(32);
|
||||
protected ReusableByteInStream byteInputStream = new ReusableByteInStream();
|
||||
protected Json json = new Json();
|
||||
|
||||
public void setJson(Json json){
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
public void setCompressed(boolean compressed){
|
||||
this.writeCompressed = compressed;
|
||||
}
|
||||
|
||||
public String getAppName(){
|
||||
return appName;
|
||||
}
|
||||
|
||||
public void setAppName(String name){
|
||||
appName = name;
|
||||
}
|
||||
|
||||
/**Sets the error handler function.
|
||||
* This function gets called when {@link #forceSave} or {@link #load} fails. This can occur most often on browsers,
|
||||
* where extensions can block writing to local storage.*/
|
||||
public void setErrorHandler(Cons<Throwable> handler){
|
||||
errorHandler = handler;
|
||||
}
|
||||
|
||||
/** Set whether the data should autosave immediately upon changing a value.
|
||||
* Default value: true. */
|
||||
public void setAutosave(boolean autosave){
|
||||
this.shouldAutosave = autosave;
|
||||
}
|
||||
|
||||
public boolean modified(){
|
||||
return modified;
|
||||
}
|
||||
|
||||
/** Loads all values and keybinds. */
|
||||
public synchronized void load(){
|
||||
try{
|
||||
loadValues();
|
||||
}catch(Throwable error){
|
||||
Log.err("Error loading settings", error);
|
||||
if(errorHandler != null){
|
||||
if(!hasErrored) errorHandler.get(error);
|
||||
}else{
|
||||
throw error;
|
||||
}
|
||||
hasErrored = true;
|
||||
}
|
||||
//if loading failed, it still counts
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
/** Saves all values and keybinds. */
|
||||
public synchronized void forceSave(){
|
||||
//never loaded, nothing to save
|
||||
if(!loaded) return;
|
||||
try{
|
||||
saveValues();
|
||||
}catch(Throwable error){
|
||||
Log.err("Error writing settings", error);
|
||||
if(errorHandler != null){
|
||||
if(!hasErrored) errorHandler.get(error);
|
||||
}else{
|
||||
throw error;
|
||||
}
|
||||
hasErrored = true;
|
||||
}
|
||||
modified = false;
|
||||
}
|
||||
|
||||
/** Manually save, if the settings have been loaded at some point. */
|
||||
public synchronized void manualSave(){
|
||||
if(loaded){
|
||||
forceSave();
|
||||
}
|
||||
}
|
||||
|
||||
/** Saves if any modifications were done. */
|
||||
public synchronized void autosave(){
|
||||
if(modified && shouldAutosave){
|
||||
forceSave();
|
||||
modified = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Loads a settings file into {@link #values} using the specified appName. */
|
||||
public synchronized void loadValues(){
|
||||
//don't load settings files if neither of them exist
|
||||
if(!getSettingsFile().exists() && !getBackupSettingsFile().exists()){
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
loadValues(getSettingsFile());
|
||||
|
||||
//back up the save file, as the values have now been loaded successfully
|
||||
getSettingsFile().copyTo(getBackupSettingsFile());
|
||||
}catch(Throwable e){
|
||||
Log.err("Failed to load base settings file, attempting to load backup.", e);
|
||||
|
||||
Seq<Fi> attempts = getBackupFolder().seq().add(getBackupSettingsFile());
|
||||
//sort with latest modified file first
|
||||
attempts.sort(Structs.comparingLong(f -> -f.lastModified()));
|
||||
|
||||
for(Fi attempt : attempts){
|
||||
try{
|
||||
|
||||
loadValues(attempt);
|
||||
attempt.copyTo(getSettingsFile());
|
||||
|
||||
Log.info("Loaded backup settings file successfully!");
|
||||
|
||||
//break out of loop, we're done here
|
||||
return;
|
||||
}catch(Throwable e3){
|
||||
Log.err("Failed to load backup settings file.", e3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void loadValues(Fi file) throws IOException{
|
||||
//read the first few bytes to check if it is compressed.
|
||||
byte[] header = new byte[2];
|
||||
file.readBytes(header, 0, 2);
|
||||
boolean compressed = header[0] == (byte)0x78 && (header[1] == (byte)0x01 || header[1] == (byte)0x5E || header[1] == (byte)0x9c || header[1] == (byte)0xda);
|
||||
|
||||
try(DataInputStream stream = new DataInputStream(compressed ? new InflaterInputStream(file.read(8192)) : file.read(8192))){
|
||||
int amount = stream.readInt();
|
||||
//current theory: when corruptions happen, the only things written to the stream are a bunch of zeroes
|
||||
//try to anticipate this case and throw an exception when 0 values are written
|
||||
if(amount <= 0) throw new IOException("0 values are not allowed.");
|
||||
for(int i = 0; i < amount; i++){
|
||||
String key = stream.readUTF();
|
||||
|
||||
byte type = stream.readByte();
|
||||
|
||||
switch(type){
|
||||
case typeBool:
|
||||
values.put(key, stream.readBoolean());
|
||||
break;
|
||||
case typeInt:
|
||||
values.put(key, stream.readInt());
|
||||
break;
|
||||
case typeLong:
|
||||
values.put(key, stream.readLong());
|
||||
break;
|
||||
case typeFloat:
|
||||
values.put(key, stream.readFloat());
|
||||
break;
|
||||
case typeString:
|
||||
values.put(key, stream.readUTF());
|
||||
break;
|
||||
case typeBinary:
|
||||
int length = stream.readInt();
|
||||
byte[] bytes = new byte[length];
|
||||
stream.readFully(bytes);
|
||||
values.put(key, bytes);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Unknown key type: " + type);
|
||||
}
|
||||
}
|
||||
//make sure all data was read - this helps with potential corruption
|
||||
int end = stream.read();
|
||||
if(end != -1){
|
||||
throw new IOException("Trailing settings data; expected EOF, but got: " + end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Saves all entries from {@link #values} into the correct location. */
|
||||
public synchronized void saveValues(){
|
||||
Fi file = getSettingsFile();
|
||||
|
||||
try(DataOutputStream stream = new DataOutputStream(writeCompressed ? new FastDeflaterOutputStream(file.write(false, 8192)) : file.write(false, 8192))){
|
||||
stream.writeInt(values.size());
|
||||
|
||||
for(Map.Entry<String, Object> entry : values.entrySet()){
|
||||
stream.writeUTF(entry.getKey());
|
||||
|
||||
Object value = entry.getValue();
|
||||
|
||||
if(value instanceof Boolean){
|
||||
stream.writeByte(typeBool);
|
||||
stream.writeBoolean((Boolean)value);
|
||||
}else if(value instanceof Integer){
|
||||
stream.writeByte(typeInt);
|
||||
stream.writeInt((Integer)value);
|
||||
}else if(value instanceof Long){
|
||||
stream.writeByte(typeLong);
|
||||
stream.writeLong((Long)value);
|
||||
}else if(value instanceof Float){
|
||||
stream.writeByte(typeFloat);
|
||||
stream.writeFloat((Float)value);
|
||||
}else if(value instanceof String){
|
||||
stream.writeByte(typeString);
|
||||
stream.writeUTF((String)value);
|
||||
}else if(value instanceof byte[]){
|
||||
stream.writeByte(typeBinary);
|
||||
stream.writeInt(((byte[])value).length);
|
||||
stream.write((byte[])value);
|
||||
}
|
||||
}
|
||||
|
||||
}catch(Throwable e){
|
||||
//file is now corrupt, delete it
|
||||
file.delete();
|
||||
throw new RuntimeException("Error writing preferences: " + file, e);
|
||||
}
|
||||
|
||||
if(Time.timeSinceMillis(lastBackupTime) > minBackupIntervalMs){
|
||||
lastBackupTime = Time.millis();
|
||||
|
||||
executor.submit(() -> {
|
||||
//make sure two backups can't happen at once.
|
||||
synchronized(this){
|
||||
Fi backupFolder = getBackupFolder();
|
||||
|
||||
Seq<Fi> previous = backupFolder.seq();
|
||||
//make sure first file is most recent, last is oldest
|
||||
previous.sort(Structs.comparingLong(f -> -f.lastModified()));
|
||||
|
||||
//create new entry in the backup folder
|
||||
file.copyTo(backupFolder.child(System.currentTimeMillis() + ".bin"));
|
||||
|
||||
//delete older backups if they exceed the max backup count
|
||||
while(previous.size >= maxBackups){
|
||||
previous.pop().delete();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the file used for writing settings to. Not available on all platforms! */
|
||||
public Fi getSettingsFile(){
|
||||
return getDataDirectory().child("settings.bin");
|
||||
}
|
||||
|
||||
public Fi getBackupFolder(){
|
||||
return getDataDirectory().child("settings_backups");
|
||||
}
|
||||
|
||||
public Fi getBackupSettingsFile(){
|
||||
return getDataDirectory().child("settings_backup.bin");
|
||||
}
|
||||
|
||||
/** Returns the directory where all settings and data is placed. */
|
||||
public Fi getDataDirectory(){
|
||||
return dataDirectory == null ? Core.files.absolute(OS.getAppDataDirectoryString(appName)) : dataDirectory;
|
||||
}
|
||||
|
||||
/** Sets the settings file where everything is written to. */
|
||||
public void setDataDirectory(Fi file){
|
||||
this.dataDirectory = file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a list of defaults values.
|
||||
* Format: name1, default1, name2, default2, etc
|
||||
*/
|
||||
public synchronized void defaults(Object... objects){
|
||||
for(int i = 0; i < objects.length; i += 2){
|
||||
defaults.put((String)objects[i], objects[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Clears all preference values. */
|
||||
public synchronized void clear(){
|
||||
values.clear();
|
||||
}
|
||||
|
||||
public synchronized Object getDefault(String name){
|
||||
return defaults.get(name);
|
||||
}
|
||||
|
||||
public synchronized boolean has(String name){
|
||||
return values.containsKey(name);
|
||||
}
|
||||
|
||||
public synchronized Object get(String name, Object def){
|
||||
return values.containsKey(name) ? values.get(name) : def;
|
||||
}
|
||||
|
||||
public boolean isModified(){
|
||||
return modified;
|
||||
}
|
||||
|
||||
public synchronized void putJson(String name, Object value){
|
||||
putJson(name, null, value);
|
||||
}
|
||||
|
||||
public synchronized void putJson(String name, Class<?> elementType, Object value){
|
||||
byteStream.reset();
|
||||
|
||||
json.toUBJson(value, value == null ? null : value.getClass(), elementType, byteStream);
|
||||
|
||||
put(name, byteStream.toByteArray());
|
||||
|
||||
modified = true;
|
||||
}
|
||||
|
||||
public synchronized <T> T getJson(String name, Class<T> type, Class elementType, Prov<T> def){
|
||||
try{
|
||||
if(!has(name)) return def.get();
|
||||
byteInputStream.setBytes(getBytes(name));
|
||||
return json.readValue(type, elementType, UBJson.read(byteInputStream));
|
||||
}catch(Throwable e){
|
||||
Log.err("Error reading JSON with key '" + name + "'", e);
|
||||
return def.get();
|
||||
}
|
||||
}
|
||||
|
||||
public <T> T getJson(String name, Class<T> type, Prov<T> def){
|
||||
return getJson(name, type, null, def);
|
||||
}
|
||||
|
||||
public float getFloat(String name, float def){
|
||||
return (float)get(name, def);
|
||||
}
|
||||
|
||||
public long getLong(String name, long def){
|
||||
return (long)get(name, def);
|
||||
}
|
||||
|
||||
public Long getLong(String name){
|
||||
return getLong(name, 0);
|
||||
}
|
||||
|
||||
public int getInt(String name, int def){
|
||||
return (int)get(name, def);
|
||||
}
|
||||
|
||||
public boolean getBool(String name, boolean def){
|
||||
Object val = get(name, def);
|
||||
return val instanceof Boolean ? (Boolean)val : def;
|
||||
}
|
||||
|
||||
public byte[] getBytes(String name, byte[] def){
|
||||
return (byte[])get(name, def);
|
||||
}
|
||||
|
||||
public String getString(String name, String def){
|
||||
return (String)get(name, def);
|
||||
}
|
||||
|
||||
public float getFloat(String name){
|
||||
return getFloat(name, (float)defaults.get(name, 0f));
|
||||
}
|
||||
|
||||
public int getInt(String name){
|
||||
return getInt(name, (int)defaults.get(name, 0));
|
||||
}
|
||||
|
||||
public boolean getBool(String name){
|
||||
return getBool(name, (boolean)defaults.get(name, false));
|
||||
}
|
||||
|
||||
/** Runs the specified code once, and never again. */
|
||||
public void getBoolOnce(String name, Runnable run){
|
||||
if(!getBool(name, false)){
|
||||
run.run();
|
||||
put(name, true);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true once, and never again. */
|
||||
public boolean getBoolOnce(String name){
|
||||
boolean val = getBool(name, false);
|
||||
put(name, true);
|
||||
return !val;
|
||||
}
|
||||
|
||||
public byte[] getBytes(String name){
|
||||
return getBytes(name, (byte[])defaults.get(name));
|
||||
}
|
||||
|
||||
public String getString(String name){
|
||||
return getString(name, (String)defaults.get(name));
|
||||
}
|
||||
|
||||
public void putAll(ObjectMap<String, Object> map){
|
||||
for(Entry<String, Object> entry : map.entries()){
|
||||
put(entry.key, entry.value);
|
||||
}
|
||||
}
|
||||
|
||||
/** Toggles a boolean value. */
|
||||
public void toggle(String name){
|
||||
put(name, !getBool(name));
|
||||
}
|
||||
|
||||
/** JS compatibility method. */
|
||||
public void putInt(String name, int value){
|
||||
put(name, value);
|
||||
}
|
||||
|
||||
/** JS compatibility method. */
|
||||
public void putFloat(String name, float value){
|
||||
put(name, value);
|
||||
}
|
||||
|
||||
/** Stores an object in the preference map. */
|
||||
public synchronized void put(String name, Object object){
|
||||
if(object instanceof Float || object instanceof Integer || object instanceof Boolean || object instanceof Long
|
||||
|| object instanceof String || object instanceof byte[]){
|
||||
values.put(name, object);
|
||||
modified = true;
|
||||
}else{
|
||||
throw new IllegalArgumentException("Invalid object stored: " + (object == null ? null : object.getClass()) + ".");
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void remove(String name){
|
||||
values.remove(name);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
public synchronized Iterable<String> keys(){
|
||||
return values.keySet();
|
||||
}
|
||||
|
||||
public synchronized int keySize(){
|
||||
return values.size();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package arc.assets;
|
||||
|
||||
import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.util.*;
|
||||
|
||||
/**
|
||||
* Describes an asset to be loaded by its filename, type and {@link AssetLoaderParameters}. Instances of this are used in
|
||||
* {@link AssetLoadingTask} to load the actual asset.
|
||||
* @author mzechner
|
||||
*/
|
||||
public class AssetDescriptor<T>{
|
||||
public final String fileName;
|
||||
public final Class<T> type;
|
||||
public final AssetLoaderParameters params;
|
||||
/** The resolved file. May be null if the fileName has not been resolved yet. */
|
||||
public Fi file;
|
||||
/** Callback for when this asset is loaded.*/
|
||||
public Cons<T> loaded = t -> {};
|
||||
/** Callback for when this asset has an error.*/
|
||||
public @Nullable Cons<Throwable> errored = null;
|
||||
|
||||
public AssetDescriptor(Class<T> assetType){
|
||||
this(assetType.getSimpleName(), assetType, null);
|
||||
}
|
||||
|
||||
public AssetDescriptor(String fileName, Class<T> assetType){
|
||||
this(fileName, assetType, null);
|
||||
}
|
||||
|
||||
/** Creates an AssetDescriptor with an already resolved name. */
|
||||
public AssetDescriptor(Fi file, Class<T> assetType){
|
||||
this(file, assetType, null);
|
||||
}
|
||||
|
||||
public AssetDescriptor(String fileName, Class<T> assetType, AssetLoaderParameters<T> params){
|
||||
this.fileName = fileName.replaceAll("\\\\", "/");
|
||||
this.type = assetType;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
/** Creates an AssetDescriptor with an already resolved name. */
|
||||
public AssetDescriptor(Fi file, Class<T> assetType, AssetLoaderParameters<T> params){
|
||||
this.fileName = file.path().replaceAll("\\\\", "/");
|
||||
this.file = file;
|
||||
this.type = assetType;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return fileName +
|
||||
", " +
|
||||
type.getName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package arc.assets;
|
||||
|
||||
public interface AssetErrorListener{
|
||||
void error(AssetDescriptor asset, Throwable throwable);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package arc.assets;
|
||||
|
||||
public class AssetLoaderParameters<T>{
|
||||
public LoadedCallback loadedCallback;
|
||||
|
||||
public AssetLoaderParameters(){
|
||||
|
||||
}
|
||||
|
||||
public AssetLoaderParameters(LoadedCallback loadedCallback){
|
||||
this.loadedCallback = loadedCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback interface that will be invoked when the {@link AssetManager} loaded an asset.
|
||||
* @author mzechner
|
||||
*/
|
||||
public interface LoadedCallback{
|
||||
void finishedLoading(AssetManager assetManager, String fileName, Class type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package arc.assets;
|
||||
|
||||
import arc.assets.loaders.AssetLoader;
|
||||
import arc.assets.loaders.AsynchronousAssetLoader;
|
||||
import arc.assets.loaders.SynchronousAssetLoader;
|
||||
import arc.struct.Seq;
|
||||
import arc.files.Fi;
|
||||
import arc.util.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* Responsible for loading an asset through an {@link AssetLoader} based on an {@link AssetDescriptor}.
|
||||
* @author mzechner
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
class AssetLoadingTask implements Callable<Void>{
|
||||
final AssetDescriptor assetDesc;
|
||||
final AssetLoader loader;
|
||||
final ExecutorService executor;
|
||||
final long startTime;
|
||||
AssetManager manager;
|
||||
volatile boolean asyncDone = false;
|
||||
volatile boolean dependenciesLoaded = false;
|
||||
volatile Seq<AssetDescriptor> dependencies;
|
||||
volatile Future<Void> depsFuture = null;
|
||||
volatile Future<Void> loadFuture = null;
|
||||
volatile Object asset = null;
|
||||
|
||||
int ticks = 0;
|
||||
volatile boolean cancel = false;
|
||||
|
||||
public AssetLoadingTask(AssetManager manager, AssetDescriptor assetDesc, AssetLoader loader, ExecutorService threadPool){
|
||||
this.manager = manager;
|
||||
this.assetDesc = assetDesc;
|
||||
this.loader = loader;
|
||||
this.executor = threadPool;
|
||||
startTime = Time.nanos();
|
||||
}
|
||||
|
||||
/** Loads parts of the asset asynchronously if the loader is an {@link AsynchronousAssetLoader}. */
|
||||
@Override
|
||||
public Void call(){
|
||||
AsynchronousAssetLoader asyncLoader = (AsynchronousAssetLoader)loader;
|
||||
if(!dependenciesLoaded){
|
||||
dependencies = asyncLoader.getDependencies(assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
if(dependencies != null){
|
||||
removeDuplicates(dependencies);
|
||||
manager.injectDependencies(assetDesc.fileName, dependencies);
|
||||
}else{
|
||||
// if we have no dependencies, we load the async part of the task immediately.
|
||||
asyncLoader.loadAsync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
asyncDone = true;
|
||||
}
|
||||
}else{
|
||||
asyncLoader.loadAsync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the loading of the asset. In case the asset is loaded with an {@link AsynchronousAssetLoader}, the loaders
|
||||
* {@link AsynchronousAssetLoader#loadAsync(AssetManager, String, Fi, AssetLoaderParameters)} method is first called on
|
||||
* a worker thread. Once this method returns, the rest of the asset is loaded on the rendering thread via
|
||||
* {@link AsynchronousAssetLoader#loadSync(AssetManager, String, Fi, AssetLoaderParameters)}.
|
||||
* @return true in case the asset was fully loaded, false otherwise
|
||||
*/
|
||||
public boolean update(){
|
||||
ticks++;
|
||||
if(loader instanceof SynchronousAssetLoader){
|
||||
handleSyncLoader();
|
||||
}else{
|
||||
handleAsyncLoader();
|
||||
}
|
||||
return asset != null;
|
||||
}
|
||||
|
||||
private void handleSyncLoader(){
|
||||
SynchronousAssetLoader syncLoader = (SynchronousAssetLoader)loader;
|
||||
if(!dependenciesLoaded){
|
||||
dependenciesLoaded = true;
|
||||
dependencies = syncLoader.getDependencies(assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
if(dependencies == null){
|
||||
asset = syncLoader.load(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
return;
|
||||
}
|
||||
removeDuplicates(dependencies);
|
||||
manager.injectDependencies(assetDesc.fileName, dependencies);
|
||||
}else{
|
||||
asset = syncLoader.load(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAsyncLoader(){
|
||||
AsynchronousAssetLoader asyncLoader = (AsynchronousAssetLoader)loader;
|
||||
if(!dependenciesLoaded){
|
||||
if(depsFuture == null){
|
||||
depsFuture = executor.submit(this);
|
||||
}else{
|
||||
if(depsFuture.isDone()){
|
||||
try{
|
||||
depsFuture.get();
|
||||
}catch(Exception e){
|
||||
throw new ArcRuntimeException("Couldn't load dependencies of asset: " + assetDesc.fileName, e);
|
||||
}
|
||||
dependenciesLoaded = true;
|
||||
if(asyncDone){
|
||||
asset = asyncLoader.loadSync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(loadFuture == null && !asyncDone){
|
||||
loadFuture = executor.submit(this);
|
||||
}else{
|
||||
if(asyncDone){
|
||||
asset = asyncLoader.loadSync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
}else if(loadFuture.isDone()){
|
||||
try{
|
||||
loadFuture.get();
|
||||
}catch(Exception e){
|
||||
throw new ArcRuntimeException("Couldn't load asset: " + assetDesc.fileName, e);
|
||||
}
|
||||
asset = asyncLoader.loadSync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Fi resolve(AssetLoader loader, AssetDescriptor assetDesc){
|
||||
if(assetDesc.file == null) assetDesc.file = loader.resolve(assetDesc.fileName);
|
||||
return assetDesc.file;
|
||||
}
|
||||
|
||||
public Object getAsset(){
|
||||
return asset;
|
||||
}
|
||||
|
||||
private void removeDuplicates(Seq<AssetDescriptor> array){
|
||||
boolean ordered = array.ordered;
|
||||
array.ordered = true;
|
||||
for(int i = 0; i < array.size; ++i){
|
||||
final String fn = array.get(i).fileName;
|
||||
final Class type = array.get(i).type;
|
||||
for(int j = array.size - 1; j > i; --j){
|
||||
if(type == array.get(j).type && fn.equals(array.get(j).fileName))
|
||||
array.remove(j);
|
||||
}
|
||||
}
|
||||
array.ordered = ordered;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,877 @@
|
||||
package arc.assets;
|
||||
|
||||
import arc.*;
|
||||
import arc.assets.loaders.*;
|
||||
import arc.audio.*;
|
||||
import arc.files.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.font.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.struct.*;
|
||||
import arc.struct.ObjectMap.*;
|
||||
import arc.util.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* Loads and stores assets like textures, bitmapfonts, tile maps, sounds, music and so on.
|
||||
* @author mzechner
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class AssetManager implements Disposable{
|
||||
final ObjectMap<Class, ObjectMap<String, RefCountedContainer>> assets = new ObjectMap<>();
|
||||
final ObjectMap<String, Class> assetTypes = new ObjectMap<>();
|
||||
final ObjectMap<String, Seq<String>> assetDependencies = new ObjectMap<>();
|
||||
final ObjectSet<String> injected = new ObjectSet<>();
|
||||
|
||||
final ObjectMap<Class, ObjectMap<String, AssetLoader>> loaders = new ObjectMap<>();
|
||||
final Seq<AssetDescriptor> loadQueue = new Seq<>();
|
||||
final ExecutorService executor;
|
||||
|
||||
final Seq<AssetLoadingTask> tasks = new Seq<>();
|
||||
final FileHandleResolver resolver;
|
||||
AssetErrorListener listener = null;
|
||||
int loaded = 0;
|
||||
int toLoad = 0;
|
||||
int peakTasks = 0;
|
||||
|
||||
/** Creates a new AssetManager with all default loaders. */
|
||||
public AssetManager(){
|
||||
this(Core.files::internal);
|
||||
}
|
||||
|
||||
/** Creates a new AssetManager with all default loaders. */
|
||||
public AssetManager(FileHandleResolver resolver){
|
||||
this(resolver, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new AssetManager with optionally all default loaders. If you don't add the default loaders then you do have to
|
||||
* manually add the loaders you need, including any loaders they might depend on.
|
||||
* @param defaultLoaders whether to add the default loaders
|
||||
*/
|
||||
public AssetManager(FileHandleResolver resolver, boolean defaultLoaders){
|
||||
this.resolver = resolver;
|
||||
if(defaultLoaders){
|
||||
setLoader(Font.class, new FontLoader(resolver));
|
||||
setLoader(Music.class, new MusicLoader(resolver));
|
||||
setLoader(Pixmap.class, new PixmapLoader(resolver));
|
||||
setLoader(Sound.class, new SoundLoader(resolver));
|
||||
setLoader(TextureAtlas.class, new TextureAtlasLoader(resolver));
|
||||
setLoader(Texture.class, new TextureLoader(resolver));
|
||||
setLoader(I18NBundle.class, new I18NBundleLoader(resolver));
|
||||
setLoader(Shader.class, new ShaderProgramLoader(resolver));
|
||||
setLoader(Cubemap.class, new CubemapLoader(resolver));
|
||||
}
|
||||
executor = Threads.executor("Assets", 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link FileHandleResolver} for which this AssetManager was loaded with.
|
||||
* @return the file handle resolver which this AssetManager uses
|
||||
*/
|
||||
public FileHandleResolver getFileHandleResolver(){
|
||||
return resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName the asset file name
|
||||
* @return the asset
|
||||
*/
|
||||
public synchronized <T> T get(String fileName){
|
||||
Class<T> type = assetTypes.get(fileName);
|
||||
if(type == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
ObjectMap<String, RefCountedContainer> assetsByType = assets.get(type);
|
||||
if(assetsByType == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
RefCountedContainer assetContainer = assetsByType.get(fileName);
|
||||
if(assetContainer == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
T asset = (T)assetContainer.object;
|
||||
if(asset == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
return asset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName the asset file name
|
||||
* @param type the asset type
|
||||
* @return the asset
|
||||
*/
|
||||
public synchronized <T> T get(String fileName, Class<T> type){
|
||||
ObjectMap<String, RefCountedContainer> assetsByType = assets.get(type);
|
||||
if(assetsByType == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
RefCountedContainer assetContainer = assetsByType.get(fileName);
|
||||
if(assetContainer == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
T asset = (T)assetContainer.object;
|
||||
if(asset == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
return asset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName the asset file name
|
||||
* @param type the asset type
|
||||
* @return the asset, or null if not found
|
||||
*/
|
||||
public synchronized <T> T getOrNull(String fileName, Class<T> type){
|
||||
ObjectMap<String, RefCountedContainer> assetsByType = assets.get(type);
|
||||
if(assetsByType == null) return null;
|
||||
RefCountedContainer assetContainer = assetsByType.get(fileName);
|
||||
if(assetContainer == null) return null;
|
||||
return (T)assetContainer.object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type the asset type
|
||||
* @return all the assets matching the specified type
|
||||
*/
|
||||
public synchronized <T> Seq<T> getAll(Class<T> type, Seq<T> out){
|
||||
ObjectMap<String, RefCountedContainer> assetsByType = assets.get(type);
|
||||
if(assetsByType != null){
|
||||
for(ObjectMap.Entry<String, RefCountedContainer> asset : assetsByType.entries()){
|
||||
out.add((T)asset.value.object);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type the asset type
|
||||
* @return all the assets matching the specified type as entries by file name
|
||||
*/
|
||||
public synchronized <T> Seq<Entry<String, T>> getAllEntries(Class<T> type, Seq<Entry<String, T>> out){
|
||||
ObjectMap<String, RefCountedContainer> assetsByType = assets.get(type);
|
||||
if(assetsByType != null){
|
||||
for(ObjectMap.Entry<String, RefCountedContainer> asset : assetsByType.entries()){
|
||||
Entry<String, T> entry = new Entry<>();
|
||||
entry.key = asset.key;
|
||||
entry.value = (T)asset.value.object;
|
||||
out.add(entry);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param assetDescriptor the asset descriptor
|
||||
* @return the asset
|
||||
*/
|
||||
public synchronized <T> T get(AssetDescriptor<T> assetDescriptor){
|
||||
return get(assetDescriptor.fileName, assetDescriptor.type);
|
||||
}
|
||||
|
||||
/** Returns true if an asset with the specified name is loading, queued to be loaded, or has been loaded. */
|
||||
public synchronized boolean contains(String fileName){
|
||||
if(tasks.size > 0 && tasks.first().assetDesc.fileName.equals(fileName)) return true;
|
||||
|
||||
for(int i = 0; i < loadQueue.size; i++)
|
||||
if(loadQueue.get(i).fileName.equals(fileName)) return true;
|
||||
|
||||
return isLoaded(fileName);
|
||||
}
|
||||
|
||||
/** Returns true if an asset with the specified name and type is loading, queued to be loaded, or has been loaded. */
|
||||
public synchronized boolean contains(String fileName, Class type){
|
||||
if(tasks.size > 0){
|
||||
AssetDescriptor assetDesc = tasks.first().assetDesc;
|
||||
if(assetDesc.type == type && assetDesc.fileName.equals(fileName)) return true;
|
||||
}
|
||||
|
||||
for(int i = 0; i < loadQueue.size; i++){
|
||||
AssetDescriptor assetDesc = loadQueue.get(i);
|
||||
if(assetDesc.type == type && assetDesc.fileName.equals(fileName)) return true;
|
||||
}
|
||||
|
||||
return isLoaded(fileName, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the asset and all its dependencies, if they are not used by other assets.
|
||||
* @param fileName the file name
|
||||
*/
|
||||
public synchronized void unload(String fileName){
|
||||
// check if it's currently processed (and the first element in the stack, thus not a dependency) and cancel if necessary
|
||||
if(tasks.size > 0){
|
||||
AssetLoadingTask currAsset = tasks.first();
|
||||
if(currAsset.assetDesc.fileName.equals(fileName)){
|
||||
currAsset.cancel = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// check if it's in the queue
|
||||
int foundIndex = -1;
|
||||
for(int i = 0; i < loadQueue.size; i++){
|
||||
if(loadQueue.get(i).fileName.equals(fileName)){
|
||||
foundIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(foundIndex != -1){
|
||||
toLoad--;
|
||||
loadQueue.remove(foundIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// get the asset and its type
|
||||
Class type = assetTypes.get(fileName);
|
||||
if(type == null) return;
|
||||
|
||||
RefCountedContainer assetRef = assets.get(type).get(fileName);
|
||||
|
||||
if(assetRef == null) return;
|
||||
|
||||
// if it is reference counted, decrement ref count and check if we can really get rid of it.
|
||||
assetRef.count--;
|
||||
if(assetRef.count <= 0){
|
||||
|
||||
// if it is disposable dispose it
|
||||
if(assetRef.object instanceof Disposable)
|
||||
((Disposable)assetRef.object).dispose();
|
||||
|
||||
// remove the asset from the manager.
|
||||
assetTypes.remove(fileName);
|
||||
assets.get(type).remove(fileName);
|
||||
}
|
||||
|
||||
// remove any dependencies (or just decrement their ref count).
|
||||
Seq<String> dependencies = assetDependencies.get(fileName);
|
||||
if(dependencies != null){
|
||||
for(String dependency : dependencies){
|
||||
if(isLoaded(dependency)) unload(dependency);
|
||||
}
|
||||
}
|
||||
// remove dependencies if ref count < 0
|
||||
if(assetRef.count <= 0){
|
||||
assetDependencies.remove(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param asset the asset
|
||||
* @return whether the asset is contained in this manager
|
||||
*/
|
||||
public synchronized <T> boolean containsAsset(T asset){
|
||||
ObjectMap<String, RefCountedContainer> assetsByType = assets.get(asset.getClass());
|
||||
if(assetsByType == null) return false;
|
||||
for(String fileName : assetsByType.keys()){
|
||||
T otherAsset = (T)assetsByType.get(fileName).object;
|
||||
if(otherAsset == asset || asset.equals(otherAsset)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param asset the asset
|
||||
* @return the filename of the asset or null
|
||||
*/
|
||||
public synchronized <T> String getAssetFileName(T asset){
|
||||
for(Class assetType : assets.keys()){
|
||||
ObjectMap<String, RefCountedContainer> assetsByType = assets.get(assetType);
|
||||
for(String fileName : assetsByType.keys()){
|
||||
T otherAsset = (T)assetsByType.get(fileName).object;
|
||||
if(otherAsset == asset || asset.equals(otherAsset)) return fileName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param assetDesc the AssetDescriptor of the asset
|
||||
* @return whether the asset is loaded
|
||||
*/
|
||||
public synchronized boolean isLoaded(AssetDescriptor assetDesc){
|
||||
return isLoaded(assetDesc.fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName the file name of the asset
|
||||
* @return whether the asset is loaded
|
||||
*/
|
||||
public synchronized boolean isLoaded(String fileName){
|
||||
if(fileName == null) return false;
|
||||
return assetTypes.containsKey(fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName the file name of the asset
|
||||
* @return whether the asset is loaded
|
||||
*/
|
||||
public synchronized boolean isLoaded(String fileName, Class type){
|
||||
ObjectMap<String, RefCountedContainer> assetsByType = assets.get(type);
|
||||
if(assetsByType == null) return false;
|
||||
RefCountedContainer assetContainer = assetsByType.get(fileName);
|
||||
if(assetContainer == null) return false;
|
||||
return assetContainer.object != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default loader for the given type
|
||||
* @param type The type of the loader to get
|
||||
* @return The loader capable of loading the type, or null if none exists
|
||||
*/
|
||||
public <T> AssetLoader getLoader(final Class<T> type){
|
||||
return getLoader(type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the loader for the given type and the specified filename. If no loader exists for the specific filename, the
|
||||
* default loader for that type is returned.
|
||||
* @param type The type of the loader to get
|
||||
* @param fileName The filename of the asset to get a loader for, or null to get the default loader
|
||||
* @return The loader capable of loading the type and filename, or null if none exists
|
||||
*/
|
||||
public <T> AssetLoader getLoader(final Class<T> type, final String fileName){
|
||||
final ObjectMap<String, AssetLoader> loaders = this.loaders.get(type);
|
||||
if(loaders == null || loaders.size < 1) return null;
|
||||
if(fileName == null) return loaders.get("");
|
||||
AssetLoader result = null;
|
||||
int l = -1;
|
||||
for(ObjectMap.Entry<String, AssetLoader> entry : loaders.entries()){
|
||||
if(entry.key.length() > l && fileName.endsWith(entry.key)){
|
||||
result = entry.value;
|
||||
l = entry.key.length();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given asset to the loading queue of the AssetManager.
|
||||
* @param fileName the file name (interpretation depends on {@link AssetLoader})
|
||||
* @param type the type of the asset.
|
||||
*/
|
||||
public synchronized <T> AssetDescriptor<T> load(String fileName, Class<T> type){
|
||||
return load(fileName, type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a custom one-time 'asset' that knows how to load itself.
|
||||
*/
|
||||
public synchronized AssetDescriptor loadRun(String name, Class<?> type, Runnable loadasync){
|
||||
return loadRun(name, type, loadasync, () -> {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a custom one-time 'asset' that knows how to load itself.
|
||||
*/
|
||||
public synchronized AssetDescriptor loadRun(String name, Class<?> type, Runnable loadasync, Runnable loadsync){
|
||||
if(getLoader(type) == null){
|
||||
setLoader(type, new CustomLoader(){
|
||||
@Override
|
||||
public void loadAsync(AssetManager manager, String fileName, Fi file, AssetLoaderParameters parameter){
|
||||
loadasync.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object loadSync(AssetManager manager, String fileName, Fi file, AssetLoaderParameters parameter){
|
||||
loadsync.run();
|
||||
return super.loadSync(manager, fileName, file, parameter);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
throw new IllegalArgumentException("Class already registered or loaded: " + type);
|
||||
}
|
||||
return load(name, type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a custom one-time 'asset' that knows how to load itself.
|
||||
* @param load the asset
|
||||
*/
|
||||
public synchronized AssetDescriptor load(Loadable load){
|
||||
if(getLoader(load.getClass()) == null){
|
||||
setLoader(load.getClass(), new AsynchronousAssetLoader(Core.files::internal){
|
||||
@Override
|
||||
public void loadAsync(AssetManager manager, String fileName, Fi file, AssetLoaderParameters parameter){
|
||||
load.loadAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object loadSync(AssetManager manager, String fileName, Fi file, AssetLoaderParameters parameter){
|
||||
load.loadSync();
|
||||
return load;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Seq<AssetDescriptor> getDependencies(String fileName, Fi file, AssetLoaderParameters parameter){
|
||||
return load.getDependencies();
|
||||
}
|
||||
});
|
||||
}
|
||||
return load(load.getName(), load.getClass(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given asset to the loading queue of the AssetManager.
|
||||
* @param fileName the file name (interpretation depends on {@link AssetLoader})
|
||||
* @param type the type of the asset.
|
||||
* @param parameter parameters for the AssetLoader.
|
||||
*/
|
||||
public synchronized <T> AssetDescriptor<T> load(String fileName, Class<T> type, AssetLoaderParameters<T> parameter){
|
||||
AssetLoader loader = getLoader(type, fileName);
|
||||
if(loader == null) throw new ArcRuntimeException("No loader for type: " + type.getSimpleName());
|
||||
|
||||
// reset stats
|
||||
if(loadQueue.size == 0){
|
||||
loaded = 0;
|
||||
toLoad = 0;
|
||||
peakTasks = 0;
|
||||
}
|
||||
|
||||
// check if an asset with the same name but a different type has already been added.
|
||||
|
||||
// check preload queue
|
||||
for(int i = 0; i < loadQueue.size; i++){
|
||||
AssetDescriptor desc = loadQueue.get(i);
|
||||
if(desc.fileName.equals(fileName) && !desc.type.equals(type)) throw new ArcRuntimeException(
|
||||
"Asset with name '" + fileName + "' already in preload queue, but has different type (expected: "
|
||||
+ type.getSimpleName() + ", found: " + desc.type.getSimpleName() + ")");
|
||||
}
|
||||
|
||||
// check task list
|
||||
for(int i = 0; i < tasks.size; i++){
|
||||
AssetDescriptor desc = tasks.get(i).assetDesc;
|
||||
if(desc.fileName.equals(fileName) && !desc.type.equals(type)) throw new ArcRuntimeException(
|
||||
"Asset with name '" + fileName + "' already in task list, but has different type (expected: "
|
||||
+ type.getSimpleName() + ", found: " + desc.type.getSimpleName() + ")");
|
||||
}
|
||||
|
||||
// check loaded assets
|
||||
Class otherType = assetTypes.get(fileName);
|
||||
if(otherType != null && !otherType.equals(type))
|
||||
throw new ArcRuntimeException("Asset with name '" + fileName + "' already loaded, but has different type (expected: "
|
||||
+ type.getSimpleName() + ", found: " + otherType.getSimpleName() + ")");
|
||||
|
||||
toLoad++;
|
||||
AssetDescriptor assetDesc = new AssetDescriptor<>(fileName, type, parameter);
|
||||
loadQueue.add(assetDesc);
|
||||
return assetDesc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given asset to the loading queue of the AssetManager.
|
||||
* @param desc the {@link AssetDescriptor}
|
||||
*/
|
||||
public synchronized <T> AssetDescriptor<T> load(AssetDescriptor<T> desc){
|
||||
return load(desc.fileName, desc.type, desc.params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the AssetManager, keeping it loading any assets in the preload queue.
|
||||
* @return true if all loading is finished.
|
||||
*/
|
||||
public synchronized boolean update(){
|
||||
try{
|
||||
if(tasks.size == 0){
|
||||
// loop until we have a new task ready to be processed
|
||||
while(loadQueue.size != 0 && tasks.size == 0){
|
||||
nextTask();
|
||||
}
|
||||
// have we not found a task? We are done!
|
||||
if(tasks.size == 0) return true;
|
||||
}
|
||||
return updateTask() && loadQueue.size == 0 && tasks.size == 0;
|
||||
}catch(Throwable t){
|
||||
handleTaskError(t);
|
||||
return loadQueue.size == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return the asset loading task that is currently being processed.
|
||||
* May return null if nothing is being loaded. */
|
||||
public synchronized AssetDescriptor getCurrentLoading(){
|
||||
if(tasks.size > 0){
|
||||
return tasks.first().assetDesc;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the AssetManager continuously for the specified number of milliseconds, yielding the CPU to the loading thread
|
||||
* between updates. This may block for less time if all loading tasks are complete. This may block for more time if the portion
|
||||
* of a single task that happens in the GL thread takes a long time.
|
||||
* @return true if all loading is finished.
|
||||
*/
|
||||
public boolean update(int millis){
|
||||
long endTime = Time.millis() + millis;
|
||||
while(true){
|
||||
boolean done = update();
|
||||
if(done || Time.millis() > endTime) return done;
|
||||
Thread.yield();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true when all assets are loaded. Can be called from any thread. */
|
||||
public synchronized boolean isFinished(){
|
||||
return loadQueue.size == 0 && tasks.size == 0;
|
||||
}
|
||||
|
||||
/** Blocks until all assets are loaded. */
|
||||
public void finishLoading(){
|
||||
while(!update())
|
||||
Thread.yield();
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks until the specified asset is loaded.
|
||||
* @param assetDesc the AssetDescriptor of the asset
|
||||
*/
|
||||
public void finishLoadingAsset(AssetDescriptor assetDesc){
|
||||
finishLoadingAsset(assetDesc.fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks until the specified asset is loaded.
|
||||
* @param fileName the file name (interpretation depends on {@link AssetLoader})
|
||||
*/
|
||||
public void finishLoadingAsset(String fileName){
|
||||
while(!isLoaded(fileName)){
|
||||
update();
|
||||
Thread.yield();
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void injectDependencies(String parentAssetFilename, Seq<AssetDescriptor> dependendAssetDescs){
|
||||
ObjectSet<String> injected = this.injected;
|
||||
for(AssetDescriptor desc : dependendAssetDescs){
|
||||
if(injected.contains(desc.fileName)) continue; // Ignore subsequent dependencies if there are duplicates.
|
||||
injected.add(desc.fileName);
|
||||
injectDependency(parentAssetFilename, desc);
|
||||
}
|
||||
injected.clear();
|
||||
}
|
||||
|
||||
private synchronized void injectDependency(String parentAssetFilename, AssetDescriptor dependendAssetDesc){
|
||||
// add the asset as a dependency of the parent asset
|
||||
Seq<String> dependencies = assetDependencies.get(parentAssetFilename);
|
||||
if(dependencies == null){
|
||||
dependencies = new Seq();
|
||||
assetDependencies.put(parentAssetFilename, dependencies);
|
||||
}
|
||||
dependencies.add(dependendAssetDesc.fileName);
|
||||
|
||||
// if the asset is already loaded, increase its reference count.
|
||||
if(isLoaded(dependendAssetDesc.fileName)){
|
||||
Class type = assetTypes.get(dependendAssetDesc.fileName);
|
||||
RefCountedContainer assetRef = assets.get(type).get(dependendAssetDesc.fileName);
|
||||
assetRef.count++;
|
||||
incrementRefCountedDependencies(dependendAssetDesc.fileName);
|
||||
}
|
||||
// else add a new task for the asset.
|
||||
else{
|
||||
addTask(dependendAssetDesc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a task from the loadQueue and adds it to the task stack. If the asset is already loaded (which can happen if it was
|
||||
* a dependency of a previously loaded asset) its reference count will be increased.
|
||||
*/
|
||||
private void nextTask(){
|
||||
AssetDescriptor assetDesc = loadQueue.remove(0);
|
||||
//Log.info("Loading asset task: {0}", assetDesc.fileName);
|
||||
|
||||
// if the asset not meant to be reloaded and is already loaded, increase its reference count
|
||||
if(isLoaded(assetDesc.fileName)){
|
||||
Class type = assetTypes.get(assetDesc.fileName);
|
||||
RefCountedContainer assetRef = assets.get(type).get(assetDesc.fileName);
|
||||
assetRef.count++;
|
||||
incrementRefCountedDependencies(assetDesc.fileName);
|
||||
if(assetDesc.params != null && assetDesc.params.loadedCallback != null){
|
||||
assetDesc.params.loadedCallback.finishedLoading(this, assetDesc.fileName, assetDesc.type);
|
||||
}
|
||||
loaded++;
|
||||
}else{
|
||||
// else add a new task for the asset.
|
||||
addTask(assetDesc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link AssetLoadingTask} to the task stack for the given asset.
|
||||
*/
|
||||
private void addTask(AssetDescriptor assetDesc){
|
||||
AssetLoader loader = getLoader(assetDesc.type, assetDesc.fileName);
|
||||
if(loader == null)
|
||||
throw new ArcRuntimeException("No loader for type: " + assetDesc.type.getSimpleName());
|
||||
tasks.add(new AssetLoadingTask(this, assetDesc, loader, executor));
|
||||
peakTasks++;
|
||||
}
|
||||
|
||||
/** Adds an asset to this AssetManager */
|
||||
public <T> void addAsset(final String fileName, Class<T> type, T asset){
|
||||
// add the asset to the filename lookup
|
||||
assetTypes.put(fileName, type);
|
||||
|
||||
// add the asset to the type lookup
|
||||
ObjectMap<String, RefCountedContainer> typeToAssets = assets.get(type);
|
||||
if(typeToAssets == null){
|
||||
typeToAssets = new ObjectMap<>();
|
||||
assets.put(type, typeToAssets);
|
||||
}
|
||||
typeToAssets.put(fileName, new RefCountedContainer(asset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current task on the top of the task stack.
|
||||
* @return true if the asset is loaded or the task was cancelled.
|
||||
*/
|
||||
private boolean updateTask(){
|
||||
AssetLoadingTask task = tasks.peek();
|
||||
|
||||
boolean complete = true;
|
||||
try{
|
||||
complete = task.cancel || task.update();
|
||||
}catch(RuntimeException ex){
|
||||
task.cancel = true;
|
||||
taskFailed(task.assetDesc, ex);
|
||||
}
|
||||
|
||||
// if the task has been cancelled or has finished loading
|
||||
if(complete){
|
||||
// increase the number of loaded assets and pop the task from the stack
|
||||
if(tasks.size == 1){
|
||||
loaded++;
|
||||
peakTasks = 0;
|
||||
}
|
||||
tasks.pop();
|
||||
|
||||
if(task.cancel) return true;
|
||||
|
||||
addAsset(task.assetDesc.fileName, task.assetDesc.type, task.getAsset());
|
||||
|
||||
// otherwise, if a listener was found in the parameter invoke it
|
||||
if(task.assetDesc.params != null && task.assetDesc.params.loadedCallback != null){
|
||||
task.assetDesc.params.loadedCallback.finishedLoading(this, task.assetDesc.fileName, task.assetDesc.type);
|
||||
}
|
||||
|
||||
task.assetDesc.loaded.get(task.getAsset());
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a task throws an exception during loading. The default implementation rethrows the exception. A subclass may
|
||||
* supress the default implementation when loading assets where loading failure is recoverable.
|
||||
*/
|
||||
protected void taskFailed(AssetDescriptor assetDesc, RuntimeException ex){
|
||||
throw ex;
|
||||
}
|
||||
|
||||
private void incrementRefCountedDependencies(String parent){
|
||||
Seq<String> dependencies = assetDependencies.get(parent);
|
||||
if(dependencies == null) return;
|
||||
|
||||
for(String dependency : dependencies){
|
||||
Class type = assetTypes.get(dependency);
|
||||
RefCountedContainer assetRef = assets.get(type).get(dependency);
|
||||
assetRef.count++;
|
||||
incrementRefCountedDependencies(dependency);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a runtime/loading error in {@link #update()} by optionally invoking the {@link AssetErrorListener}.
|
||||
*/
|
||||
private void handleTaskError(Throwable t){
|
||||
if(tasks.isEmpty()) throw new ArcRuntimeException(t);
|
||||
|
||||
// pop the faulty task from the stack
|
||||
AssetLoadingTask task = tasks.pop();
|
||||
AssetDescriptor assetDesc = task.assetDesc;
|
||||
|
||||
// remove all dependencies
|
||||
if(task.dependenciesLoaded && task.dependencies != null){
|
||||
for(AssetDescriptor desc : task.dependencies){
|
||||
unload(desc.fileName);
|
||||
}
|
||||
}
|
||||
|
||||
// clear the rest of the stack
|
||||
tasks.clear();
|
||||
|
||||
// inform the listener that something bad happened
|
||||
if(listener != null){
|
||||
listener.error(assetDesc, t);
|
||||
}
|
||||
|
||||
if(assetDesc.errored != null){
|
||||
assetDesc.errored.get(t);
|
||||
}else{
|
||||
throw new ArcRuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new {@link AssetLoader} for the given type.
|
||||
* @param type the type of the asset
|
||||
* @param loader the loader
|
||||
*/
|
||||
public synchronized <T, P extends AssetLoaderParameters<T>> void setLoader(Class<T> type, AssetLoader<T, P> loader){
|
||||
setLoader(type, null, loader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new {@link AssetLoader} for the given type.
|
||||
* @param type the type of the asset
|
||||
* @param suffix the suffix the filename must have for this loader to be used or null to specify the default loader.
|
||||
* @param loader the loader
|
||||
*/
|
||||
public synchronized <T, P extends AssetLoaderParameters<T>> void setLoader(Class<T> type, String suffix,
|
||||
AssetLoader<T, P> loader){
|
||||
if(type == null) throw new IllegalArgumentException("type cannot be null.");
|
||||
if(loader == null) throw new IllegalArgumentException("loader cannot be null.");
|
||||
ObjectMap<String, AssetLoader> loaders = this.loaders.get(type);
|
||||
if(loaders == null) this.loaders.put(type, loaders = new ObjectMap<>());
|
||||
loaders.put(suffix == null ? "" : suffix, loader);
|
||||
}
|
||||
|
||||
/** @return the number of loaded assets */
|
||||
public synchronized int getLoadedAssets(){
|
||||
return assetTypes.size;
|
||||
}
|
||||
|
||||
/** @return the number of currently queued assets */
|
||||
public synchronized int getQueuedAssets(){
|
||||
return loadQueue.size + tasks.size;
|
||||
}
|
||||
|
||||
/** @return the progress in percent of completion. */
|
||||
public synchronized float getProgress(){
|
||||
if(toLoad == 0) return 1;
|
||||
float fractionalLoaded = (float)loaded;
|
||||
if(peakTasks > 0){
|
||||
fractionalLoaded += ((peakTasks - tasks.size) / (float)peakTasks);
|
||||
}
|
||||
return Math.min(1, fractionalLoaded / (float)toLoad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an {@link AssetErrorListener} to be invoked in case loading an asset failed.
|
||||
* @param listener the listener or null
|
||||
*/
|
||||
public synchronized void setErrorListener(AssetErrorListener listener){
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/** Disposes all assets in the manager and stops all asynchronous loading. */
|
||||
@Override
|
||||
public synchronized void dispose(){
|
||||
clear();
|
||||
Threads.await(executor);
|
||||
}
|
||||
|
||||
/** Clears and disposes all assets and the preloading queue. */
|
||||
public synchronized void clear(){
|
||||
loadQueue.clear();
|
||||
while(!update());
|
||||
|
||||
ObjectIntMap<String> dependencyCount = new ObjectIntMap<>();
|
||||
while(assetTypes.size > 0){
|
||||
// for each asset, figure out how often it was referenced
|
||||
dependencyCount.clear();
|
||||
Seq<String> assets = assetTypes.keys().toSeq();
|
||||
for(String asset : assets){
|
||||
dependencyCount.put(asset, 0);
|
||||
}
|
||||
|
||||
for(String asset : assets){
|
||||
Seq<String> dependencies = assetDependencies.get(asset);
|
||||
if(dependencies == null) continue;
|
||||
for(String dependency : dependencies){
|
||||
int count = dependencyCount.get(dependency, 0);
|
||||
count++;
|
||||
dependencyCount.put(dependency, count);
|
||||
}
|
||||
}
|
||||
|
||||
// only dispose of assets that are root assets (not referenced)
|
||||
for(String asset : assets){
|
||||
if(dependencyCount.get(asset, 0) == 0){
|
||||
unload(asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.assets.clear();
|
||||
this.assetTypes.clear();
|
||||
this.assetDependencies.clear();
|
||||
this.loaded = 0;
|
||||
this.toLoad = 0;
|
||||
this.peakTasks = 0;
|
||||
this.loadQueue.clear();
|
||||
this.tasks.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the reference count of an asset.
|
||||
*/
|
||||
public synchronized int getReferenceCount(String fileName){
|
||||
Class type = assetTypes.get(fileName);
|
||||
if(type == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
return assets.get(type).get(fileName).count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the reference count of an asset.
|
||||
*/
|
||||
public synchronized void setReferenceCount(String fileName, int refCount){
|
||||
Class type = assetTypes.get(fileName);
|
||||
if(type == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
assets.get(type).get(fileName).count = refCount;
|
||||
}
|
||||
|
||||
/** @return a string containing ref count and dependency information for all assets. */
|
||||
public synchronized String getDiagnostics(){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(String fileName : assetTypes.keys()){
|
||||
sb.append(fileName);
|
||||
sb.append(", ");
|
||||
|
||||
Class type = assetTypes.get(fileName);
|
||||
RefCountedContainer assetRef = assets.get(type).get(fileName);
|
||||
Seq<String> dependencies = assetDependencies.get(fileName);
|
||||
|
||||
sb.append(type.getSimpleName());
|
||||
|
||||
sb.append(", refs: ");
|
||||
sb.append(assetRef.count);
|
||||
|
||||
if(dependencies != null){
|
||||
sb.append(", deps: [");
|
||||
for(String dep : dependencies){
|
||||
sb.append(dep);
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("]");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/** @return the file names of all loaded assets. */
|
||||
public synchronized Seq<String> getAssetNames(){
|
||||
return assetTypes.keys().toSeq();
|
||||
}
|
||||
|
||||
/** @return the dependencies of an asset or null if the asset has no dependencies. */
|
||||
public synchronized Seq<String> getDependencies(String fileName){
|
||||
return assetDependencies.get(fileName);
|
||||
}
|
||||
|
||||
/** @return the type of a loaded asset. */
|
||||
public synchronized Class getAssetType(String fileName){
|
||||
return assetTypes.get(fileName);
|
||||
}
|
||||
|
||||
static class RefCountedContainer{
|
||||
Object object;
|
||||
int count = 1;
|
||||
|
||||
public RefCountedContainer(Object object){
|
||||
if(object == null) throw new IllegalArgumentException("Object must not be null");
|
||||
this.object = object;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package arc.assets;
|
||||
|
||||
import arc.struct.*;
|
||||
|
||||
public interface Loadable{
|
||||
default void loadAsync(){
|
||||
|
||||
}
|
||||
|
||||
default void loadSync(){
|
||||
|
||||
}
|
||||
|
||||
default String getName(){
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
|
||||
default Seq<AssetDescriptor> getDependencies(){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package arc.assets.loaders;
|
||||
|
||||
import arc.assets.AssetDescriptor;
|
||||
import arc.assets.AssetLoaderParameters;
|
||||
import arc.struct.Seq;
|
||||
import arc.files.Fi;
|
||||
|
||||
/**
|
||||
* Abstract base class for asset loaders.
|
||||
* @param <T> the class of the asset the loader supports
|
||||
* @param <P> the class of the loading parameters the loader supports.
|
||||
* @author mzechner
|
||||
*/
|
||||
public abstract class AssetLoader<T, P extends AssetLoaderParameters<T>>{
|
||||
/** {@link FileHandleResolver} used to map from plain asset names to {@link Fi} instances **/
|
||||
private FileHandleResolver resolver;
|
||||
|
||||
/**
|
||||
* Constructor, sets the {@link FileHandleResolver} to use to resolve the file associated with the asset name.
|
||||
*/
|
||||
public AssetLoader(FileHandleResolver resolver){
|
||||
this.resolver = resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName file name to resolve
|
||||
* @return handle to the file, as resolved by the {@link FileHandleResolver} set on the loader
|
||||
*/
|
||||
public Fi resolve(String fileName){
|
||||
return resolver.resolve(fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the assets this asset requires to be loaded first. This method may be called on a thread other than the GL thread.
|
||||
* @param fileName name of the asset to load
|
||||
* @param file the resolved file to load
|
||||
* @param parameter parameters for loading the asset
|
||||
* @return other assets that the asset depends on and need to be loaded first or null if there are no dependencies.
|
||||
*/
|
||||
public Seq<AssetDescriptor> getDependencies(String fileName, Fi file, P parameter){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package arc.assets.loaders;
|
||||
|
||||
import arc.assets.AssetLoaderParameters;
|
||||
import arc.assets.AssetManager;
|
||||
import arc.files.Fi;
|
||||
|
||||
/**
|
||||
* Base class for asynchronous {@link AssetLoader} instances. Such loaders try to load parts of an OpenGL resource, like the
|
||||
* Pixmap, on a separate thread to then load the actual resource on the thread the OpenGL context is active on.
|
||||
* @author mzechner
|
||||
*/
|
||||
public abstract class AsynchronousAssetLoader<T, P extends AssetLoaderParameters<T>> extends AssetLoader<T, P>{
|
||||
|
||||
public AsynchronousAssetLoader(FileHandleResolver resolver){
|
||||
super(resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the non-OpenGL part of the asset and injects any dependencies of the asset into the AssetManager.
|
||||
* @param fileName the name of the asset to load
|
||||
* @param file the resolved file to load
|
||||
* @param parameter the parameters to use for loading the asset
|
||||
*/
|
||||
public abstract void loadAsync(AssetManager manager, String fileName, Fi file, P parameter);
|
||||
|
||||
/**
|
||||
* Loads the OpenGL part of the asset.
|
||||
* @param file the resolved file to load
|
||||
*/
|
||||
public abstract T loadSync(AssetManager manager, String fileName, Fi file, P parameter);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package arc.assets.loaders;
|
||||
|
||||
import arc.assets.*;
|
||||
import arc.files.*;
|
||||
import arc.graphics.*;
|
||||
import arc.util.*;
|
||||
|
||||
/**
|
||||
* {@link AssetLoader} for {@link Cubemap} instances. The pixel data is loaded asynchronously. The texture is then created on the
|
||||
* rendering thread, synchronously. Passing a {@link CubemapParameter} to
|
||||
* {@link AssetManager#load(String, Class, AssetLoaderParameters)} allows one to specify parameters as can be passed to the
|
||||
* various Cubemap constructors, e.g. filtering and so on.
|
||||
* @author mzechner, Vincent Bousquet
|
||||
*/
|
||||
public class CubemapLoader extends AsynchronousAssetLoader<Cubemap, CubemapLoader.CubemapParameter>{
|
||||
private static final String[] names = {"right.png", "left.png", "top.png", "bottom.png", "front.png", "back.png"};
|
||||
final CubemapLoaderInfo info = new CubemapLoaderInfo();
|
||||
|
||||
public CubemapLoader(FileHandleResolver resolver){
|
||||
super(resolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadAsync(AssetManager manager, String fileName, Fi file, CubemapParameter parameter){
|
||||
info.pixmaps = new Pixmap[6];
|
||||
for(int i = 0; i < 6; i++){
|
||||
info.pixmaps[i] = new Pixmap(file.sibling(file.nameWithoutExtension() + names[i]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cubemap loadSync(AssetManager manager, String fileName, Fi file, CubemapParameter parameter){
|
||||
if(info.pixmaps == null) return null;
|
||||
|
||||
Cubemap cubemap = parameter == null ? null : parameter.cubemap;
|
||||
if(cubemap == null) cubemap = new Cubemap();
|
||||
cubemap.load(info.pixmaps, parameter != null && parameter.mipmaps, true);
|
||||
|
||||
if(parameter != null){
|
||||
cubemap.setFilter(parameter.minFilter, parameter.magFilter);
|
||||
cubemap.setWrap(parameter.wrapU, parameter.wrapV);
|
||||
}
|
||||
return cubemap;
|
||||
}
|
||||
|
||||
public static class CubemapLoaderInfo{
|
||||
Pixmap[] pixmaps;
|
||||
}
|
||||
|
||||
public static class CubemapParameter extends AssetLoaderParameters<Cubemap>{
|
||||
/** The texture to put the data in, optional. **/
|
||||
public @Nullable Cubemap cubemap = null;
|
||||
public boolean mipmaps;
|
||||
public TextureFilter minFilter = TextureFilter.nearest;
|
||||
public TextureFilter magFilter = TextureFilter.nearest;
|
||||
public TextureWrap wrapU = TextureWrap.clampToEdge;
|
||||
public TextureWrap wrapV = TextureWrap.clampToEdge;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package arc.assets.loaders;
|
||||
|
||||
import arc.*;
|
||||
import arc.assets.*;
|
||||
import arc.files.*;
|
||||
import arc.struct.*;
|
||||
|
||||
public abstract class CustomLoader extends AsynchronousAssetLoader{
|
||||
public Runnable loaded = () -> {};
|
||||
|
||||
public CustomLoader(){
|
||||
super(Core.files::internal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object loadSync(AssetManager manager, String fileName, Fi file, AssetLoaderParameters parameter){
|
||||
loaded.run();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Seq<AssetDescriptor> getDependencies(String fileName, Fi file, AssetLoaderParameters parameter){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package arc.assets.loaders;
|
||||
|
||||
import arc.assets.AssetManager;
|
||||
import arc.files.Fi;
|
||||
|
||||
/**
|
||||
* Interface for classes the can map a file name to a {@link Fi}. Used to allow the {@link AssetManager} to load resources
|
||||
* from anywhere or implement caching strategies.
|
||||
* @author mzechner
|
||||
*/
|
||||
public interface FileHandleResolver{
|
||||
Fi resolve(String fileName);
|
||||
}
|
||||