Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a021e362ee | ||
|
|
467cd51d5b | ||
|
|
7f6827d219 | ||
|
|
9dfd0ed49a | ||
|
|
6ef7c1509d | ||
|
|
0e09fbbc2c | ||
|
|
65ade81fb6 | ||
|
|
601ce4d35f | ||
|
|
b87f1d226b | ||
|
|
33a2d0a59e | ||
|
|
c1081205d6 | ||
|
|
fe5016c6fd | ||
|
|
ed017975cc | ||
|
|
2c1ee3a564 | ||
|
|
b90793c2fc | ||
|
|
606e88d683 | ||
|
|
294aeaad46 | ||
|
|
8e519a0e24 | ||
|
|
fc9b6067af | ||
|
|
24721e4aa0 | ||
|
|
b63b494e49 | ||
|
|
9863d3985d | ||
|
|
7e825d4c30 | ||
|
|
b1fff2ad15 | ||
|
|
5809c4486b | ||
|
|
c8f4ec05f3 | ||
|
|
1ce95dc9c9 | ||
|
|
30e3d1438c | ||
|
|
b09de7f881 | ||
|
|
e39fd3d43a | ||
|
|
f7736b953c | ||
|
|
1e3a9638ab | ||
|
|
c9f149d7c7 | ||
|
|
4e0a20d28d | ||
|
|
47e4603391 | ||
|
|
5d75db97f5 | ||
|
|
5a01cfefe4 | ||
|
|
74d28ddc5d | ||
|
|
1d1608da56 | ||
|
|
4bddd5f23b | ||
|
|
15317122be | ||
|
|
221b4a840b | ||
|
|
2fb6d2503e | ||
|
|
724bdc661a | ||
|
|
9caa1df777 | ||
|
|
3769737d33 | ||
|
|
9780b09705 | ||
|
|
de480b9c18 | ||
|
|
0d3888343f | ||
|
|
74c31b272e | ||
|
|
f93a746061 | ||
|
|
f478112fd8 | ||
|
|
a49ad98445 | ||
|
|
71799611bf | ||
|
|
ea38db6d06 | ||
|
|
61156bf527 | ||
|
|
4b27c6ece4 | ||
|
|
8d4b4b4851 | ||
|
|
88dc7efbe2 | ||
|
|
b25ffe9516 | ||
|
|
fedb8b8366 | ||
|
|
fb29be30fa | ||
|
|
4985dee0ad | ||
|
|
0a7afa3e7c | ||
|
|
813d47895d | ||
|
|
43936c0150 | ||
|
|
4b525a8a84 | ||
|
|
79eaa03abd | ||
|
|
2d9010dc2d | ||
|
|
1641cde4b7 | ||
|
|
ac31e065ab | ||
|
|
f29d00ee0a | ||
|
|
c35c1b37b3 | ||
|
|
7fb87c6d08 | ||
|
|
174791cdd4 | ||
|
|
305cf07c75 | ||
|
|
22c63a3ffa | ||
|
|
3bd96cb383 | ||
|
|
765f4842eb | ||
|
|
5c19b87425 | ||
|
|
0645f6f82d | ||
|
|
871f3256cb | ||
|
|
e30eaa3001 | ||
|
|
b2b541c880 | ||
|
|
624df89739 | ||
|
|
34772a337e | ||
|
|
29bd7c877a | ||
|
|
28fd4b2663 | ||
|
|
a71c473d01 | ||
|
|
d0e9c2767a | ||
|
|
0a316ff99e | ||
|
|
b1f269a5fc | ||
|
|
a4ce362e4b | ||
|
|
992db3ed7b | ||
|
|
cbd19fb4fa | ||
|
|
7fe820f7fb | ||
|
|
378e20478d | ||
|
|
fddac9c821 | ||
|
|
0faddee3c8 | ||
|
|
733db5035b | ||
|
|
0011a1503a | ||
|
|
8396d5c9fa | ||
|
|
318ca1cc8e | ||
|
|
13597bc402 | ||
|
|
6a9b816f84 | ||
|
|
9e06c7f88f | ||
|
|
da62ba6e2a | ||
|
|
0dffe0ee2b | ||
|
|
db57f0cf91 | ||
|
|
d7d0506e3f | ||
|
|
162083a0d5 | ||
|
|
65da207421 | ||
|
|
4969d0423f | ||
|
|
59d11e56a5 | ||
|
|
c92cd927d1 | ||
|
|
979ba53b56 | ||
|
|
081409e687 | ||
|
|
9aca7ec86d | ||
|
|
8b6fe795d2 | ||
|
|
9426d4c670 | ||
|
|
4dcadcf3cf | ||
|
|
0650d5ae41 | ||
|
|
2b2c3f8d30 | ||
|
|
772bf60a3c | ||
|
|
fc1761d6fe | ||
|
|
0bcc96b5a2 | ||
|
|
fb4bddd163 | ||
|
|
5c7cacaafe | ||
|
|
9c7b0981a6 | ||
|
|
57221d4a9f | ||
|
|
7f982b303b | ||
|
|
51561bcb85 | ||
|
|
9b7556a078 | ||
|
|
cf2f3f82b5 | ||
|
|
3be43b1a54 | ||
|
|
1ee9684d96 | ||
|
|
f490ce4a82 | ||
|
|
b0a3e52aad | ||
|
|
0a4d1a7fe7 | ||
|
|
fd11c61292 | ||
|
|
240d7667f6 | ||
|
|
1a34b786a7 | ||
|
|
b7f1681157 | ||
|
|
961e8b6e60 | ||
|
|
d0048f32de | ||
|
|
867dd29ba8 | ||
|
|
8041a9de3f | ||
|
|
eeeec99215 | ||
|
|
854a71eb32 | ||
|
|
22f0b9a5c7 | ||
|
|
25bc99c68b | ||
|
|
2b9937c3be | ||
|
|
9f0bd7f579 | ||
|
|
3267830fc9 | ||
|
|
309f56b108 | ||
|
|
684c840080 | ||
|
|
72d5df4c75 | ||
|
|
85654ee544 | ||
|
|
5da3bbd414 | ||
|
|
3f40c250d3 | ||
|
|
31436ac146 | ||
|
|
d827f2e218 | ||
|
|
0d8eaa1a1f | ||
|
|
1befd890cc | ||
|
|
56485c39f4 | ||
|
|
292552618a | ||
|
|
e26205a4f7 | ||
|
|
c5d67a56fd | ||
|
|
807e9a4db5 | ||
|
|
340b2192b9 | ||
|
|
9ebf3b966e | ||
|
|
e7c4e44530 | ||
|
|
87b1bc1ad6 | ||
|
|
5daa268748 | ||
|
|
ff427fbffb | ||
|
|
ea8cad4f2a | ||
|
|
4a89218e69 | ||
|
|
8109637349 | ||
|
|
9ae1ccebf1 | ||
|
|
b41f40e6ff | ||
|
|
43025b373a | ||
|
|
8fad301da8 | ||
|
|
cb6c5b1c5d | ||
|
|
33b7f32eb5 | ||
|
|
02785f49e8 | ||
|
|
a7053b9c1c | ||
|
|
5558211c4f | ||
|
|
3ecdd5ff2a | ||
|
|
1ada6b9fdd | ||
|
|
6209a63bba | ||
|
|
7af49d733d | ||
|
|
fb34738d60 | ||
|
|
8761f0f810 | ||
|
|
3d2521a06c | ||
|
|
1924e1fb0e | ||
|
|
98afd42aac | ||
|
|
6c09e9f09a | ||
|
|
fbf4482c73 | ||
|
|
08dbcde924 | ||
|
|
7241bdece6 | ||
|
|
dc5e877af6 | ||
|
|
668c8dfd93 | ||
|
|
7ac0ce5bf5 | ||
|
|
856a2c5967 | ||
|
|
856332c93f | ||
|
|
be890fcf56 | ||
|
|
a19fa7f871 | ||
|
|
9cadceb7b7 | ||
|
|
5d23c9b3af | ||
|
|
9e648dd34f | ||
|
|
1dfdc28c66 | ||
|
|
f00e7c4d85 | ||
|
|
308d38bd65 | ||
|
|
be7f811761 | ||
|
|
8d5e02dfbe | ||
|
|
8318b5cb71 | ||
|
|
014f23c7a0 | ||
|
|
910a4e7b00 | ||
|
|
aef8998058 | ||
|
|
4ae6053ace | ||
|
|
9823981222 | ||
|
|
69f7fbfbb2 | ||
|
|
4a2e76c415 | ||
|
|
002541aa27 | ||
|
|
beaed55c08 | ||
|
|
2e3491cd39 | ||
|
|
3492446a73 | ||
|
|
aa9f39f82f | ||
|
|
0df2b4487f | ||
|
|
0a9d5c5005 | ||
|
|
d581160603 | ||
|
|
364f5a8d9b | ||
|
|
04c1aab69c | ||
|
|
12290b4d53 | ||
|
|
a8f6222f0c | ||
|
|
c3785ed4fc | ||
|
|
10b478b1bd | ||
|
|
90a6bf2508 | ||
|
|
b1480d4270 | ||
|
|
709ab337f1 | ||
|
|
9a71b203af | ||
|
|
88b2f6e94e | ||
|
|
601034adfa | ||
|
|
919ffd4430 | ||
|
|
b194fcbe2c | ||
|
|
b3f9fd9a71 | ||
|
|
3e84b28154 | ||
|
|
61c96dbc67 | ||
|
|
6283c10cd7 | ||
|
|
fe7f458a2f | ||
|
|
e4ceff01d4 | ||
|
|
e4d4b51962 | ||
|
|
ff30daa79a | ||
|
|
3d21e11360 | ||
|
|
4856af4916 | ||
|
|
345750ac61 | ||
|
|
13fecec653 | ||
|
|
a4c87caa04 | ||
|
|
c397c797ed | ||
|
|
8eacc17ce4 | ||
|
|
475746e75d | ||
|
|
5c7e7074ba | ||
|
|
d225eaf624 | ||
|
|
1314db7295 | ||
|
|
d9d904a5c8 | ||
|
|
e1e9f886a3 | ||
|
|
66ea037add | ||
|
|
24c38894bb | ||
|
|
dc1e36bcd2 | ||
|
|
48d3fa1c11 | ||
|
|
88e487303d | ||
|
|
a6b29b854c |
@@ -0,0 +1,8 @@
|
||||
# Container image that runs your code
|
||||
FROM ubuntu:18.04
|
||||
|
||||
# Copies your code file from your action repository to the filesystem path `/` of the container
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
|
||||
# Code file to execute when the docker container starts up (`entrypoint.sh`)
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
@@ -0,0 +1,5 @@
|
||||
name: 'Build Linux Natives in Docker Container'
|
||||
description: 'Build Linux Natives in Ubuntu 18.04 docker container'
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
@@ -0,0 +1,41 @@
|
||||
#!/bin/sh -l
|
||||
|
||||
# ubuntu dockerfile is very minimal (only 122 packages are installed)
|
||||
# need to install updated git (from official git ppa)
|
||||
apt-get -q update
|
||||
apt-get -yq install software-properties-common
|
||||
add-apt-repository ppa:git-core/ppa -y
|
||||
# install dependencies expected by other steps
|
||||
apt-get -q update
|
||||
apt-get -yq install git \
|
||||
curl \
|
||||
ca-certificates \
|
||||
wget \
|
||||
bzip2 \
|
||||
zip \
|
||||
unzip \
|
||||
xz-utils \
|
||||
sudo gnupg locales
|
||||
|
||||
# set Locale to en_US.UTF-8 (avoids hang during compilation)
|
||||
locale-gen en_US.UTF-8
|
||||
export LANG=en_US.UTF-8
|
||||
export LANGUAGE=en_US.UTF-8
|
||||
export LC_ALL=en_US.UTF-8
|
||||
|
||||
# add zulu apt repository - https://docs.azul.com/core/install/debian
|
||||
curl -s https://repos.azul.com/azul-repo.key | gpg --dearmor -o /usr/share/keyrings/azul.gpg
|
||||
echo "deb [signed-by=/usr/share/keyrings/azul.gpg] https://repos.azul.com/zulu/deb stable main" | tee /etc/apt/sources.list.d/zulu.list
|
||||
apt-get -q update
|
||||
# install zulu JDK and Java build tools
|
||||
apt-get -yq install zulu17-jdk-headless
|
||||
|
||||
# Install cross-compilation toolchains
|
||||
apt-get -yq --force-yes install gcc g++
|
||||
apt-get -yq --force-yes install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||
apt-get -yq --force-yes install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf libc6-dev-armhf-cross
|
||||
apt-get -yq --force-yes install gcc-riscv64-linux-gnu g++-riscv64-linux-gnu libc6-dev-riscv64-cross
|
||||
|
||||
# Build Linux natives
|
||||
cd arc
|
||||
./gradlew arc-core:jnigenBuildAllLinux --no-daemon
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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,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"/>
|
||||
|
||||
@@ -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){
|
||||
|
||||
|
After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 28 KiB |
@@ -2,4 +2,4 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
</adaptive-icon>
|
||||
@@ -2,4 +2,4 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -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;
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
# Temporary Files
|
||||
*~
|
||||
.*.swp
|
||||
.DS_STORE
|
||||
robovm-build/
|
||||
project.xcworkspace/
|
||||
xcuserdata/
|
||||
xcode/native/
|
||||
.gradle/
|
||||
build/
|
||||
bin/
|
||||
target/
|
||||
obj/
|
||||
out/
|
||||
classes/
|
||||
war/
|
||||
gen/
|
||||
gen-external-apklibs/
|
||||
Debug/
|
||||
Release/
|
||||
robovm-build/
|
||||
ipch/
|
||||
/dist
|
||||
|
||||
#for now
|
||||
extensions/physic/
|
||||
|
||||
#maven sucks
|
||||
pom.xml
|
||||
|
||||
*.dll.mdb
|
||||
*.userprefs
|
||||
*.sdf
|
||||
*.class
|
||||
hs_err_pid*
|
||||
Thumbs.db
|
||||
|
||||
arc-*.zip
|
||||
arc-*.zip.MD5
|
||||
|
||||
*.iml
|
||||
/.idea/
|
||||
|
||||
#core & extension libs/ folders that have no 3rd party dependencies in them
|
||||
/backends/arc-backend-robovm/libs/
|
||||
/extensions/freetype/libs/
|
||||
/extensions/freetype/jni/
|
||||
/extensions/freetype/Users/
|
||||
/extensions/filedialogs/jni/
|
||||
/extensions/filedialogs/libs/
|
||||
/backends/backend-sdl/jni
|
||||
/backends/backend-robovm/res/META-INF/robovm/ios/libs/
|
||||
/arc-core/libs/
|
||||
/arc-core/jni/
|
||||
/arc-core/obj/
|
||||
/arc-core/Users/
|
||||
/arc-core/csrc/stb_image.h
|
||||
/arc-core/csrc/soloud
|
||||
/arc-core/csrc/freetype
|
||||
/arc-core/csrc/freetype.tar.gz
|
||||
#directories created by fetch.xml
|
||||
/tmp/
|
||||
|
||||
#gradle wrapper
|
||||
/local.properties
|
||||
@@ -0,0 +1,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
|
||||
}
|
||||
@@ -0,0 +1,756 @@
|
||||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
/* Header for class arc_backend_robovm_IOSGLES30 */
|
||||
|
||||
#ifndef _Included_arc_backend_robovm_IOSGLES30
|
||||
#define _Included_arc_backend_robovm_IOSGLES30
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: init
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_init
|
||||
(JNIEnv *, jclass);
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glReadBuffer
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glReadBuffer
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDrawRangeElements
|
||||
* Signature: (IIIIILjava/nio/Buffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDrawRangeElements__IIIIILjava_nio_Buffer_2
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDrawRangeElements
|
||||
* Signature: (IIIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDrawRangeElements__IIIIII
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glTexImage3D
|
||||
* Signature: (IIIIIIIIILjava/nio/Buffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glTexImage3D__IIIIIIIIILjava_nio_Buffer_2
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint, jint, jint, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glTexImage3D
|
||||
* Signature: (IIIIIIIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glTexImage3D__IIIIIIIIII
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glTexSubImage3D
|
||||
* Signature: (IIIIIIIIIILjava/nio/Buffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glTexSubImage3D__IIIIIIIIIILjava_nio_Buffer_2
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint, jint, jint, jint, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glTexSubImage3D
|
||||
* Signature: (IIIIIIIIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glTexSubImage3D__IIIIIIIIIII
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint, jint, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glCopyTexSubImage3D
|
||||
* Signature: (IIIIIIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glCopyTexSubImage3D
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGenQueries
|
||||
* Signature: (I[II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGenQueries__I_3II
|
||||
(JNIEnv *, jobject, jint, jintArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGenQueries
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGenQueries__ILjava_nio_IntBuffer_2
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDeleteQueries
|
||||
* Signature: (I[II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDeleteQueries__I_3II
|
||||
(JNIEnv *, jobject, jint, jintArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDeleteQueries
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDeleteQueries__ILjava_nio_IntBuffer_2
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glIsQuery
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_arc_backend_robovm_IOSGLES30_glIsQuery
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glBeginQuery
|
||||
* Signature: (II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glBeginQuery
|
||||
(JNIEnv *, jobject, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glEndQuery
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glEndQuery
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetQueryiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetQueryiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetQueryObjectuiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetQueryObjectuiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUnmapBuffer
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_arc_backend_robovm_IOSGLES30_glUnmapBuffer
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetBufferPointerv
|
||||
* Signature: (II)Ljava/nio/Buffer;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_arc_backend_robovm_IOSGLES30_glGetBufferPointerv
|
||||
(JNIEnv *, jobject, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDrawBuffers
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDrawBuffers
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniformMatrix2x3fv
|
||||
* Signature: (IIZLjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniformMatrix2x3fv
|
||||
(JNIEnv *, jobject, jint, jint, jboolean, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniformMatrix3x2fv
|
||||
* Signature: (IIZLjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniformMatrix3x2fv
|
||||
(JNIEnv *, jobject, jint, jint, jboolean, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniformMatrix2x4fv
|
||||
* Signature: (IIZLjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniformMatrix2x4fv
|
||||
(JNIEnv *, jobject, jint, jint, jboolean, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniformMatrix4x2fv
|
||||
* Signature: (IIZLjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniformMatrix4x2fv
|
||||
(JNIEnv *, jobject, jint, jint, jboolean, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniformMatrix3x4fv
|
||||
* Signature: (IIZLjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniformMatrix3x4fv
|
||||
(JNIEnv *, jobject, jint, jint, jboolean, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniformMatrix4x3fv
|
||||
* Signature: (IIZLjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniformMatrix4x3fv
|
||||
(JNIEnv *, jobject, jint, jint, jboolean, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glBlitFramebuffer
|
||||
* Signature: (IIIIIIIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glBlitFramebuffer
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glRenderbufferStorageMultisample
|
||||
* Signature: (IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glRenderbufferStorageMultisample
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glFramebufferTextureLayer
|
||||
* Signature: (IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glFramebufferTextureLayer
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glFlushMappedBufferRange
|
||||
* Signature: (III)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glFlushMappedBufferRange
|
||||
(JNIEnv *, jobject, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glBindVertexArray
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glBindVertexArray
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDeleteVertexArrays
|
||||
* Signature: (I[II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDeleteVertexArrays__I_3II
|
||||
(JNIEnv *, jobject, jint, jintArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDeleteVertexArrays
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDeleteVertexArrays__ILjava_nio_IntBuffer_2
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGenVertexArrays
|
||||
* Signature: (I[II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGenVertexArrays__I_3II
|
||||
(JNIEnv *, jobject, jint, jintArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGenVertexArrays
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGenVertexArrays__ILjava_nio_IntBuffer_2
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glIsVertexArray
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_arc_backend_robovm_IOSGLES30_glIsVertexArray
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glBeginTransformFeedback
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glBeginTransformFeedback
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glEndTransformFeedback
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glEndTransformFeedback
|
||||
(JNIEnv *, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glBindBufferRange
|
||||
* Signature: (IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glBindBufferRange
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glBindBufferBase
|
||||
* Signature: (III)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glBindBufferBase
|
||||
(JNIEnv *, jobject, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glTransformFeedbackVaryings
|
||||
* Signature: (I[Ljava/lang/String;I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glTransformFeedbackVaryings
|
||||
(JNIEnv *, jobject, jint, jobjectArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glVertexAttribIPointer
|
||||
* Signature: (IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glVertexAttribIPointer
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetVertexAttribIiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetVertexAttribIiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetVertexAttribIuiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetVertexAttribIuiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glVertexAttribI4i
|
||||
* Signature: (IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glVertexAttribI4i
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glVertexAttribI4ui
|
||||
* Signature: (IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glVertexAttribI4ui
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetUniformuiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetUniformuiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetFragDataLocation
|
||||
* Signature: (ILjava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_arc_backend_robovm_IOSGLES30_glGetFragDataLocation
|
||||
(JNIEnv *, jobject, jint, jstring);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniform1uiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniform1uiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniform3uiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniform3uiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniform4uiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniform4uiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glClearBufferiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glClearBufferiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glClearBufferuiv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glClearBufferuiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glClearBufferfv
|
||||
* Signature: (IILjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glClearBufferfv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glClearBufferfi
|
||||
* Signature: (IIFI)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glClearBufferfi
|
||||
(JNIEnv *, jobject, jint, jint, jfloat, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetStringi
|
||||
* Signature: (II)Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jstring JNICALL Java_arc_backend_robovm_IOSGLES30_glGetStringi
|
||||
(JNIEnv *, jobject, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glCopyBufferSubData
|
||||
* Signature: (IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glCopyBufferSubData
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetUniformIndices
|
||||
* Signature: (I[Ljava/lang/String;Ljava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetUniformIndices
|
||||
(JNIEnv *, jobject, jint, jobjectArray, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetActiveUniformsiv
|
||||
* Signature: (IILjava/nio/IntBuffer;ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetActiveUniformsiv
|
||||
(JNIEnv *, jobject, jint, jint, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetUniformBlockIndex
|
||||
* Signature: (ILjava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_arc_backend_robovm_IOSGLES30_glGetUniformBlockIndex
|
||||
(JNIEnv *, jobject, jint, jstring);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetActiveUniformBlockiv
|
||||
* Signature: (IIILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetActiveUniformBlockiv
|
||||
(JNIEnv *, jobject, jint, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetActiveUniformBlockName
|
||||
* Signature: (IILjava/nio/Buffer;Ljava/nio/Buffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetActiveUniformBlockName__IILjava_nio_Buffer_2Ljava_nio_Buffer_2
|
||||
(JNIEnv *, jobject, jint, jint, jobject, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetActiveUniformBlockName
|
||||
* Signature: (II)Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jstring JNICALL Java_arc_backend_robovm_IOSGLES30_glGetActiveUniformBlockName__II
|
||||
(JNIEnv *, jobject, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glUniformBlockBinding
|
||||
* Signature: (III)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glUniformBlockBinding
|
||||
(JNIEnv *, jobject, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDrawArraysInstanced
|
||||
* Signature: (IIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDrawArraysInstanced
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDrawElementsInstanced
|
||||
* Signature: (IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDrawElementsInstanced
|
||||
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetInteger64v
|
||||
* Signature: (ILjava/nio/LongBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetInteger64v
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetBufferParameteri64v
|
||||
* Signature: (IILjava/nio/LongBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetBufferParameteri64v
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGenSamplers
|
||||
* Signature: (I[II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGenSamplers__I_3II
|
||||
(JNIEnv *, jobject, jint, jintArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGenSamplers
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGenSamplers__ILjava_nio_IntBuffer_2
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDeleteSamplers
|
||||
* Signature: (I[II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDeleteSamplers__I_3II
|
||||
(JNIEnv *, jobject, jint, jintArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDeleteSamplers
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDeleteSamplers__ILjava_nio_IntBuffer_2
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glIsSampler
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_arc_backend_robovm_IOSGLES30_glIsSampler
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glBindSampler
|
||||
* Signature: (II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glBindSampler
|
||||
(JNIEnv *, jobject, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glSamplerParameteri
|
||||
* Signature: (III)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glSamplerParameteri
|
||||
(JNIEnv *, jobject, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glSamplerParameteriv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glSamplerParameteriv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glSamplerParameterf
|
||||
* Signature: (IIF)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glSamplerParameterf
|
||||
(JNIEnv *, jobject, jint, jint, jfloat);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glSamplerParameterfv
|
||||
* Signature: (IILjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glSamplerParameterfv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetSamplerParameteriv
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetSamplerParameteriv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGetSamplerParameterfv
|
||||
* Signature: (IILjava/nio/FloatBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGetSamplerParameterfv
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glVertexAttribDivisor
|
||||
* Signature: (II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glVertexAttribDivisor
|
||||
(JNIEnv *, jobject, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glBindTransformFeedback
|
||||
* Signature: (II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glBindTransformFeedback
|
||||
(JNIEnv *, jobject, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDeleteTransformFeedbacks
|
||||
* Signature: (I[II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDeleteTransformFeedbacks__I_3II
|
||||
(JNIEnv *, jobject, jint, jintArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glDeleteTransformFeedbacks
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glDeleteTransformFeedbacks__ILjava_nio_IntBuffer_2
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGenTransformFeedbacks
|
||||
* Signature: (I[II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGenTransformFeedbacks__I_3II
|
||||
(JNIEnv *, jobject, jint, jintArray, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glGenTransformFeedbacks
|
||||
* Signature: (ILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glGenTransformFeedbacks__ILjava_nio_IntBuffer_2
|
||||
(JNIEnv *, jobject, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glIsTransformFeedback
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_arc_backend_robovm_IOSGLES30_glIsTransformFeedback
|
||||
(JNIEnv *, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glPauseTransformFeedback
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glPauseTransformFeedback
|
||||
(JNIEnv *, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glResumeTransformFeedback
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glResumeTransformFeedback
|
||||
(JNIEnv *, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glProgramParameteri
|
||||
* Signature: (III)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glProgramParameteri
|
||||
(JNIEnv *, jobject, jint, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glInvalidateFramebuffer
|
||||
* Signature: (IILjava/nio/IntBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glInvalidateFramebuffer
|
||||
(JNIEnv *, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
* Class: arc_backend_robovm_IOSGLES30
|
||||
* Method: glInvalidateSubFramebuffer
|
||||
* Signature: (IILjava/nio/IntBuffer;IIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_arc_backend_robovm_IOSGLES30_glInvalidateSubFramebuffer
|
||||
(JNIEnv *, jobject, jint, jint, jobject, jint, jint, jint, jint);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,148 @@
|
||||
package arc;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
|
||||
import java.net.*;
|
||||
|
||||
public interface Application extends Disposable{
|
||||
|
||||
/** Returns a list of all the application listeners used. */
|
||||
Seq<ApplicationListener> getListeners();
|
||||
|
||||
/** Adds a new application listener. */
|
||||
default void addListener(ApplicationListener listener){
|
||||
synchronized(getListeners()){
|
||||
getListeners().add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes an application listener. */
|
||||
default void removeListener(ApplicationListener listener){
|
||||
post(() -> {
|
||||
synchronized(getListeners()){
|
||||
getListeners().remove(listener);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Call this before update() in each backend. */
|
||||
default void defaultUpdate(){
|
||||
Core.settings.autosave();
|
||||
Time.updateGlobal();
|
||||
}
|
||||
|
||||
/** @return what {@link ApplicationType} this application has, e.g. Android or Desktop */
|
||||
ApplicationType getType();
|
||||
|
||||
default boolean isDesktop(){
|
||||
return getType() == ApplicationType.desktop;
|
||||
}
|
||||
|
||||
default boolean isHeadless(){
|
||||
return getType() == ApplicationType.headless;
|
||||
}
|
||||
|
||||
default boolean isAndroid(){
|
||||
return getType() == ApplicationType.android;
|
||||
}
|
||||
|
||||
default boolean isIOS(){
|
||||
return getType() == ApplicationType.iOS;
|
||||
}
|
||||
|
||||
default boolean isMobile(){
|
||||
return isAndroid() || isIOS();
|
||||
}
|
||||
|
||||
default boolean isWeb(){
|
||||
return getType() == ApplicationType.web;
|
||||
}
|
||||
|
||||
/** @return the Android API level on Android, the major OS version on iOS (5, 6, 7, ..), or 0 on the desktop. */
|
||||
default int getVersion(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @return the Java heap memory use in bytes. */
|
||||
default long getJavaHeap(){
|
||||
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
|
||||
}
|
||||
|
||||
/** @return the Native heap memory use in bytes. Only valid on Android. */
|
||||
default long getNativeHeap(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @return the main graphics thread, upon which all ApplicationListener methods are called. May return null if not initialized. */
|
||||
default @Nullable Thread getMainThread(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the currently executing thread is the main thread.
|
||||
* If the main thread is not initialized, returns true by default. This is used for error checking purposes.
|
||||
* */
|
||||
default boolean isOnMainThread(){
|
||||
Thread thread = getMainThread();
|
||||
return thread == null || Thread.currentThread() == thread;
|
||||
}
|
||||
|
||||
@Nullable String getClipboardText();
|
||||
|
||||
void setClipboardText(String text);
|
||||
|
||||
default void setClipboardImage(Pixmap pixmap){
|
||||
//no-op
|
||||
}
|
||||
|
||||
/** Open a folder in the system's file browser.
|
||||
* @return whether this operation was successful. */
|
||||
default boolean openFolder(String file){
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches the default browser to display a URI. If the default browser is not able to handle the specified URI, the
|
||||
* application registered for handling URIs of the specified type is invoked. The application is determined from the protocol
|
||||
* and path of the URI. A best effort is made to open the given URI; however, since external applications are involved, no guarantee
|
||||
* can be made as to whether the URI was actually opened. If it is known that the URI was not opened, false will be returned;
|
||||
* otherwise, true will be returned.
|
||||
* @param URI the URI to be opened.
|
||||
* @return false if it is known the uri was not opened, true otherwise.
|
||||
*/
|
||||
default boolean openURI(String URI){
|
||||
return false;
|
||||
}
|
||||
|
||||
default void getDnsServers(Seq<InetSocketAddress> out){}
|
||||
|
||||
/** Posts a runnable on the main loop thread.*/
|
||||
void post(Runnable runnable);
|
||||
|
||||
/**
|
||||
* Schedule an exit from the application. On android, this will cause a call to pause() and dispose() some time in the future,
|
||||
* it will not immediately finish your application.
|
||||
* On iOS this should be avoided in production as it breaks Apples guidelines.
|
||||
*/
|
||||
void exit();
|
||||
|
||||
/** Disposes of core resources. */
|
||||
@Override
|
||||
default void dispose(){
|
||||
//flush any changes to settings upon dispose
|
||||
if(Core.settings != null){
|
||||
Core.settings.autosave();
|
||||
}
|
||||
|
||||
if(Core.audio != null){
|
||||
Core.audio.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/** Enumeration of possible {@link Application} types */
|
||||
enum ApplicationType{
|
||||
android, desktop, headless, web, iOS
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package arc;
|
||||
|
||||
import arc.files.Fi;
|
||||
|
||||
public abstract class ApplicationCore implements ApplicationListener{
|
||||
protected ApplicationListener[] modules = {};
|
||||
|
||||
public void add(ApplicationListener module){
|
||||
//use an array instead of a seq/list, for faster iteration; modules do not get added often, so a resize each time is acceptable
|
||||
ApplicationListener[] news = new ApplicationListener[modules.length + 1];
|
||||
news[news.length - 1] = module;
|
||||
System.arraycopy(modules, 0, news, 0, modules.length);
|
||||
modules = news;
|
||||
}
|
||||
|
||||
public abstract void setup();
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
setup();
|
||||
|
||||
for(ApplicationListener listener : modules){
|
||||
listener.init();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height){
|
||||
for(ApplicationListener listener : modules){
|
||||
listener.resize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
for(ApplicationListener listener : modules){
|
||||
listener.update();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause(){
|
||||
for(ApplicationListener listener : modules){
|
||||
listener.pause();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume(){
|
||||
for(ApplicationListener listener : modules){
|
||||
listener.resume();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
for(ApplicationListener listener : modules){
|
||||
listener.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileDropped(Fi file){
|
||||
for(ApplicationListener listener : modules){
|
||||
listener.fileDropped(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package arc;
|
||||
|
||||
import arc.files.Fi;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An <code>ApplicationListener</code> is called when the {@link Application} is created, resumed, rendering, paused or destroyed.
|
||||
* All methods are called in a thread that has the OpenGL context current. You can thus safely create and manipulate graphics
|
||||
* resources.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The <code>ApplicationListener</code> interface follows the standard Android activity life-cycle and is emulated on the desktop
|
||||
* accordingly.
|
||||
* </p>
|
||||
* @author mzechner
|
||||
*/
|
||||
public interface ApplicationListener{
|
||||
/**
|
||||
* Called when the {@link Application} is first created.
|
||||
* Only gets called if the application is created before the listener is added.
|
||||
*/
|
||||
default void init(){
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the {@link Application} is resized. This can happen at any point during a non-paused state but will never happen
|
||||
* before a call to {@link #init()}.
|
||||
* @param width the new width in pixels
|
||||
* @param height the new height in pixels
|
||||
*/
|
||||
default void resize(int width, int height){
|
||||
}
|
||||
|
||||
/** Called when the {@link Application} should update itself. */
|
||||
default void update(){
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the {@link Application} is paused, usually when it's not active or visible on screen. An Application is also
|
||||
* paused before it is destroyed.
|
||||
*/
|
||||
default void pause(){
|
||||
}
|
||||
|
||||
/** Called when the {@link Application} is resumed from a paused state, usually when it regains focus. */
|
||||
default void resume(){
|
||||
}
|
||||
|
||||
/** Called when the {@link Application} is destroyed. Preceded by a call to {@link #pause()}. */
|
||||
default void dispose(){
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the applications exits gracefully, either through `Core.app.exit()` or through a window closing.
|
||||
* Never called after a crash, unlike dispose().
|
||||
* */
|
||||
default void exit(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an external file is dropped into the window, e.g from the desktop.
|
||||
*/
|
||||
default void fileDropped(Fi file){
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package arc;
|
||||
|
||||
import arc.assets.*;
|
||||
import arc.audio.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.graphics.gl.*;
|
||||
import arc.scene.*;
|
||||
import arc.util.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/** Global references to all of Arc's core modules. */
|
||||
public class Core{
|
||||
public static Application app;
|
||||
public static Graphics graphics;
|
||||
public static Audio audio;
|
||||
public static Input input;
|
||||
public static Files files;
|
||||
public static Settings settings;
|
||||
|
||||
public static I18NBundle bundle = I18NBundle.createEmptyBundle();
|
||||
public static Camera camera;
|
||||
public static Batch batch;
|
||||
public static Scene scene;
|
||||
public static AssetManager assets;
|
||||
public static TextureAtlas atlas;
|
||||
public static ExecutorService executor = Threads.executor("Main Executor", OS.cores);
|
||||
|
||||
/** This class should never be used directly - use {@link Gl} instead. */
|
||||
public static GLProvider glProvider;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package arc;
|
||||
|
||||
|
||||
import arc.struct.Seq;
|
||||
import arc.struct.ObjectMap;
|
||||
import arc.func.Cons;
|
||||
|
||||
/** Simple global event listener system. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public class Events{
|
||||
private static final ObjectMap<Object, Seq<Cons<?>>> events = new ObjectMap<>();
|
||||
|
||||
/** Handle an event by class. */
|
||||
public static <T> void on(Class<T> type, Cons<T> listener){
|
||||
events.get(type, () -> new Seq<>(Cons.class)).add(listener);
|
||||
}
|
||||
|
||||
/** Handle an event by enum trigger. */
|
||||
public static void run(Object type, Runnable listener){
|
||||
events.get(type, () -> new Seq<>(Cons.class)).add(e -> listener.run());
|
||||
}
|
||||
|
||||
/** Only use this method if you have the reference to the exact listener object that was used. */
|
||||
public static <T> boolean remove(Class<T> type, Cons<T> listener){
|
||||
return events.get(type, () -> new Seq<>(Cons.class)).remove(listener);
|
||||
}
|
||||
|
||||
/** Fires an enum trigger. */
|
||||
public static <T extends Enum<T>> void fire(Enum<T> type){
|
||||
Seq<Cons<?>> listeners = events.get(type);
|
||||
|
||||
if(listeners != null){
|
||||
int len = listeners.size;
|
||||
Cons[] items = listeners.items;
|
||||
for(int i = 0; i < len; i++){
|
||||
items[i].get(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Fires a non-enum event by class. */
|
||||
public static <T> void fire(T type){
|
||||
fire(type.getClass(), type);
|
||||
}
|
||||
|
||||
public static <T> void fire(Class<?> ctype, T type){
|
||||
Seq<Cons<?>> listeners = events.get(ctype);
|
||||
|
||||
if(listeners != null){
|
||||
int len = listeners.size;
|
||||
Cons[] items = listeners.items;
|
||||
for(int i = 0; i < len; i++){
|
||||
items[i].get(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't do this. */
|
||||
public static void clear(){
|
||||
events.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package arc;
|
||||
|
||||
import arc.audio.Audio;
|
||||
import arc.files.Fi;
|
||||
import arc.util.ArcRuntimeException;
|
||||
|
||||
/**
|
||||
* Provides standard access to the filesystem, classpath, Android SD card, and Android assets directory.
|
||||
* @author mzechner
|
||||
* @author Nathan Sweet
|
||||
*/
|
||||
public interface Files{
|
||||
/**
|
||||
* Returns a handle representing a file or directory.
|
||||
* @param type Determines how the path is resolved.
|
||||
* @throws ArcRuntimeException if the type is classpath or internal and the file does not exist.
|
||||
* @see FileType
|
||||
*/
|
||||
Fi get(String path, FileType type);
|
||||
|
||||
/** Convenience method that returns a {@link FileType#classpath} file handle. */
|
||||
default Fi classpath(String path){
|
||||
return get(path, FileType.classpath);
|
||||
}
|
||||
|
||||
/** Convenience method that returns a {@link FileType#internal} file handle. */
|
||||
default Fi internal(String path){
|
||||
return get(path, FileType.internal);
|
||||
}
|
||||
|
||||
/** Convenience method that returns a {@link FileType#external} file handle. */
|
||||
default Fi external(String path){
|
||||
return get(path, FileType.external);
|
||||
}
|
||||
|
||||
/** Convenience method that returns a {@link FileType#absolute} file handle. */
|
||||
default Fi absolute(String path){
|
||||
return get(path, FileType.absolute);
|
||||
}
|
||||
|
||||
/** Convenience method that returns a {@link FileType#local} file handle. */
|
||||
default Fi local(String path){
|
||||
return get(path, FileType.local);
|
||||
}
|
||||
|
||||
/** Convenience method that returns a cache file handle. */
|
||||
default Fi cache(String path){
|
||||
return get(getCachePath(), FileType.absolute).child(path);
|
||||
}
|
||||
|
||||
/** @return absolute path to cache directory. */
|
||||
default String getCachePath(){
|
||||
return local("cache").absolutePath();
|
||||
}
|
||||
|
||||
/** @return on IOS, the internal path to the assets directory. Not used on other platforms. */
|
||||
default String getInternalStoragePath(){
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the external storage path directory. This is the SD card on Android and the home directory of the current user on
|
||||
* the desktop.
|
||||
*/
|
||||
String getExternalStoragePath();
|
||||
|
||||
/**
|
||||
* @return true if the external storage is ready for file IO. Eg, on Android, the SD card is not available when mounted for use
|
||||
* with a PC.
|
||||
*/
|
||||
boolean isExternalStorageAvailable();
|
||||
|
||||
/**
|
||||
* @return the local storage path directory. This is the private files directory on Android and the directory of the jar on the
|
||||
* desktop.
|
||||
*/
|
||||
String getLocalStoragePath();
|
||||
|
||||
/** @return true if the local storage is ready for file IO. */
|
||||
boolean isLocalStorageAvailable();
|
||||
|
||||
/**
|
||||
* Indicates how to resolve a path to a file.
|
||||
* @author mzechner
|
||||
* @author Nathan Sweet
|
||||
*/
|
||||
enum FileType{
|
||||
/**
|
||||
* Path relative to the root of the classpath. Classpath files are always readonly. Note that classpath files are not
|
||||
* compatible with some functionality on Android, such as {@link arc.audio.Audio#newSound(Fi)} and
|
||||
* {@link Audio#newMusic(Fi)}.
|
||||
*/
|
||||
classpath,
|
||||
|
||||
/**
|
||||
* Path relative to the asset directory on Android and to the application's root directory on the desktop. On the desktop,
|
||||
* if the file is not found, then the classpath is checked. This enables files to be found when using JWS or applets.
|
||||
* Internal files are always readonly.
|
||||
*/
|
||||
internal,
|
||||
|
||||
/** Path relative to the root of the SD card on Android and to the home directory of the current user on the desktop. */
|
||||
external,
|
||||
|
||||
/**
|
||||
* Path that is a fully qualified, absolute filesystem path. To ensure portability across platforms use absolute files only
|
||||
* when absolutely (heh) necessary.
|
||||
*/
|
||||
absolute,
|
||||
|
||||
/** Path relative to the private files directory on Android and to the application's root directory on the desktop. */
|
||||
local
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
package arc;
|
||||
|
||||
import arc.Graphics.Cursor.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.gl.*;
|
||||
import arc.graphics.gl.GLVersion.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
|
||||
/**
|
||||
* This interface encapsulates communication with the graphics processor. Depending on the available hardware and the current
|
||||
* {@link Application} configuration, access to {@link GLProvider} are provided here.
|
||||
* <p>
|
||||
* If supported by the backend, this interface lets you query the available display modes (graphics resolution and color depth)
|
||||
* and change it.
|
||||
* <p>
|
||||
* This interface can be used to switch between continuous and non-continuous rendering (see
|
||||
* {@link #setContinuousRendering(boolean)}), and to explicitly {@link #requestRendering()}.
|
||||
* <p>
|
||||
* @author mzechner
|
||||
*/
|
||||
public abstract class Graphics implements Disposable{
|
||||
/** The last cursor used. Can be Cursor or SystemCursor.*/
|
||||
private Object lastCursor;
|
||||
|
||||
/** @return whether instancing with glVertexAttribDivisor and glDrawElementsInstanced is available on this platform. */
|
||||
public boolean supportsInstancing(){
|
||||
//instancing via glDrawElementsInstanced + glVertexAttribDivisor is available on GL ES 3.0; otherwise, only >= Gl 3.3 has glVertexAttribDivisor on desktop
|
||||
return getGLVersion().type == GlType.GLES || getGLVersion().atLeast(3, 3);
|
||||
}
|
||||
|
||||
/** Clears the color buffer using the specified color. */
|
||||
public void clear(float r, float g, float b, float a){
|
||||
Gl.clearColor(r, g, b, a);
|
||||
Gl.clear(Gl.colorBufferBit);
|
||||
}
|
||||
|
||||
/** Clears the color buffer using the specified color. */
|
||||
public void clear(Color color){
|
||||
clear(color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
/** Returns whether this application is probably in portrait mode, e.g. if width < height.*/
|
||||
public boolean isPortrait(){
|
||||
return getWidth() < getHeight();
|
||||
}
|
||||
|
||||
/** @return the width of the client area in logical pixels. */
|
||||
public abstract int getWidth();
|
||||
|
||||
/** @return the height of the client area in logical pixels */
|
||||
public abstract int getHeight();
|
||||
|
||||
/** @return the aspect ratio, e.g. width/height */
|
||||
public float getAspect(){
|
||||
return (float)getWidth() / getHeight();
|
||||
}
|
||||
|
||||
/** @return whether the window is 'hidden', e.g. whether width or height of the window is less than 2.
|
||||
* This is due to Windows reporting window size as 0 or 1 when minimized. This causes framebuffers to crash if resized.
|
||||
*/
|
||||
public boolean isHidden(){
|
||||
return getWidth() < 2 || getHeight() < 2;
|
||||
}
|
||||
|
||||
/** @return the width of the framebuffer in physical pixels */
|
||||
public abstract int getBackBufferWidth();
|
||||
|
||||
/** @return the height of the framebuffer in physical pixels */
|
||||
public abstract int getBackBufferHeight();
|
||||
|
||||
/** @return the safe area insets, in the order left-right-top-bottom. */
|
||||
public int[] getSafeInsets(){
|
||||
return new int[4];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the current frame. The general contract of this method is that the id is incremented only when the
|
||||
* application is in the running state right before calling the {@link ApplicationListener#update()} method. Also, the id of
|
||||
* the first frame is 0; the id of subsequent frames is guaranteed to take increasing values for 2<sup>63</sup>-1 rendering
|
||||
* cycles.
|
||||
* @return the id of the current frame
|
||||
*/
|
||||
public abstract long getFrameId();
|
||||
|
||||
/** @return the time span between the current frame and the last frame in seconds. Might be smoothed over n frames. */
|
||||
public abstract float getDeltaTime();
|
||||
|
||||
/** @return the average number of frames per second */
|
||||
public abstract int getFramesPerSecond();
|
||||
|
||||
/** @return the {@link GLVersion} of this Graphics instance */
|
||||
public abstract GLVersion getGLVersion();
|
||||
|
||||
/** @return the pixels per inch on the x-axis */
|
||||
public abstract float getPpiX();
|
||||
|
||||
/** @return the pixels per inch on the y-axis */
|
||||
public abstract float getPpiY();
|
||||
|
||||
/** @return the pixels per centimeter on the x-axis */
|
||||
public abstract float getPpcX();
|
||||
|
||||
/** @return the pixels per centimeter on the y-axis. */
|
||||
public abstract float getPpcY();
|
||||
|
||||
/**
|
||||
* This is a scaling factor for the Density Independent Pixel unit, following the same conventions as
|
||||
* android.util.DisplayMetrics#density, where one DIP is one pixel on an approximately 160 dpi screen. Thus on a 160dpi screen
|
||||
* this density value will be 1; on a 120 dpi screen it would be .75; etc.
|
||||
* @return the logical density of the Display.
|
||||
*/
|
||||
public abstract float getDensity();
|
||||
|
||||
/**
|
||||
* Sets the window to full-screen mode.
|
||||
* @return whether the operation succeeded.
|
||||
*/
|
||||
public boolean setFullscreen(boolean fullscreen){
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the window position on its current monitor.
|
||||
*/
|
||||
public void setWindowPosition(int x, int y){}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the window size in pixels.
|
||||
*/
|
||||
public void setWindowSize(int width, int height){}
|
||||
|
||||
/**
|
||||
* Sets the title of the window. Ignored on Android.
|
||||
* @param title the title.
|
||||
*/
|
||||
public abstract void setTitle(String title);
|
||||
|
||||
/**
|
||||
* Enable/Disable vsynching. This is a best-effort attempt which might not work on all platforms.
|
||||
* @param vsync vsync enabled or not.
|
||||
*/
|
||||
public abstract void setVSync(boolean vsync);
|
||||
|
||||
/** @return the format of the color, depth and stencil buffer in a {@link BufferFormat} instance */
|
||||
public abstract BufferFormat getBufferFormat();
|
||||
|
||||
/**
|
||||
* @param extension the extension name
|
||||
* @return whether the extension is supported
|
||||
*/
|
||||
public abstract boolean supportsExtension(String extension);
|
||||
|
||||
/** On iOS, sets the preferred frame rate. A value of 0 sets the maximum supported value for this device. */
|
||||
public void setPreferredFPS(int fps){}
|
||||
|
||||
/** @return whether rendering is continuous. */
|
||||
public abstract boolean isContinuousRendering();
|
||||
|
||||
/**
|
||||
* Sets whether to render continuously. In case rendering is performed non-continuously, the following events will trigger a
|
||||
* redraw:
|
||||
*
|
||||
* <ul>
|
||||
* <li>A call to {@link #requestRendering()}</li>
|
||||
* <li>Input events from the touch screen/mouse or keyboard</li>
|
||||
* <li>A {@link Runnable} is posted to the rendering thread via {@link Application#post(Runnable)}. In the case
|
||||
* of a multi-window app, all windows will request rendering if a runnable is posted to the application. To avoid this,
|
||||
* post a runnable to the window instead. </li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Life-cycle events will also be reported as usual, see {@link ApplicationListener}. This method can be called from any
|
||||
* thread.
|
||||
* @param isContinuous whether the rendering should be continuous or not.
|
||||
*/
|
||||
public abstract void setContinuousRendering(boolean isContinuous);
|
||||
|
||||
/** Requests a new frame to be rendered if the rendering mode is non-continuous. This method can be called from any thread. */
|
||||
public abstract void requestRendering();
|
||||
|
||||
/** Whether the app is fullscreen or not */
|
||||
public abstract boolean isFullscreen();
|
||||
|
||||
/**
|
||||
* Create a new cursor represented by the {@link arc.graphics.Pixmap}. The Pixmap must be in RGBA8888 format,
|
||||
* width & height must be powers-of-two greater than zero (not necessarily equal) and of a certain minimum size (32x32 is a safe bet),
|
||||
* and alpha transparency must be single-bit (i.e., 0x00 or 0xFF only). This function returns a Cursor object that can be set as the
|
||||
* system cursor by calling {@link #setCursor(Cursor)} .
|
||||
* @param pixmap the mouse cursor image as a {@link arc.graphics.Pixmap}
|
||||
* @param xHotspot the x location of the hotspot pixel within the cursor image (origin top-left corner)
|
||||
* @param yHotspot the y location of the hotspot pixel within the cursor image (origin top-left corner)
|
||||
* @return a cursor object that can be used by calling {@link #setCursor(Cursor)} or null if not supported
|
||||
*/
|
||||
public abstract Cursor newCursor(Pixmap pixmap, int xHotspot, int yHotspot);
|
||||
|
||||
/**
|
||||
* Creates a new cursor by scaling a pixmap and adding an outline.
|
||||
* @param pixmap The base pixmap. Unscaled.
|
||||
* @param scaling The factor by which to scale the base pixmap.
|
||||
* @param outlineColor The color of the cursor's outline.
|
||||
*/
|
||||
public Cursor newCursor(Pixmap pixmap, int scaling, Color outlineColor, int outlineThickness){
|
||||
Pixmap out = Pixmaps.outline(pixmap, outlineColor, outlineThickness);
|
||||
Pixmap out2 = Pixmaps.scale(out, scaling);
|
||||
|
||||
if(!Mathf.isPowerOfTwo(out2.width)){
|
||||
Pixmap old = out2;
|
||||
out2 = Pixmaps.resize(out2, Mathf.nextPowerOfTwo(out2.width), Mathf.nextPowerOfTwo(out2.width));
|
||||
old.dispose();
|
||||
}
|
||||
|
||||
out.dispose();
|
||||
pixmap.dispose();
|
||||
|
||||
Cursor cur = newCursor(out2, out2.width / 2, out2.height / 2);
|
||||
out2.dispose();
|
||||
return cur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new cursor by file name.
|
||||
* @param filename the name of the cursor .png file, found in the internal file "cursors/{name}.png"
|
||||
*/
|
||||
public Cursor newCursor(String filename, int scale){
|
||||
if(scale == 1 || OS.isAndroid || OS.isIos) return newCursor(filename);
|
||||
Pixmap base = new Pixmap(Core.files.internal("cursors/" + filename + ".png"));
|
||||
Pixmap result = Pixmaps.scale(base, base.width * scale, base.height * scale);
|
||||
base.dispose();
|
||||
Cursor cur = newCursor(result, result.width /2, result.height /2);
|
||||
result.dispose();
|
||||
return cur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new cursor by file name.
|
||||
* @param filename the name of the cursor .png file, found in the internal file "cursors/{name}.png"
|
||||
*/
|
||||
public Cursor newCursor(String filename){
|
||||
Pixmap p = new Pixmap(Core.files.internal("cursors/" + filename + ".png"));
|
||||
Cursor result = newCursor(p, p.width /2, p.height /2);
|
||||
p.dispose();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new cursor by file name.
|
||||
* @param filename the name of the cursor .png file, found in the internal file "cursors/{name}.png"
|
||||
*/
|
||||
public Cursor newCursor(String filename, int scaling, Color outlineColor, int outlineScaling){
|
||||
return newCursor(new Pixmap(Core.files.internal("cursors/" + filename + ".png")), scaling, outlineColor, outlineScaling);
|
||||
}
|
||||
|
||||
/**Sets the cursor to the default value, e.g. {@link SystemCursor#arrow}.*/
|
||||
public void restoreCursor(){
|
||||
cursor(SystemCursor.arrow);
|
||||
}
|
||||
|
||||
/**Sets the display cursor.*/
|
||||
public void cursor(Cursor cursor){
|
||||
if(lastCursor == cursor) return;
|
||||
|
||||
if(cursor instanceof SystemCursor){
|
||||
if(((SystemCursor)cursor).cursor != null){
|
||||
setCursor(((SystemCursor)cursor).cursor);
|
||||
}else{
|
||||
setSystemCursor((SystemCursor)cursor);
|
||||
}
|
||||
}else{
|
||||
setCursor(cursor);
|
||||
}
|
||||
|
||||
lastCursor = cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only viable on desktop and web. Browsers that support cursor:url() and support the png format (the
|
||||
* pixmap is converted to a data-url of type image/png) should also support custom cursors. Will set the mouse cursor image to
|
||||
* the image represented by the {@link Cursor}. It is recommended to call this function in the main render thread, and maximum one time per frame.
|
||||
* Internal use only!
|
||||
* @param cursor the mouse cursor as a {@link Cursor}
|
||||
*/
|
||||
protected abstract void setCursor(Cursor cursor);
|
||||
|
||||
/**Sets one of the predefined {@link SystemCursor}s.
|
||||
* Internal use only!*/
|
||||
protected abstract void setSystemCursor(SystemCursor systemCursor);
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
for(SystemCursor cursor : SystemCursor.values()){
|
||||
cursor.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/** Class describing the bits per pixel, depth buffer precision, stencil precision and number of MSAA samples. */
|
||||
public static class BufferFormat{
|
||||
/* number of bits per color channel */
|
||||
public final int r, g, b, a;
|
||||
/* number of bits for depth and stencil buffer */
|
||||
public final int depth, stencil;
|
||||
/** number of samples for multi-sample anti-aliasing (MSAA) **/
|
||||
public final int samples;
|
||||
/** whether coverage sampling anti-aliasing is used. in that case you have to clear the coverage buffer as well! */
|
||||
public final boolean coverageSampling;
|
||||
|
||||
public BufferFormat(int r, int g, int b, int a, int depth, int stencil, int samples, boolean coverageSampling){
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
this.depth = depth;
|
||||
this.stencil = stencil;
|
||||
this.samples = samples;
|
||||
this.coverageSampling = coverageSampling;
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return "r: " + r + ", g: " + g + ", b: " + b + ", a: " + a + ", depth: " + depth + ", stencil: " + stencil
|
||||
+ ", num samples: " + samples + ", coverage sampling: " + coverageSampling;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Represents a mouse cursor. Create a cursor via
|
||||
* {@link Graphics#newCursor(Pixmap, int, int)}. To
|
||||
* set the cursor use {@link Graphics#setCursor(Cursor)}.
|
||||
* To use one of the system cursors, call Graphics#setSystemCursor
|
||||
* </p>
|
||||
**/
|
||||
public interface Cursor extends arc.util.Disposable{
|
||||
|
||||
enum SystemCursor implements Cursor{
|
||||
arrow,
|
||||
ibeam,
|
||||
crosshair,
|
||||
hand,
|
||||
horizontalResize,
|
||||
verticalResize;
|
||||
|
||||
/** The override cursor to use when setting this cursor.*/
|
||||
@Nullable Cursor cursor;
|
||||
|
||||
/** Sets the alias for this cursor. */
|
||||
public void set(Cursor cursor){
|
||||
this.cursor = cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
if(cursor != null && !(cursor instanceof SystemCursor)){
|
||||
cursor.dispose();
|
||||
cursor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
package arc;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.input.*;
|
||||
import arc.input.KeyBind.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Interface to the input facilities. This allows polling the state of the keyboard, the touch screen and the accelerometer. On
|
||||
* some backends (desktop) the touch screen is replaced by mouse input. The accelerometer is of course not available on
|
||||
* all backends.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The class also offers methods to use (and test for the presence of) other input systems like vibration, compass, on-screen
|
||||
* keyboards, and cursor capture. Support for simple input dialogs is also provided.
|
||||
* </p>
|
||||
* @author mzechner
|
||||
*/
|
||||
public abstract class Input{
|
||||
/** The default input device (keyboard) */
|
||||
protected KeyboardDevice keyboard = new KeyboardDevice();
|
||||
/** An input multiplexer to handle events. */
|
||||
protected InputMultiplexer inputMultiplexer = new InputMultiplexer(keyboard);
|
||||
/** List of caught keys for Android. */
|
||||
protected IntSet caughtKeys = new IntSet();
|
||||
/** Return Vec2 value for various functions. */
|
||||
protected Vec2 mouseReturn = new Vec2();
|
||||
/** Whether to use keyboard controls on Android. */
|
||||
protected boolean useKeyboard;
|
||||
|
||||
/**Returns the unprojected mouse position (screen -> world).*/
|
||||
public Vec2 mouseWorld(float x, float y){
|
||||
return Core.camera.unproject(mouseReturn.set(x, y));
|
||||
}
|
||||
|
||||
/**Returns the projected mouse position (world -> screen).*/
|
||||
public Vec2 mouseScreen(float x, float y){
|
||||
return Core.camera.project(mouseReturn.set(x, y));
|
||||
}
|
||||
|
||||
/** @return the unprojected mouse position in the world.*/
|
||||
public float mouseWorldX(){
|
||||
return Core.camera.unproject(mouse()).x;
|
||||
}
|
||||
|
||||
/** @return the unprojected mouse position in the world.*/
|
||||
public float mouseWorldY(){
|
||||
return Core.camera.unproject(mouse()).y;
|
||||
}
|
||||
|
||||
/**Returns the unprojected mouse position in the world.*/
|
||||
public Vec2 mouseWorld(){
|
||||
return Core.camera.unproject(mouse());
|
||||
}
|
||||
|
||||
/**Returns the mouse position as a Vec2.*/
|
||||
public Vec2 mouse(){
|
||||
return mouseReturn.set(mouseX(), mouseY());
|
||||
}
|
||||
|
||||
public void setUseKeyboard(boolean useKeyboard){
|
||||
this.useKeyboard = useKeyboard;
|
||||
}
|
||||
|
||||
/** @return whether the keyboard should be preferred for mobile devices - used in text fields. */
|
||||
public boolean useKeyboard(){
|
||||
return useKeyboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The x coordinate of the last touch on touch screen devices and the current mouse position on desktop for the first
|
||||
* pointer in screen coordinates. The screen origin is the top left corner.
|
||||
*/
|
||||
public abstract int mouseX();
|
||||
|
||||
/**
|
||||
* Returns the x coordinate in screen coordinates of the given pointer. Pointers are indexed from 0 to n. The pointer id
|
||||
* identifies the order in which the fingers went down on the screen, e.g. 0 is the first finger, 1 is the second and so on.
|
||||
* When two fingers are touched down and the first one is lifted the second one keeps its index. If another finger is placed on
|
||||
* the touch screen the first free index will be used.
|
||||
* @param pointer the pointer id.
|
||||
* @return the x coordinate
|
||||
*/
|
||||
public abstract int mouseX(int pointer);
|
||||
|
||||
/** @return the different between the current pointer location and the last pointer location on the x-axis. */
|
||||
public abstract int deltaX();
|
||||
|
||||
/** @return the different between the current pointer location and the last pointer location on the x-axis. */
|
||||
public abstract int deltaX(int pointer);
|
||||
|
||||
/**
|
||||
* @return The y coordinate of the last touch on touch screen devices and the current mouse position on desktop for the first
|
||||
* pointer in screen coordinates. The screen origin is the bottom left corner.
|
||||
*/
|
||||
public abstract int mouseY();
|
||||
|
||||
/**
|
||||
* Returns the y coordinate in screen coordinates of the given pointer. Pointers are indexed from 0 to n. The pointer id
|
||||
* identifies the order in which the fingers went down on the screen, e.g. 0 is the first finger, 1 is the second and so on.
|
||||
* When two fingers are touched down and the first one is lifted the second one keeps its index. If another finger is placed on
|
||||
* the touch screen the first free index will be used.
|
||||
* @param pointer the pointer id.
|
||||
* @return the y coordinate
|
||||
*/
|
||||
public abstract int mouseY(int pointer);
|
||||
|
||||
/** @return the different between the current pointer location and the last pointer location on the y-axis. */
|
||||
public abstract int deltaY();
|
||||
|
||||
/** @return the different between the current pointer location and the last pointer location on the y-axis. */
|
||||
public abstract int deltaY(int pointer);
|
||||
|
||||
/** @return whether the screen is currently touched. */
|
||||
public abstract boolean isTouched();
|
||||
|
||||
/** @return whether a new touch down event just occurred. */
|
||||
public abstract boolean justTouched();
|
||||
|
||||
public int getTouches(){
|
||||
int sum = 0;
|
||||
for(int i = 0; i < 10; i++){
|
||||
if(isTouched(i)) sum ++;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the screen is currently touched by the pointer with the given index. Pointers are indexed from 0 to n. The pointer
|
||||
* id identifies the order in which the fingers went down on the screen, e.g. 0 is the first finger, 1 is the second and so on.
|
||||
* When two fingers are touched down and the first one is lifted the second one keeps its index. If another finger is placed on
|
||||
* the touch screen the first free index will be used.
|
||||
* @param pointer the pointer
|
||||
* @return whether the screen is touched by the pointer
|
||||
*/
|
||||
public abstract boolean isTouched(int pointer);
|
||||
|
||||
/** @return the pressure of the first pointer */
|
||||
public float getPressure(){
|
||||
return getPressure(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pressure of the given pointer, where 0 is untouched. On Android it should be
|
||||
* up to 1.0, but it can go above that slightly and its not consistent between devices. On iOS 1.0 is the normal touch
|
||||
* and significantly more of hard touch. Check relevant manufacturer documentation for details.
|
||||
* Check availability with {@link Input#isPeripheralAvailable(Peripheral)}. If not supported, returns 1.0 when touched.
|
||||
* @param pointer the pointer id.
|
||||
* @return the pressure
|
||||
*/
|
||||
public float getPressure(int pointer){
|
||||
return isTouched(pointer) ? 1f : 0f;
|
||||
}
|
||||
|
||||
/** Returns whether one of the two shift keys is currently pressed.*/
|
||||
public boolean shift(){
|
||||
return keyDown(KeyCode.shiftLeft) || keyDown(KeyCode.shiftRight);
|
||||
}
|
||||
|
||||
/** Returns whether one of the two control keys is currently pressed - or, on Macs, the cmd key.*/
|
||||
public boolean ctrl(){
|
||||
return OS.isMac ? keyDown(KeyCode.sym) : keyDown(KeyCode.controlLeft) || keyDown(KeyCode.controlRight);
|
||||
}
|
||||
|
||||
/** Returns whether one of the two alt keys is pressed.*/
|
||||
public boolean alt(){
|
||||
return keyDown(KeyCode.altLeft) || keyDown(KeyCode.altRight);
|
||||
}
|
||||
|
||||
/** Returns whether the key is pressed. */
|
||||
public boolean keyDown(KeyCode key){
|
||||
return keyboard.isPressed(key);
|
||||
}
|
||||
|
||||
/** Returns whether the key has just been pressed. */
|
||||
public boolean keyTap(KeyCode key){
|
||||
return keyboard.isTapped(key);
|
||||
}
|
||||
|
||||
/** Returns whether the key has just been released. */
|
||||
public boolean keyRelease(KeyCode key){
|
||||
return keyboard.isReleased(key);
|
||||
}
|
||||
|
||||
/** Returns the [-1, 1] axis value of a key. */
|
||||
public float axis(KeyCode key){
|
||||
return keyboard.getAxis(key);
|
||||
}
|
||||
|
||||
/** Returns whether the keybind is pressed. */
|
||||
public boolean keyDown(KeyBind key){
|
||||
return key.value.key != null && keyboard.isPressed(key.value.key);
|
||||
}
|
||||
|
||||
/** Returns whether the key has just been pressed. */
|
||||
public boolean keyTap(KeyBind key){
|
||||
return key.value.key != null && keyboard.isTapped(key.value.key);
|
||||
}
|
||||
|
||||
/** Returns whether the key has just been released. */
|
||||
public boolean keyRelease(KeyBind key){
|
||||
return key.value.key != null && keyboard.isReleased(key.value.key);
|
||||
}
|
||||
|
||||
/** Returns the [-1, 1] axis value of a key. */
|
||||
public float axis(KeyBind key){
|
||||
Axis axis = key.value;
|
||||
if(axis.key != null){
|
||||
return keyboard.getAxis(axis.key);
|
||||
}else{
|
||||
return keyboard.isPressed(axis.min) && keyboard.isPressed(axis.max) ? 0 :
|
||||
keyboard.isPressed(axis.min) ? -1 : keyboard.isPressed(axis.max) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the [-1, 1] axis value of a key.
|
||||
* In the case of keyboard-based axes, this will only return a value if one of the axes was just pressed. */
|
||||
public float axisTap(KeyBind key){
|
||||
Axis axis = key.value;
|
||||
if(axis.key != null){
|
||||
return keyboard.getAxis(axis.key);
|
||||
}else{
|
||||
return keyboard.isTapped(axis.min) ? -1 : keyboard.isTapped(axis.max) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* System dependent method to input a string of text. A dialog box will be created with the given title and the given text as a
|
||||
* message for the user. Once the dialog has been closed the consumer be called on the rendering thread.
|
||||
*/
|
||||
public void getTextInput(TextInput input){
|
||||
}
|
||||
|
||||
/** @return on mobile, whether text input is currently being fetched. Not implemented on other platforms. */
|
||||
public boolean isShowingTextInput(){
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the on-screen keyboard visible if available. Only applicable on mobile.
|
||||
* @param visible visible or not
|
||||
*/
|
||||
public void setOnscreenKeyboardVisible(boolean visible){
|
||||
}
|
||||
|
||||
/**
|
||||
* Vibrates for the given amount of time. Note that you'll need the permission
|
||||
* <code> <uses-permission android:name="android.permission.VIBRATE" /></code> in your manifest file in order for this to work.
|
||||
* @param milliseconds the number of milliseconds to vibrate.
|
||||
*/
|
||||
public void vibrate(int milliseconds){
|
||||
}
|
||||
|
||||
/**
|
||||
* Vibrate with a given pattern. Pass in an array of ints that are the times at which to turn on or off the vibrator. The first
|
||||
* one is how long to wait before turning it on, and then after that it alternates. If you want to repeat, pass the index into
|
||||
* the pattern at which to start the repeat.
|
||||
* @param pattern an array of longs of times to turn the vibrator on or off.
|
||||
* @param repeat the index into pattern at which to repeat, or -1 if you don't want to repeat.
|
||||
*/
|
||||
public void vibrate(long[] pattern, int repeat){
|
||||
}
|
||||
|
||||
/** Stops the vibrator */
|
||||
public void cancelVibrate(){
|
||||
}
|
||||
|
||||
/** @return The acceleration force in m/s^2 applied to the device, including the force of gravity */
|
||||
public Vec3 getAccelerometer(){
|
||||
return Vec3.Zero;
|
||||
}
|
||||
|
||||
/** @return The rate of rotation in rad/s. */
|
||||
public Vec3 getGyroscope(){
|
||||
return Vec3.Zero;
|
||||
}
|
||||
|
||||
/** @return the device's orientation in degrees in the format (pitch, roll, azimuth) corresponding to x,y,z. */
|
||||
public Vec3 getOrientation(){
|
||||
return Vec3.Zero;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rotation matrix describing the devices rotation as per <a href=
|
||||
* "http://developer.android.com/reference/android/hardware/SensorManager.html#getRotationMatrix(float[], float[], float[], float[])"
|
||||
* >SensorManager#getRotationMatrix(float[], float[], float[], float[])</a>. Does not manipulate the matrix if the platform
|
||||
* does not have an accelerometer.
|
||||
*/
|
||||
public void getRotationMatrix(float[] matrix){
|
||||
}
|
||||
|
||||
/** @return the time of the event currently reported to the {@link InputProcessor}. */
|
||||
public abstract long getCurrentEventTime();
|
||||
|
||||
/**
|
||||
* Sets whether the specified button on Android should be caught. This will prevent the app from processing the key. Will have no effect
|
||||
* on the desktop.
|
||||
* @param c whether to catch the button
|
||||
*/
|
||||
public void setCatch(KeyCode code, boolean c){
|
||||
if(c){
|
||||
caughtKeys.add(code.ordinal());
|
||||
}else{
|
||||
caughtKeys.remove(code.ordinal());
|
||||
}
|
||||
}
|
||||
|
||||
/** @return whether the back button is currently being caught */
|
||||
public boolean isCatch(KeyCode code){
|
||||
return caughtKeys.contains(code.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link InputProcessor} that will receive all touch and key input events. It will be called before the
|
||||
* {@link ApplicationListener#update()} method each frame.
|
||||
* @param processor the InputProcessor
|
||||
* */
|
||||
public void addProcessor(InputProcessor processor){
|
||||
inputMultiplexer.addProcessor(processor);
|
||||
}
|
||||
|
||||
/**Removes a {@link InputProcessor} from the chain.*/
|
||||
public void removeProcessor(InputProcessor processor){
|
||||
inputMultiplexer.removeProcessor(processor);
|
||||
}
|
||||
|
||||
/** @return the currently set {@link InputProcessor} or null. */
|
||||
public Seq<InputProcessor> getInputProcessors(){
|
||||
return inputMultiplexer.getProcessors();
|
||||
}
|
||||
|
||||
public InputMultiplexer getInputMultiplexer(){
|
||||
return inputMultiplexer;
|
||||
}
|
||||
|
||||
/** Returns the default input device (keyboard). */
|
||||
public KeyboardDevice getKeyboard(){
|
||||
return keyboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries whether a {@link Peripheral} is currently available. In case of Android and the {@link Peripheral#hardwareKeyboard}
|
||||
* this returns the whether the keyboard is currently slid out or not.
|
||||
* @param peripheral the {@link Peripheral}
|
||||
* @return whether the peripheral is available or not.
|
||||
*/
|
||||
public boolean isPeripheralAvailable(Peripheral peripheral){
|
||||
return peripheral == Peripheral.hardwareKeyboard;
|
||||
}
|
||||
|
||||
/** @return the rotation of the device with respect to its native orientation. */
|
||||
public int getRotation(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @return the native orientation of the device. */
|
||||
public Orientation getNativeOrientation(){
|
||||
return Orientation.landscape;
|
||||
}
|
||||
|
||||
/** Internal method for looking up a key's name based on keyboard layout. Internal use only - use {@link KeyCode#getName()} instead. */
|
||||
public String getKeyName(KeyCode code){
|
||||
return code.value;
|
||||
}
|
||||
|
||||
public enum Orientation{
|
||||
landscape, portrait
|
||||
}
|
||||
|
||||
/** Enumeration of potentially available peripherals. Use with {@link Input#isPeripheralAvailable(Peripheral)}. */
|
||||
public enum Peripheral{
|
||||
hardwareKeyboard, onscreenKeyboard, multitouchScreen, accelerometer, compass, vibrator, gyroscope, rotationVector, pressure
|
||||
}
|
||||
|
||||
/** Parameters for text input. */
|
||||
public static class TextInput{
|
||||
public boolean multiline = false;
|
||||
public boolean allowEmpty = true;
|
||||
public String title = "";
|
||||
public String text = "";
|
||||
public String message = "";
|
||||
public boolean numeric;
|
||||
public Cons<String> accepted = s -> { };
|
||||
public Runnable canceled = () -> { };
|
||||
public int maxLength = -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,463 @@
|
||||
package arc;
|
||||
|
||||
import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.struct.*;
|
||||
import arc.struct.ObjectMap.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import arc.util.serialization.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
public class Settings{
|
||||
protected final static byte typeBool = 0, typeInt = 1, typeLong = 2, typeFloat = 3, typeString = 4, typeBinary = 5;
|
||||
protected final static int maxBackups = 10, minBackupIntervalMs = 1000 * 60 * 2;
|
||||
|
||||
//general state data
|
||||
protected Fi dataDirectory;
|
||||
protected String appName = "app";
|
||||
protected ObjectMap<String, Object> defaults = new ObjectMap<>();
|
||||
protected HashMap<String, Object> values = new HashMap<>();
|
||||
protected boolean modified;
|
||||
protected Cons<Throwable> errorHandler;
|
||||
protected boolean hasErrored;
|
||||
protected boolean shouldAutosave = true;
|
||||
protected boolean loaded = false;
|
||||
protected boolean writeCompressed = false;
|
||||
private long lastBackupTime;
|
||||
protected ExecutorService executor = Threads.executor("Settings Backup", 1);
|
||||
|
||||
//IO utility objects
|
||||
protected ByteArrayOutputStream byteStream = new ByteArrayOutputStream(32);
|
||||
protected ReusableByteInStream byteInputStream = new ReusableByteInStream();
|
||||
protected Json json = new Json();
|
||||
|
||||
public void setJson(Json json){
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
public void setCompressed(boolean compressed){
|
||||
this.writeCompressed = compressed;
|
||||
}
|
||||
|
||||
public String getAppName(){
|
||||
return appName;
|
||||
}
|
||||
|
||||
public void setAppName(String name){
|
||||
appName = name;
|
||||
}
|
||||
|
||||
/**Sets the error handler function.
|
||||
* This function gets called when {@link #forceSave} or {@link #load} fails. This can occur most often on browsers,
|
||||
* where extensions can block writing to local storage.*/
|
||||
public void setErrorHandler(Cons<Throwable> handler){
|
||||
errorHandler = handler;
|
||||
}
|
||||
|
||||
/** Set whether the data should autosave immediately upon changing a value.
|
||||
* Default value: true. */
|
||||
public void setAutosave(boolean autosave){
|
||||
this.shouldAutosave = autosave;
|
||||
}
|
||||
|
||||
public boolean modified(){
|
||||
return modified;
|
||||
}
|
||||
|
||||
/** Loads all values and keybinds. */
|
||||
public synchronized void load(){
|
||||
try{
|
||||
loadValues();
|
||||
}catch(Throwable error){
|
||||
Log.err("Error loading settings", error);
|
||||
if(errorHandler != null){
|
||||
if(!hasErrored) errorHandler.get(error);
|
||||
}else{
|
||||
throw error;
|
||||
}
|
||||
hasErrored = true;
|
||||
}
|
||||
//if loading failed, it still counts
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
/** Saves all values and keybinds. */
|
||||
public synchronized void forceSave(){
|
||||
//never loaded, nothing to save
|
||||
if(!loaded) return;
|
||||
try{
|
||||
saveValues();
|
||||
}catch(Throwable error){
|
||||
Log.err("Error writing settings", error);
|
||||
if(errorHandler != null){
|
||||
if(!hasErrored) errorHandler.get(error);
|
||||
}else{
|
||||
throw error;
|
||||
}
|
||||
hasErrored = true;
|
||||
}
|
||||
modified = false;
|
||||
}
|
||||
|
||||
/** Manually save, if the settings have been loaded at some point. */
|
||||
public synchronized void manualSave(){
|
||||
if(loaded){
|
||||
forceSave();
|
||||
}
|
||||
}
|
||||
|
||||
/** Saves if any modifications were done. */
|
||||
public synchronized void autosave(){
|
||||
if(modified && shouldAutosave){
|
||||
forceSave();
|
||||
modified = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Loads a settings file into {@link #values} using the specified appName. */
|
||||
public synchronized void loadValues(){
|
||||
//don't load settings files if neither of them exist
|
||||
if(!getSettingsFile().exists() && !getBackupSettingsFile().exists()){
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
loadValues(getSettingsFile());
|
||||
|
||||
//back up the save file, as the values have now been loaded successfully
|
||||
getSettingsFile().copyTo(getBackupSettingsFile());
|
||||
}catch(Throwable e){
|
||||
Log.err("Failed to load base settings file, attempting to load backup.", e);
|
||||
|
||||
Seq<Fi> attempts = getBackupFolder().seq().add(getBackupSettingsFile());
|
||||
//sort with latest modified file first
|
||||
attempts.sort(Structs.comparingLong(f -> -f.lastModified()));
|
||||
|
||||
for(Fi attempt : attempts){
|
||||
try{
|
||||
|
||||
loadValues(attempt);
|
||||
attempt.copyTo(getSettingsFile());
|
||||
|
||||
Log.info("Loaded backup settings file successfully!");
|
||||
|
||||
//break out of loop, we're done here
|
||||
return;
|
||||
}catch(Throwable e3){
|
||||
Log.err("Failed to load backup settings file.", e3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void loadValues(Fi file) throws IOException{
|
||||
//read the first few bytes to check if it is compressed.
|
||||
byte[] header = new byte[2];
|
||||
file.readBytes(header, 0, 2);
|
||||
boolean compressed = header[0] == (byte)0x78 && (header[1] == (byte)0x01 || header[1] == (byte)0x5E || header[1] == (byte)0x9c || header[1] == (byte)0xda);
|
||||
|
||||
try(DataInputStream stream = new DataInputStream(compressed ? new InflaterInputStream(file.read(8192)) : file.read(8192))){
|
||||
int amount = stream.readInt();
|
||||
//current theory: when corruptions happen, the only things written to the stream are a bunch of zeroes
|
||||
//try to anticipate this case and throw an exception when 0 values are written
|
||||
if(amount <= 0) throw new IOException("0 values are not allowed.");
|
||||
for(int i = 0; i < amount; i++){
|
||||
String key = stream.readUTF();
|
||||
|
||||
byte type = stream.readByte();
|
||||
|
||||
switch(type){
|
||||
case typeBool:
|
||||
values.put(key, stream.readBoolean());
|
||||
break;
|
||||
case typeInt:
|
||||
values.put(key, stream.readInt());
|
||||
break;
|
||||
case typeLong:
|
||||
values.put(key, stream.readLong());
|
||||
break;
|
||||
case typeFloat:
|
||||
values.put(key, stream.readFloat());
|
||||
break;
|
||||
case typeString:
|
||||
values.put(key, stream.readUTF());
|
||||
break;
|
||||
case typeBinary:
|
||||
int length = stream.readInt();
|
||||
byte[] bytes = new byte[length];
|
||||
stream.readFully(bytes);
|
||||
values.put(key, bytes);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Unknown key type: " + type);
|
||||
}
|
||||
}
|
||||
//make sure all data was read - this helps with potential corruption
|
||||
int end = stream.read();
|
||||
if(end != -1){
|
||||
throw new IOException("Trailing settings data; expected EOF, but got: " + end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Saves all entries from {@link #values} into the correct location. */
|
||||
public synchronized void saveValues(){
|
||||
Fi file = getSettingsFile();
|
||||
|
||||
try(DataOutputStream stream = new DataOutputStream(writeCompressed ? new FastDeflaterOutputStream(file.write(false, 8192)) : file.write(false, 8192))){
|
||||
stream.writeInt(values.size());
|
||||
|
||||
for(Map.Entry<String, Object> entry : values.entrySet()){
|
||||
stream.writeUTF(entry.getKey());
|
||||
|
||||
Object value = entry.getValue();
|
||||
|
||||
if(value instanceof Boolean){
|
||||
stream.writeByte(typeBool);
|
||||
stream.writeBoolean((Boolean)value);
|
||||
}else if(value instanceof Integer){
|
||||
stream.writeByte(typeInt);
|
||||
stream.writeInt((Integer)value);
|
||||
}else if(value instanceof Long){
|
||||
stream.writeByte(typeLong);
|
||||
stream.writeLong((Long)value);
|
||||
}else if(value instanceof Float){
|
||||
stream.writeByte(typeFloat);
|
||||
stream.writeFloat((Float)value);
|
||||
}else if(value instanceof String){
|
||||
stream.writeByte(typeString);
|
||||
stream.writeUTF((String)value);
|
||||
}else if(value instanceof byte[]){
|
||||
stream.writeByte(typeBinary);
|
||||
stream.writeInt(((byte[])value).length);
|
||||
stream.write((byte[])value);
|
||||
}
|
||||
}
|
||||
|
||||
}catch(Throwable e){
|
||||
//file is now corrupt, delete it
|
||||
file.delete();
|
||||
throw new RuntimeException("Error writing preferences: " + file, e);
|
||||
}
|
||||
|
||||
if(Time.timeSinceMillis(lastBackupTime) > minBackupIntervalMs){
|
||||
lastBackupTime = Time.millis();
|
||||
|
||||
executor.submit(() -> {
|
||||
//make sure two backups can't happen at once.
|
||||
synchronized(this){
|
||||
Fi backupFolder = getBackupFolder();
|
||||
|
||||
Seq<Fi> previous = backupFolder.seq();
|
||||
//make sure first file is most recent, last is oldest
|
||||
previous.sort(Structs.comparingLong(f -> -f.lastModified()));
|
||||
|
||||
//create new entry in the backup folder
|
||||
file.copyTo(backupFolder.child(System.currentTimeMillis() + ".bin"));
|
||||
|
||||
//delete older backups if they exceed the max backup count
|
||||
while(previous.size >= maxBackups){
|
||||
previous.pop().delete();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the file used for writing settings to. Not available on all platforms! */
|
||||
public Fi getSettingsFile(){
|
||||
return getDataDirectory().child("settings.bin");
|
||||
}
|
||||
|
||||
public Fi getBackupFolder(){
|
||||
return getDataDirectory().child("settings_backups");
|
||||
}
|
||||
|
||||
public Fi getBackupSettingsFile(){
|
||||
return getDataDirectory().child("settings_backup.bin");
|
||||
}
|
||||
|
||||
/** Returns the directory where all settings and data is placed. */
|
||||
public Fi getDataDirectory(){
|
||||
return dataDirectory == null ? Core.files.absolute(OS.getAppDataDirectoryString(appName)) : dataDirectory;
|
||||
}
|
||||
|
||||
/** Sets the settings file where everything is written to. */
|
||||
public void setDataDirectory(Fi file){
|
||||
this.dataDirectory = file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a list of defaults values.
|
||||
* Format: name1, default1, name2, default2, etc
|
||||
*/
|
||||
public synchronized void defaults(Object... objects){
|
||||
for(int i = 0; i < objects.length; i += 2){
|
||||
defaults.put((String)objects[i], objects[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Clears all preference values. */
|
||||
public synchronized void clear(){
|
||||
values.clear();
|
||||
}
|
||||
|
||||
public synchronized Object getDefault(String name){
|
||||
return defaults.get(name);
|
||||
}
|
||||
|
||||
public synchronized boolean has(String name){
|
||||
return values.containsKey(name);
|
||||
}
|
||||
|
||||
public synchronized Object get(String name, Object def){
|
||||
return values.containsKey(name) ? values.get(name) : def;
|
||||
}
|
||||
|
||||
public boolean isModified(){
|
||||
return modified;
|
||||
}
|
||||
|
||||
public synchronized void putJson(String name, Object value){
|
||||
putJson(name, null, value);
|
||||
}
|
||||
|
||||
public synchronized void putJson(String name, Class<?> elementType, Object value){
|
||||
byteStream.reset();
|
||||
|
||||
json.toUBJson(value, value == null ? null : value.getClass(), elementType, byteStream);
|
||||
|
||||
put(name, byteStream.toByteArray());
|
||||
|
||||
modified = true;
|
||||
}
|
||||
|
||||
public synchronized <T> T getJson(String name, Class<T> type, Class elementType, Prov<T> def){
|
||||
try{
|
||||
if(!has(name)) return def.get();
|
||||
byteInputStream.setBytes(getBytes(name));
|
||||
return json.readValue(type, elementType, UBJson.read(byteInputStream));
|
||||
}catch(Throwable e){
|
||||
Log.err("Error reading JSON with key '" + name + "'", e);
|
||||
return def.get();
|
||||
}
|
||||
}
|
||||
|
||||
public <T> T getJson(String name, Class<T> type, Prov<T> def){
|
||||
return getJson(name, type, null, def);
|
||||
}
|
||||
|
||||
public float getFloat(String name, float def){
|
||||
return (float)get(name, def);
|
||||
}
|
||||
|
||||
public long getLong(String name, long def){
|
||||
return (long)get(name, def);
|
||||
}
|
||||
|
||||
public Long getLong(String name){
|
||||
return getLong(name, 0);
|
||||
}
|
||||
|
||||
public int getInt(String name, int def){
|
||||
return (int)get(name, def);
|
||||
}
|
||||
|
||||
public boolean getBool(String name, boolean def){
|
||||
Object val = get(name, def);
|
||||
return val instanceof Boolean ? (Boolean)val : def;
|
||||
}
|
||||
|
||||
public byte[] getBytes(String name, byte[] def){
|
||||
return (byte[])get(name, def);
|
||||
}
|
||||
|
||||
public String getString(String name, String def){
|
||||
return (String)get(name, def);
|
||||
}
|
||||
|
||||
public float getFloat(String name){
|
||||
return getFloat(name, (float)defaults.get(name, 0f));
|
||||
}
|
||||
|
||||
public int getInt(String name){
|
||||
return getInt(name, (int)defaults.get(name, 0));
|
||||
}
|
||||
|
||||
public boolean getBool(String name){
|
||||
return getBool(name, (boolean)defaults.get(name, false));
|
||||
}
|
||||
|
||||
/** Runs the specified code once, and never again. */
|
||||
public void getBoolOnce(String name, Runnable run){
|
||||
if(!getBool(name, false)){
|
||||
run.run();
|
||||
put(name, true);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true once, and never again. */
|
||||
public boolean getBoolOnce(String name){
|
||||
boolean val = getBool(name, false);
|
||||
put(name, true);
|
||||
return !val;
|
||||
}
|
||||
|
||||
public byte[] getBytes(String name){
|
||||
return getBytes(name, (byte[])defaults.get(name));
|
||||
}
|
||||
|
||||
public String getString(String name){
|
||||
return getString(name, (String)defaults.get(name));
|
||||
}
|
||||
|
||||
public void putAll(ObjectMap<String, Object> map){
|
||||
for(Entry<String, Object> entry : map.entries()){
|
||||
put(entry.key, entry.value);
|
||||
}
|
||||
}
|
||||
|
||||
/** Toggles a boolean value. */
|
||||
public void toggle(String name){
|
||||
put(name, !getBool(name));
|
||||
}
|
||||
|
||||
/** JS compatibility method. */
|
||||
public void putInt(String name, int value){
|
||||
put(name, value);
|
||||
}
|
||||
|
||||
/** JS compatibility method. */
|
||||
public void putFloat(String name, float value){
|
||||
put(name, value);
|
||||
}
|
||||
|
||||
/** Stores an object in the preference map. */
|
||||
public synchronized void put(String name, Object object){
|
||||
if(object instanceof Float || object instanceof Integer || object instanceof Boolean || object instanceof Long
|
||||
|| object instanceof String || object instanceof byte[]){
|
||||
values.put(name, object);
|
||||
modified = true;
|
||||
}else{
|
||||
throw new IllegalArgumentException("Invalid object stored: " + (object == null ? null : object.getClass()) + ".");
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void remove(String name){
|
||||
values.remove(name);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
public synchronized Iterable<String> keys(){
|
||||
return values.keySet();
|
||||
}
|
||||
|
||||
public synchronized int keySize(){
|
||||
return values.size();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package arc.assets;
|
||||
|
||||
import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.util.*;
|
||||
|
||||
/**
|
||||
* Describes an asset to be loaded by its filename, type and {@link AssetLoaderParameters}. Instances of this are used in
|
||||
* {@link AssetLoadingTask} to load the actual asset.
|
||||
* @author mzechner
|
||||
*/
|
||||
public class AssetDescriptor<T>{
|
||||
public final String fileName;
|
||||
public final Class<T> type;
|
||||
public final AssetLoaderParameters params;
|
||||
/** The resolved file. May be null if the fileName has not been resolved yet. */
|
||||
public Fi file;
|
||||
/** Callback for when this asset is loaded.*/
|
||||
public Cons<T> loaded = t -> {};
|
||||
/** Callback for when this asset has an error.*/
|
||||
public @Nullable Cons<Throwable> errored = null;
|
||||
|
||||
public AssetDescriptor(Class<T> assetType){
|
||||
this(assetType.getSimpleName(), assetType, null);
|
||||
}
|
||||
|
||||
public AssetDescriptor(String fileName, Class<T> assetType){
|
||||
this(fileName, assetType, null);
|
||||
}
|
||||
|
||||
/** Creates an AssetDescriptor with an already resolved name. */
|
||||
public AssetDescriptor(Fi file, Class<T> assetType){
|
||||
this(file, assetType, null);
|
||||
}
|
||||
|
||||
public AssetDescriptor(String fileName, Class<T> assetType, AssetLoaderParameters<T> params){
|
||||
this.fileName = fileName.replaceAll("\\\\", "/");
|
||||
this.type = assetType;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
/** Creates an AssetDescriptor with an already resolved name. */
|
||||
public AssetDescriptor(Fi file, Class<T> assetType, AssetLoaderParameters<T> params){
|
||||
this.fileName = file.path().replaceAll("\\\\", "/");
|
||||
this.file = file;
|
||||
this.type = assetType;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return fileName +
|
||||
", " +
|
||||
type.getName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package arc.assets;
|
||||
|
||||
public interface AssetErrorListener{
|
||||
void error(AssetDescriptor asset, Throwable throwable);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package arc.assets;
|
||||
|
||||
public class AssetLoaderParameters<T>{
|
||||
public LoadedCallback loadedCallback;
|
||||
|
||||
public AssetLoaderParameters(){
|
||||
|
||||
}
|
||||
|
||||
public AssetLoaderParameters(LoadedCallback loadedCallback){
|
||||
this.loadedCallback = loadedCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback interface that will be invoked when the {@link AssetManager} loaded an asset.
|
||||
* @author mzechner
|
||||
*/
|
||||
public interface LoadedCallback{
|
||||
void finishedLoading(AssetManager assetManager, String fileName, Class type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package arc.assets;
|
||||
|
||||
import arc.assets.loaders.AssetLoader;
|
||||
import arc.assets.loaders.AsynchronousAssetLoader;
|
||||
import arc.assets.loaders.SynchronousAssetLoader;
|
||||
import arc.struct.Seq;
|
||||
import arc.files.Fi;
|
||||
import arc.util.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* Responsible for loading an asset through an {@link AssetLoader} based on an {@link AssetDescriptor}.
|
||||
* @author mzechner
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
class AssetLoadingTask implements Callable<Void>{
|
||||
final AssetDescriptor assetDesc;
|
||||
final AssetLoader loader;
|
||||
final ExecutorService executor;
|
||||
final long startTime;
|
||||
AssetManager manager;
|
||||
volatile boolean asyncDone = false;
|
||||
volatile boolean dependenciesLoaded = false;
|
||||
volatile Seq<AssetDescriptor> dependencies;
|
||||
volatile Future<Void> depsFuture = null;
|
||||
volatile Future<Void> loadFuture = null;
|
||||
volatile Object asset = null;
|
||||
|
||||
int ticks = 0;
|
||||
volatile boolean cancel = false;
|
||||
|
||||
public AssetLoadingTask(AssetManager manager, AssetDescriptor assetDesc, AssetLoader loader, ExecutorService threadPool){
|
||||
this.manager = manager;
|
||||
this.assetDesc = assetDesc;
|
||||
this.loader = loader;
|
||||
this.executor = threadPool;
|
||||
startTime = Time.nanos();
|
||||
}
|
||||
|
||||
/** Loads parts of the asset asynchronously if the loader is an {@link AsynchronousAssetLoader}. */
|
||||
@Override
|
||||
public Void call(){
|
||||
AsynchronousAssetLoader asyncLoader = (AsynchronousAssetLoader)loader;
|
||||
if(!dependenciesLoaded){
|
||||
dependencies = asyncLoader.getDependencies(assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
if(dependencies != null){
|
||||
removeDuplicates(dependencies);
|
||||
manager.injectDependencies(assetDesc.fileName, dependencies);
|
||||
}else{
|
||||
// if we have no dependencies, we load the async part of the task immediately.
|
||||
asyncLoader.loadAsync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
asyncDone = true;
|
||||
}
|
||||
}else{
|
||||
asyncLoader.loadAsync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the loading of the asset. In case the asset is loaded with an {@link AsynchronousAssetLoader}, the loaders
|
||||
* {@link AsynchronousAssetLoader#loadAsync(AssetManager, String, Fi, AssetLoaderParameters)} method is first called on
|
||||
* a worker thread. Once this method returns, the rest of the asset is loaded on the rendering thread via
|
||||
* {@link AsynchronousAssetLoader#loadSync(AssetManager, String, Fi, AssetLoaderParameters)}.
|
||||
* @return true in case the asset was fully loaded, false otherwise
|
||||
*/
|
||||
public boolean update(){
|
||||
ticks++;
|
||||
if(loader instanceof SynchronousAssetLoader){
|
||||
handleSyncLoader();
|
||||
}else{
|
||||
handleAsyncLoader();
|
||||
}
|
||||
return asset != null;
|
||||
}
|
||||
|
||||
private void handleSyncLoader(){
|
||||
SynchronousAssetLoader syncLoader = (SynchronousAssetLoader)loader;
|
||||
if(!dependenciesLoaded){
|
||||
dependenciesLoaded = true;
|
||||
dependencies = syncLoader.getDependencies(assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
if(dependencies == null){
|
||||
asset = syncLoader.load(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
return;
|
||||
}
|
||||
removeDuplicates(dependencies);
|
||||
manager.injectDependencies(assetDesc.fileName, dependencies);
|
||||
}else{
|
||||
asset = syncLoader.load(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAsyncLoader(){
|
||||
AsynchronousAssetLoader asyncLoader = (AsynchronousAssetLoader)loader;
|
||||
if(!dependenciesLoaded){
|
||||
if(depsFuture == null){
|
||||
depsFuture = executor.submit(this);
|
||||
}else{
|
||||
if(depsFuture.isDone()){
|
||||
try{
|
||||
depsFuture.get();
|
||||
}catch(Exception e){
|
||||
throw new ArcRuntimeException("Couldn't load dependencies of asset: " + assetDesc.fileName, e);
|
||||
}
|
||||
dependenciesLoaded = true;
|
||||
if(asyncDone){
|
||||
asset = asyncLoader.loadSync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(loadFuture == null && !asyncDone){
|
||||
loadFuture = executor.submit(this);
|
||||
}else{
|
||||
if(asyncDone){
|
||||
asset = asyncLoader.loadSync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
}else if(loadFuture.isDone()){
|
||||
try{
|
||||
loadFuture.get();
|
||||
}catch(Exception e){
|
||||
throw new ArcRuntimeException("Couldn't load asset: " + assetDesc.fileName, e);
|
||||
}
|
||||
asset = asyncLoader.loadSync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Fi resolve(AssetLoader loader, AssetDescriptor assetDesc){
|
||||
if(assetDesc.file == null) assetDesc.file = loader.resolve(assetDesc.fileName);
|
||||
return assetDesc.file;
|
||||
}
|
||||
|
||||
public Object getAsset(){
|
||||
return asset;
|
||||
}
|
||||
|
||||
private void removeDuplicates(Seq<AssetDescriptor> array){
|
||||
boolean ordered = array.ordered;
|
||||
array.ordered = true;
|
||||
for(int i = 0; i < array.size; ++i){
|
||||
final String fn = array.get(i).fileName;
|
||||
final Class type = array.get(i).type;
|
||||
for(int j = array.size - 1; j > i; --j){
|
||||
if(type == array.get(j).type && fn.equals(array.get(j).fileName))
|
||||
array.remove(j);
|
||||
}
|
||||
}
|
||||
array.ordered = ordered;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,877 @@
|
||||
package arc.assets;
|
||||
|
||||
import arc.*;
|
||||
import arc.assets.loaders.*;
|
||||
import arc.audio.*;
|
||||
import arc.files.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.font.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.struct.*;
|
||||
import arc.struct.ObjectMap.*;
|
||||
import arc.util.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* Loads and stores assets like textures, bitmapfonts, tile maps, sounds, music and so on.
|
||||
* @author mzechner
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class AssetManager implements Disposable{
|
||||
final ObjectMap<Class, ObjectMap<String, RefCountedContainer>> assets = new ObjectMap<>();
|
||||
final ObjectMap<String, Class> assetTypes = new ObjectMap<>();
|
||||
final ObjectMap<String, Seq<String>> assetDependencies = new ObjectMap<>();
|
||||
final ObjectSet<String> injected = new ObjectSet<>();
|
||||
|
||||
final ObjectMap<Class, ObjectMap<String, AssetLoader>> loaders = new ObjectMap<>();
|
||||
final Seq<AssetDescriptor> loadQueue = new Seq<>();
|
||||
final ExecutorService executor;
|
||||
|
||||
final Seq<AssetLoadingTask> tasks = new Seq<>();
|
||||
final FileHandleResolver resolver;
|
||||
AssetErrorListener listener = null;
|
||||
int loaded = 0;
|
||||
int toLoad = 0;
|
||||
int peakTasks = 0;
|
||||
|
||||
/** Creates a new AssetManager with all default loaders. */
|
||||
public AssetManager(){
|
||||
this(Core.files::internal);
|
||||
}
|
||||
|
||||
/** Creates a new AssetManager with all default loaders. */
|
||||
public AssetManager(FileHandleResolver resolver){
|
||||
this(resolver, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new AssetManager with optionally all default loaders. If you don't add the default loaders then you do have to
|
||||
* manually add the loaders you need, including any loaders they might depend on.
|
||||
* @param defaultLoaders whether to add the default loaders
|
||||
*/
|
||||
public AssetManager(FileHandleResolver resolver, boolean defaultLoaders){
|
||||
this.resolver = resolver;
|
||||
if(defaultLoaders){
|
||||
setLoader(Font.class, new FontLoader(resolver));
|
||||
setLoader(Music.class, new MusicLoader(resolver));
|
||||
setLoader(Pixmap.class, new PixmapLoader(resolver));
|
||||
setLoader(Sound.class, new SoundLoader(resolver));
|
||||
setLoader(TextureAtlas.class, new TextureAtlasLoader(resolver));
|
||||
setLoader(Texture.class, new TextureLoader(resolver));
|
||||
setLoader(I18NBundle.class, new I18NBundleLoader(resolver));
|
||||
setLoader(Shader.class, new ShaderProgramLoader(resolver));
|
||||
setLoader(Cubemap.class, new CubemapLoader(resolver));
|
||||
}
|
||||
executor = Threads.executor("Assets", 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link FileHandleResolver} for which this AssetManager was loaded with.
|
||||
* @return the file handle resolver which this AssetManager uses
|
||||
*/
|
||||
public FileHandleResolver getFileHandleResolver(){
|
||||
return resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName the asset file name
|
||||
* @return the asset
|
||||
*/
|
||||
public synchronized <T> T get(String fileName){
|
||||
Class<T> type = assetTypes.get(fileName);
|
||||
if(type == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
ObjectMap<String, RefCountedContainer> assetsByType = assets.get(type);
|
||||
if(assetsByType == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
RefCountedContainer assetContainer = assetsByType.get(fileName);
|
||||
if(assetContainer == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
T asset = (T)assetContainer.object;
|
||||
if(asset == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
return asset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName the asset file name
|
||||
* @param type the asset type
|
||||
* @return the asset
|
||||
*/
|
||||
public synchronized <T> T get(String fileName, Class<T> type){
|
||||
ObjectMap<String, RefCountedContainer> assetsByType = assets.get(type);
|
||||
if(assetsByType == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
RefCountedContainer assetContainer = assetsByType.get(fileName);
|
||||
if(assetContainer == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
T asset = (T)assetContainer.object;
|
||||
if(asset == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
return asset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName the asset file name
|
||||
* @param type the asset type
|
||||
* @return the asset, or null if not found
|
||||
*/
|
||||
public synchronized <T> T getOrNull(String fileName, Class<T> type){
|
||||
ObjectMap<String, RefCountedContainer> assetsByType = assets.get(type);
|
||||
if(assetsByType == null) return null;
|
||||
RefCountedContainer assetContainer = assetsByType.get(fileName);
|
||||
if(assetContainer == null) return null;
|
||||
return (T)assetContainer.object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type the asset type
|
||||
* @return all the assets matching the specified type
|
||||
*/
|
||||
public synchronized <T> Seq<T> getAll(Class<T> type, Seq<T> out){
|
||||
ObjectMap<String, RefCountedContainer> assetsByType = assets.get(type);
|
||||
if(assetsByType != null){
|
||||
for(ObjectMap.Entry<String, RefCountedContainer> asset : assetsByType.entries()){
|
||||
out.add((T)asset.value.object);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type the asset type
|
||||
* @return all the assets matching the specified type as entries by file name
|
||||
*/
|
||||
public synchronized <T> Seq<Entry<String, T>> getAllEntries(Class<T> type, Seq<Entry<String, T>> out){
|
||||
ObjectMap<String, RefCountedContainer> assetsByType = assets.get(type);
|
||||
if(assetsByType != null){
|
||||
for(ObjectMap.Entry<String, RefCountedContainer> asset : assetsByType.entries()){
|
||||
Entry<String, T> entry = new Entry<>();
|
||||
entry.key = asset.key;
|
||||
entry.value = (T)asset.value.object;
|
||||
out.add(entry);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param assetDescriptor the asset descriptor
|
||||
* @return the asset
|
||||
*/
|
||||
public synchronized <T> T get(AssetDescriptor<T> assetDescriptor){
|
||||
return get(assetDescriptor.fileName, assetDescriptor.type);
|
||||
}
|
||||
|
||||
/** Returns true if an asset with the specified name is loading, queued to be loaded, or has been loaded. */
|
||||
public synchronized boolean contains(String fileName){
|
||||
if(tasks.size > 0 && tasks.first().assetDesc.fileName.equals(fileName)) return true;
|
||||
|
||||
for(int i = 0; i < loadQueue.size; i++)
|
||||
if(loadQueue.get(i).fileName.equals(fileName)) return true;
|
||||
|
||||
return isLoaded(fileName);
|
||||
}
|
||||
|
||||
/** Returns true if an asset with the specified name and type is loading, queued to be loaded, or has been loaded. */
|
||||
public synchronized boolean contains(String fileName, Class type){
|
||||
if(tasks.size > 0){
|
||||
AssetDescriptor assetDesc = tasks.first().assetDesc;
|
||||
if(assetDesc.type == type && assetDesc.fileName.equals(fileName)) return true;
|
||||
}
|
||||
|
||||
for(int i = 0; i < loadQueue.size; i++){
|
||||
AssetDescriptor assetDesc = loadQueue.get(i);
|
||||
if(assetDesc.type == type && assetDesc.fileName.equals(fileName)) return true;
|
||||
}
|
||||
|
||||
return isLoaded(fileName, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the asset and all its dependencies, if they are not used by other assets.
|
||||
* @param fileName the file name
|
||||
*/
|
||||
public synchronized void unload(String fileName){
|
||||
// check if it's currently processed (and the first element in the stack, thus not a dependency) and cancel if necessary
|
||||
if(tasks.size > 0){
|
||||
AssetLoadingTask currAsset = tasks.first();
|
||||
if(currAsset.assetDesc.fileName.equals(fileName)){
|
||||
currAsset.cancel = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// check if it's in the queue
|
||||
int foundIndex = -1;
|
||||
for(int i = 0; i < loadQueue.size; i++){
|
||||
if(loadQueue.get(i).fileName.equals(fileName)){
|
||||
foundIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(foundIndex != -1){
|
||||
toLoad--;
|
||||
loadQueue.remove(foundIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// get the asset and its type
|
||||
Class type = assetTypes.get(fileName);
|
||||
if(type == null) return;
|
||||
|
||||
RefCountedContainer assetRef = assets.get(type).get(fileName);
|
||||
|
||||
if(assetRef == null) return;
|
||||
|
||||
// if it is reference counted, decrement ref count and check if we can really get rid of it.
|
||||
assetRef.count--;
|
||||
if(assetRef.count <= 0){
|
||||
|
||||
// if it is disposable dispose it
|
||||
if(assetRef.object instanceof Disposable)
|
||||
((Disposable)assetRef.object).dispose();
|
||||
|
||||
// remove the asset from the manager.
|
||||
assetTypes.remove(fileName);
|
||||
assets.get(type).remove(fileName);
|
||||
}
|
||||
|
||||
// remove any dependencies (or just decrement their ref count).
|
||||
Seq<String> dependencies = assetDependencies.get(fileName);
|
||||
if(dependencies != null){
|
||||
for(String dependency : dependencies){
|
||||
if(isLoaded(dependency)) unload(dependency);
|
||||
}
|
||||
}
|
||||
// remove dependencies if ref count < 0
|
||||
if(assetRef.count <= 0){
|
||||
assetDependencies.remove(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param asset the asset
|
||||
* @return whether the asset is contained in this manager
|
||||
*/
|
||||
public synchronized <T> boolean containsAsset(T asset){
|
||||
ObjectMap<String, RefCountedContainer> assetsByType = assets.get(asset.getClass());
|
||||
if(assetsByType == null) return false;
|
||||
for(String fileName : assetsByType.keys()){
|
||||
T otherAsset = (T)assetsByType.get(fileName).object;
|
||||
if(otherAsset == asset || asset.equals(otherAsset)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param asset the asset
|
||||
* @return the filename of the asset or null
|
||||
*/
|
||||
public synchronized <T> String getAssetFileName(T asset){
|
||||
for(Class assetType : assets.keys()){
|
||||
ObjectMap<String, RefCountedContainer> assetsByType = assets.get(assetType);
|
||||
for(String fileName : assetsByType.keys()){
|
||||
T otherAsset = (T)assetsByType.get(fileName).object;
|
||||
if(otherAsset == asset || asset.equals(otherAsset)) return fileName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param assetDesc the AssetDescriptor of the asset
|
||||
* @return whether the asset is loaded
|
||||
*/
|
||||
public synchronized boolean isLoaded(AssetDescriptor assetDesc){
|
||||
return isLoaded(assetDesc.fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName the file name of the asset
|
||||
* @return whether the asset is loaded
|
||||
*/
|
||||
public synchronized boolean isLoaded(String fileName){
|
||||
if(fileName == null) return false;
|
||||
return assetTypes.containsKey(fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName the file name of the asset
|
||||
* @return whether the asset is loaded
|
||||
*/
|
||||
public synchronized boolean isLoaded(String fileName, Class type){
|
||||
ObjectMap<String, RefCountedContainer> assetsByType = assets.get(type);
|
||||
if(assetsByType == null) return false;
|
||||
RefCountedContainer assetContainer = assetsByType.get(fileName);
|
||||
if(assetContainer == null) return false;
|
||||
return assetContainer.object != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default loader for the given type
|
||||
* @param type The type of the loader to get
|
||||
* @return The loader capable of loading the type, or null if none exists
|
||||
*/
|
||||
public <T> AssetLoader getLoader(final Class<T> type){
|
||||
return getLoader(type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the loader for the given type and the specified filename. If no loader exists for the specific filename, the
|
||||
* default loader for that type is returned.
|
||||
* @param type The type of the loader to get
|
||||
* @param fileName The filename of the asset to get a loader for, or null to get the default loader
|
||||
* @return The loader capable of loading the type and filename, or null if none exists
|
||||
*/
|
||||
public <T> AssetLoader getLoader(final Class<T> type, final String fileName){
|
||||
final ObjectMap<String, AssetLoader> loaders = this.loaders.get(type);
|
||||
if(loaders == null || loaders.size < 1) return null;
|
||||
if(fileName == null) return loaders.get("");
|
||||
AssetLoader result = null;
|
||||
int l = -1;
|
||||
for(ObjectMap.Entry<String, AssetLoader> entry : loaders.entries()){
|
||||
if(entry.key.length() > l && fileName.endsWith(entry.key)){
|
||||
result = entry.value;
|
||||
l = entry.key.length();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given asset to the loading queue of the AssetManager.
|
||||
* @param fileName the file name (interpretation depends on {@link AssetLoader})
|
||||
* @param type the type of the asset.
|
||||
*/
|
||||
public synchronized <T> AssetDescriptor<T> load(String fileName, Class<T> type){
|
||||
return load(fileName, type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a custom one-time 'asset' that knows how to load itself.
|
||||
*/
|
||||
public synchronized AssetDescriptor loadRun(String name, Class<?> type, Runnable loadasync){
|
||||
return loadRun(name, type, loadasync, () -> {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a custom one-time 'asset' that knows how to load itself.
|
||||
*/
|
||||
public synchronized AssetDescriptor loadRun(String name, Class<?> type, Runnable loadasync, Runnable loadsync){
|
||||
if(getLoader(type) == null){
|
||||
setLoader(type, new CustomLoader(){
|
||||
@Override
|
||||
public void loadAsync(AssetManager manager, String fileName, Fi file, AssetLoaderParameters parameter){
|
||||
loadasync.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object loadSync(AssetManager manager, String fileName, Fi file, AssetLoaderParameters parameter){
|
||||
loadsync.run();
|
||||
return super.loadSync(manager, fileName, file, parameter);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
throw new IllegalArgumentException("Class already registered or loaded: " + type);
|
||||
}
|
||||
return load(name, type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a custom one-time 'asset' that knows how to load itself.
|
||||
* @param load the asset
|
||||
*/
|
||||
public synchronized AssetDescriptor load(Loadable load){
|
||||
if(getLoader(load.getClass()) == null){
|
||||
setLoader(load.getClass(), new AsynchronousAssetLoader(Core.files::internal){
|
||||
@Override
|
||||
public void loadAsync(AssetManager manager, String fileName, Fi file, AssetLoaderParameters parameter){
|
||||
load.loadAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object loadSync(AssetManager manager, String fileName, Fi file, AssetLoaderParameters parameter){
|
||||
load.loadSync();
|
||||
return load;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Seq<AssetDescriptor> getDependencies(String fileName, Fi file, AssetLoaderParameters parameter){
|
||||
return load.getDependencies();
|
||||
}
|
||||
});
|
||||
}
|
||||
return load(load.getName(), load.getClass(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given asset to the loading queue of the AssetManager.
|
||||
* @param fileName the file name (interpretation depends on {@link AssetLoader})
|
||||
* @param type the type of the asset.
|
||||
* @param parameter parameters for the AssetLoader.
|
||||
*/
|
||||
public synchronized <T> AssetDescriptor<T> load(String fileName, Class<T> type, AssetLoaderParameters<T> parameter){
|
||||
AssetLoader loader = getLoader(type, fileName);
|
||||
if(loader == null) throw new ArcRuntimeException("No loader for type: " + type.getSimpleName());
|
||||
|
||||
// reset stats
|
||||
if(loadQueue.size == 0){
|
||||
loaded = 0;
|
||||
toLoad = 0;
|
||||
peakTasks = 0;
|
||||
}
|
||||
|
||||
// check if an asset with the same name but a different type has already been added.
|
||||
|
||||
// check preload queue
|
||||
for(int i = 0; i < loadQueue.size; i++){
|
||||
AssetDescriptor desc = loadQueue.get(i);
|
||||
if(desc.fileName.equals(fileName) && !desc.type.equals(type)) throw new ArcRuntimeException(
|
||||
"Asset with name '" + fileName + "' already in preload queue, but has different type (expected: "
|
||||
+ type.getSimpleName() + ", found: " + desc.type.getSimpleName() + ")");
|
||||
}
|
||||
|
||||
// check task list
|
||||
for(int i = 0; i < tasks.size; i++){
|
||||
AssetDescriptor desc = tasks.get(i).assetDesc;
|
||||
if(desc.fileName.equals(fileName) && !desc.type.equals(type)) throw new ArcRuntimeException(
|
||||
"Asset with name '" + fileName + "' already in task list, but has different type (expected: "
|
||||
+ type.getSimpleName() + ", found: " + desc.type.getSimpleName() + ")");
|
||||
}
|
||||
|
||||
// check loaded assets
|
||||
Class otherType = assetTypes.get(fileName);
|
||||
if(otherType != null && !otherType.equals(type))
|
||||
throw new ArcRuntimeException("Asset with name '" + fileName + "' already loaded, but has different type (expected: "
|
||||
+ type.getSimpleName() + ", found: " + otherType.getSimpleName() + ")");
|
||||
|
||||
toLoad++;
|
||||
AssetDescriptor assetDesc = new AssetDescriptor<>(fileName, type, parameter);
|
||||
loadQueue.add(assetDesc);
|
||||
return assetDesc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given asset to the loading queue of the AssetManager.
|
||||
* @param desc the {@link AssetDescriptor}
|
||||
*/
|
||||
public synchronized <T> AssetDescriptor<T> load(AssetDescriptor<T> desc){
|
||||
return load(desc.fileName, desc.type, desc.params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the AssetManager, keeping it loading any assets in the preload queue.
|
||||
* @return true if all loading is finished.
|
||||
*/
|
||||
public synchronized boolean update(){
|
||||
try{
|
||||
if(tasks.size == 0){
|
||||
// loop until we have a new task ready to be processed
|
||||
while(loadQueue.size != 0 && tasks.size == 0){
|
||||
nextTask();
|
||||
}
|
||||
// have we not found a task? We are done!
|
||||
if(tasks.size == 0) return true;
|
||||
}
|
||||
return updateTask() && loadQueue.size == 0 && tasks.size == 0;
|
||||
}catch(Throwable t){
|
||||
handleTaskError(t);
|
||||
return loadQueue.size == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return the asset loading task that is currently being processed.
|
||||
* May return null if nothing is being loaded. */
|
||||
public synchronized AssetDescriptor getCurrentLoading(){
|
||||
if(tasks.size > 0){
|
||||
return tasks.first().assetDesc;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the AssetManager continuously for the specified number of milliseconds, yielding the CPU to the loading thread
|
||||
* between updates. This may block for less time if all loading tasks are complete. This may block for more time if the portion
|
||||
* of a single task that happens in the GL thread takes a long time.
|
||||
* @return true if all loading is finished.
|
||||
*/
|
||||
public boolean update(int millis){
|
||||
long endTime = Time.millis() + millis;
|
||||
while(true){
|
||||
boolean done = update();
|
||||
if(done || Time.millis() > endTime) return done;
|
||||
Thread.yield();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true when all assets are loaded. Can be called from any thread. */
|
||||
public synchronized boolean isFinished(){
|
||||
return loadQueue.size == 0 && tasks.size == 0;
|
||||
}
|
||||
|
||||
/** Blocks until all assets are loaded. */
|
||||
public void finishLoading(){
|
||||
while(!update())
|
||||
Thread.yield();
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks until the specified asset is loaded.
|
||||
* @param assetDesc the AssetDescriptor of the asset
|
||||
*/
|
||||
public void finishLoadingAsset(AssetDescriptor assetDesc){
|
||||
finishLoadingAsset(assetDesc.fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks until the specified asset is loaded.
|
||||
* @param fileName the file name (interpretation depends on {@link AssetLoader})
|
||||
*/
|
||||
public void finishLoadingAsset(String fileName){
|
||||
while(!isLoaded(fileName)){
|
||||
update();
|
||||
Thread.yield();
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void injectDependencies(String parentAssetFilename, Seq<AssetDescriptor> dependendAssetDescs){
|
||||
ObjectSet<String> injected = this.injected;
|
||||
for(AssetDescriptor desc : dependendAssetDescs){
|
||||
if(injected.contains(desc.fileName)) continue; // Ignore subsequent dependencies if there are duplicates.
|
||||
injected.add(desc.fileName);
|
||||
injectDependency(parentAssetFilename, desc);
|
||||
}
|
||||
injected.clear();
|
||||
}
|
||||
|
||||
private synchronized void injectDependency(String parentAssetFilename, AssetDescriptor dependendAssetDesc){
|
||||
// add the asset as a dependency of the parent asset
|
||||
Seq<String> dependencies = assetDependencies.get(parentAssetFilename);
|
||||
if(dependencies == null){
|
||||
dependencies = new Seq();
|
||||
assetDependencies.put(parentAssetFilename, dependencies);
|
||||
}
|
||||
dependencies.add(dependendAssetDesc.fileName);
|
||||
|
||||
// if the asset is already loaded, increase its reference count.
|
||||
if(isLoaded(dependendAssetDesc.fileName)){
|
||||
Class type = assetTypes.get(dependendAssetDesc.fileName);
|
||||
RefCountedContainer assetRef = assets.get(type).get(dependendAssetDesc.fileName);
|
||||
assetRef.count++;
|
||||
incrementRefCountedDependencies(dependendAssetDesc.fileName);
|
||||
}
|
||||
// else add a new task for the asset.
|
||||
else{
|
||||
addTask(dependendAssetDesc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a task from the loadQueue and adds it to the task stack. If the asset is already loaded (which can happen if it was
|
||||
* a dependency of a previously loaded asset) its reference count will be increased.
|
||||
*/
|
||||
private void nextTask(){
|
||||
AssetDescriptor assetDesc = loadQueue.remove(0);
|
||||
//Log.info("Loading asset task: {0}", assetDesc.fileName);
|
||||
|
||||
// if the asset not meant to be reloaded and is already loaded, increase its reference count
|
||||
if(isLoaded(assetDesc.fileName)){
|
||||
Class type = assetTypes.get(assetDesc.fileName);
|
||||
RefCountedContainer assetRef = assets.get(type).get(assetDesc.fileName);
|
||||
assetRef.count++;
|
||||
incrementRefCountedDependencies(assetDesc.fileName);
|
||||
if(assetDesc.params != null && assetDesc.params.loadedCallback != null){
|
||||
assetDesc.params.loadedCallback.finishedLoading(this, assetDesc.fileName, assetDesc.type);
|
||||
}
|
||||
loaded++;
|
||||
}else{
|
||||
// else add a new task for the asset.
|
||||
addTask(assetDesc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link AssetLoadingTask} to the task stack for the given asset.
|
||||
*/
|
||||
private void addTask(AssetDescriptor assetDesc){
|
||||
AssetLoader loader = getLoader(assetDesc.type, assetDesc.fileName);
|
||||
if(loader == null)
|
||||
throw new ArcRuntimeException("No loader for type: " + assetDesc.type.getSimpleName());
|
||||
tasks.add(new AssetLoadingTask(this, assetDesc, loader, executor));
|
||||
peakTasks++;
|
||||
}
|
||||
|
||||
/** Adds an asset to this AssetManager */
|
||||
public <T> void addAsset(final String fileName, Class<T> type, T asset){
|
||||
// add the asset to the filename lookup
|
||||
assetTypes.put(fileName, type);
|
||||
|
||||
// add the asset to the type lookup
|
||||
ObjectMap<String, RefCountedContainer> typeToAssets = assets.get(type);
|
||||
if(typeToAssets == null){
|
||||
typeToAssets = new ObjectMap<>();
|
||||
assets.put(type, typeToAssets);
|
||||
}
|
||||
typeToAssets.put(fileName, new RefCountedContainer(asset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current task on the top of the task stack.
|
||||
* @return true if the asset is loaded or the task was cancelled.
|
||||
*/
|
||||
private boolean updateTask(){
|
||||
AssetLoadingTask task = tasks.peek();
|
||||
|
||||
boolean complete = true;
|
||||
try{
|
||||
complete = task.cancel || task.update();
|
||||
}catch(RuntimeException ex){
|
||||
task.cancel = true;
|
||||
taskFailed(task.assetDesc, ex);
|
||||
}
|
||||
|
||||
// if the task has been cancelled or has finished loading
|
||||
if(complete){
|
||||
// increase the number of loaded assets and pop the task from the stack
|
||||
if(tasks.size == 1){
|
||||
loaded++;
|
||||
peakTasks = 0;
|
||||
}
|
||||
tasks.pop();
|
||||
|
||||
if(task.cancel) return true;
|
||||
|
||||
addAsset(task.assetDesc.fileName, task.assetDesc.type, task.getAsset());
|
||||
|
||||
// otherwise, if a listener was found in the parameter invoke it
|
||||
if(task.assetDesc.params != null && task.assetDesc.params.loadedCallback != null){
|
||||
task.assetDesc.params.loadedCallback.finishedLoading(this, task.assetDesc.fileName, task.assetDesc.type);
|
||||
}
|
||||
|
||||
task.assetDesc.loaded.get(task.getAsset());
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a task throws an exception during loading. The default implementation rethrows the exception. A subclass may
|
||||
* supress the default implementation when loading assets where loading failure is recoverable.
|
||||
*/
|
||||
protected void taskFailed(AssetDescriptor assetDesc, RuntimeException ex){
|
||||
throw ex;
|
||||
}
|
||||
|
||||
private void incrementRefCountedDependencies(String parent){
|
||||
Seq<String> dependencies = assetDependencies.get(parent);
|
||||
if(dependencies == null) return;
|
||||
|
||||
for(String dependency : dependencies){
|
||||
Class type = assetTypes.get(dependency);
|
||||
RefCountedContainer assetRef = assets.get(type).get(dependency);
|
||||
assetRef.count++;
|
||||
incrementRefCountedDependencies(dependency);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a runtime/loading error in {@link #update()} by optionally invoking the {@link AssetErrorListener}.
|
||||
*/
|
||||
private void handleTaskError(Throwable t){
|
||||
if(tasks.isEmpty()) throw new ArcRuntimeException(t);
|
||||
|
||||
// pop the faulty task from the stack
|
||||
AssetLoadingTask task = tasks.pop();
|
||||
AssetDescriptor assetDesc = task.assetDesc;
|
||||
|
||||
// remove all dependencies
|
||||
if(task.dependenciesLoaded && task.dependencies != null){
|
||||
for(AssetDescriptor desc : task.dependencies){
|
||||
unload(desc.fileName);
|
||||
}
|
||||
}
|
||||
|
||||
// clear the rest of the stack
|
||||
tasks.clear();
|
||||
|
||||
// inform the listener that something bad happened
|
||||
if(listener != null){
|
||||
listener.error(assetDesc, t);
|
||||
}
|
||||
|
||||
if(assetDesc.errored != null){
|
||||
assetDesc.errored.get(t);
|
||||
}else{
|
||||
throw new ArcRuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new {@link AssetLoader} for the given type.
|
||||
* @param type the type of the asset
|
||||
* @param loader the loader
|
||||
*/
|
||||
public synchronized <T, P extends AssetLoaderParameters<T>> void setLoader(Class<T> type, AssetLoader<T, P> loader){
|
||||
setLoader(type, null, loader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new {@link AssetLoader} for the given type.
|
||||
* @param type the type of the asset
|
||||
* @param suffix the suffix the filename must have for this loader to be used or null to specify the default loader.
|
||||
* @param loader the loader
|
||||
*/
|
||||
public synchronized <T, P extends AssetLoaderParameters<T>> void setLoader(Class<T> type, String suffix,
|
||||
AssetLoader<T, P> loader){
|
||||
if(type == null) throw new IllegalArgumentException("type cannot be null.");
|
||||
if(loader == null) throw new IllegalArgumentException("loader cannot be null.");
|
||||
ObjectMap<String, AssetLoader> loaders = this.loaders.get(type);
|
||||
if(loaders == null) this.loaders.put(type, loaders = new ObjectMap<>());
|
||||
loaders.put(suffix == null ? "" : suffix, loader);
|
||||
}
|
||||
|
||||
/** @return the number of loaded assets */
|
||||
public synchronized int getLoadedAssets(){
|
||||
return assetTypes.size;
|
||||
}
|
||||
|
||||
/** @return the number of currently queued assets */
|
||||
public synchronized int getQueuedAssets(){
|
||||
return loadQueue.size + tasks.size;
|
||||
}
|
||||
|
||||
/** @return the progress in percent of completion. */
|
||||
public synchronized float getProgress(){
|
||||
if(toLoad == 0) return 1;
|
||||
float fractionalLoaded = (float)loaded;
|
||||
if(peakTasks > 0){
|
||||
fractionalLoaded += ((peakTasks - tasks.size) / (float)peakTasks);
|
||||
}
|
||||
return Math.min(1, fractionalLoaded / (float)toLoad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an {@link AssetErrorListener} to be invoked in case loading an asset failed.
|
||||
* @param listener the listener or null
|
||||
*/
|
||||
public synchronized void setErrorListener(AssetErrorListener listener){
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/** Disposes all assets in the manager and stops all asynchronous loading. */
|
||||
@Override
|
||||
public synchronized void dispose(){
|
||||
clear();
|
||||
Threads.await(executor);
|
||||
}
|
||||
|
||||
/** Clears and disposes all assets and the preloading queue. */
|
||||
public synchronized void clear(){
|
||||
loadQueue.clear();
|
||||
while(!update());
|
||||
|
||||
ObjectIntMap<String> dependencyCount = new ObjectIntMap<>();
|
||||
while(assetTypes.size > 0){
|
||||
// for each asset, figure out how often it was referenced
|
||||
dependencyCount.clear();
|
||||
Seq<String> assets = assetTypes.keys().toSeq();
|
||||
for(String asset : assets){
|
||||
dependencyCount.put(asset, 0);
|
||||
}
|
||||
|
||||
for(String asset : assets){
|
||||
Seq<String> dependencies = assetDependencies.get(asset);
|
||||
if(dependencies == null) continue;
|
||||
for(String dependency : dependencies){
|
||||
int count = dependencyCount.get(dependency, 0);
|
||||
count++;
|
||||
dependencyCount.put(dependency, count);
|
||||
}
|
||||
}
|
||||
|
||||
// only dispose of assets that are root assets (not referenced)
|
||||
for(String asset : assets){
|
||||
if(dependencyCount.get(asset, 0) == 0){
|
||||
unload(asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.assets.clear();
|
||||
this.assetTypes.clear();
|
||||
this.assetDependencies.clear();
|
||||
this.loaded = 0;
|
||||
this.toLoad = 0;
|
||||
this.peakTasks = 0;
|
||||
this.loadQueue.clear();
|
||||
this.tasks.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the reference count of an asset.
|
||||
*/
|
||||
public synchronized int getReferenceCount(String fileName){
|
||||
Class type = assetTypes.get(fileName);
|
||||
if(type == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
return assets.get(type).get(fileName).count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the reference count of an asset.
|
||||
*/
|
||||
public synchronized void setReferenceCount(String fileName, int refCount){
|
||||
Class type = assetTypes.get(fileName);
|
||||
if(type == null) throw new ArcRuntimeException("Asset not loaded: " + fileName);
|
||||
assets.get(type).get(fileName).count = refCount;
|
||||
}
|
||||
|
||||
/** @return a string containing ref count and dependency information for all assets. */
|
||||
public synchronized String getDiagnostics(){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(String fileName : assetTypes.keys()){
|
||||
sb.append(fileName);
|
||||
sb.append(", ");
|
||||
|
||||
Class type = assetTypes.get(fileName);
|
||||
RefCountedContainer assetRef = assets.get(type).get(fileName);
|
||||
Seq<String> dependencies = assetDependencies.get(fileName);
|
||||
|
||||
sb.append(type.getSimpleName());
|
||||
|
||||
sb.append(", refs: ");
|
||||
sb.append(assetRef.count);
|
||||
|
||||
if(dependencies != null){
|
||||
sb.append(", deps: [");
|
||||
for(String dep : dependencies){
|
||||
sb.append(dep);
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("]");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/** @return the file names of all loaded assets. */
|
||||
public synchronized Seq<String> getAssetNames(){
|
||||
return assetTypes.keys().toSeq();
|
||||
}
|
||||
|
||||
/** @return the dependencies of an asset or null if the asset has no dependencies. */
|
||||
public synchronized Seq<String> getDependencies(String fileName){
|
||||
return assetDependencies.get(fileName);
|
||||
}
|
||||
|
||||
/** @return the type of a loaded asset. */
|
||||
public synchronized Class getAssetType(String fileName){
|
||||
return assetTypes.get(fileName);
|
||||
}
|
||||
|
||||
static class RefCountedContainer{
|
||||
Object object;
|
||||
int count = 1;
|
||||
|
||||
public RefCountedContainer(Object object){
|
||||
if(object == null) throw new IllegalArgumentException("Object must not be null");
|
||||
this.object = object;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package arc.assets;
|
||||
|
||||
import arc.struct.*;
|
||||
|
||||
public interface Loadable{
|
||||
default void loadAsync(){
|
||||
|
||||
}
|
||||
|
||||
default void loadSync(){
|
||||
|
||||
}
|
||||
|
||||
default String getName(){
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
|
||||
default Seq<AssetDescriptor> getDependencies(){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package arc.assets.loaders;
|
||||
|
||||
import arc.assets.AssetDescriptor;
|
||||
import arc.assets.AssetLoaderParameters;
|
||||
import arc.struct.Seq;
|
||||
import arc.files.Fi;
|
||||
|
||||
/**
|
||||
* Abstract base class for asset loaders.
|
||||
* @param <T> the class of the asset the loader supports
|
||||
* @param <P> the class of the loading parameters the loader supports.
|
||||
* @author mzechner
|
||||
*/
|
||||
public abstract class AssetLoader<T, P extends AssetLoaderParameters<T>>{
|
||||
/** {@link FileHandleResolver} used to map from plain asset names to {@link Fi} instances **/
|
||||
private FileHandleResolver resolver;
|
||||
|
||||
/**
|
||||
* Constructor, sets the {@link FileHandleResolver} to use to resolve the file associated with the asset name.
|
||||
*/
|
||||
public AssetLoader(FileHandleResolver resolver){
|
||||
this.resolver = resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName file name to resolve
|
||||
* @return handle to the file, as resolved by the {@link FileHandleResolver} set on the loader
|
||||
*/
|
||||
public Fi resolve(String fileName){
|
||||
return resolver.resolve(fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the assets this asset requires to be loaded first. This method may be called on a thread other than the GL thread.
|
||||
* @param fileName name of the asset to load
|
||||
* @param file the resolved file to load
|
||||
* @param parameter parameters for loading the asset
|
||||
* @return other assets that the asset depends on and need to be loaded first or null if there are no dependencies.
|
||||
*/
|
||||
public Seq<AssetDescriptor> getDependencies(String fileName, Fi file, P parameter){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package arc.assets.loaders;
|
||||
|
||||
import arc.assets.AssetLoaderParameters;
|
||||
import arc.assets.AssetManager;
|
||||
import arc.files.Fi;
|
||||
|
||||
/**
|
||||
* Base class for asynchronous {@link AssetLoader} instances. Such loaders try to load parts of an OpenGL resource, like the
|
||||
* Pixmap, on a separate thread to then load the actual resource on the thread the OpenGL context is active on.
|
||||
* @author mzechner
|
||||
*/
|
||||
public abstract class AsynchronousAssetLoader<T, P extends AssetLoaderParameters<T>> extends AssetLoader<T, P>{
|
||||
|
||||
public AsynchronousAssetLoader(FileHandleResolver resolver){
|
||||
super(resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the non-OpenGL part of the asset and injects any dependencies of the asset into the AssetManager.
|
||||
* @param fileName the name of the asset to load
|
||||
* @param file the resolved file to load
|
||||
* @param parameter the parameters to use for loading the asset
|
||||
*/
|
||||
public abstract void loadAsync(AssetManager manager, String fileName, Fi file, P parameter);
|
||||
|
||||
/**
|
||||
* Loads the OpenGL part of the asset.
|
||||
* @param file the resolved file to load
|
||||
*/
|
||||
public abstract T loadSync(AssetManager manager, String fileName, Fi file, P parameter);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package arc.assets.loaders;
|
||||
|
||||
import arc.assets.*;
|
||||
import arc.files.*;
|
||||
import arc.graphics.*;
|
||||
import arc.util.*;
|
||||
|
||||
/**
|
||||
* {@link AssetLoader} for {@link Cubemap} instances. The pixel data is loaded asynchronously. The texture is then created on the
|
||||
* rendering thread, synchronously. Passing a {@link CubemapParameter} to
|
||||
* {@link AssetManager#load(String, Class, AssetLoaderParameters)} allows one to specify parameters as can be passed to the
|
||||
* various Cubemap constructors, e.g. filtering and so on.
|
||||
* @author mzechner, Vincent Bousquet
|
||||
*/
|
||||
public class CubemapLoader extends AsynchronousAssetLoader<Cubemap, CubemapLoader.CubemapParameter>{
|
||||
private static final String[] names = {"right.png", "left.png", "top.png", "bottom.png", "front.png", "back.png"};
|
||||
final CubemapLoaderInfo info = new CubemapLoaderInfo();
|
||||
|
||||
public CubemapLoader(FileHandleResolver resolver){
|
||||
super(resolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadAsync(AssetManager manager, String fileName, Fi file, CubemapParameter parameter){
|
||||
info.pixmaps = new Pixmap[6];
|
||||
for(int i = 0; i < 6; i++){
|
||||
info.pixmaps[i] = new Pixmap(file.sibling(file.nameWithoutExtension() + names[i]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cubemap loadSync(AssetManager manager, String fileName, Fi file, CubemapParameter parameter){
|
||||
if(info.pixmaps == null) return null;
|
||||
|
||||
Cubemap cubemap = parameter == null ? null : parameter.cubemap;
|
||||
if(cubemap == null) cubemap = new Cubemap();
|
||||
cubemap.load(info.pixmaps, parameter != null && parameter.mipmaps, true);
|
||||
|
||||
if(parameter != null){
|
||||
cubemap.setFilter(parameter.minFilter, parameter.magFilter);
|
||||
cubemap.setWrap(parameter.wrapU, parameter.wrapV);
|
||||
}
|
||||
return cubemap;
|
||||
}
|
||||
|
||||
public static class CubemapLoaderInfo{
|
||||
Pixmap[] pixmaps;
|
||||
}
|
||||
|
||||
public static class CubemapParameter extends AssetLoaderParameters<Cubemap>{
|
||||
/** The texture to put the data in, optional. **/
|
||||
public @Nullable Cubemap cubemap = null;
|
||||
public boolean mipmaps;
|
||||
public TextureFilter minFilter = TextureFilter.nearest;
|
||||
public TextureFilter magFilter = TextureFilter.nearest;
|
||||
public TextureWrap wrapU = TextureWrap.clampToEdge;
|
||||
public TextureWrap wrapV = TextureWrap.clampToEdge;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package arc.assets.loaders;
|
||||
|
||||
import arc.*;
|
||||
import arc.assets.*;
|
||||
import arc.files.*;
|
||||
import arc.struct.*;
|
||||
|
||||
public abstract class CustomLoader extends AsynchronousAssetLoader{
|
||||
public Runnable loaded = () -> {};
|
||||
|
||||
public CustomLoader(){
|
||||
super(Core.files::internal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object loadSync(AssetManager manager, String fileName, Fi file, AssetLoaderParameters parameter){
|
||||
loaded.run();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Seq<AssetDescriptor> getDependencies(String fileName, Fi file, AssetLoaderParameters parameter){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package arc.assets.loaders;
|
||||
|
||||
import arc.assets.AssetManager;
|
||||
import arc.files.Fi;
|
||||
|
||||
/**
|
||||
* Interface for classes the can map a file name to a {@link Fi}. Used to allow the {@link AssetManager} to load resources
|
||||
* from anywhere or implement caching strategies.
|
||||
* @author mzechner
|
||||
*/
|
||||
public interface FileHandleResolver{
|
||||
Fi resolve(String fileName);
|
||||
}
|
||||
@@ -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("i18n/message", I18NBundle.class, new I18NBundleParameter(Locale.ITALIAN));
|
||||
* manager.load("i18n/message", 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(){
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||