Compare commits

...
272 Commits
Author SHA1 Message Date
Anuken a021e362ee Deleted leftover folders 2026-10-02 04:04:48 -04:00
Anuken 467cd51d5b Don't request GL 4.6 2026-10-02 04:02:25 -04:00
Anuken 7f6827d219 Fixed some test issues 2026-10-02 03:54:37 -04:00
Anuken 9dfd0ed49a Merge remote-tracking branch 'origin/v9' into v9 2026-10-02 03:47:02 -04:00
Anuken 6ef7c1509d Experimental graphical client tests for CI 2026-10-02 03:46:53 -04:00
Github Actions 0e09fbbc2c Automatic class mapping and map name update 2026-10-02 07:29:00 +00:00
Anuken 65ade81fb6 Arc merged into Mindustry; no more hash nonsense 2026-10-02 03:24:13 -04:00
Anuken 601ce4d35f Crash fix 2026-10-02 01:14:08 -04:00
Anuken b87f1d226b Fixed tests 2026-10-01 18:03:37 -04:00
Anuken 33a2d0a59e Updated build scripts for IJ compatibility 2026-10-01 15:59:14 -04:00
Anuken c1081205d6 nothing 2026-10-01 12:01:22 -04:00
Anuken fe5016c6fd Fixed compilation for server 2026-10-01 11:46:27 -04:00
Anuken ed017975cc arc 2026-09-30 22:55:32 -04:00
Anuken 2c1ee3a564 Cleanup 2026-09-30 22:33:37 -04:00
Anuken b90793c2fc Merge branch 'v9' of https://github.com/Anuken/Mindustry into new-icons 2026-09-30 22:29:04 -04:00
Anuken 606e88d683 Merge branch 'v9' of https://github.com/Anuken/Mindustry into v9 2026-09-30 22:28:47 -04:00
Anuken 294aeaad46 Fixed some missing reset() calls 2026-09-30 22:27:50 -04:00
Github Actions 8e519a0e24 Automatic class mapping and map name update 2026-10-01 00:29:24 +00:00
Anuken fc9b6067af Build fix 2026-09-30 20:26:57 -04:00
Anuken 24721e4aa0 Fixed process starting 2026-09-30 18:19:37 -04:00
Anuken b63b494e49 Merge branch 'v9' of https://github.com/Anuken/Mindustry into new-icons 2026-09-30 18:14:14 -04:00
Anuken 9863d3985d Codegen fix 2026-09-30 18:03:58 -04:00
Anuken 7e825d4c30 Reverted the revert
This reverts commit e337452d46.
2026-09-30 17:59:42 -04:00
Anuken b1fff2ad15 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9 2026-09-30 17:59:22 -04:00
Anuken 5809c4486b Merge branch 'master' of https://github.com/Anuken/Mindustry into v9 2026-09-30 17:58:08 -04:00
EggleEgg c8f4ec05f3 v9 bugfix: "absorbable" do your job (#12728) 2026-09-30 17:57:56 -04:00
Anuken 1ce95dc9c9 Massive logic name refactor 2026-09-30 17:54:02 -04:00
Anuken 30e3d1438c Split up instructions and statements 2026-09-30 17:48:05 -04:00
Anuken b09de7f881 Entity ID assignment moved to state 2026-09-30 17:37:28 -04:00
Anuken e39fd3d43a Timers removed from buildings 2026-09-30 17:15:10 -04:00
Anuken f7736b953c Merge branch 'v9' of https://github.com/Anuken/Mindustry into new-icons
# Conflicts:
#	gradle.properties
2026-09-30 16:55:04 -04:00
Anuken 1e3a9638ab Waves provider moved to a static method 2026-09-30 16:54:33 -04:00
Anuken c9f149d7c7 Async processes moved to state 2026-09-30 16:45:30 -04:00
Anuken 4e0a20d28d Cleanup 2026-09-30 16:34:00 -04:00
Anuken 47e4603391 Fog moved to state 2026-09-30 16:16:07 -04:00
Anuken 5d75db97f5 Pathfinding moved to state 2026-09-30 16:09:22 -04:00
Anuken 5a01cfefe4 Entity state refactor 2026-09-30 15:19:25 -04:00
Anuken 74d28ddc5d Cleanup 2026-09-30 14:44:02 -04:00
Anuken 1d1608da56 The tests pass, but at what cost? 2026-09-30 14:29:37 -04:00
Anuken 4bddd5f23b Cleanup 2026-09-30 13:59:14 -04:00
Anuken 15317122be State refactor (game actually works now) 2026-09-30 13:52:15 -04:00
Anuken 221b4a840b Massive state refactor, part 2 2026-09-30 12:47:07 -04:00
Anuken 2fb6d2503e Massive state refactor, part 1 2026-09-30 12:44:46 -04:00
Anuken 724bdc661a Fixed launch issue 2026-09-29 22:56:27 -04:00
Anuken 9caa1df777 Font fallback fixes 2026-09-29 21:54:46 -04:00
Anuken 3769737d33 Fixed some incremental font issues 2026-09-29 21:38:03 -04:00
Anuken 9780b09705 Moved runes into separate font 2026-09-29 21:16:37 -04:00
Anuken de480b9c18 Fixed some serialization issues & warnings 2026-09-29 16:31:03 -04:00
Anuken 0d3888343f Merge branch 'v9' into new-icons
# Conflicts:
#	gradle.properties
2026-09-29 15:16:40 -04:00
Anuken 74c31b272e Effect initialized noPatch 2026-09-29 15:16:27 -04:00
Anuken f93a746061 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	gradle.properties
2026-09-29 14:30:05 -04:00
Anuken f478112fd8 Updated icon to proper resolution, fixed icon on mac 2026-09-29 14:21:54 -04:00
Anuken a49ad98445 Merge remote-tracking branch 'origin/v9' into v9 2026-09-29 14:10:43 -04:00
Anuken 71799611bf Updated Android Gradle plugin (less painful this time I hope) 2026-09-29 14:10:35 -04:00
Github Actions ea38db6d06 Automatic class mapping and map name update 2026-09-29 17:39:06 +00:00
Anuken 61156bf527 Patcher system refactor 2026-09-29 13:36:58 -04:00
Anuken 4b27c6ece4 Fixed MacOS icon and export 2026-09-29 13:10:09 -04:00
Anuken 8d4b4b4851 Initial setup for new icons 2026-09-29 10:08:54 -04:00
Anuken 88dc7efbe2 Minor icon tweak 2026-09-28 18:51:21 -04:00
Anuken b25ffe9516 New icon 2026-09-28 17:44:23 -04:00
Anuken fedb8b8366 Minor sprite gen cleanup 2026-09-28 17:04:10 -04:00
Anuken fb29be30fa Fixed status effect icons 2026-09-28 13:47:39 -04:00
Anuken 4985dee0ad Autotiling moved to shared packing code 2026-09-28 13:26:43 -04:00
Anuken 0a7afa3e7c Pixmap outline API change 2026-09-27 12:17:12 -04:00
Anuken 813d47895d Merge remote-tracking branch 'origin/v9' into v9 2026-09-25 17:45:25 -04:00
Anuken 43936c0150 Fixed tests 2026-09-25 17:45:19 -04:00
Github Actions 4b525a8a84 Automatic class mapping and map name update 2026-09-25 21:19:15 +00:00
Anuken 79eaa03abd Custom difficulty support for mods 2026-09-25 17:17:01 -04:00
Anuken 2d9010dc2d Merge remote-tracking branch 'origin/v9' into v9 2026-09-25 17:11:20 -04:00
Anuken 1641cde4b7 Conditions refactored 2026-09-25 17:11:12 -04:00
Github Actions ac31e065ab Automatic class mapping and map name update 2026-09-25 21:10:01 +00:00
Anuken f29d00ee0a Merge remote-tracking branch 'origin/v9' into v9 2026-09-25 17:08:11 -04:00
Anuken c35c1b37b3 Massive package & objective refactor 2026-09-25 17:08:00 -04:00
Github Actions 7fb87c6d08 Automatic class mapping and map name update 2026-09-25 21:00:39 +00:00
Anuken 174791cdd4 Fixed player events not having proper suffix 2026-09-25 16:58:41 -04:00
Anuken 305cf07c75 Fixed SMAA crash 2026-09-25 16:54:22 -04:00
Anuken 22c63a3ffa Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	core/src/mindustry/ClientLauncher.java
#	core/src/mindustry/mod/DataManager.java
2026-09-25 16:48:21 -04:00
Anuken 3bd96cb383 Fixed mod patch loading 2026-09-25 16:44:38 -04:00
Anuken 765f4842eb Merge remote-tracking branch 'origin/v9' into v9 2026-09-25 13:01:37 -04:00
Anuken 5c19b87425 Fixed some content image loading issues 2026-09-25 13:01:26 -04:00
Anuken 0645f6f82d Removed defunct allure test 2026-09-25 11:42:11 -04:00
Anuken 871f3256cb JSON mods now use patches, not content overriding 2026-09-25 11:40:14 -04:00
Anuken e30eaa3001 WIP glow corner 2026-09-25 10:57:37 -04:00
Github Actions b2b541c880 Automatic class mapping and map name update 2026-09-24 16:56:48 +00:00
Anuken 624df89739 Refactored sector threat to enum, fixed some icon bugs 2026-09-24 12:54:27 -04:00
Anuken 34772a337e Fixed quad marker 2026-09-24 04:12:07 -04:00
Anuken 29bd7c877a NinePatch tint support 2026-09-24 02:04:19 -04:00
Anuken 28fd4b2663 Fixed corner pixmap upload 2026-09-24 00:22:42 -04:00
Anuken a71c473d01 Fixed some bugs with the new texture packing 2026-09-23 23:53:01 -04:00
Anuken d0e9c2767a Cleanup & tools sprite generation performance improvement 2026-09-23 19:15:53 -04:00
Anuken 0a316ff99e Unification of mod and vanilla sprite generation logic 2026-09-23 15:38:09 -04:00
Anuken b1f269a5fc Fixed some issues with naval unit crush damage 2026-09-23 09:33:07 -04:00
Anuken a4ce362e4b Cleanup 2026-09-22 20:58:26 -04:00
Anuken 992db3ed7b SMAA on by default for desktop discrete GPUs 2026-09-22 17:43:00 -04:00
Anuken cbd19fb4fa Fixed floor rendering/linear filtering issues on phones 2026-09-22 16:54:22 -04:00
Anuken 7fe820f7fb Alternate optimization for shields 2026-09-22 16:37:09 -04:00
Anuken 378e20478d Shield bounds check for rendering 2026-09-22 16:08:42 -04:00
Anuken fddac9c821 Better shield shader 2026-09-22 15:51:28 -04:00
Anuken 0faddee3c8 Make force projectors vulnerable when first placed 2026-09-22 12:44:08 -04:00
Anuken 733db5035b Fixed sap bullets being able to pierce shields + placeholder balance adjustment for Eclipse lasers 2026-09-21 19:53:52 -04:00
Anuken 0011a1503a Shield absorption support for flame/point laser bullets 2026-09-21 19:32:52 -04:00
Anuken 8396d5c9fa Made setStats accept an object instead of using an instance field 2026-09-21 15:48:14 -04:00
Anuken 318ca1cc8e Cleanup 2026-09-21 15:42:20 -04:00
Anuken 13597bc402 Begin refactoring mod sprite packing 2026-09-21 15:15:33 -04:00
Anuken 6a9b816f84 Visual artifact fixes for Erekir core units + orbit rendering tweaks 2026-09-21 13:24:07 -04:00
Anuken 9e06c7f88f Merge remote-tracking branch 'origin/v9' into v9 2026-09-21 11:52:23 -04:00
Anuken da62ba6e2a Fixed visual artifacts between cell and highlights on Serpulo core units 2026-09-21 11:52:14 -04:00
Github Actions 0dffe0ee2b Automatic bundle update 2026-09-21 05:58:47 +00:00
Anuken db57f0cf91 Merge remote-tracking branch 'origin/v9' into v9 2026-09-21 01:57:55 -04:00
Anuken d7d0506e3f Antialiasing (SMAA) implementation 2026-09-21 01:57:43 -04:00
Github Actions 162083a0d5 Automatic class mapping and map name update 2026-09-21 04:53:38 +00:00
Anuken 65da207421 Merge remote-tracking branch 'origin/v9' into v9 2026-09-21 00:51:28 -04:00
Anuken 4969d0423f Explosion absorption fixes with shields 2026-09-21 00:50:39 -04:00
Anuken 59d11e56a5 Made unit shields absorb lasers/bullets too 2026-09-21 00:35:56 -04:00
Anuken c92cd927d1 WIP: shields absorb rail/laser bullets 2026-09-21 00:04:42 -04:00
Github Actions 979ba53b56 Automatic class mapping and map name update 2026-09-21 03:38:25 +00:00
Anuken 081409e687 Refactored shield provider interface 2026-09-20 23:36:14 -04:00
Anuken 9aca7ec86d Fixed door solid check 2026-09-20 22:57:29 -04:00
Anuken 8b6fe795d2 Better unit drown effect 2026-09-20 18:24:04 -04:00
Anuken 9426d4c670 Naval unit collisions + ramming with conveyors 2026-09-20 18:06:37 -04:00
Anuken 4dcadcf3cf Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	gradle.properties
2026-09-20 17:28:10 -04:00
Anuken 0650d5ae41 Animated surface setting cleanup 2026-09-19 12:18:09 -04:00
Anuken 2b2c3f8d30 Updated PR template 2026-09-18 10:17:13 -04:00
Anuken 772bf60a3c Fixed #12677 2026-09-17 18:31:36 -04:00
Anuken fc1761d6fe Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	core/src/mindustry/mod/DataImagePacker.java
2026-09-16 10:18:34 -04:00
Anuken 0bcc96b5a2 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9 2026-09-14 10:57:02 -04:00
Anuken fb4bddd163 Support for dynamically-generated rounded/angled corners in UI 2026-09-13 20:23:30 -04:00
Anuken 5c7cacaafe Merge branch 'master' of https://github.com/Anuken/Mindustry into v9 2026-09-13 09:51:01 -04:00
Anuken 9c7b0981a6 Schematic reading simplification 2026-09-13 09:50:52 -04:00
Anuken 57221d4a9f Merge remote-tracking branch 'origin/v9' into v9 2026-09-12 12:30:52 -04:00
Anuken 7f982b303b Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	gradle.properties
2026-09-12 12:30:31 -04:00
Cardillan 51561bcb85 Allow ucontrol deconstruct in campaign (#12628) 2026-09-12 10:55:24 -04:00
EggleEgg 9b7556a078 No hasItems for generic crafters 2026-09-12 10:54:47 -04:00
Github Actions cf2f3f82b5 Automatic bundle update 2026-09-12 14:54:29 +00:00
Александр Голомшток 3be43b1a54 Add sort-by-recently-played option to the map list (#12638)
* Add sort-by-recently-played option to the map list

The existing "recent" sort orders by the map file's last-modified
time, which only reflects edits - built-in maps never touch their
file, so it's meaningless for them, and for custom maps it tracks
last edited, not last played.

Track an actual last-played timestamp per map, the same way
high scores are already tracked (Core.settings, keyed by filename),
updated whenever a map finishes loading successfully outside of
playtesting. Add it as a fifth MapPriority option alongside the
existing four.

Resolves Anuken/Mindustry-Suggestions#5115

* Use eyeSmall icon per review feedback
2026-09-12 10:53:42 -04:00
Александр Голомшток 1ee9684d96 Leave sinking wreckage when units die over liquids (#12633) 2026-09-12 10:37:17 -04:00
Anuken f490ce4a82 Removed sortPlans call 2026-09-11 21:04:48 -04:00
Anuken b0a3e52aad Removed deprecated fields/methods 2026-09-11 15:03:04 -04:00
Anuken 0a4d1a7fe7 Fixed default schematic size offset 2026-09-11 14:56:12 -04:00
Anuken fd11c61292 Fixed objective loading 2026-09-11 14:53:42 -04:00
Anuken 240d7667f6 Removed default loadout from generator 2026-09-11 14:50:26 -04:00
Anuken 1a34b786a7 Merge remote-tracking branch 'origin/v9' into v9 2026-09-11 12:59:31 -04:00
Anuken b7f1681157 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	gradle.properties
2026-09-11 12:58:55 -04:00
Github Actions 961e8b6e60 Automatic bundle update 2026-09-09 21:56:59 +00:00
Anuken d0048f32de Merge fixes 2026-09-09 17:55:43 -04:00
Anuken 867dd29ba8 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	core/assets/bundles/bundle_es.properties
#	core/assets/bundles/bundle_it.properties
#	core/src/mindustry/game/MapObjectives.java
#	core/src/mindustry/logic/LExecutor.java
#	core/src/mindustry/world/blocks/payloads/PayloadMassDriver.java
#	gradle.properties
#	tests/src/test/java/ApplicationTests.java
2026-09-09 17:51:03 -04:00
Anuken 8041a9de3f var 2026-08-29 13:44:54 -04:00
Anuken eeeec99215 Merge remote-tracking branch 'origin/v9' into v9 2026-08-29 09:57:41 -04:00
Anuken 854a71eb32 Removed deprecated methods 2026-08-29 09:57:31 -04:00
Anuken 22f0b9a5c7 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	core/src/mindustry/logic/LExecutor.java
#	core/src/mindustry/world/blocks/logic/LogicDisplay.java
#	core/src/mindustry/world/meta/StatValues.java
#	gradle.properties
2026-08-29 09:53:28 -04:00
Github Actions 25bc99c68b Automatic class mapping and map name update 2026-08-26 20:59:40 +00:00
Anuken 2b9937c3be arc 2026-08-26 16:56:24 -04:00
Anuken 9f0bd7f579 Allow world processors to configure + sense bridges/nodes/mass drivers
Replaces & closes #11503
2026-08-26 16:55:10 -04:00
Anuken 3267830fc9 More drill rendering fixes post-PR 2026-08-26 15:38:49 -04:00
Anuken 309f56b108 Drill rim rendering tweaks 2026-08-26 15:35:06 -04:00
Anuken 684c840080 Fixed drill rim rendering 2026-08-26 15:33:02 -04:00
Anuken 72d5df4c75 Compile fix 2026-08-26 15:25:40 -04:00
85654ee544 Give Drill type drawers (#10524)
* Give Drill type drawers

* quick style fix

* reversal whups

* Allow legacy rim

* quick

* Fix preview generation leaking buildings into world (#12511)

This wasn't ever noticed before but now that world logic can do crazy stuff, it causes a lot of issues.

* Add armor value access to Block and BuildingComp (#12510)

* Fixed #12509

* Fixed #12512

* Fix PlacementFragment at large ui scales. (#12515)

* Fixed save load text wrapping

* Update bug_report.yml

* Call afterDestroyed method on build in destroyed() (#12525)

* Fixed 'escelating' objective typo in existing saves

* Removed camera sensors from non-privileged logic

* Qol: .info descriptions to unit weapons (#12527)

* Fixed #12531

---------

Co-authored-by: buthed010203 <naguiar010203@gmail.com>
Co-authored-by: Kaleinova <160325865+Kaleinova@users.noreply.github.com>
Co-authored-by: Anuken <arnukren@gmail.com>
Co-authored-by: MCxzfwq <mcxzfwq@outlook.com>
Co-authored-by: EggleEgg <125359838+EggleEgg@users.noreply.github.com>
2026-08-26 15:20:53 -04:00
Anuken 5da3bbd414 Merge remote-tracking branch 'origin/v9' into v9 2026-08-26 09:32:20 -04:00
Anuken 3f40c250d3 Switched to .hjson for texture packer config 2026-08-26 09:32:08 -04:00
Anuken 31436ac146 Merge remote-tracking branch 'origin/v9' into v9 2026-08-19 10:40:20 +02:00
Anuken d827f2e218 Cleanup 2026-08-19 10:40:11 +02:00
Anuken 0d8eaa1a1f Merge remote-tracking branch 'origin/master' into v9
# Conflicts:
#	core/src/mindustry/mod/DataImagePacker.java
2026-08-19 10:39:37 +02:00
Github Actions 1befd890cc Automatic class mapping and map name update 2026-08-14 16:24:02 +00:00
Anuken 56485c39f4 Merge fixes 2026-08-14 12:21:58 -04:00
Anuken 292552618a Merge fixes 2026-08-14 12:20:56 -04:00
Anuken e26205a4f7 Merge remote-tracking branch 'origin/master' into v9
# Conflicts:
#	core/assets/bundles/bundle_ca.properties
#	core/assets/scripts/global.js
#	core/src/mindustry/mod/DataImagePacker.java
#	gradle.properties
#	tools/src/mindustry/tools/SchemaGenerator.java
2026-08-14 12:20:03 -04:00
Anuken c5d67a56fd Merge remote-tracking branch 'origin/v9' into v9 2026-08-11 14:31:24 -04:00
Anuken 807e9a4db5 Remove Elem class 2026-08-11 14:31:14 -04:00
NiTiSonandAnuken 340b2192b9 Ease access to the additionalSetup field (#12478)
* Fixed #12473

* Fixed #12472

* Fixed #12471

* ItemModule.empty capacity check

* JSON schema generation tweaks / Logic link name fixes

* Ease access to the additionalSetup field

---------

Co-authored-by: Anuken <arnukren@gmail.com>
2026-08-11 14:11:50 -04:00
Anuken 9ebf3b966e Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	gradle.properties
2026-08-10 12:15:15 -04:00
Anuken e7c4e44530 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	core/src/mindustry/mod/DataImagePacker.java
#	gradle.properties
2026-08-06 21:54:58 -04:00
Anuken 87b1bc1ad6 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	core/src/mindustry/mod/DataImagePacker.java
#	gradle.properties
2026-08-04 21:46:46 -04:00
Anuken 5daa268748 Cleanup 2026-08-02 04:18:40 -04:00
Anuken ff427fbffb Fixed pack logging 2026-08-02 02:38:09 -04:00
Anuken ea8cad4f2a Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	gradle.properties
2026-08-02 01:14:26 -04:00
Anuken 4a89218e69 Merge remote-tracking branch 'origin/v9' into v9 2026-08-02 01:14:11 -04:00
Anuken 8109637349 Fixed server json usage 2026-08-02 00:24:53 -04:00
Anuken 9ae1ccebf1 Fixed server json usage 2026-08-02 00:22:23 -04:00
Anuken b41f40e6ff Removed writer field from Json 2026-08-01 23:55:28 -04:00
Anuken 43025b373a cleanup 2026-08-01 23:37:19 -04:00
Anuken 8fad301da8 Jval migration made functional 2026-08-01 22:53:46 -04:00
Anuken cb6c5b1c5d This may have been a bad idea 2026-07-30 22:30:45 -04:00
Github Actions 33b7f32eb5 Automatic bundle update 2026-07-31 02:02:44 +00:00
Anuken 02785f49e8 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	core/assets/bundles/bundle_pt_BR.properties
#	gradle.properties
2026-07-30 21:56:03 -04:00
Anuken a7053b9c1c Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	gradle.properties
2026-07-22 10:03:28 -04:00
Anuken 5558211c4f Fixed #12392 2026-07-20 15:40:43 -04:00
Anuken 3ecdd5ff2a Merge branch 'master' of https://github.com/Anuken/Mindustry into v9 2026-07-19 17:46:06 -04:00
Anuken 1ada6b9fdd Fixed startup dialog on mobile 2026-07-17 16:17:45 -04:00
Anuken 6209a63bba Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	core/src/mindustry/type/SectorPreset.java
#	core/src/mindustry/type/StatusEffect.java
2026-07-17 15:54:22 -04:00
Anuken 7af49d733d Merge branch 'master' into v9
# Conflicts:
#	gradle.properties
2026-07-15 16:38:10 -04:00
Anuken fb34738d60 Cleanup 2026-07-15 16:37:35 -04:00
Anuken 8761f0f810 Merge cleanup 2026-07-15 11:52:56 -04:00
Anuken 3d2521a06c Merge remote-tracking branch 'origin/v9' into v9 2026-07-15 11:49:18 -04:00
Anuken 1924e1fb0e Merge cleanup 2026-07-15 11:49:12 -04:00
Anuken 98afd42aac Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	core/src/mindustry/ctype/UnlockableContent.java
#	gradle.properties
2026-07-15 11:47:48 -04:00
Github Actions 6c09e9f09a Automatic bundle update 2026-07-15 02:45:32 +00:00
Anuken fbf4482c73 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	core/assets/bundles/bundle_bg.properties
#	core/src/mindustry/world/Block.java
#	gradle.properties
2026-07-14 22:44:23 -04:00
Anuken 08dbcde924 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	core/assets/bundles/bundle_uk_UA.properties
#	gradle.properties
2026-07-13 13:41:15 -04:00
Anuken 7241bdece6 Import fixes for v9 2026-07-12 11:45:03 -04:00
Anuken dc5e877af6 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9 2026-07-12 11:42:50 -04:00
Anuken 668c8dfd93 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9 2026-07-12 09:25:19 -04:00
Anuken 7ac0ce5bf5 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	core/src/mindustry/graphics/BlockRenderer.java
#	desktop/src/mindustry/desktop/DesktopLauncher.java
#	gradle.properties
2026-07-11 09:29:44 -04:00
Anuken 856a2c5967 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	gradle.properties
2026-07-08 23:08:02 -04:00
Anuken 856332c93f Green and blue team palette for blocks is back 2026-07-08 23:07:20 -04:00
Anuken be890fcf56 Removed deprecated methods 2026-07-08 10:20:43 -04:00
Anuken a19fa7f871 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	gradle.properties
2026-07-08 10:11:26 -04:00
Anuken 9cadceb7b7 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts:
#	gradle.properties
2026-07-07 12:59:57 -04:00
Anuken 5d23c9b3af Merge branch 'master' of https://github.com/Anuken/Mindustry into v9 2026-07-06 20:10:35 -04:00
Anuken 9e648dd34f Option for disabling ANGLE on windows 2026-07-06 11:57:44 -04:00
Anuken 1dfdc28c66 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9 2026-07-06 00:20:10 -04:00
Anuken f00e7c4d85 Fixed lights not rendering 2026-07-05 23:49:02 -04:00
Anuken 308d38bd65 Merge remote-tracking branch 'origin/v9' into v9 2026-07-05 14:17:39 -04:00
Anuken be7f811761 Merge branch 'master' of https://github.com/Anuken/Mindustry into v9 2026-07-05 14:17:27 -04:00
Github Actions 8d5e02dfbe Automatic class mapping and map name update 2026-07-05 18:06:58 +00:00
Anuken 8318b5cb71 Fetch correct arc branch 2026-07-05 14:04:26 -04:00
Anuken 014f23c7a0 v9 setup (no, there is no new content) 2026-07-05 14:01:04 -04:00
Anuken 910a4e7b00 Merge branch 'array-textures' of https://github.com/Anuken/Mindustry into array-textures-sdl3 2026-07-05 13:43:18 -04:00
Github Actions aef8998058 Automatic bundle update 2026-07-05 17:36:58 +00:00
Anuken 4ae6053ace Merge branch 'master' of https://github.com/Anuken/Mindustry into array-textures
# Conflicts:
#	core/assets/bundles/bundle_hu.properties
#	core/assets/bundles/bundle_ru.properties
#	core/assets/bundles/bundle_th.properties
#	core/assets/bundles/bundle_vi.properties
#	core/assets/bundles/bundle_zh_CN.properties
#	core/assets/bundles/bundle_zh_TW.properties
#	core/src/mindustry/mod/Mods.java
#	gradle.properties
2026-07-05 13:35:11 -04:00
Anuken 9823981222 Merge branch 'sdl3' of https://github.com/Anuken/Mindustry into array-textures-sdl3 2026-07-02 17:07:46 -04:00
Anuken 69f7fbfbb2 Merge branch 'master' of https://github.com/Anuken/Mindustry into array-textures
# Conflicts:
#	gradle.properties
2026-07-02 15:41:49 -04:00
Anuken 4a2e76c415 Merge branch 'master' of https://github.com/Anuken/Mindustry into array-textures
# Conflicts:
#	gradle.properties
2026-07-02 10:15:54 -04:00
Anuken 002541aa27 Force linear filtering for UI, as in main branch 2026-06-28 16:08:10 -04:00
Anuken beaed55c08 ...actually nevermind 2026-06-27 21:46:11 -04:00
Anuken 2e3491cd39 Additional registration fix 2026-06-27 21:45:50 -04:00
Anuken 3492446a73 Merge remote-tracking branch 'origin/array-textures' into array-textures 2026-06-27 21:45:26 -04:00
Anuken aa9f39f82f Fixed emojis on headless servers 2026-06-27 21:45:19 -04:00
Github Actions 0df2b4487f Automatic bundle update 2026-06-28 01:42:06 +00:00
Anuken 0a9d5c5005 Cleanup 2026-06-27 21:40:44 -04:00
Anuken d581160603 Added support for adding emojis in data patches / on servers 2026-06-27 21:40:00 -04:00
Anuken 364f5a8d9b Cleanup 2026-06-27 15:09:51 -04:00
Anuken 04c1aab69c Support for mod content icons in font 2026-06-27 14:13:48 -04:00
Anuken 12290b4d53 Merge branch 'master' of https://github.com/Anuken/Mindustry into array-textures
# Conflicts:
#	gradle.properties
2026-06-27 13:46:58 -04:00
Anuken a8f6222f0c WIP mod font icon support 2026-06-27 13:46:51 -04:00
Anuken c3785ed4fc Merged file chooser changes 2026-06-26 13:02:05 -04:00
Anuken 10b478b1bd Merge branch 'master' of https://github.com/Anuken/Mindustry into sdl3
# Conflicts:
#	desktop/src/mindustry/desktop/DesktopLauncher.java
2026-06-26 12:45:02 -04:00
Anuken 90a6bf2508 Merge branch 'master' of https://github.com/Anuken/Mindustry into array-textures
# Conflicts:
#	gradle.properties
2026-06-26 12:02:34 -04:00
Anuken b1480d4270 Consistent toArray -> toSeq usage 2026-06-26 11:59:51 -04:00
Anuken 709ab337f1 Merge remote-tracking branch 'origin/array-textures' into array-textures 2026-06-21 22:20:35 -04:00
Anuken 9a71b203af Core extension refactor 2026-06-21 22:20:26 -04:00
Anuken 88b2f6e94e Cache handle check fix 2026-06-21 19:17:28 -04:00
Anuken 601034adfa Fixed zoom clipping into Erekir atmosphere 2026-06-21 17:58:25 -04:00
Anuken 919ffd4430 arc 2026-06-21 17:11:40 -04:00
Anuken b194fcbe2c Framebuffer refactor 2026-06-21 16:39:27 -04:00
Anuken b3f9fd9a71 Erekir planet shader adjustments 2026-06-21 15:20:33 -04:00
Anuken 3e84b28154 Shader reorganization & planet atmosphere depth fix 2026-06-21 14:45:56 -04:00
Anuken 61c96dbc67 Merge branch 'master' of https://github.com/Anuken/Mindustry into array-textures
# Conflicts:
#	core/assets-raw/sprites/blocks/environment/data-patch-reserved-env.png
2026-06-21 13:05:50 -04:00
Anuken 6283c10cd7 Merge branch 'master' of https://github.com/Anuken/Mindustry into array-textures
# Conflicts:
#	core/src/mindustry/mod/DataImagePacker.java
#	gradle.properties
2026-06-21 09:53:08 -04:00
Anuken fe7f458a2f Merge branch 'master' of https://github.com/Anuken/Mindustry into array-textures 2026-06-20 17:33:49 -04:00
Anuken e4ceff01d4 Fixed NaN black screen 2026-06-20 16:21:00 -04:00
Anuken e4d4b51962 Removed accidental debug 2026-06-20 16:04:34 -04:00
Anuken ff30daa79a Imports reorganized 2026-06-20 15:05:24 -04:00
Anuken 3d21e11360 Fixed planet rendering 2026-06-20 14:46:46 -04:00
Anuken 4856af4916 Refactoring to match newest Arc commit 2026-06-20 14:28:17 -04:00
Anuken 345750ac61 Merge branch 'master' of https://github.com/Anuken/Mindustry into array-textures
# Conflicts:
#	gradle.properties
2026-06-20 10:39:53 -04:00
Anuken 13fecec653 Made data patches work with array textures 2026-06-19 23:24:30 -04:00
Anuken a4c87caa04 Misc fixes 2026-06-19 16:24:52 -04:00
Anuken c397c797ed Texture array progress 2026-06-19 15:12:10 -04:00
Anuken 8eacc17ce4 cleanup 2026-06-19 14:29:32 -04:00
Anuken 475746e75d WIP texture array refactor 2026-06-19 14:29:19 -04:00
Anuken 5c7e7074ba crash fix 2026-06-19 03:30:22 -04:00
Anuken d225eaf624 missing files 2026-06-19 03:27:15 -04:00
Anuken 1314db7295 WIP texture array sprite batch + gl3 min 2026-06-19 03:24:18 -04:00
Anuken d9d904a5c8 Merge branch 'master' of https://github.com/Anuken/Mindustry into sdl3
# Conflicts:
#	build.gradle
2026-05-28 10:38:51 -04:00
Anuken e1e9f886a3 Merge branch 'master' of https://github.com/Anuken/Mindustry into sdl3
# Conflicts:
#	core/assets/maps/serpulo/coastline.msav
#	core/src/mindustry/ui/dialogs/SettingsMenuDialog.java
#	gradle.properties
2026-04-07 11:16:09 -04:00
Anuken 66ea037add App name for SDL3 backend 2026-03-03 00:31:53 -05:00
Anuken 24c38894bb Fixed banned units on Coastline 2026-02-23 22:30:41 -05:00
Anuken dc1e36bcd2 Fixed objective team selection 2026-02-23 00:53:51 -05:00
Anuken 48d3fa1c11 Proper backend 2026-02-16 17:32:00 -05:00
Anuken 88e487303d Revert "SDL3 is broken, reverting to SDL2 (Closes #11671, #11676)"
This reverts commit 0580e1af
2026-02-16 17:31:06 -05:00
Anuken a6b29b854c Reapply "Removed borderless fullscreen option (window should always be borderless fullscreen anyway)"
This reverts commit a3682756dc.
2026-02-16 17:30:35 -05:00
1540 changed files with 141607 additions and 16115 deletions
@@ -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'
+41
View File
@@ -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
-9
View File
@@ -22,15 +22,6 @@ jobs:
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: Create artifacts
run: |
./gradlew desktop:dist server:dist core:depsJar core:assetsJar core:mergedJavadoc -Pbuildversion=${RELEASE_VERSION:1}
+68
View File
@@ -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
+5 -3
View File
@@ -16,13 +16,15 @@ jobs:
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/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@v7
+4 -2
View File
@@ -26,8 +26,6 @@ jobs:
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/actions/setup-gradle@v6
- name: Update bundles
@@ -57,3 +55,7 @@ jobs:
fi
- 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
+1 -1
View File
@@ -1,7 +1,7 @@
<?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"/>
+3 -43
View File
@@ -1,13 +1,5 @@
buildscript{
repositories{
mavenLocal()
mavenCentral()
google()
}
dependencies{
classpath 'com.android.tools.build:gradle:8.2.2'
}
plugins{
id "com.android.application"
}
apply plugin: "com.android.application"
@@ -29,7 +21,6 @@ task deploy(type: Copy){
android{
namespace = "io.anuke.mindustry"
buildToolsVersion = '36.0.0'
compileSdk = 36
sourceSets{
main{
@@ -39,12 +30,7 @@ android{
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']
}
jniLibs.srcDirs += ['../arc/natives/natives-android/libs']
}
androidTest.setRoot('tests')
@@ -125,32 +111,6 @@ dependencies{
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){
Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

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>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@@ -219,7 +219,6 @@ public class AndroidLauncher extends AndroidApplication{
}, new AndroidApplicationConfiguration(){{
useImmersiveMode = true;
hideStatusBar = true;
useGL30 = true;
}});
var intent = getIntent();
@@ -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);
@@ -503,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);
}
@@ -604,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){
@@ -660,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);
}
@@ -678,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);
@@ -691,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)");
@@ -704,13 +704,13 @@ 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, Modifier.STATIC);
.addModifiers(Modifier.PUBLIC);
//free everything pooled at the start of each updaet
groupPoolUpdate
@@ -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;
+65
View File
@@ -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
+193
View File
@@ -0,0 +1,193 @@
import java.nio.file.Files
plugins{
id "com.badlogicgames.jnigen.jnigen-gradle"
}
sourceSets.main.java.srcDirs = ["src"]
sourceSets.test.java.srcDirs = ["test"]
sourceSets.test.resources.srcDirs = ["test/resources"]
configurations{
extraLibs
}
dependencies{
testImplementation libraries.junit
testImplementation aproj(":natives:natives-desktop")
testImplementation files("unsafe/unsafe.jar")
//file generated from UnsafeBuffers.java
compileOnly files("unsafe/unsafe.jar")
extraLibs files("unsafe/unsafe.jar")
}
jar{
from{
configurations.extraLibs.collect{ it.isDirectory() ? it : 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("compileBuffersUnsafe", Exec){
workingDir "unsafe"
commandLine "javac --target 8 --source 8 -d . UnsafeBuffers.java".split(" ")
}
tasks.register("compileBuffersSafe", Exec){
workingDir "unsafe"
commandLine "javac --target 16 --source 16 -d . Java16Buffers.java".split(" ")
}
tasks.register("recompileUnsafe", Exec){
dependsOn "compileBuffersUnsafe", "compileBuffersSafe"
workingDir "unsafe"
commandLine "jar cvf unsafe.jar arc".split(" ")
doLast{
delete{
delete "unsafe/arc"
}
}
}
jar{
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
test{
testLogging{
exceptionFormat = 'full'
showStandardStreams = true
}
}
jnigen{
sharedLibName = "arc"
libsDir = "build/natives"
def root = "csrc"
def fdir = "$root/freetype"
all{
headerDirs = [
"$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 = ["$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 = [
"$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 += (String[])["$root/soloud/src/backend/miniaudio/"]
cppIncludes += (String[])["$root/soloud/src/backend/miniaudio/*.cpp"]
cppFlags += "-DWITH_MINIAUDIO"
libraries += "-lpthread -lrt -lm -ldl".split(" ")
}
addWindows(x64, x86){
headerDirs += (String[])["$root/soloud/src/backend/miniaudio/"]
cppIncludes += (String[])["$root/soloud/src/backend/miniaudio/*.cpp"]
cppFlags += ["-msse", "-DWITH_MINIAUDIO"]
}
addAndroid(){
linkerFlags += "-llog -lOpenSLES".split(" ")
cppIncludes += (String[])["$root/soloud/src/backend/opensles/*.cpp"]
cppFlags += ["-DWITH_OPENSLES"]
androidApplicationMk += ["APP_STL := c++_static"]
}
addMac(x64, ARM)
each({it.os == MacOsX}){
cppIncludes += (String[])["$root/soloud/src/backend/coreaudio/*.mm"]
cppFlags += (String[])["-std=c++11", "-DWITH_COREAUDIO"]
libraries += "-Wl,-framework,CoreAudio -Wl,-framework,AudioToolbox".split(" ")
}
addIOS{
headerDirs += (String[])["$root/iosgl"]
cppIncludes += (String[])["$root/soloud/src/backend/coreaudio/*.mm", "$root/iosgl/**.cpp"]
cppFlags += (String[])["-stdlib=libc++", "-std=c++11", "-DWITH_COREAUDIO"]
libraries += "-Wl,-framework,CoreAudio -Wl,-framework,AudioToolbox -Wl,-framework,AVFoundation -Wl,-framework,Foundation".split(" ")
linkerFlags += (String[])["-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{ delete "$rootDir/arc-core/csrc/soloud" }
delete{ delete "$rootDir/arc-core/csrc/stb_image.h" }
}
}
tasks.register('preJni'){
doFirst{
if(!file("csrc/stb_image.h").exists()){
println "Fetching stb_image source..."
//currently locked to a specific commit
"curl -o $rootDir/arc-core/csrc/stb_image.h https://raw.githubusercontent.com/nothings/stb/013ac3beddff3dbffafd5177e7972067cd2b5083/stb_image.h".execute().waitFor()
}
if(!file("csrc/soloud").exists()){
println "Fetching soloud source..."
"git clone --depth 1 --branch $versions.soloud https://github.com/Anuken/soloud.git $rootDir/arc-core/csrc/soloud".execute().waitFor()
}
def freetypeRoot = file("csrc")
def freetypeDir = file("csrc/freetype")
if(!freetypeDir.exists()){
def 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").text =
"""
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 )
"""
}
}
getTasksByName("jnigen", true).each{
it.dependsOn copyUnsafeStuff, preJni
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+756
View File
@@ -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
+148
View File
@@ -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
}
}
+68
View File
@@ -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){
}
}
+32
View 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;
}
+62
View File
@@ -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();
}
}
+114
View File
@@ -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
}
}
+359
View File
@@ -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;
}
}
}
}
}
+391
View File
@@ -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;
}
}
+463
View File
@@ -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;
}
}
}
+21
View File
@@ -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);
}
@@ -0,0 +1,114 @@
package arc.assets.loaders;
import arc.assets.AssetDescriptor;
import arc.assets.AssetLoaderParameters;
import arc.assets.AssetManager;
import arc.assets.loaders.FontLoader.*;
import arc.struct.Seq;
import arc.files.Fi;
import arc.graphics.Texture;
import arc.graphics.TextureFilter;
import arc.graphics.font.Font;
import arc.graphics.font.Font.FontData;
import arc.graphics.g2d.TextureAtlas;
import arc.graphics.g2d.TextureAtlas.AtlasRegion;
import arc.graphics.g2d.TextureRegion;
import arc.util.ArcRuntimeException;
/**
* {@link AssetLoader} for {@link Font} instances. Loads the font description file (.fnt) asynchronously, loads the
* {@link Texture} containing the glyphs as a dependency. The {@link FontParameter} allows you to set things like texture
* filters or whether to flip the glyphs vertically.
* @author mzechner
*/
public class FontLoader extends AsynchronousAssetLoader<Font, FontParameter>{
FontData data;
public FontLoader(FileHandleResolver resolver){
super(resolver);
}
@Override
public Seq<AssetDescriptor> getDependencies(String fileName, Fi file, FontParameter parameter){
Seq<AssetDescriptor> deps = new Seq();
if(parameter != null && parameter.fontData != null){
data = parameter.fontData;
return deps;
}
data = new FontData(file, parameter != null && parameter.flip);
if(parameter != null && parameter.atlasName != null){
deps.add(new AssetDescriptor(parameter.atlasName, TextureAtlas.class));
}else{
for(int i = 0; i < data.getImagePaths().length; i++){
String path = data.getImagePath(i);
Fi resolved = resolve(path);
TextureLoader.TextureParameter textureParams = new TextureLoader.TextureParameter();
if(parameter != null){
textureParams.genMipMaps = parameter.genMipMaps;
textureParams.minFilter = parameter.minFilter;
textureParams.magFilter = parameter.magFilter;
}
AssetDescriptor descriptor = new AssetDescriptor(resolved, Texture.class, textureParams);
deps.add(descriptor);
}
}
return deps;
}
@Override
public void loadAsync(AssetManager manager, String fileName, Fi file, FontParameter parameter){
}
@Override
public Font loadSync(AssetManager manager, String fileName, Fi file, FontParameter parameter){
if(parameter != null && parameter.atlasName != null){
TextureAtlas atlas = manager.get(parameter.atlasName, TextureAtlas.class);
String name = file.sibling(data.imagePaths[0]).nameWithoutExtension();
AtlasRegion region = atlas.find(name);
if(region == null)
throw new ArcRuntimeException("Could not find font region " + name + " in atlas " + parameter.atlasName);
return new Font(file, region);
}else{
int n = data.getImagePaths().length;
Seq<TextureRegion> regs = new Seq(n);
for(int i = 0; i < n; i++){
regs.add(new TextureRegion(manager.get(data.getImagePath(i), Texture.class)));
}
return new Font(data, regs, true);
}
}
/**
* Parameter to be passed to {@link AssetManager#load(String, Class, AssetLoaderParameters)} if additional configuration is
* necessary for the {@link Font}.
* @author mzechner
*/
public static class FontParameter extends AssetLoaderParameters<Font>{
/** Flips the font vertically if {@code true}. Defaults to {@code false}. **/
public boolean flip = false;
/** Generates mipmaps for the font if {@code true}. Defaults to {@code false}. **/
public boolean genMipMaps = false;
/** The {@link TextureFilter} to use when scaling down the {@link Font}. Defaults to {@link TextureFilter#nearest}. */
public TextureFilter minFilter = TextureFilter.nearest;
/** The {@link TextureFilter} to use when scaling up the {@link Font}. Defaults to {@link TextureFilter#nearest}. */
public TextureFilter magFilter = TextureFilter.nearest;
/** optional {@link FontData} to be used instead of loading the {@link Texture} directly. **/
public FontData fontData = null;
/**
* The name of the {@link TextureAtlas} to load the {@link Font} itself from. Optional; if {@code null}, will look for
* a separate image
*/
public String atlasName = null;
}
}
@@ -0,0 +1,88 @@
package arc.assets.loaders;
import arc.assets.AssetDescriptor;
import arc.assets.AssetLoaderParameters;
import arc.assets.AssetManager;
import arc.struct.Seq;
import arc.files.Fi;
import arc.util.I18NBundle;
import java.util.Locale;
/**
* {@link AssetLoader} for {@link I18NBundle} instances. The I18NBundle is loaded asynchronously.
* <p>
* Notice that you can't load two bundles with the same base name and different locale or encoding using the same {@link AssetManager}.
* For example, if you try to load the 2 bundles below
*
* <pre>
* manager.load(&quot;i18n/message&quot;, I18NBundle.class, new I18NBundleParameter(Locale.ITALIAN));
* manager.load(&quot;i18n/message&quot;, I18NBundle.class, new I18NBundleParameter(Locale.ENGLISH));
* </pre>
* <p>
* the English bundle won't be loaded because the asset manager thinks they are the same bundle since they have the same name.
* There are 2 use cases:
* <ul>
* <li>If you want to load the English bundle so to replace the Italian bundle you have to unload the Italian bundle first.
* <li>If you want to load the English bundle without replacing the Italian bundle you should use another asset manager.
* </ul>
* @author davebaol
*/
public class I18NBundleLoader extends AsynchronousAssetLoader<I18NBundle, I18NBundleLoader.I18NBundleParameter>{
I18NBundle bundle;
public I18NBundleLoader(FileHandleResolver resolver){
super(resolver);
}
@Override
public void loadAsync(AssetManager manager, String fileName, Fi file, I18NBundleParameter parameter){
this.bundle = null;
Locale locale;
String encoding;
if(parameter == null){
locale = Locale.getDefault();
encoding = null;
}else{
locale = parameter.locale == null ? Locale.getDefault() : parameter.locale;
encoding = parameter.encoding;
}
if(encoding == null){
this.bundle = I18NBundle.createBundle(file, locale);
}else{
this.bundle = I18NBundle.createBundle(file, locale, encoding);
}
}
@Override
public I18NBundle loadSync(AssetManager manager, String fileName, Fi file, I18NBundleParameter parameter){
I18NBundle bundle = this.bundle;
this.bundle = null;
return bundle;
}
@Override
public Seq<AssetDescriptor> getDependencies(String fileName, Fi file, I18NBundleParameter parameter){
return null;
}
public static class I18NBundleParameter extends AssetLoaderParameters<I18NBundle>{
public final Locale locale;
public final String encoding;
public I18NBundleParameter(){
this(null, null);
}
public I18NBundleParameter(Locale locale){
this(locale, null);
}
public I18NBundleParameter(Locale locale, String encoding){
this.locale = locale;
this.encoding = encoding;
}
}
}
@@ -0,0 +1,71 @@
package arc.assets.loaders;
import arc.*;
import arc.assets.*;
import arc.audio.*;
import arc.files.*;
import arc.struct.*;
import arc.util.*;
/**
* {@link AssetLoader} for {@link Music} instances. The Music instance is loaded synchronously.
* @author mzechner
*/
public class MusicLoader extends AsynchronousAssetLoader<Music, MusicLoader.MusicParameter>{
private Music music;
public MusicLoader(FileHandleResolver resolver){
super(resolver);
}
/**
* Returns the {@link Music} instance currently loaded by this
* {@link MusicLoader}.
* @return the currently loaded {@link Music}, otherwise {@code null} if
* no {@link Music} has been loaded yet.
*/
protected Music getLoadedMusic(){
return music;
}
@Override
public void loadAsync(AssetManager manager, String fileName, Fi file, MusicParameter parameter){
if(parameter != null && parameter.music != null){
try{
(music = parameter.music).load(file);
}catch(Exception e){
Log.err(e);
}
}else{
music = Core.audio.newMusic(file);
}
}
@Override
public Music loadSync(AssetManager manager, String fileName, Fi file, MusicParameter parameter){
Music music = this.music;
this.music = null;
return music;
}
@Override
public Seq<AssetDescriptor> getDependencies(String fileName, Fi file, MusicParameter parameter){
return null;
}
public static class MusicParameter extends AssetLoaderParameters<Music>{
public @Nullable Music music;
public MusicParameter(){
}
public MusicParameter(@Nullable Music music){
this.music = music;
}
public MusicParameter(LoadedCallback loadedCallback){
super(loadedCallback);
}
}
}
@@ -0,0 +1,41 @@
package arc.assets.loaders;
import arc.assets.AssetDescriptor;
import arc.assets.AssetLoaderParameters;
import arc.assets.AssetManager;
import arc.struct.Seq;
import arc.files.Fi;
import arc.graphics.Pixmap;
/**
* {@link AssetLoader} for {@link Pixmap} instances. The Pixmap is loaded asynchronously.
* @author mzechner
*/
public class PixmapLoader extends AsynchronousAssetLoader<Pixmap, PixmapLoader.PixmapParameter>{
Pixmap pixmap;
public PixmapLoader(FileHandleResolver resolver){
super(resolver);
}
@Override
public void loadAsync(AssetManager manager, String fileName, Fi file, PixmapParameter parameter){
pixmap = null;
pixmap = new Pixmap(file);
}
@Override
public Pixmap loadSync(AssetManager manager, String fileName, Fi file, PixmapParameter parameter){
Pixmap pixmap = this.pixmap;
this.pixmap = null;
return pixmap;
}
@Override
public Seq<AssetDescriptor> getDependencies(String fileName, Fi file, PixmapParameter parameter){
return null;
}
public static class PixmapParameter extends AssetLoaderParameters<Pixmap>{
}
}
@@ -0,0 +1,98 @@
package arc.assets.loaders;
import arc.assets.*;
import arc.files.*;
import arc.graphics.*;
import arc.struct.*;
import arc.util.*;
/**
* {@link AssetLoader} for {@link Shader} instances loaded from text files. If the file suffix is ".vert", it is assumed
* to be a vertex shader, and a fragment shader is found using the same file name with a ".frag" suffix. And vice versa if the
* file suffix is ".frag". These default suffixes can be changed in the ShaderProgramLoader constructor.
* <p>
* For all other file suffixes, the same file is used for both (and therefore should internally distinguish between the programs
* using preprocessor directives and {@link Shader#prependVertexCode} and {@link Shader#prependFragmentCode}).
* <p>
* The above default behavior for finding the files can be overridden by explicitly setting the file names in a
* {@link ShaderProgramParameter}. The parameter can also be used to prepend code to the programs.
* @author cypherdare
*/
public class ShaderProgramLoader extends AsynchronousAssetLoader<Shader, ShaderProgramLoader.ShaderProgramParameter>{
private String vertexFileSuffix = ".vert";
private String fragmentFileSuffix = ".frag";
public ShaderProgramLoader(FileHandleResolver resolver){
super(resolver);
}
public ShaderProgramLoader(FileHandleResolver resolver, String vertexFileSuffix, String fragmentFileSuffix){
super(resolver);
this.vertexFileSuffix = vertexFileSuffix;
this.fragmentFileSuffix = fragmentFileSuffix;
}
@Override
public Seq<AssetDescriptor> getDependencies(String fileName, Fi file, ShaderProgramParameter parameter){
return null;
}
@Override
public void loadAsync(AssetManager manager, String fileName, Fi file, ShaderProgramParameter parameter){
}
@Override
public Shader loadSync(AssetManager manager, String fileName, Fi file, ShaderProgramParameter parameter){
String vertFileName = null, fragFileName = null;
if(parameter != null){
if(parameter.vertexFile != null) vertFileName = parameter.vertexFile;
if(parameter.fragmentFile != null) fragFileName = parameter.fragmentFile;
}
if(vertFileName == null && fileName.endsWith(fragmentFileSuffix)){
vertFileName = fileName.substring(0, fileName.length() - fragmentFileSuffix.length()) + vertexFileSuffix;
}
if(fragFileName == null && fileName.endsWith(vertexFileSuffix)){
fragFileName = fileName.substring(0, fileName.length() - vertexFileSuffix.length()) + fragmentFileSuffix;
}
Fi vertexFile = vertFileName == null ? file : resolve(vertFileName);
Fi fragmentFile = fragFileName == null ? file : resolve(fragFileName);
String vertexCode = vertexFile.readString();
String fragmentCode = vertexFile.equals(fragmentFile) ? vertexCode : fragmentFile.readString();
if(parameter != null){
if(parameter.prependVertexCode != null) vertexCode = parameter.prependVertexCode + vertexCode;
if(parameter.prependFragmentCode != null) fragmentCode = parameter.prependFragmentCode + fragmentCode;
}
Shader shader = new Shader(vertexCode, fragmentCode);
if((parameter == null || parameter.logOnCompileFailure) && !shader.isCompiled()){
Log.err("Shader " + fileName + " failed to compile:\n" + shader.getLog());
}
return shader;
}
public static class ShaderProgramParameter extends AssetLoaderParameters<Shader>{
/**
* File name to be used for the vertex program instead of the default determined by the file name used to submit this asset
* to AssetManager.
*/
public String vertexFile;
/**
* File name to be used for the fragment program instead of the default determined by the file name used to submit this
* asset to AssetManager.
*/
public String fragmentFile;
/** Whether to log (at the error level) the shader's log if it fails to compile. Default true. */
public boolean logOnCompileFailure = true;
/**
* Code that is always added to the vertex shader code. This is added as-is, and you should include a newline (`\n`) if
* needed. {@linkplain Shader#prependVertexCode} is placed before this code.
*/
public String prependVertexCode;
/**
* Code that is always added to the fragment shader code. This is added as-is, and you should include a newline (`\n`) if
* needed. {@linkplain Shader#prependFragmentCode} is placed before this code.
*/
public String prependFragmentCode;
}
}
@@ -0,0 +1,67 @@
package arc.assets.loaders;
import arc.*;
import arc.assets.*;
import arc.audio.*;
import arc.files.*;
import arc.struct.*;
import arc.util.*;
/**
* {@link AssetLoader} to load {@link Sound} instances.
* @author mzechner
*/
public class SoundLoader extends AsynchronousAssetLoader<Sound, SoundLoader.SoundParameter>{
private Sound sound;
public SoundLoader(FileHandleResolver resolver){
super(resolver);
}
/**
* Returns the {@link Sound} instance currently loaded by this
* {@link SoundLoader}.
* @return the currently loaded {@link Sound}, otherwise {@code null} if
* no {@link Sound} has been loaded yet.
*/
protected Sound getLoadedSound(){
return sound;
}
@Override
public void loadAsync(AssetManager manager, String fileName, Fi file, SoundParameter parameter){
if(parameter != null && parameter.sound != null){
(sound = parameter.sound).load(file);
}else{
sound = Core.audio.newSound(file);
}
}
@Override
public Sound loadSync(AssetManager manager, String fileName, Fi file, SoundParameter parameter){
Sound sound = this.sound;
this.sound = null;
return sound;
}
@Override
public Seq<AssetDescriptor> getDependencies(String fileName, Fi file, SoundParameter parameter){
return null;
}
public static class SoundParameter extends AssetLoaderParameters<Sound>{
public @Nullable Sound sound;
public SoundParameter(){
}
public SoundParameter(@Nullable Sound sound){
this.sound = sound;
}
public SoundParameter(LoadedCallback loadedCallback){
super(loadedCallback);
}
}
}
@@ -0,0 +1,13 @@
package arc.assets.loaders;
import arc.assets.AssetLoaderParameters;
import arc.assets.AssetManager;
import arc.files.Fi;
public abstract class SynchronousAssetLoader<T, P extends AssetLoaderParameters<T>> extends AssetLoader<T, P>{
public SynchronousAssetLoader(FileHandleResolver resolver){
super(resolver);
}
public abstract T load(AssetManager assetManager, String fileName, Fi file, P parameter);
}
@@ -0,0 +1,98 @@
package arc.assets.loaders;
import arc.*;
import arc.assets.*;
import arc.files.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.graphics.g2d.TextureAtlas.*;
import arc.graphics.g2d.TextureAtlas.TextureAtlasData.*;
import arc.struct.*;
import arc.util.*;
import java.util.concurrent.*;
/**
* {@link AssetLoader} to load {@link TextureAtlas} instances. Passing a {@link TextureAtlasParameter} to
* {@link AssetManager#load(String, Class, AssetLoaderParameters)} allows to specify whether the atlas regions should be flipped
* on the y-axis or not.
* @author mzechner
*/
public class TextureAtlasLoader extends AsynchronousAssetLoader<TextureAtlas, TextureAtlasLoader.TextureAtlasParameter>{
TextureAtlasData data;
Seq<Future<AsyncResult>> textureLoaders = new Seq<>();
public TextureAtlasLoader(FileHandleResolver resolver){
super(resolver);
}
@Override
public void loadAsync(AssetManager manager, String fileName, Fi file, TextureAtlasParameter parameter){
Fi imgDir = file.parent();
if(parameter != null){
data = new TextureAtlasData(file, imgDir, parameter.flip);
}else{
data = new TextureAtlasData(file, imgDir, false);
}
for(AtlasPage page : data.pages){
textureLoaders.add(Core.executor.submit(() -> new AsyncResult(page, new Pixmap(page.textureFile))));
}
}
@Override
public TextureAtlas loadSync(AssetManager manager, String fileName, Fi file, TextureAtlasParameter parameter){
try{
Pixmap[] pixmaps = new Pixmap[data.pages.size];
for(Future<AsyncResult> result : textureLoaders){
AsyncResult res = result.get();
pixmaps[data.pages.indexOf(res.page)] = res.pixmap;
}
data.texture = new TextureArray(pixmaps);
for(Pixmap pix : pixmaps){
pix.dispose();
}
TextureAtlas atlas = new TextureAtlas(data);
data = null;
return atlas;
}catch(Exception e){
throw new ArcRuntimeException(e);
}
}
@Override
public Seq<AssetDescriptor> getDependencies(String fileName, Fi file, TextureAtlasParameter parameter){
return null;
}
static class AsyncResult{
final AtlasPage page;
final Pixmap pixmap;
public AsyncResult(AtlasPage page, Pixmap pixmap){
this.page = page;
this.pixmap = pixmap;
}
}
public static class TextureAtlasParameter extends AssetLoaderParameters<TextureAtlas>{
/** whether to flip the texture atlas vertically **/
public boolean flip = false;
public TextureAtlasParameter(){
}
public TextureAtlasParameter(boolean flip){
this.flip = flip;
}
public TextureAtlasParameter(LoadedCallback loadedCallback){
super(loadedCallback);
}
}
}
@@ -0,0 +1,59 @@
package arc.assets.loaders;
import arc.assets.*;
import arc.files.*;
import arc.graphics.*;
import arc.util.*;
/**
* {@link AssetLoader} for {@link Texture} instances. The pixel data is loaded asynchronously. The texture is then created on the
* rendering thread, synchronously. Passing a {@link TextureParameter} to
* {@link AssetManager#load(String, Class, AssetLoaderParameters)} allows one to specify parameters as can be passed to the
* various Texture constructors, e.g. filtering, whether to generate mipmaps and so on.
* @author mzechner
*/
public class TextureLoader extends AsynchronousAssetLoader<Texture, TextureLoader.TextureParameter>{
final TextureLoaderInfo info = new TextureLoaderInfo();
public TextureLoader(FileHandleResolver resolver){
super(resolver);
}
@Override
public void loadAsync(AssetManager manager, String fileName, Fi file, TextureParameter parameter){
info.pixmap = new Pixmap(file);
}
@Override
public Texture loadSync(AssetManager manager, String fileName, Fi file, TextureParameter parameter){
Texture texture = parameter == null ? null : parameter.texture;
if(texture == null) texture = new Texture();
texture.load(info.pixmap, true, parameter != null && parameter.genMipMaps);
if(parameter != null){
texture.setFilter(parameter.minFilter, parameter.magFilter);
texture.setWrap(parameter.wrapU, parameter.wrapV);
}
return texture;
}
public static class TextureLoaderInfo{
Pixmap pixmap;
}
public static class TextureParameter extends AssetLoaderParameters<Texture>{
/** whether to generate mipmaps **/
public boolean genMipMaps = false;
/** The texture to put the data in, optional. **/
public @Nullable Texture texture = null;
public TextureFilter minFilter = TextureFilter.nearest;
public TextureFilter magFilter = TextureFilter.nearest;
public TextureWrap wrapU = TextureWrap.clampToEdge;
public TextureWrap wrapV = TextureWrap.clampToEdge;
public TextureParameter(){
}
}
}
+184
View File
@@ -0,0 +1,184 @@
package arc.audio;
import arc.*;
import arc.files.*;
import arc.util.*;
import static arc.audio.Soloud.*;
/** High-level wrapper for the Soloud library. */
public class Audio implements Disposable{
public float globalPitch = 1f;
/** Falloff when playing audio.*/
public float falloff = 16000f;
/** Default value for maximum instances of a sound. Must be set before a sound is loaded. */
public int defaultSoundMaxConcurrent = 6;
boolean initialized;
public float sfxVolume = 0f;
/** Global bus for all sounds. */
public AudioBus soundBus = new AudioBus();
/** Global bus for all music. */
public AudioBus musicBus = new AudioBus();
/** Initializes Soloud audio by default. May throw an exception if initialization fails. */
public Audio(){
initialize();
}
/** Conditionally initializes audio. If enabled is false, does nothing. */
public Audio(boolean enabled){
if(enabled){
initialize();
}
}
public boolean initialized(){
return initialized;
}
/** Intializes Soloud audio. If this fails, prints an error and disables audio. */
protected void initialize(){
try{
init();
Log.info("[Audio] Initialized SoLoud @ using @ at @hz / @ samples / @ channels",
version(), backendString(), backendSamplerate(), backendBufferSize(), backendChannels());
initialized = true;
soundBus = new AudioBus().init();
musicBus = new AudioBus().init();
Core.app.addListener(new ApplicationListener(){
@Override
public void update(){
sfxVolume = Core.settings.getInt("sfxvol") / 100f;
}
@Override
public void pause(){
if(Core.app.isMobile()){
pauseAll(true);
}
}
@Override
public void resume(){
if(Core.app.isMobile()){
pauseAll(false);
}
}
});
}catch(Throwable error){
Log.err("Failed to initialize audio, disabling sound", error);
}
}
/** Loads a sound, logging an error and returning a dummy track upon failure. */
public Sound newSound(Fi file){
if(!initialized) return new Sound();
try{
return new Sound(file);
}catch(Throwable t){
Log.err("Error loading sound: " + file, t);
return new Sound();
}
}
/** Loads music, logging an error and returning a dummy track upon failure. */
public Music newMusic(Fi file){
if(!initialized) return new Music();
try{
return new Music(file);
}catch(Throwable t){
Log.err("Error loading music: " + file, t);
return new Music();
}
}
public boolean isPlaying(int soundId){
if(!initialized) return false;
return soundId > 0 && idValid(soundId);
}
public void protect(int voice, boolean protect){
if(!initialized) return;
idProtected(voice, protect);
}
public int play(AudioSource source, float volume, float pitch, float pan, boolean loop){
if(!initialized || source.handle == 0) return -1;
return sourcePlay(source.handle, volume, pitch, pan, loop);
}
public void stop(AudioSource source){
if(!initialized || source.handle == 0) return;
sourceStop(source.handle);
}
public void stop(int soundId){
if(!initialized) return;
idStop(soundId);
}
public void setPaused(int soundId, boolean paused){
if(!initialized) return;
idPause(soundId, paused);
}
public void setLooping(int soundId, boolean looping){
if(!initialized) return;
idLooping(soundId, looping);
}
public void setPitch(int soundId, float pitch){
if(!initialized) return;
if(!Float.isInfinite(pitch) && !Float.isNaN(pitch)) idPitch(soundId, Math.max(pitch, 0.001f));
}
public void setVolume(int soundId, float volume){
if(!initialized) return;
if(!Float.isInfinite(volume) && !Float.isNaN(volume)) idVolume(soundId, volume);
}
public void set(int soundId, float pan, float volume){
if(!initialized) return;
if(!Float.isInfinite(volume) && !Float.isNaN(volume)) idVolume(soundId, volume);
if(!Float.isInfinite(pan) && !Float.isNaN(pan)) idPan(soundId, pan);
}
public void fadeFilterParam(int voice, int filter, int attribute, float value, float timeSec){
if(!initialized) return;
filterFade(voice, filter, attribute, value, timeSec);
}
public void setFilterParam(int voice, int filter, int attribute, float value){
if(!initialized) return;
filterSet(voice, filter, attribute, value);
}
public void setFilter(int index, @Nullable AudioFilter filter){
if(!initialized) return;
setGlobalFilter(index, filter == null ? 0 : filter.handle);
}
public int countPlaying(AudioSource source){
if(!initialized || source.handle <= 0) return 0;
return sourceCount(source.handle);
}
public int countTotalPlaying(){
if(!initialized) return 0;
return activeVoiceCount();
}
@Override
public void dispose(){
if(!initialized) return;
stopAll();
deinit();
initialized = false;
}
}
+61
View File
@@ -0,0 +1,61 @@
package arc.audio;
import arc.*;
import arc.util.*;
import static arc.audio.Soloud.*;
public class AudioBus extends AudioSource{
public int id;
public AudioBus(){
if(Core.audio != null && Core.audio.initialized){
init();
}
}
@Override
public void setFilter(int index, @Nullable AudioFilter filter){
if(handle == 0) return;
sourceFilter(handle, index, filter == null ? 0 : filter.handle);
}
AudioBus init(){
if(handle != 0) return this;
this.handle = busNew();
this.id = sourcePlay(handle);
return this;
}
public boolean playing(){
return handle != 0 && Core.audio.isPlaying(id);
}
public void play(){
if(handle == 0 || idValid(id)) return;
id = sourcePlay(handle);
}
@Override
public void stop(){
Core.audio.stop(id);
id = 0;
}
@Override
public float getLength(){
return 0;
}
public void fadeFilterParam(int filter, int attribute, float value, float timeSec){
Core.audio.fadeFilterParam(id, filter, attribute, value, timeSec);
}
public void setFilterParam(int filter, int attribute, float value){
Core.audio.setFilterParam(id, filter, attribute, value);
}
public void setVolume(float volume){
Core.audio.setVolume(id, volume);
}
}
@@ -0,0 +1,9 @@
package arc.audio;
public abstract class AudioFilter{
protected long handle;
protected AudioFilter(long handle){
this.handle = handle;
}
}
+104
View File
@@ -0,0 +1,104 @@
package arc.audio;
import arc.*;
import arc.util.*;
import static arc.audio.Soloud.*;
public abstract class AudioSource implements Disposable{
protected long handle;
protected int maxConcurrent, concurrentGroup;
protected float minInterruptAbsolute, minInterruptFraction;
protected float priority;
public boolean valid(){
return handle != 0;
}
public void setFilter(int index, @Nullable AudioFilter filter){
if(handle == 0) return;
sourceFilter(handle, index, filter == null ? 0 : filter.handle);
}
public void setFilter(@Nullable AudioFilter filter){
setFilter(0, filter);
}
protected void setParamsAfterLoad(){
if(priority != 0f) setPriority(priority);
if(maxConcurrent != 0) setMaxConcurrent(maxConcurrent);
if(concurrentGroup != 0) setConcurrentGroup(concurrentGroup);
if(minInterruptFraction != 0f){
setMinConcurrentInterruptFraction(minInterruptAbsolute, minInterruptFraction);
}else if(minInterruptAbsolute != 0f){
setMinConcurrentInterrupt(minInterruptAbsolute);
}
}
/** Sets the priority of this source. Sources with higher priorities will not get cut off by those of lower priorities. */
public void setPriority(float priority){
this.priority = priority;
if(handle == 0) return;
sourcePriority(handle, priority);
}
/** Sets the priority of this source. Sources with higher priorities will not get cut off by those of lower priorities. */
public void setMaxConcurrent(int max){
this.maxConcurrent = max;
if(handle == 0) return;
sourceMaxConcurrent(handle, max);
}
/** Sets the group ID of this source, for which maxConcurrent will be enforced. If unset, a unique group will be created for this sound. */
public void setConcurrentGroup(int group){
this.concurrentGroup = group;
if(handle == 0) return;
sourceConcurrentGroup(handle, group);
}
/** Sets the minimum playtime (in seconds) that a sound must have in order to be interrupted when its concurrent limit is reached. */
public void setMinConcurrentInterrupt(float seconds){
minInterruptAbsolute = seconds;
minInterruptFraction = 0f;
if(handle == 0) return;
sourceMinConcurrentInterrupt(handle, seconds);
}
/** Sets the minimum playtime (in seconds) that a sound must have in order to be interrupted when its concurrent limit is reached. This is a fraction of length. */
public void setMinConcurrentInterruptFraction(float min, float fraction){
minInterruptFraction = fraction;
minInterruptAbsolute = min;
if(handle == 0) return;
sourceMinConcurrentInterrupt(handle, Math.min(min, getLength() * fraction));
}
/** @return number of currently playing instances */
public int countPlaying(){
if(handle == 0) return 0;
return Core.audio.countPlaying(this);
}
public void setSingleInstance(boolean singleInstance){
if(handle == 0 || !Core.audio.initialized) return;
sourceSingleInstance(handle, singleInstance);
}
public void stop(){
if(handle == 0) return;
sourceStop(handle);
}
public abstract float getLength();
/** @return true if this is a lazily loaded source (only loaded once played) */
public boolean isLazy(){
return false;
}
@Override
public void dispose(){
if(handle != 0) sourceDestroy(handle);
handle = 0;
}
}

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