Compare commits

..

6 Commits

Author SHA1 Message Date
Anuken
5c89d7d273 arc 2023-05-09 16:42:17 -04:00
Anuken
3ea742b06d debugGraphs off 2023-05-09 15:39:53 -04:00
Anuken
9bedadac79 Better conduit merge 2023-05-09 15:34:13 -04:00
Anuken
5ccadcf38f Semi-functioning implementation 2023-05-09 15:00:26 -04:00
Anuken
a32462971b checklist 2023-05-09 12:23:49 -04:00
Anuken
3faf8ca07f WIP conduit graph 2023-05-09 12:19:26 -04:00
1011 changed files with 24659 additions and 52920 deletions

30
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,30 @@
---
name: Bug report
about: Create a report to help fix an issue.
title: ''
labels: bug
assignees: ''
---
**Platform**: *The type of device you were playing on - Android/iOS/Mac/Windows/Linux* ("All" is NOT a platform!)
**Build**: *The build number under the title in the main menu. Required. "LATEST" IS NOT A VERSION, I NEED THE EXACT BUILD NUMBER OF YOUR GAME.*
**Issue**: *Explain your issue in detail.*
**Steps to reproduce**: *How you happened across the issue, and what exactly you did to make the bug happen.*
**Link(s) to mod(s) used**: *The mod repositories or zip files that are related to the issue, if applicable.*
**Save file**: *The (zipped) save file you were playing on when the bug happened. THIS IS REQUIRED FOR ANY ISSUE HAPPENING IN-GAME OR IN MULTIPLAYER, REGARDLESS OF WHETHER YOU THINK IT HAPPENS EVERYWHERE. DO NOT DELETE OR OMIT THIS LINE UNLESS YOU ARE SURE THAT THE ISSUE DOES NOT HAPPEN IN-GAME. IF YOU DO NOT HAVE A SAVE, DON'T WASTE TIME OPENING THIS ISSUE.*
If you remove the line above without reading it properly and understanding what it means, I will reap your soul. Even if you're playing on someone's server, you can still save the game to a slot.
**(Crash) logs**: *Either crash reports from the crash folder, or the file you get when you go into Settings -> Game Data -> Export Crash logs. REQUIRED if you are reporting a crash.*
---
*Place an X (no spaces) between the brackets to confirm that you have read the line below.*
- [ ] **I have updated to the latest release (https://github.com/Anuken/Mindustry/releases) to make sure my issue has not been fixed.**
- [ ] **I have searched the closed and open issues to make sure that this problem has not already been reported.**

View File

@@ -1,76 +0,0 @@
name: Bug report
description: The type of device you were playing on
labels: ["bug"]
body:
- type: dropdown
id: platform
attributes:
label: Platforms
description: On what platforms do you know the bug happens?
multiple: false
options:
- Android
- iOS
- Mac
- Windows
- Linux
validations:
required: true
- type: input
id: build
attributes:
label: Build
description: The build number under the title in the main menu.
placeholder: LATEST IS NOT A VERSION, I NEED THE EXACT BUILD NUMBER OF YOUR GAME.
validations:
required: true
- type: textarea
id: issue
attributes:
label: Issue
description: Explain your issue in detail.
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps to reproduce
description: How you happened across the issue, and what exactly you did to make the bug happen.
validations:
required: true
- type: textarea
id: mods
attributes:
label: Mods used
description: The mod repositories or zip files that are related to the issue, if applicable.
validations:
required: false
- type: textarea
id: save-file
attributes:
label: Save file
description: The (zipped) save file you were playing on when the bug happened. If this happened in the campaign, specify the sector, and attach the file you get from Settings -> Game Data -> Export Data. For custom games, attach the .msav file exported from the save dialog, zipped.
placeholder: THIS IS REQUIRED FOR ANY ISSUE HAPPENING IN-GAME OR IN MULTIPLAYER, REGARDLESS OF WHETHER YOU THINK IT HAPPENS EVERYWHERE. DO NOT OMIT THIS LINE UNLESS YOU ARE SURE THAT THE ISSUE DOES NOT HAPPEN IN-GAME. IF YOU DO NOT HAVE A SAVE, DON'T WASTE TIME OPENING THIS ISSUE.
validations:
required: false
- type: textarea
id: logs
attributes:
label: (Crash) logs
description: Either crash reports from the crash folder, or the file you get when you go into Settings -> Game Data -> Export Crash logs.
placeholder: REQUIRED if you are reporting a crash.
validations:
required: false
- type: checkboxes
id: agreement
attributes:
label: Submission
description: Check the boxes to confirm that you have read the lines below.
options:
- label: I have updated to the latest release (https://github.com/Anuken/Mindustry/releases) to make sure my issue has not been fixed.
required: true
- label: I have searched the closed and open issues to make sure that this problem has not already been reported.
required: true
- label: "I am not using Foo's Client, and have made sure the bug is not caused by mods I have installed."
required: true

View File

@@ -44,7 +44,7 @@ jobs:
rm -rf .github rm -rf .github
rm README.md rm README.md
git add . git add .
git commit --allow-empty -m "Updating" git commit --allow-empty -m "${GITHUB_SHA}"
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryJitpack git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryJitpack
git tag ${RELEASE_VERSION} git tag ${RELEASE_VERSION}
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryJitpack git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryJitpack
@@ -73,7 +73,7 @@ jobs:
cd ../MindustryBuilds cd ../MindustryBuilds
echo "Updating version to ${RELEASE_VERSION:1}" echo "Updating version to ${RELEASE_VERSION:1}"
BNUM=$(($GITHUB_RUN_NUMBER + 1000)) BNUM=$(($GITHUB_RUN_NUMBER + 1000))
echo versionName=8-fdroid-${RELEASE_VERSION:1}$'\n'versionCode=${BNUM} > version_fdroid.txt echo versionName=7-fdroid-${RELEASE_VERSION:1}$'\n'versionCode=${BNUM} > version_fdroid.txt
git add . git add .
git commit -m "Updating to build ${RELEASE_VERSION:1}" git commit -m "Updating to build ${RELEASE_VERSION:1}"
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryBuilds git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryBuilds

View File

@@ -1,10 +0,0 @@
name: "Validate Gradle Wrapper"
on: [push, pull_request]
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v2

View File

@@ -17,12 +17,10 @@ jobs:
java-version: 17 java-version: 17
- name: Setup Gradle - name: Setup Gradle
uses: gradle/gradle-build-action@v2 uses: gradle/gradle-build-action@v2
- name: Run unit tests
run: ./gradlew tests:test --stacktrace --rerun
- name: Run unit tests and build JAR - name: Run unit tests and build JAR
run: ./gradlew desktop:dist run: ./gradlew test desktop:dist
- name: Upload desktop JAR for testing - name: Upload desktop JAR for testing
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v2
with: with:
name: Desktop JAR (zipped) name: Desktop JAR (zipped)
path: desktop/build/libs/Mindustry.jar path: desktop/build/libs/Mindustry.jar

View File

@@ -33,8 +33,6 @@ jobs:
./gradlew updateBundles ./gradlew updateBundles
if [ -n "$(git status --porcelain)" ]; then if [ -n "$(git status --porcelain)" ]; then
git config --global user.name "Github Actions"
git config --global user.email "actions@github.com"
git add core/assets/bundles/* git add core/assets/bundles/*
git commit -m "Automatic bundle update" git commit -m "Automatic bundle update"
git push git push
@@ -43,7 +41,7 @@ jobs:
if: ${{ github.repository == 'Anuken/Mindustry' }} if: ${{ github.repository == 'Anuken/Mindustry' }}
run: | run: |
git config --global user.name "Github Actions" git config --global user.name "Github Actions"
git config --global user.email "actions@github.com" git config --global user.email "cli@github.com"
cd ../ cd ../
cp -r ./Mindustry ./MindustryJitpack cp -r ./Mindustry ./MindustryJitpack
cd MindustryJitpack cd MindustryJitpack
@@ -54,8 +52,8 @@ jobs:
rm -rf .github rm -rf .github
rm README.md rm README.md
git add . git add .
git commit --allow-empty -m "Updating" git commit --allow-empty -m "${GITHUB_SHA}"
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryJitpack git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryJitpack
cd ../Mindustry cd ../Mindustry
- name: Run unit tests - name: Run unit tests
run: ./gradlew tests:test --rerun --stacktrace run: ./gradlew clean cleanTest test --stacktrace

6
.gitignore vendored
View File

@@ -6,7 +6,6 @@ logs/
/core/assets/.gifimages/ /core/assets/.gifimages/
/deploy/ /deploy/
/out/ /out/
ios/libs/
/desktop/packr-out/ /desktop/packr-out/
/desktop/packr-export/ /desktop/packr-export/
/desktop/mindustry-saves/ /desktop/mindustry-saves/
@@ -23,7 +22,6 @@ ios/libs/
/tools/build/ /tools/build/
/tests/build/ /tests/build/
/server/build/ /server/build/
ios/libs/
changelog changelog
saves/ saves/
/core/assets-raw/fontgen/out/ /core/assets-raw/fontgen/out/
@@ -45,7 +43,6 @@ steam_appid.txt
ios/robovm.properties ios/robovm.properties
packr-out/ packr-out/
config/ config/
buildSrc/
*.gif *.gif
/tests/out /tests/out
@@ -168,6 +165,3 @@ android/libs/
# ignored due to frequent branch conflicts. # ignored due to frequent branch conflicts.
core/assets/logicids.dat core/assets/logicids.dat
# project files for the sectors
core/assets-raw/sprites/ui/sectors/*.json

View File

@@ -13,13 +13,11 @@ If you are submitting a new block, make sure it has a name and description, and
### Do not make large changes before discussing them first. ### Do not make large changes before discussing them first.
If you are interested in adding a large mechanic/feature or changing large amounts of code, first contact me (Anuken) via [Discord](https://discord.gg/mindustry) - either via PM or by posting in the `#pulls` channel. If you are interested in adding a large mechanic/feature or changing large amounts of code, first contact me (Anuken) via [Discord](https://discord.gg/mindustry) - either via PM or by posting in the `#pulls` channel.
For most changes, this should not be necessary. I just want to know if you're doing something big, so I can offer advice and/or make sure you're not wasting your time on it. For most changes, this should not be necessary. I just want to know if you're doing something big so I can offer advice and/or make sure you're not wasting your time on it.
### Do not make formatting or "cleanup" PRs. ### Do not make formatting PRs.
Yes, there are occurrences of trailing spaces, extra newlines, empty indents, and other tiny errors. No, I don't want to merge, view, or get notified by your 1-line PR fixing it. If you're implementing a PR with modification of *actual code*, feel free to fix formatting in the general vicinity of your changes, but please don't waste everyone's time with pointless changes. Yes, there are occurrences of trailing spaces, extra newlines, empty indents, and other tiny errors. No, I don't want to merge, view, or get notified by your 1-line PR fixing it. If you're implementing a PR with modification of *actual code*, feel free to fix formatting in the general vicinity of your changes, but please don't waste everyone's time with pointless changes.
I **especially** do not want to see PRs that apply any kind of automated analysis to the source code to "optimize" anything - my IDE can do that already. If the PR doesn't actually change anything useful, I'm not going to review or merge it.
## Style Guidelines ## Style Guidelines
### Follow the formatting guidelines. ### Follow the formatting guidelines.
@@ -36,7 +34,7 @@ This means:
Import [this style file](.github/Mindustry-CodeStyle-IJ.xml) into IntelliJ to get correct formatting when developing Mindustry. Import [this style file](.github/Mindustry-CodeStyle-IJ.xml) into IntelliJ to get correct formatting when developing Mindustry.
### Do not use incompatible Java features (java.util.function, java.awt, java.lang.Objects). ### Do not use incompatible Java features (java.util.function, java.awt).
Android and RoboVM (iOS) do not support many of Java 8's features, such as the packages `java.util.function`, `java.util.stream` or `forEach` in collections. Do not use these in your code. Android and RoboVM (iOS) do not support many of Java 8's features, such as the packages `java.util.function`, `java.util.stream` or `forEach` in collections. Do not use these in your code.
If you need to use functional interfaces, use the ones in `arc.func`, which are more or less the same with different naming schemes. If you need to use functional interfaces, use the ones in `arc.func`, which are more or less the same with different naming schemes.
@@ -68,7 +66,7 @@ Otherwise, use the `Tmp` variables for things like vector/shape operations, or c
If using a list, make it a static variable and clear it every time it is used. Re-use as much as possible. If using a list, make it a static variable and clear it every time it is used. Re-use as much as possible.
### Avoid bloated code and unnecessary getters/setters. ### Avoid bloated code and unnecessary getters/setters.
This is situational, but in essence, what it means is to avoid using any sort of getters and setters unless absolutely necessary. Public or protected fields should suffice for most things. This is situational, but in essence what it means is to avoid using any sort of getters and setters unless absolutely necessary. Public or protected fields should suffice for most things.
If something needs to be encapsulated in the future, IntelliJ can handle it with a few clicks. If something needs to be encapsulated in the future, IntelliJ can handle it with a few clicks.

View File

@@ -18,7 +18,7 @@ See [CONTRIBUTING](CONTRIBUTING.md).
Bleeding-edge builds are generated automatically for every commit. You can see them [here](https://github.com/Anuken/MindustryBuilds/releases). Bleeding-edge builds are generated automatically for every commit. You can see them [here](https://github.com/Anuken/MindustryBuilds/releases).
If you'd rather compile on your own, follow these instructions. If you'd rather compile on your own, follow these instructions.
First, make sure you have [JDK 17](https://adoptium.net/archive.html?variant=openjdk17&jvmVariant=hotspot) installed. **Other JDK versions will not work.** Open a terminal in the Mindustry directory and run the following commands: First, make sure you have [JDK 16-17](https://adoptium.net/archive.html?variant=openjdk17&jvmVariant=hotspot) installed. **Other JDK versions will not work.** Open a terminal in the Mindustry directory and run the following commands:
### Windows ### Windows
@@ -53,16 +53,6 @@ To debug the application on a connected device/emulator, run `gradlew android:in
If the terminal returns `Permission denied` or `Command not found` on Mac/Linux, run `chmod +x ./gradlew` before running `./gradlew`. *This is a one-time procedure.* If the terminal returns `Permission denied` or `Command not found` on Mac/Linux, run `chmod +x ./gradlew` before running `./gradlew`. *This is a one-time procedure.*
#### Where is the `mindustry.gen` package?
As the name implies, `mindustry.gen` is generated *at build time* based on other code. You will not find source code for this package in the repository, and it should not be edited by hand.
The following is a non-exhaustive list of the "source" of generated code in `mindustry.gen`:
- `Call`, `*Packet` classes: Generated from methods marked with `@Remote`.
- All entity classes (`Unit`, `EffectState`, `Posc`, etc): Generated from component classes in the `mindustry.entities.comp` package, and combined using definitions in `mindustry.content.UnitTypes`.
- `Sounds`, `Musics`, `Tex`, `Icon`, etc: Generated based on files in the respective asset folders.
--- ---
Gradle may take up to several minutes to download files. Be patient. <br> Gradle may take up to several minutes to download files. Be patient. <br>

View File

@@ -1,11 +1,7 @@
# Note: The v7 server list is frozen. No new servers will be accepted. All v8 server PRs should be made [here](https://github.com/Anuken/MindustryServerList/blob/main/servers_v8.json).
*PRs to edit addresses of existing servers will still be accepted, although very infrequently.*
### Adding a server to the list ### Adding a server to the list
Mindustry now has a public list of servers that everyone can see and connect to. Mindustry now has a public list of servers that everyone can see and connect to.
This is done by letting clients `GET` a [JSON list of servers](https://github.com/Anuken/Mindustry/blob/master/servers_v7.json) in this repository. This is done by letting clients `GET` a [JSON list of servers](https://github.com/Anuken/Mindustry/blob/master/servers_v6.json) in this repository.
You may want to add your server to this list. The steps for getting this done are as follows: You may want to add your server to this list. The steps for getting this done are as follows:
@@ -22,16 +18,13 @@ You'll need to either hire some moderators, or make use of (currently non-existe
4. **Get some good maps.** *(optional, but highly recommended)*. Add some maps to your server and set the map rotation to custom-only. You can get maps from the Steam workshop by subscribing and exporting them; using the `#maps` channel on Discord is also an option. 4. **Get some good maps.** *(optional, but highly recommended)*. Add some maps to your server and set the map rotation to custom-only. You can get maps from the Steam workshop by subscribing and exporting them; using the `#maps` channel on Discord is also an option.
5. **Check your server configuration.** *(optional)* I would recommend adding a message rate limit of 1 second (`config messageRateLimit 1`), and disabling connect/disconnect messages to reduce spam (`config showConnectMessages false`). 5. **Check your server configuration.** *(optional)* I would recommend adding a message rate limit of 1 second (`config messageRateLimit 1`), and disabling connect/disconnect messages to reduce spam (`config showConnectMessages false`).
6. Finally, **submit a pull request** to add your server's IP to the list. 6. Finally, **submit a pull request** to add your server's IP to the list.
This should be fairly straightforward: Press the edit button on the [server file](https://github.com/Anuken/Mindustry/blob/master/servers_v7.json), then add a JSON object with the following format: This should be fairly straightforward: Press the edit button on the [server file](https://github.com/Anuken/Mindustry/blob/master/servers_v6.json), then add a JSON object with a single key, indicating your server address.
For example, if your server address is `example.com:6000`, you would add a comma after the last entry and insert:
```json ```json
{ {
"name": "Your Server Group Name", "address": "example.com:6000"
"address": ["your.server.address"]
} }
``` ```
If your group has multiple servers, simply add extra addresses inside the square brackets, separated by commas. For example: `["address1", "address2"]`
> Note that Mindustry also support SRV records. This allows you to use a subdomain for your server address instead of specifying the port. For example, if you want to use `play.example.com` instead of `example.com:6000`, in the dns settings of your domain, add an SRV record with `_mindustry` as the service, `tcp` as the protocol, `play` as the target and `6000` as the port. You can also setup fallback servers by modifying the weight or priority of the record. Although SRV records are very convenient, keep in mind they are slower than regular addresses. Avoid using them in the server list, but rather as an easy way to share your server address. > Note that Mindustry also support SRV records. This allows you to use a subdomain for your server address instead of specifying the port. For example, if you want to use `play.example.com` instead of `example.com:6000`, in the dns settings of your domain, add an SRV record with `_mindustry` as the service, `tcp` as the protocol, `play` as the target and `6000` as the port. You can also setup fallback servers by modifying the weight or priority of the record. Although SRV records are very convenient, keep in mind they are slower than regular addresses. Avoid using them in the server list, but rather as an easy way to share your server address.
Then, press the *'submit pull request'* button and I'll take a look at your server. If I have any issues with it, I'll let you know in the PR comments. Then, press the *'submit pull request'* button and I'll take a look at your server. If I have any issues with it, I'll let you know in the PR comments.

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.anuke.mindustry">
<uses-feature android:glEsVersion="0x00020000" android:required="true"/> <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
<uses-feature android:name="android.hardware.type.pc" android:required="false" /> <uses-feature android:name="android.hardware.type.pc" android:required="false" />
@@ -17,8 +18,7 @@
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
android:appCategory="game" android:appCategory="game"
android:label="@string/app_name" android:label="@string/app_name"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules">
android:largeHeap="true">
<meta-data android:name="android.max_aspect" android:value="2.1"/> <meta-data android:name="android.max_aspect" android:value="2.1"/>
<activity <activity
android:name="mindustry.android.AndroidLauncher" android:name="mindustry.android.AndroidLauncher"

View File

@@ -7,7 +7,7 @@ buildscript{
} }
dependencies{ dependencies{
classpath 'com.android.tools.build:gradle:8.2.2' classpath 'com.android.tools.build:gradle:7.2.1'
} }
} }
@@ -29,9 +29,8 @@ task deploy(type: Copy){
} }
android{ android{
namespace = "io.anuke.mindustry" buildToolsVersion '31.0.0'
buildToolsVersion = '34.0.0' compileSdkVersion 31
compileSdk = 34
sourceSets{ sourceSets{
main{ main{
manifest.srcFile 'AndroidManifest.xml' manifest.srcFile 'AndroidManifest.xml'
@@ -57,7 +56,7 @@ android{
applicationId "io.anuke.mindustry" applicationId "io.anuke.mindustry"
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 34 targetSdkVersion 31
versionName versionNameResult versionName versionNameResult
versionCode = vcode versionCode = vcode
@@ -66,8 +65,6 @@ android{
props['androidBuildCode'] = (vcode + 1).toString() props['androidBuildCode'] = (vcode + 1).toString()
} }
props.store(file('../core/assets/version.properties').newWriter(), null) props.store(file('../core/assets/version.properties').newWriter(), null)
multiDexEnabled true
} }
compileOptions{ compileOptions{
@@ -75,7 +72,7 @@ android{
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
flavorDimensions = ["google"] flavorDimensions "google"
signingConfigs{ signingConfigs{
release{ release{
@@ -122,8 +119,8 @@ dependencies{
implementation arcModule("backends:backend-android") implementation arcModule("backends:backend-android")
implementation 'com.jakewharton.android.repackaged:dalvik-dx:9.0.0_r3' implementation 'com.jakewharton.android.repackaged:dalvik-dx:9.0.0_r3'
natives "com.github.Anuken.Arc:natives-android:$arcHash" natives "com.github.Anuken.Arc:natives-android:${getArcHash()}"
natives "com.github.Anuken.Arc:natives-freetype-android:$arcHash" natives "com.github.Anuken.Arc:natives-freetype-android:${getArcHash()}"
def version; def version;
def highestVersion; def highestVersion;

View File

@@ -1,12 +1,11 @@
-dontobfuscate -dontobfuscate
#these are essential packages that should not be "optimized" in any way
#the main purpose of d8 here is to shrink the absurdly-large google play games libraries
-keep class mindustry.** { *; } -keep class mindustry.** { *; }
-keep class arc.** { *; } -keep class arc.** { *; }
-keep class net.jpountz.** { *; } -keep class net.jpountz.** { *; }
-keep class rhino.** { *; } -keep class rhino.** { *; }
-keep class com.android.dex.** { *; } -keep class com.android.dex.** { *; }
-keepattributes Signature,*Annotation*,InnerClasses,EnclosingMethod
-dontwarn javax.naming.**
#-printusage out.txt #-printusage out.txt

View File

@@ -38,7 +38,7 @@ public class AndroidLauncher extends AndroidApplication{
UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler(); UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler((thread, error) -> { Thread.setDefaultUncaughtExceptionHandler((thread, error) -> {
CrashHandler.log(error); CrashSender.log(error);
//try to forward exception to system handler //try to forward exception to system handler
if(handler != null){ if(handler != null){
@@ -72,8 +72,6 @@ public class AndroidLauncher extends AndroidApplication{
@Override @Override
public ClassLoader loadJar(Fi jar, ClassLoader parent) throws Exception{ public ClassLoader loadJar(Fi jar, ClassLoader parent) throws Exception{
//Required to load jar files in Android 14: https://developer.android.com/about/versions/14/behavior-changes-14#safer-dynamic-code-loading
jar.file().setReadOnly();
return new DexClassLoader(jar.file().getPath(), getFilesDir().getPath(), null, parent){ return new DexClassLoader(jar.file().getPath(), getFilesDir().getPath(), null, parent){
@Override @Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
@@ -103,69 +101,64 @@ public class AndroidLauncher extends AndroidApplication{
} }
void showFileChooser(boolean open, String title, Cons<Fi> cons, String... extensions){ void showFileChooser(boolean open, String title, Cons<Fi> cons, String... extensions){
try{ String extension = extensions[0];
String extension = extensions[0];
if(VERSION.SDK_INT >= VERSION_CODES.Q){ if(VERSION.SDK_INT >= VERSION_CODES.Q){
Intent intent = new Intent(open ? Intent.ACTION_OPEN_DOCUMENT : Intent.ACTION_CREATE_DOCUMENT); Intent intent = new Intent(open ? Intent.ACTION_OPEN_DOCUMENT : Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE); intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(extension.equals("zip") && !open && extensions.length == 1 ? "application/zip" : "*/*"); intent.setType(extension.equals("zip") && !open && extensions.length == 1 ? "application/zip" : "*/*");
intent.putExtra(Intent.EXTRA_TITLE, "export." + extension);
addResultListener(i -> startActivityForResult(intent, i), (code, in) -> { addResultListener(i -> startActivityForResult(intent, i), (code, in) -> {
if(code == Activity.RESULT_OK && in != null && in.getData() != null){ if(code == Activity.RESULT_OK && in != null && in.getData() != null){
Uri uri = in.getData(); Uri uri = in.getData();
if(uri.getPath().contains("(invalid)")) return; if(uri.getPath().contains("(invalid)")) return;
Core.app.post(() -> Core.app.post(() -> cons.get(new Fi(uri.getPath()){ Core.app.post(() -> Core.app.post(() -> cons.get(new Fi(uri.getPath()){
@Override @Override
public InputStream read(){ public InputStream read(){
try{ try{
return getContentResolver().openInputStream(uri); return getContentResolver().openInputStream(uri);
}catch(IOException e){ }catch(IOException e){
throw new ArcRuntimeException(e); throw new ArcRuntimeException(e);
}
} }
}
@Override @Override
public OutputStream write(boolean append){ public OutputStream write(boolean append){
try{ try{
return getContentResolver().openOutputStream(uri); return getContentResolver().openOutputStream(uri);
}catch(IOException e){ }catch(IOException e){
throw new ArcRuntimeException(e); throw new ArcRuntimeException(e);
}
} }
}))); }
} })));
}); }
}else if(VERSION.SDK_INT >= VERSION_CODES.M && !(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && });
}else if(VERSION.SDK_INT >= VERSION_CODES.M && !(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)){ checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)){
chooser = new FileChooser(title, file -> Structs.contains(extensions, file.extension().toLowerCase()), open, file -> { chooser = new FileChooser(title, file -> Structs.contains(extensions, file.extension().toLowerCase()), open, file -> {
if(!open){ if(!open){
cons.get(file.parent().child(file.nameWithoutExtension() + "." + extension)); cons.get(file.parent().child(file.nameWithoutExtension() + "." + extension));
}else{
cons.get(file);
}
});
ArrayList<String> perms = new ArrayList<>();
if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
perms.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
if(checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
perms.add(Manifest.permission.READ_EXTERNAL_STORAGE);
}
requestPermissions(perms.toArray(new String[0]), PERMISSION_REQUEST_CODE);
}else{
if(open){
new FileChooser(title, file -> Structs.contains(extensions, file.extension().toLowerCase()), true, cons).show();
}else{ }else{
super.showFileChooser(open, "@open", extension, cons); cons.get(file);
} }
});
ArrayList<String> perms = new ArrayList<>();
if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
perms.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
if(checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
perms.add(Manifest.permission.READ_EXTERNAL_STORAGE);
}
requestPermissions(perms.toArray(new String[0]), PERMISSION_REQUEST_CODE);
}else{
if(open){
new FileChooser(title, file -> Structs.contains(extensions, file.extension().toLowerCase()), true, cons).show();
}else{
super.showFileChooser(open, "@open", extension, cons);
} }
}catch(Throwable error){
Core.app.post(() -> Vars.ui.showException(error));
} }
} }
@@ -187,7 +180,6 @@ public class AndroidLauncher extends AndroidApplication{
}, new AndroidApplicationConfiguration(){{ }, new AndroidApplicationConfiguration(){{
useImmersiveMode = true; useImmersiveMode = true;
hideStatusBar = true; hideStatusBar = true;
useGL30 = true;
}}); }});
checkFiles(getIntent()); checkFiles(getIntent());

View File

@@ -8,12 +8,14 @@ public class Annotations{
/** Indicates that a method overrides other methods. */ /** Indicates that a method overrides other methods. */
@Target({ElementType.METHOD}) @Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface Replace{} public @interface Replace{
}
/** Indicates that a method should be final in all implementing classes. */ /** Indicates that a method should be final in all implementing classes. */
@Target({ElementType.METHOD}) @Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface Final{} public @interface Final{
}
/** Indicates that a field will be interpolated when synced. */ /** Indicates that a field will be interpolated when synced. */
@Target({ElementType.FIELD}) @Target({ElementType.FIELD})
@@ -28,18 +30,15 @@ public class Annotations{
/** Indicates that a field will not be read from the server when syncing the local player state. */ /** Indicates that a field will not be read from the server when syncing the local player state. */
@Target({ElementType.FIELD}) @Target({ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface SyncLocal{} public @interface SyncLocal{
/** Indicates that a field should not be synced to clients (but may still be non-transient) */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface NoSync{}
}
/** Indicates that a component field is imported from other components. This means it doesn't actually exist. */ /** Indicates that a component field is imported from other components. This means it doesn't actually exist. */
@Target({ElementType.FIELD}) @Target({ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface Import{} public @interface Import{
}
/** Indicates that a component field is read-only. */ /** Indicates that a component field is read-only. */
@Target({ElementType.FIELD, ElementType.METHOD}) @Target({ElementType.FIELD, ElementType.METHOD})
@@ -54,8 +53,6 @@ public class Annotations{
/** Whether to generate a base class for this components. /** Whether to generate a base class for this components.
* An entity cannot have two base classes, so only one component can have base be true. */ * An entity cannot have two base classes, so only one component can have base be true. */
boolean base() default false; boolean base() default false;
/** Whether to generate a proper interface for this component class. */
boolean genInterface() default true;
} }
/** Indicates that a method is implemented by the annotation processor. */ /** Indicates that a method is implemented by the annotation processor. */
@@ -108,7 +105,8 @@ public class Annotations{
/** Indicates an internal interface for entity components. */ /** Indicates an internal interface for entity components. */
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface EntityInterface{} public @interface EntityInterface{
}
//endregion //endregion
//region misc. utility //region misc. utility
@@ -147,12 +145,15 @@ public class Annotations{
/** Indicates that a method should always call its super version. */ /** Indicates that a method should always call its super version. */
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface CallSuper{} public @interface CallSuper{
}
/** Annotation that allows overriding CallSuper annotation. To be used on method that overrides method with CallSuper annotation from parent class. */ /** Annotation that allows overriding CallSuper annotation. To be used on method that overrides method with CallSuper annotation from parent class. */
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface OverrideCallSuper{} public @interface OverrideCallSuper{
}
//endregion //endregion
//region struct //region struct
@@ -160,7 +161,9 @@ public class Annotations{
/** Marks a class as a special value type struct. Class name must end in 'Struct'. */ /** Marks a class as a special value type struct. Class name must end in 'Struct'. */
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface Struct{} public @interface Struct{
}
/** Marks a field of a struct. Optional. */ /** Marks a field of a struct. Optional. */
@Target(ElementType.FIELD) @Target(ElementType.FIELD)
@@ -184,16 +187,18 @@ public class Annotations{
/** A set of two booleans, one specifying server and one specifying client. */ /** A set of two booleans, one specifying server and one specifying client. */
public enum Loc{ public enum Loc{
/** Server only. */ /** Method can only be invoked on the client from the server. */
server(true, false), server(true, false),
/** Client only. */ /** Method can only be invoked on the server from the client. */
client(false, true), client(false, true),
/** Both server and client. */ /** Method can be invoked from anywhere */
both(true, true), both(true, true),
/** Neither server nor client. */ /** Neither server nor client. */
none(false, false); none(false, false);
/** If true, this method can be invoked ON clients FROM servers. */
public final boolean isServer; public final boolean isServer;
/** If true, this method can be invoked ON servers FROM clients. */
public final boolean isClient; public final boolean isClient;
Loc(boolean server, boolean client){ Loc(boolean server, boolean client){
@@ -222,16 +227,16 @@ public class Annotations{
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface Remote{ public @interface Remote{
/** Specifies the locations from which this method can cause remote invocations (This -> Remote) [Default: Server -> Client]. */ /** Specifies the locations from which this method can be invoked. */
Loc targets() default Loc.server; Loc targets() default Loc.server;
/** Specifies which methods are generated. Only affects server-to-client methods (Server -> Client(s)) [Default: Server -> Client & Server -> All Clients]. */ /** Specifies which methods are generated. Only affects server-to-client methods. */
Variant variants() default Variant.all; Variant variants() default Variant.all;
/** The locations where this method is called locally, when invoked locally (This -> This) [Default: No local invocations]. */ /** The local locations where this method is called locally, when invoked. */
Loc called() default Loc.none; Loc called() default Loc.none;
/** Whether the server should forward this packet to all other clients upon receival from a client (Client -> Server -> Other Clients). [Default: Don't Forward Client Invocations] */ /** Whether to forward this packet to all other clients upon receival. Client only. */
boolean forward() default false; boolean forward() default false;
/** /**
@@ -246,7 +251,8 @@ public class Annotations{
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface TypeIOHandler{ } public @interface TypeIOHandler{
}
//endregion //endregion
} }

View File

@@ -118,16 +118,13 @@ public class EntityIO{
} }
} }
void writeSync(MethodSpec.Builder method, boolean write, Seq<Svar> allFields) throws Exception{ void writeSync(MethodSpec.Builder method, boolean write, Seq<Svar> syncFields, Seq<Svar> allFields) throws Exception{
this.method = method; this.method = method;
this.write = write; this.write = write;
if(write){ if(write){
//write uses most recent revision //write uses most recent revision
for(RevisionField field : revisions.peek().fields){ for(RevisionField field : revisions.peek().fields){
Svar var = allFields.find(s -> s.name().equals(field.name));
if(var == null || var.has(NoSync.class)) continue;
io(field.type, "this." + field.name, true); io(field.type, "this." + field.name, true);
} }
}else{ }else{
@@ -141,7 +138,6 @@ public class EntityIO{
//add code for reading revision //add code for reading revision
for(RevisionField field : rev.fields){ for(RevisionField field : rev.fields){
Svar var = allFields.find(s -> s.name().equals(field.name)); Svar var = allFields.find(s -> s.name().equals(field.name));
if(var == null || var.has(NoSync.class)) continue;
boolean sf = var.has(SyncField.class), sl = var.has(SyncLocal.class); boolean sf = var.has(SyncField.class), sl = var.has(SyncLocal.class);
if(sl) cont("if(!islocal)"); if(sl) cont("if(!islocal)");
@@ -227,7 +223,7 @@ public class EntityIO{
if(BaseProcessor.isPrimitive(type)){ if(BaseProcessor.isPrimitive(type)){
s(type.equals("boolean") ? "bool" : type.charAt(0) + "", field); 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.ctype.Content")){
if(write){ if(write){
s("s", field + ".id"); s("s", field + ".id");
}else{ }else{

View File

@@ -19,7 +19,6 @@ import javax.annotation.processing.*;
import javax.lang.model.element.*; import javax.lang.model.element.*;
import javax.lang.model.type.*; import javax.lang.model.type.*;
import java.lang.annotation.*; import java.lang.annotation.*;
import java.util.*;
@SupportedAnnotationTypes({ @SupportedAnnotationTypes({
"mindustry.annotations.Annotations.EntityDef", "mindustry.annotations.Annotations.EntityDef",
@@ -98,8 +97,6 @@ public class EntityProcess extends BaseProcessor{
//create component interfaces //create component interfaces
for(Stype component : allComponents){ for(Stype component : allComponents){
TypeSpec.Builder inter = TypeSpec.interfaceBuilder(interfaceName(component)) TypeSpec.Builder inter = TypeSpec.interfaceBuilder(interfaceName(component))
.addModifiers(Modifier.PUBLIC).addAnnotation(EntityInterface.class); .addModifiers(Modifier.PUBLIC).addAnnotation(EntityInterface.class);
@@ -119,47 +116,45 @@ public class EntityProcess extends BaseProcessor{
inter.addSuperinterface(ClassName.get(packageName, interfaceName(type))); inter.addSuperinterface(ClassName.get(packageName, interfaceName(type)));
} }
if(component.annotation(Component.class).genInterface()){ ObjectSet<String> signatures = new ObjectSet<>();
ObjectSet<String> signatures = new ObjectSet<>();
//add utility methods to interface //add utility methods to interface
for(Smethod method : component.methods()){ for(Smethod method : component.methods()){
//skip private methods, those are for internal use. //skip private methods, those are for internal use.
if(method.isAny(Modifier.PRIVATE, Modifier.STATIC)) continue; if(method.isAny(Modifier.PRIVATE, Modifier.STATIC)) continue;
//keep track of signatures used to prevent dupes //keep track of signatures used to prevent dupes
signatures.add(method.e.toString()); signatures.add(method.e.toString());
inter.addMethod(MethodSpec.methodBuilder(method.name()) inter.addMethod(MethodSpec.methodBuilder(method.name())
.addJavadoc(method.doc() == null ? "" : method.doc()) .addJavadoc(method.doc() == null ? "" : method.doc())
.addExceptions(method.thrownt()) .addExceptions(method.thrownt())
.addTypeVariables(method.typeVariables().map(TypeVariableName::get)) .addTypeVariables(method.typeVariables().map(TypeVariableName::get))
.returns(method.ret().toString().equals("void") ? TypeName.VOID : method.retn()) .returns(method.ret().toString().equals("void") ? TypeName.VOID : method.retn())
.addParameters(method.params().map(v -> ParameterSpec.builder(v.tname(), v.name()) .addParameters(method.params().map(v -> ParameterSpec.builder(v.tname(), v.name())
.build())).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT).build()); .build())).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT).build());
}
//generate interface getters and setters for all "standard" fields
for(Svar field : component.fields().select(e -> !e.is(Modifier.STATIC) && !e.is(Modifier.PRIVATE) && !e.has(Import.class))){
String cname = field.name();
//getter
if(!signatures.contains(cname + "()")){
inter.addMethod(MethodSpec.methodBuilder(cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
.addAnnotations(Seq.with(field.annotations()).select(a -> a.toString().contains("Null") || a.toString().contains("Deprecated")).map(AnnotationSpec::get))
.addJavadoc(field.doc() == null ? "" : field.doc())
.returns(field.tname()).build());
} }
//generate interface getters and setters for all "standard" fields //setter
for(Svar field : component.fields().select(e -> !e.is(Modifier.STATIC) && !e.is(Modifier.PRIVATE) && !e.has(Import.class))){ if(!field.is(Modifier.FINAL) && !signatures.contains(cname + "(" + field.mirror().toString() + ")") &&
String cname = field.name(); !field.annotations().contains(f -> f.toString().equals("@mindustry.annotations.Annotations.ReadOnly"))){
inter.addMethod(MethodSpec.methodBuilder(cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
//getter .addJavadoc(field.doc() == null ? "" : field.doc())
if(!signatures.contains(cname + "()")){ .addParameter(ParameterSpec.builder(field.tname(), field.name())
inter.addMethod(MethodSpec.methodBuilder(cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC) .addAnnotations(Seq.with(field.annotations())
.addAnnotations(Seq.with(field.annotations()).select(a -> a.toString().contains("Null") || a.toString().contains("Deprecated")).map(AnnotationSpec::get)) .select(a -> a.toString().contains("Null") || a.toString().contains("Deprecated")).map(AnnotationSpec::get)).build()).build());
.addJavadoc(field.doc() == null ? "" : field.doc())
.returns(field.tname()).build());
}
//setter
if(!field.is(Modifier.FINAL) && !signatures.contains(cname + "(" + field.mirror().toString() + ")") &&
!field.annotations().contains(f -> f.toString().equals("@mindustry.annotations.Annotations.ReadOnly"))){
inter.addMethod(MethodSpec.methodBuilder(cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
.addJavadoc(field.doc() == null ? "" : field.doc())
.addParameter(ParameterSpec.builder(field.tname(), field.name())
.addAnnotations(Seq.with(field.annotations())
.select(a -> a.toString().contains("Null") || a.toString().contains("Deprecated")).map(AnnotationSpec::get)).build()).build());
}
} }
} }
@@ -421,34 +416,19 @@ public class EntityProcess extends BaseProcessor{
//add all methods from components //add all methods from components
for(ObjectMap.Entry<String, Seq<Smethod>> entry : methods){ for(ObjectMap.Entry<String, Seq<Smethod>> entry : methods){
if(entry.value.contains(m -> m.has(Replace.class))){
//there are multiple @Replace implementations, or multiple non-void implementations. //check replacements
if(entry.value.size > 1 && (entry.value.contains(m -> m.has(Replace.class)) || entry.value.count(m -> !m.isAny(Modifier.NATIVE, Modifier.ABSTRACT) && !m.isVoid()) > 1)){ if(entry.value.count(m -> m.has(Replace.class)) > 1){
err("Type " + type + " has multiple components replacing method " + entry.key + ".");
//remove clutter
entry.value.removeAll(s -> s.is(Modifier.ABSTRACT));
Comparator<Smethod> comp = Structs.comps(
Structs.comps(
//highest priority first
Structs.comparingFloat(m -> m.has(MethodPriority.class) ? m.annotation(MethodPriority.class).value() : 0f),
//replacement means priority
Structs.comparingBool(m -> m.has(Replace.class))
),
//otherwise, the 'highest' subclass (most dependencies)
Structs.comparingInt(m -> getDependencies(m.type()).size)
);
Smethod best = entry.value.max(comp);
if(entry.value.contains(s -> best != s && comp.compare(s, best) == 0)){
err("Type " + type + " has multiple components implementing method " + entry.value.first() + " in an ambiguous way. Use MethodPriority to designate which one should be used. Implementations: " +
entry.value.map(s -> s.descString()));
} }
Smethod base = entry.value.find(m -> m.has(Replace.class));
entry.value.clear(); entry.value.clear();
entry.value.add(best); entry.value.add(base);
}
//check multi return
if(entry.value.count(m -> !m.isAny(Modifier.NATIVE, Modifier.ABSTRACT) && !m.isVoid()) > 1){
err("Type " + type + " has multiple components implementing non-void method " + entry.key + ".");
} }
entry.value.sort(Structs.comps(Structs.comparingFloat(m -> m.has(MethodPriority.class) ? m.annotation(MethodPriority.class).value() : 0), Structs.comparing(s -> s.up().getSimpleName().toString()))); entry.value.sort(Structs.comps(Structs.comparingFloat(m -> m.has(MethodPriority.class) ? m.annotation(MethodPriority.class).value() : 0), Structs.comparing(s -> s.up().getSimpleName().toString())));
@@ -465,7 +445,6 @@ public class EntityProcess extends BaseProcessor{
MethodSpec.Builder mbuilder = MethodSpec.methodBuilder(first.name()).addModifiers(first.is(Modifier.PRIVATE) ? Modifier.PRIVATE : Modifier.PUBLIC); MethodSpec.Builder mbuilder = MethodSpec.methodBuilder(first.name()).addModifiers(first.is(Modifier.PRIVATE) ? Modifier.PRIVATE : Modifier.PUBLIC);
//if(isFinal || entry.value.contains(s -> s.has(Final.class))) mbuilder.addModifiers(Modifier.FINAL); //if(isFinal || entry.value.contains(s -> s.has(Final.class))) mbuilder.addModifiers(Modifier.FINAL);
if(entry.value.contains(s -> s.has(CallSuper.class))) mbuilder.addAnnotation(CallSuper.class); //add callSuper here if necessary if(entry.value.contains(s -> s.has(CallSuper.class))) mbuilder.addAnnotation(CallSuper.class); //add callSuper here if necessary
if(first.has(Nullable.class)) mbuilder.addAnnotation(Nullable.class);
if(first.is(Modifier.STATIC)) mbuilder.addModifiers(Modifier.STATIC); if(first.is(Modifier.STATIC)) mbuilder.addModifiers(Modifier.STATIC);
mbuilder.addTypeVariables(first.typeVariables().map(TypeVariableName::get)); mbuilder.addTypeVariables(first.typeVariables().map(TypeVariableName::get));
mbuilder.returns(first.retn()); mbuilder.returns(first.retn());
@@ -511,7 +490,7 @@ public class EntityProcess extends BaseProcessor{
//SPECIAL CASE: sync I/O code //SPECIAL CASE: sync I/O code
if((first.name().equals("readSync") || first.name().equals("writeSync"))){ if((first.name().equals("readSync") || first.name().equals("writeSync"))){
io.writeSync(mbuilder, first.name().equals("writeSync"), allFields); io.writeSync(mbuilder, first.name().equals("writeSync"), syncedFields, allFields);
} }
//SPECIAL CASE: sync I/O code for writing to/from a manual buffer //SPECIAL CASE: sync I/O code for writing to/from a manual buffer
@@ -872,6 +851,89 @@ public class EntityProcess extends BaseProcessor{
for(TypeSpec.Builder b : baseClasses){ for(TypeSpec.Builder b : baseClasses){
write(b, imports.toSeq()); write(b, imports.toSeq());
} }
//TODO nulls were an awful idea
//store nulls
TypeSpec.Builder nullsBuilder = TypeSpec.classBuilder("Nulls").addModifiers(Modifier.PUBLIC).addModifiers(Modifier.FINAL);
//TODO should be dynamic
ObjectSet<String> nullList = ObjectSet.with("unit");
//create mock types of all components
for(Stype interf : allInterfaces){
//indirect interfaces to implement methods for
Seq<Stype> dependencies = interf.allInterfaces().add(interf);
Seq<Smethod> methods = dependencies.flatMap(Stype::methods);
methods.sortComparing(Object::toString);
//optionally add superclass
Stype superclass = dependencies.map(this::interfaceToComp).find(s -> s != null && s.annotation(Component.class).base());
//use the base type when the interface being emulated has a base
TypeName type = superclass != null && interfaceToComp(interf).annotation(Component.class).base() ? tname(baseName(superclass)) : interf.tname();
//used method signatures
ObjectSet<String> signatures = new ObjectSet<>();
//create null builder
String baseName = interf.name().substring(0, interf.name().length() - 1);
//prevent Nulls bloat
if(!nullList.contains(Strings.camelize(baseName))){
continue;
}
String className = "Null" + baseName;
TypeSpec.Builder nullBuilder = TypeSpec.classBuilder(className)
.addModifiers(Modifier.FINAL);
skipDeprecated(nullBuilder);
nullBuilder.addSuperinterface(interf.tname());
if(superclass != null) nullBuilder.superclass(tname(baseName(superclass)));
for(Smethod method : methods){
String signature = method.toString();
if(!signatures.add(signature)) continue;
Stype compType = interfaceToComp(method.type());
MethodSpec.Builder builder = MethodSpec.overriding(method.e).addModifiers(Modifier.PUBLIC, Modifier.FINAL);
int index = 0;
for(ParameterSpec spec : builder.parameters){
Reflect.set(spec, "name", "arg" + index++);
}
builder.addAnnotation(OverrideCallSuper.class); //just in case
if(!method.isVoid()){
String methodName = method.name();
switch(methodName){
case "isNull":
builder.addStatement("return true");
break;
case "id":
builder.addStatement("return -1");
break;
case "toString":
builder.addStatement("return $S", className);
break;
default:
Svar variable = compType == null || method.params().size > 0 ? null : compType.fields().find(v -> v.name().equals(methodName));
String desc = variable == null ? null : variable.descString();
if(variable == null || !varInitializers.containsKey(desc)){
builder.addStatement("return " + getDefault(method.ret().toString()));
}else{
String init = varInitializers.get(desc);
builder.addStatement("return " + (init.equals("{}") ? "new " + variable.mirror().toString() : "") + init);
}
}
}
nullBuilder.addMethod(builder.build());
}
nullsBuilder.addField(FieldSpec.builder(type, Strings.camelize(baseName)).initializer("new " + className + "()").addModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PUBLIC).build());
write(nullBuilder, imports.toSeq());
}
write(nullsBuilder);
} }
} }

View File

@@ -57,9 +57,6 @@ public class AssetsProcess extends BaseProcessor{
ichtype.addField(FieldSpec.builder(ParameterizedTypeName.get(ObjectIntMap.class, String.class), ichtype.addField(FieldSpec.builder(ParameterizedTypeName.get(ObjectIntMap.class, String.class),
"codes", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("new ObjectIntMap<>()").build()); "codes", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("new ObjectIntMap<>()").build());
ichtype.addField(FieldSpec.builder(ParameterizedTypeName.get(IntMap.class, String.class),
"codeToName", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("new IntMap<>()").build());
ObjectSet<String> used = new ObjectSet<>(); ObjectSet<String> used = new ObjectSet<>();
for(Jval val : icons.get("glyphs").asArray()){ for(Jval val : icons.get("glyphs").asArray()){
@@ -70,9 +67,7 @@ public class AssetsProcess extends BaseProcessor{
int code = val.getInt("code", 0); int code = val.getInt("code", 0);
iconcAll.append((char)code); iconcAll.append((char)code);
ichtype.addField(FieldSpec.builder(char.class, name, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).addJavadoc(String.format("\\u%04x", code)).initializer("'" + ((char)code) + "'").build()); ichtype.addField(FieldSpec.builder(char.class, name, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).addJavadoc(String.format("\\u%04x", code)).initializer("'" + ((char)code) + "'").build());
ichinit.addStatement("codes.put($S, $L)", name, code); ichinit.addStatement("codes.put($S, $L)", name, code);
ichinit.addStatement("codeToName.put($L, $S)", code, name);
ictype.addField(TextureRegionDrawable.class, name + "Small", Modifier.PUBLIC, Modifier.STATIC); ictype.addField(TextureRegionDrawable.class, name + "Small", Modifier.PUBLIC, Modifier.STATIC);
icload.addStatement(name + "Small = mindustry.ui.Fonts.getGlyph(mindustry.ui.Fonts.def, (char)" + code + ")"); icload.addStatement(name + "Small = mindustry.ui.Fonts.getGlyph(mindustry.ui.Fonts.def, (char)" + code + ")");

View File

@@ -102,7 +102,7 @@ public class StructProcess extends BaseProcessor{
//bools: single bit, needs special case to clear things //bools: single bit, needs special case to clear things
setter.beginControlFlow("if(value)"); setter.beginControlFlow("if(value)");
setter.addStatement("return ($T)($L | (1L << $LL))", structType, structParam, offset); setter.addStatement("return ($T)(($L & ~(1L << $LL)) | (1L << $LL))", structType, structParam, offset, offset);
setter.nextControlFlow("else"); setter.nextControlFlow("else");
setter.addStatement("return ($T)(($L & ~(1L << $LL)))", structType, structParam, offset); setter.addStatement("return ($T)(($L & ~(1L << $LL)))", structType, structParam, offset);
setter.endControlFlow(); setter.endControlFlow();

View File

@@ -28,10 +28,6 @@ public class Stype extends Selement<TypeElement>{
return interfaces().flatMap(s -> s.allInterfaces().add(s)).distinct(); return interfaces().flatMap(s -> s.allInterfaces().add(s)).distinct();
} }
public boolean isInterface(){
return e.getKind() == ElementKind.INTERFACE;
}
public Seq<Stype> superclasses(){ public Seq<Stype> superclasses(){
return Seq.with(BaseProcessor.typeu.directSupertypes(mirror())).map(Stype::of); return Seq.with(BaseProcessor.typeu.directSupertypes(mirror())).map(Stype::of);
} }

View File

@@ -15,6 +15,7 @@ manifold=36
mega=5 mega=5
mindustry.entities.comp.BuildingComp=6 mindustry.entities.comp.BuildingComp=6
mindustry.entities.comp.BulletComp=7 mindustry.entities.comp.BulletComp=7
mindustry.entities.comp.ConduitGraphUpdaterComp=48
mindustry.entities.comp.DecalComp=8 mindustry.entities.comp.DecalComp=8
mindustry.entities.comp.EffectStateComp=9 mindustry.entities.comp.EffectStateComp=9
mindustry.entities.comp.FireComp=10 mindustry.entities.comp.FireComp=10

View File

@@ -0,0 +1 @@
{fields:[]}

View File

@@ -1 +0,0 @@
{version:1,fields:[{name:admin,type:boolean},{name:boosting,type:boolean},{name:color,type:arc.graphics.Color},{name:lastCommand,type:mindustry.ai.UnitCommand},{name:mouseX,type:float},{name:mouseY,type:float},{name:name,type:java.lang.String},{name:shooting,type:boolean},{name:team,type:mindustry.game.Team},{name:typing,type:boolean},{name:unit,type:Unit},{name:x,type:float},{name:y,type:float}]}

View File

@@ -1,14 +1,10 @@
buildscript{ buildscript{
ext{ ext{
arcHash = property("archash") getArcHash = {
return new Properties().with{ p -> p.load(file('gradle.properties').newReader()); return p }["archash"]
localArc = !project.hasProperty("release") && new File(rootDir.parent, 'Arc').exists() && !project.hasProperty("noLocalArc")
arcModule = { String name ->
//skip to last submodule
name = name.substring(name.lastIndexOf(':') + 1)
return "com.github.Anuken${localArc ? "" : ".Arc"}:$name:$arcHash"
} }
arcHash = getArcHash()
} }
repositories{ repositories{
@@ -20,14 +16,14 @@ buildscript{
} }
dependencies{ dependencies{
classpath arcModule(":extensions:packer") classpath "com.github.Anuken.Arc:packer:$arcHash"
classpath arcModule(":arc-core") classpath "com.github.Anuken.Arc:arc-core:$arcHash"
} }
} }
plugins{ plugins{
id "org.jetbrains.kotlin.jvm" version "2.1.10" id "org.jetbrains.kotlin.jvm" version "1.6.0"
id "org.jetbrains.kotlin.kapt" version "2.1.10" id "org.jetbrains.kotlin.kapt" version "1.6.0"
} }
allprojects{ allprojects{
@@ -37,8 +33,8 @@ allprojects{
group = 'com.github.Anuken' group = 'com.github.Anuken'
ext{ ext{
versionNumber = '8' versionNumber = '7'
if(!project.hasProperty("versionModifier")) versionModifier = 'beta' if(!project.hasProperty("versionModifier")) versionModifier = 'release'
if(!project.hasProperty("versionType")) versionType = 'official' if(!project.hasProperty("versionType")) versionType = 'official'
appName = 'Mindustry' appName = 'Mindustry'
steamworksVersion = '0b86023401880bb5e586bc404bedbaae9b1f1c94' steamworksVersion = '0b86023401880bb5e586bc404bedbaae9b1f1c94'
@@ -52,6 +48,20 @@ allprojects{
return new File(projectDir.parent, '../Mindustry-Debug').exists() && !project.hasProperty("release") && project.hasProperty("args") return new File(projectDir.parent, '../Mindustry-Debug').exists() && !project.hasProperty("release") && project.hasProperty("args")
} }
localArc = {
return !project.hasProperty("release") && !project.hasProperty("noLocalArc") && new File(projectDir.parent, '../Arc').exists()
}
arcModule = { String name ->
if(localArc()){
return project(":Arc:$name")
}else{
//skip to last submodule
if(name.contains(':')) name = name.split(':').last()
return "com.github.Anuken.Arc:$name:${getArcHash()}"
}
}
generateDeployName = { String platform -> generateDeployName = { String platform ->
if(platform == "windows"){ if(platform == "windows"){
platform += "64" platform += "64"
@@ -89,10 +99,6 @@ allprojects{
return project.getProperties()["buildversion"] return project.getProperties()["buildversion"]
} }
getCommitHash = {
return 'git rev-parse --verify --short HEAD'.execute().text.trim()
}
getPackage = { getPackage = {
return project.ext.mainClassName.substring(0, project.ext.mainClassName.indexOf("desktop") - 1) return project.ext.mainClassName.substring(0, project.ext.mainClassName.indexOf("desktop") - 1)
} }
@@ -110,12 +116,12 @@ allprojects{
generateLocales = { generateLocales = {
def output = 'en\n' def output = 'en\n'
def bundles = new File(project(':core').projectDir, 'assets/bundles/') def bundles = new File(project(':core').projectDir, 'assets/bundles/')
bundles.list().sort().each{ name -> bundles.listFiles().each{ other ->
if(name == "bundle.properties") return if(other.name == "bundle.properties") return
output += name.substring("bundle".length() + 1, name.lastIndexOf('.')) + "\n" output += other.name.substring("bundle".length() + 1, other.name.lastIndexOf('.')) + "\n"
} }
new File(project(':core').projectDir, 'assets/locales').text = output new File(project(':core').projectDir, 'assets/locales').text = output
new File(project(':core').projectDir, 'assets/basepartnames').text = new File(project(':core').projectDir, 'assets/baseparts/').list().sort().join("\n") new File(project(':core').projectDir, 'assets/basepartnames').text = new File(project(':core').projectDir, 'assets/baseparts/').list().join("\n")
} }
writeVersion = { writeVersion = {
@@ -137,10 +143,6 @@ allprojects{
props["number"] = versionNumber props["number"] = versionNumber
props["modifier"] = versionModifier props["modifier"] = versionModifier
props["build"] = buildid props["build"] = buildid
props["commitHash"] = "unknown"
if(project.hasProperty("showCommitHash")){
props["commitHash"] = getCommitHash()
}
props.store(pfile.newWriter(), "Autogenerated file. Do not modify.") props.store(pfile.newWriter(), "Autogenerated file. Do not modify.")
} }
@@ -193,7 +195,7 @@ allprojects{
tasks.withType(JavaCompile){ tasks.withType(JavaCompile){
targetCompatibility = 8 targetCompatibility = 8
sourceCompatibility = JavaVersion.VERSION_17 sourceCompatibility = JavaVersion.VERSION_16
options.encoding = "UTF-8" options.encoding = "UTF-8"
options.compilerArgs += ["-Xlint:deprecation"] options.compilerArgs += ["-Xlint:deprecation"]
dependsOn clearCache dependsOn clearCache
@@ -228,7 +230,7 @@ configure(subprojects - project(":annotations")){
tasks.withType(Javadoc){ tasks.withType(Javadoc){
options{ options{
addStringOption('Xdoclint:none', '-quiet') addStringOption('Xdoclint:none', '-quiet')
addStringOption('-release', '17') addStringOption('-release', '16')
encoding('UTF-8') encoding('UTF-8')
} }
} }
@@ -242,7 +244,6 @@ project(":desktop"){
dependencies{ dependencies{
implementation project(":core") implementation project(":core")
implementation arcModule("extensions:discord") implementation arcModule("extensions:discord")
implementation arcModule("natives:natives-filedialogs")
implementation arcModule("natives:natives-desktop") implementation arcModule("natives:natives-desktop")
implementation arcModule("natives:natives-freetype-desktop") implementation arcModule("natives:natives-freetype-desktop")
@@ -251,7 +252,6 @@ project(":desktop"){
implementation "com.github.Anuken:steamworks4j:$steamworksVersion" implementation "com.github.Anuken:steamworks4j:$steamworksVersion"
implementation arcModule("backends:backend-sdl") implementation arcModule("backends:backend-sdl")
annotationProcessor 'com.github.Anuken:jabel:0.9.0'
} }
} }
@@ -262,7 +262,7 @@ project(":core"){
kapt{ kapt{
javacOptions{ javacOptions{
option("-source", "17") option("-source", "16")
option("-target", "1.8") option("-target", "1.8")
} }
} }
@@ -309,7 +309,7 @@ project(":core"){
task assetsJar(type: Jar, dependsOn: ":tools:pack"){ task assetsJar(type: Jar, dependsOn: ":tools:pack"){
archiveClassifier = 'assets' archiveClassifier = 'assets'
from files("assets"){ from files("assets"){
exclude "config", "cache", "music", "sounds", "sprites/fallback" exclude "config", "cache", "music", "sounds"
} }
} }
@@ -320,6 +320,11 @@ project(":core"){
} }
} }
artifacts{
archives sourcesJar
archives assetsJar
}
dependencies{ dependencies{
compileJava.dependsOn(preGen) compileJava.dependsOn(preGen)
@@ -330,14 +335,13 @@ project(":core"){
api arcModule("extensions:g3d") api arcModule("extensions:g3d")
api arcModule("extensions:fx") api arcModule("extensions:fx")
api arcModule("extensions:arcnet") api arcModule("extensions:arcnet")
implementation arcModule("extensions:filedialogs")
api "com.github.Anuken:rhino:$rhinoVersion" api "com.github.Anuken:rhino:$rhinoVersion"
if(localArc && debugged()) api arcModule("extensions:recorder") if(localArc() && debugged()) api arcModule("extensions:recorder")
if(localArc) api arcModule(":extensions:packer") if(localArc()) api arcModule(":extensions:packer")
annotationProcessor 'com.github.Anuken:jabel:0.9.0' annotationProcessor 'com.github.Anuken:jabel:0.9.0'
compileOnly project(":annotations") compileOnly project(":annotations")
if(!project.hasProperty("noKapt")) kapt project(":annotations") kapt project(":annotations")
} }
afterEvaluate{ afterEvaluate{
@@ -366,6 +370,7 @@ project(":core"){
//these are completely unnecessary //these are completely unnecessary
tasks.kaptGenerateStubsKotlin.onlyIf{ false } tasks.kaptGenerateStubsKotlin.onlyIf{ false }
tasks.compileKotlin.onlyIf{ false } tasks.compileKotlin.onlyIf{ false }
tasks.inspectClassesForKotlinIC.onlyIf{ false }
} }
//comp** classes are only used for code generation //comp** classes are only used for code generation
@@ -380,7 +385,6 @@ project(":server"){
dependencies{ dependencies{
implementation project(":core") implementation project(":core")
implementation arcModule("backends:backend-headless") implementation arcModule("backends:backend-headless")
annotationProcessor 'com.github.Anuken:jabel:0.9.0'
} }
} }
@@ -392,7 +396,6 @@ project(":tests"){
testImplementation "org.junit.jupiter:junit-jupiter-params:5.7.1" testImplementation "org.junit.jupiter:junit-jupiter-params:5.7.1"
testImplementation "org.junit.jupiter:junit-jupiter-api:5.7.1" testImplementation "org.junit.jupiter:junit-jupiter-api:5.7.1"
testImplementation arcModule("backends:backend-headless") testImplementation arcModule("backends:backend-headless")
testImplementation "org.json:json:20230618"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.7.1" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.7.1"
} }
@@ -417,9 +420,6 @@ project(":tools"){
implementation arcModule("natives:natives-desktop") implementation arcModule("natives:natives-desktop")
implementation arcModule("natives:natives-freetype-desktop") implementation arcModule("natives:natives-freetype-desktop")
implementation arcModule("backends:backend-headless") implementation arcModule("backends:backend-headless")
implementation("com.google.guava:guava:33.3.1-jre")
annotationProcessor 'com.github.Anuken:jabel:0.9.0'
} }
} }
@@ -428,7 +428,7 @@ project(":annotations"){
dependencies{ dependencies{
implementation 'com.squareup:javapoet:1.12.1' implementation 'com.squareup:javapoet:1.12.1'
implementation arcModule("arc-core") implementation "com.github.Anuken.Arc:arc-core:$arcHash"
} }
} }
@@ -442,9 +442,6 @@ configure([":core", ":server"].collect{project(it)}){
publications{ publications{
maven(MavenPublication){ maven(MavenPublication){
from components.java from components.java
if(project.name == "core"){
artifact(tasks.named("assetsJar"))
}
} }
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 360 B

After

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 496 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 405 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 424 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 784 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 981 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 429 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 464 B

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

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