Compare commits

..
13 Commits
Author SHA1 Message Date
Anuken 63b92e6dd6 Better caching 2024-01-28 12:54:16 -05:00
Anuken 3be67aa52a No main executor call 2024-01-28 11:31:03 -05:00
Anuken a73b783d98 SRV support 2024-01-27 17:46:52 -05:00
Anuken db4c861dde AAAA record support 2024-01-27 12:50:26 -05:00
Anuken c7a35ae789 IPv6 address support 2024-01-27 11:15:58 -05:00
Anuken 3e5ad07e8c Merge branch 'master' of https://github.com/Anuken/Mindustry into async-ping
 Conflicts:
	core/src/mindustry/net/ArcNetProvider.java
	gradle.properties
2024-01-27 10:11:09 -05:00
Anuken 584b22300d a few comments 2023-05-09 16:45:46 -04:00
Anuken 6eb049c419 Merge branch 'master' of https://github.com/Anuken/Mindustry into async-ping 2023-05-09 16:45:42 -04:00
Anuken 6be497878c cleanup 2023-02-26 15:44:51 -05:00
Anuken 8e9b409b63 cleanup 2023-02-26 15:41:49 -05:00
Anuken b496e8457c it works minus SRV 2023-02-26 15:32:35 -05:00
Anuken 41b87b9345 static 2023-02-26 12:06:06 -05:00
Anuken 84e52bdee3 async UDP ping 2023-02-26 11:40:53 -05:00
1904 changed files with 34853 additions and 113514 deletions
+5 -11
View File
@@ -1,5 +1,5 @@
name: Bug report
description: An issue with the game.
description: The type of device you were playing on
labels: ["bug"]
body:
@@ -43,17 +43,17 @@ body:
id: mods
attributes:
label: Mods used
description: The mod repositories or zip files that are related to the issue, if applicable. If you are positive that you have none enabled, write 'none'.
description: The mod repositories or zip files that are related to the issue, if applicable.
validations:
required: true
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 IN-GAME ISSUE. IF YOU DO NOT HAVE A SAVE, DO NOT WASTE TIME OPENING THIS ISSUE.
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: true
required: false
- type: textarea
id: logs
attributes:
@@ -72,9 +72,3 @@ body:
required: true
- label: I have searched the closed and open issues to make sure that this problem has not already been reported.
required: true
- label: "I am not using Foo's Client."
required: true
- label: "This issue occurs with no mods enabled."
required: true
- label: "I have attached a valid save file."
required: true
+3 -3
View File
@@ -1,5 +1,5 @@
If your pull request is **not** translation-related, read the list of requirements below and check each box:
If your pull request is **not** translation or serverlist-related, read the list of requirements below and check each box:
- [ ] I have read the [contribution guidelines](https://github.com/Anuken/Mindustry/blob/master/CONTRIBUTING.md).
- [ ] I have ensured that any new features or fixes in this PR function correctly in-game, if applicable.
- [ ] I affirm that this code follows Mindustry's [AI policy](https://github.com/Anuken/Mindustry/blob/master/CONTRIBUTING.md#ai-policy).
- [ ] I have ensured that my code compiles, if applicable.
- [ ] I have ensured that any new features in this PR function correctly in-game, if applicable.
+24 -21
View File
@@ -14,11 +14,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v2
- name: Set up JDK 17
uses: actions/setup-java@v5
uses: actions/setup-java@v1
with:
distribution: 'temurin'
java-version: 17
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
@@ -31,9 +30,28 @@ jobs:
git tag ${RELEASE_VERSION}
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/Arc ${RELEASE_VERSION};
cd ../Mindustry
- name: Update JITpack repo
run: |
cd ../
cp -r ./Mindustry ./MindustryJitpack
cd MindustryJitpack
git config --global user.name "Github Actions"
git config --global user.email "actions@github.com"
git clone --depth 1 https://github.com/Anuken/MindustryJitpack.git
rm -rf .git
cp -r ./MindustryJitpack/.git ./.git
rm -rf MindustryJitpack
rm -rf .github
rm README.md
git add .
git commit --allow-empty -m "Updating"
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryJitpack
git tag ${RELEASE_VERSION}
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryJitpack
cd ../Mindustry
- name: Create artifacts
run: |
./gradlew desktop:dist server:dist core:depsJar core:assetsJar core:mergedJavadoc -Pbuildversion=${RELEASE_VERSION:1}
./gradlew desktop:dist server:dist core:mergedJavadoc -Pbuildversion=${RELEASE_VERSION:1}
- name: Update docs
run: |
cd ../
@@ -47,7 +65,7 @@ jobs:
cd docs
git add .
git commit --allow-empty -m "Update ${RELEASE_VERSION:1}"
git push https://Anuken:${{ secrets.API_TOKEN_ORG_GITHUB }}@github.com/MindustryGame/docs
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/MindustryGame/docs
cd ../Mindustry
- name: Update F-Droid build string
run: |
@@ -55,7 +73,7 @@ jobs:
cd ../MindustryBuilds
echo "Updating version to ${RELEASE_VERSION:1}"
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 commit -m "Updating to build ${RELEASE_VERSION:1}"
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryBuilds
@@ -73,18 +91,3 @@ jobs:
file: server/build/libs/server-release.jar
tag: ${{ github.ref }}
- name: Upload dependency JAR
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: core/build/libs/core-release-deps.jar
asset_name: dependencies.jar
tag: ${{ github.ref }}
- name: Upload assets JAR
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: core/build/libs/core-release-assets.jar
asset_name: assets.jar
tag: ${{ github.ref }}
@@ -1,10 +0,0 @@
name: "Validate Gradle Wrapper"
on: [push, pull_request]
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: gradle/actions/wrapper-validation@v3
+6 -10
View File
@@ -10,23 +10,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v2
- name: Set up JDK 17
uses: actions/setup-java@v5
uses: actions/setup-java@v1
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
uses: gradle/gradle-build-action@v2
- name: Run unit tests
run: ./gradlew tests:test --stacktrace --rerun
run: ./gradlew clean cleanTest test --stacktrace
- name: Run unit tests and build JAR
run: ./gradlew desktop:dist
- name: Upload desktop JAR for testing
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v2
with:
name: Desktop JAR
archive: false
name: Desktop JAR (zipped)
path: desktop/build/libs/Mindustry.jar
+21 -20
View File
@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v2
- name: Trigger BE build
if: ${{ github.repository == 'Anuken/Mindustry' }}
run: |
@@ -22,38 +22,39 @@ jobs:
git config --global user.name "Github Actions"
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryBuilds ${BNUM}
- name: Set up JDK 17
uses: actions/setup-java@v5
uses: actions/setup-java@v1
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
uses: gradle/gradle-build-action@v2
- name: Update bundles
if: ${{ github.repository == 'Anuken/Mindustry' }}
run: |
./gradlew -DcompileMethodParams updateBundles
./gradlew updateBundles
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 commit -m "Automatic bundle update"
git push
fi
- name: Update scripts, class mappings and map names
- name: Update JITpack repo
if: ${{ github.repository == 'Anuken/Mindustry' }}
run: |
./gradlew -DcompileMethodParams updateScripts fixMaps
if [ -n "$(git status --porcelain)" ]; then
git config --global user.name "Github Actions"
git config --global user.email "actions@github.com"
git add core/assets/scripts/* core/src/mindustry/mod/ClassMap.java
git add core/assets/maps/*
git commit -m "Automatic class mapping and map name update"
git push
fi
git config --global user.name "Github Actions"
git config --global user.email "cli@github.com"
cd ../
cp -r ./Mindustry ./MindustryJitpack
cd MindustryJitpack
git clone --depth 1 https://github.com/Anuken/MindustryJitpack.git
rm -rf .git
cp -r ./MindustryJitpack/.git ./.git
rm -rf MindustryJitpack
rm -rf .github
rm README.md
git add .
git commit --allow-empty -m "Updating"
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryJitpack
cd ../Mindustry
- name: Run unit tests
run: ./gradlew tests:test --rerun --stacktrace
run: ./gradlew clean cleanTest test --stacktrace
-4
View File
@@ -23,7 +23,6 @@ ios/libs/
/tools/build/
/tests/build/
/server/build/
ios/libs/
changelog
saves/
/core/assets-raw/fontgen/out/
@@ -168,6 +167,3 @@ android/libs/
# ignored due to frequent branch conflicts.
core/assets/logicids.dat
# project files for the sectors
core/assets-raw/sprites/ui/sectors/*.json
+11 -27
View File
@@ -4,33 +4,20 @@ This is for code contributions. For translations, see [TRANSLATING](TRANSLATING.
## Basic Guidelines
### Use an IDE.
Specifically, IntelliJ IDEA. Download the (free) Community Edition of it [here](https://www.jetbrains.com/idea/download/). Some people use other tools, like VS Code, but I would personally not recommend them for Java development.
### Always test your changes.
Do not submit something without at least running the game to see if it compiles.
If you are submitting a new block, make sure it has a name and description, and that it works correctly in-game. If you are changing existing block mechanics, test them out first.
### 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.
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.
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.
## AI Policy
Mindustry's AI contribution policies are similar to that of [the Rust language](https://forge.rust-lang.org/policies/llm-usage.html), which are summarized as follows:
- ✔️ You **are** allowed use AI to review your own code, summarize, inform, and answer questions.
- ✔️ You **are** allowed to use AI to translate your *human-written* messages into English, if necessary.
- ❌ You are **not** allowed to submit PRs that are generated by AI.
- ❌ You are **not** allowed to use AI to *generate* any text posted (PRs, issues, commit messages, etc.)
- ⚠️ If you post *anything* that is based on AI output (potential solution to a bug, crash diagnosis...), it must be clearly marked as being sourced from AI.
Repeated (or particularly egregious) violations of these rules will get you banned from this repository.
## Style Guidelines
### Follow the formatting guidelines.
@@ -39,7 +26,7 @@ This means:
- Same-line braces.
- 4 spaces indentation
- `camelCase`, **even for constants or enums**. Why? Because `SCREAMING_CASE` is ugly, annoying to type and does not achieve anything useful. Constants are *less* dangerous than variables, not more. Any reasonable IDE should highlight them for you anyway.
- No underscores for anything.
- No underscores for anything. (Yes, I know `Bindings` violates this principle, but that's for legacy reasons and really should be cleaned up some day)
- Do not use braceless `if/else` statements. `if(x) statement else statement2` should **never** be done. In very specific situations, having braceless if-statements on one line is allowed: `if(cond) return;` would be valid.
- Prefer single-line javadoc `/** @return for example */` instead of multiline javadoc whenever possible
- Short method/variable names (multipleLongWords should be avoided if it's possible to do so reasonably, especially for variables)
@@ -47,7 +34,7 @@ This means:
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.
If you need to use functional interfaces, use the ones in `arc.func`, which are more or less the same with different naming schemes.
@@ -72,22 +59,19 @@ What you'll usually need to change:
### Avoid boxed types (Integer, Boolean)
Never create variables or collections with boxed types `Seq<Integer>` or `ObjectMap<Integer, ...>`. Use the collections specialized for this task, e.g. `IntSeq` and `IntMap`.
### Do not allocate anything if possible.
Never allocate `new` objects in the main loop. If you absolutely require new objects, use `Pools` to obtain and free object instances.
Otherwise, use the `Tmp` variables for things like vector/shape operations, or create `static` variables for re-use.
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.
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.
### Do not create methods unless necessary.
Unless a block of code is very large or used in more than 1-2 places, don't split it up into a separate method. Making unnecessary methods only creates confusion, and may slightly decrease performance.
## Other Notes
- If you would like your name to appear in the game's credits, add it to the [list of contributors](https://github.com/Anuken/Mindustry/blob/master/core/assets/contributors) as part of your PR.
- Using an IDE is highly recommended, but not required.
- I use IntelliJ; download it [here](https://www.jetbrains.com/idea/download/).
- Some people use other tools, like VS Code, but I haven't used them for Java myself, and cannot guarantee that they will work well for this codebase.
If you would like your name to appear in the game's credits, add it to the [list of contributors](https://github.com/Anuken/Mindustry/blob/master/core/assets/contributors) as part of your PR.
+2 -14
View File
@@ -11,16 +11,14 @@ _[Javadoc](https://mindustrygame.github.io/docs/)_
## Contributing
See [CONTRIBUTING](CONTRIBUTING.md) for general code style and PR guidelines.
If you are a first-time contributor looking for features to implement or bugs to fix, see the issues tagged with 'candidate' [in the Mindustry-Suggestions repostiory](https://github.com/Anuken/Mindustry-Suggestions/issues?q=is%3Aissue%20state%3Aopen%20label%3Acandidate).
See [CONTRIBUTING](CONTRIBUTING.md).
## Building
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.
First, make sure you have [JDK 17](https://adoptium.net/temurin/releases/?os=any&arch=any&version=17) 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
@@ -55,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.*
#### 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>
+30
View File
@@ -0,0 +1,30 @@
### Adding a server to the list
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.
You may want to add your server to this list. The steps for getting this done are as follows:
0. **Take note of the fact that modded servers are not allowed on this list.** Such servers confuse users, and there's currently no easy way to fix mod incompatibilities after a failed connection.
1. **Ensure your server is properly moderated.** For the most part, this applies to survival servers, but PvP servers can be affected as well.
You'll need to either hire some moderators, or make use of (currently non-existent) anti-grief and anti-curse plugins.
*Consider enabling a rate limit:* `config messageRateLimit 2` will make it so that players can only send messages every 2 seconds, for example.
2. Make sure that your server is able to handle inappropriate content - this includes NSFW display/sorter art and abusive messages. **Servers that allow such content will be removed immediately.** Consider banning display blocks if it is a problem for your server: `rules add bannedBlocks ["canvas", "logic-display", "large-logic-display"]`.
3. **Set an appropriate MOTD, name and description.** This is set with `config <name/desc/motd> <value>`. "Appropriate" means that:
- Your name or description must reflect the type of server you're hosting.
Since new players may be exposed to the server list early on, put in a phrase like "Co-op survival" or "PvP" so players know what they're getting into. Yes, this is also displayed in the server mode info text, but having extra info in the name doesn't hurt.
- Make sure players know where to refer to for server support. It should be fairly clear that the server owner is not me, but you.
- Try to be professional in your text; use common sense.
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`).
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 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
{
"address": "example.com:6000"
}
```
> 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.
+7 -27
View File
@@ -1,5 +1,6 @@
<?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:name="android.hardware.type.pc" android:required="false" />
@@ -8,25 +9,23 @@
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:enableOnBackInvokedCallback="false"
android:resizeableActivity="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:isGame="true"
android:theme="@style/ArcTheme"
android:usesCleartextTraffic="true"
android:appCategory="game"
android:label="@string/app_name"
android:fullBackupContent="@xml/backup_rules"
android:largeHeap="true">
android:fullBackupContent="@xml/backup_rules">
<meta-data android:name="android.max_aspect" android:value="2.1"/>
<activity
android:name="mindustry.android.AndroidLauncher"
android:label="@string/app_name"
android:screenOrientation="user"
android:exported="true"
android:launchMode="singleTask"
android:configChanges="uiMode|keyboard|keyboardHidden|orientation|screenSize|screenLayout">
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -36,29 +35,10 @@
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="mindustry"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content" />
<category android:name="android.intent.category.BROWSABLE"/>
<data android:mimeType="application/octet-stream" />
<data android:mimeType="application/x-msav" />
<data android:scheme="file" android:host="*" android:pathPattern=".*\\.msav"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:host="*" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.msav" />
</intent-filter>
</activity>
</application>
+24 -34
View File
@@ -3,10 +3,11 @@ buildscript{
mavenLocal()
mavenCentral()
google()
maven{ url "https://oss.sonatype.org/content/repositories/snapshots/" }
}
dependencies{
classpath 'com.android.tools.build:gradle:8.2.2'
classpath 'com.android.tools.build:gradle:7.2.1'
}
}
@@ -16,7 +17,7 @@ configurations{ natives }
repositories{
mavenCentral()
maven{ url = "https://maven.google.com" }
maven{ url "https://maven.google.com" }
}
task deploy(type: Copy){
@@ -28,9 +29,8 @@ task deploy(type: Copy){
}
android{
namespace = "io.anuke.mindustry"
buildToolsVersion = '36.0.0'
compileSdk = 36
buildToolsVersion '33.0.2'
compileSdkVersion 33
sourceSets{
main{
manifest.srcFile 'AndroidManifest.xml'
@@ -39,12 +39,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 = ['libs']
}
androidTest.setRoot('tests')
@@ -60,9 +55,9 @@ android{
def versionNameResult = "$versionNumber-$versionType-${getBuildVersion().replace(" ", "-")}"
applicationId "io.anuke.mindustry"
minSdkVersion 21
targetSdkVersion 36
minSdkVersion 14
targetSdkVersion 33
versionName versionNameResult
versionCode = vcode
@@ -70,16 +65,14 @@ android{
props['androidBuildCode'] = (vcode + 1).toString()
}
props.store(file('../core/assets/version.properties').newWriter(), null)
multiDexEnabled = true
}
compileOptions{
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
flavorDimensions = ["google"]
flavorDimensions "google"
signingConfigs{
release{
@@ -114,7 +107,7 @@ android{
if(project.hasProperty("RELEASE_STORE_FILE") || System.getenv("CI") == "true"){
buildTypes{
release{
signingConfig = signingConfigs.release
signingConfig signingConfigs.release
}
}
}
@@ -126,15 +119,14 @@ 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"
}
natives "com.github.Anuken.Arc:natives-android:$arcHash"
natives "com.github.Anuken.Arc:natives-freetype-android:$arcHash"
def version, highestVersion
def version;
def highestVersion;
new File((String)findSdkDir(), "/platforms").eachFileMatch ~/android-\d+/, {
version = it.name.find(/\d+/).toInteger()
highestVersion = version > highestVersion ? version : highestVersion
version = it.name.find(/\d+/).toInteger();
highestVersion = version > highestVersion ? version : highestVersion;
}
def sdkFile = new File((String)findSdkDir(), "/platforms/android-${highestVersion}/android.jar")
@@ -142,13 +134,11 @@ dependencies{
}
task copyAndroidNatives(){
if(!localArc){
configurations.natives.files.each{ jar ->
copy{
from zipTree(jar)
into file("libs/")
include "**"
}
configurations.natives.files.each{ jar ->
copy{
from zipTree(jar)
into file("libs/")
include "**"
}
}
}
+2 -4
View File
@@ -1,13 +1,11 @@
-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 arc.** { *; }
-keep class net.jpountz.** { *; }
-keep class rhino.** { *; }
-keep class com.android.dex.** { *; }
-keep class com.android.dx.** { *; }
-keepattributes Signature,*Annotation*,InnerClasses,EnclosingMethod
-dontwarn javax.naming.**
#-printusage out.txt
+128 -160
View File
@@ -5,21 +5,20 @@ import android.app.*;
import android.content.*;
import android.content.pm.*;
import android.net.*;
import android.os.*;
import android.os.Build.*;
import android.os.*;
import android.telephony.*;
import arc.*;
import arc.backend.android.*;
import arc.files.*;
import arc.func.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import dalvik.system.*;
import mindustry.*;
import mindustry.game.EventType.*;
import mindustry.game.Saves.*;
import mindustry.io.*;
import mindustry.net.*;
import mindustry.ui.*;
import mindustry.ui.FileChooser.*;
import mindustry.ui.dialogs.*;
import java.io.*;
@@ -31,7 +30,7 @@ import static mindustry.Vars.*;
public class AndroidLauncher extends AndroidApplication{
public static final int PERMISSION_REQUEST_CODE = 1;
boolean doubleScaleTablets = true;
FileChooserDialog chooser;
FileChooser chooser;
Runnable permCallback;
@Override
@@ -39,7 +38,7 @@ public class AndroidLauncher extends AndroidApplication{
UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler((thread, error) -> {
CrashHandler.log(error);
CrashSender.log(error);
//try to forward exception to system handler
if(handler != null){
@@ -73,137 +72,99 @@ public class AndroidLauncher extends AndroidApplication{
@Override
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
try{
jar.file().setReadOnly();
return new DexClassLoader(jar.file().getPath(), getFilesDir().getPath(), null, parent){
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
//check for loaded state
Class<?> loadedClass = findLoadedClass(name);
if(loadedClass == null){
try{
//try to load own class first
loadedClass = findClass(name);
}catch(ClassNotFoundException | NoClassDefFoundError e){
//use parent if not found
return parent.loadClass(name);
}
return new DexClassLoader(jar.file().getPath(), getFilesDir().getPath(), null, parent){
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
//check for loaded state
Class<?> loadedClass = findLoadedClass(name);
if(loadedClass == null){
try{
//try to load own class first
loadedClass = findClass(name);
}catch(ClassNotFoundException | NoClassDefFoundError e){
//use parent if not found
return parent.loadClass(name);
}
if(resolve){
resolveClass(loadedClass);
}
return loadedClass;
}
};
}catch(SecurityException e){
//`setReadOnly` to jar file in `/sdcard/Android/data/...` does not work on some Android 14 devices, but in `/data/...`, it does
if(Build.VERSION.SDK_INT < VERSION_CODES.O_MR1){
throw e;
if(resolve){
resolveClass(loadedClass);
}
return loadedClass;
}
};
}
Fi cacheDir = new Fi(getCacheDir()).child("mods");
cacheDir.mkdirs();
@Override
public void showFileChooser(boolean open, String title, String extension, Cons<Fi> cons){
showFileChooser(open, title, cons, extension);
}
//long file name support
Fi modCacheDir = cacheDir.child(jar.nameWithoutExtension());
Fi modCache = modCacheDir.child(Long.toHexString(jar.lastModified()) + ".zip");
void showFileChooser(boolean open, String title, Cons<Fi> cons, String... extensions){
String extension = extensions[0];
if(modCacheDir.equals(jar.parent())){
//should not reach here, just in case
throw e;
if(VERSION.SDK_INT >= VERSION_CODES.Q){
Intent intent = new Intent(open ? Intent.ACTION_OPEN_DOCUMENT : Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(extension.equals("zip") && !open && extensions.length == 1 ? "application/zip" : "*/*");
addResultListener(i -> startActivityForResult(intent, i), (code, in) -> {
if(code == Activity.RESULT_OK && in != null && in.getData() != null){
Uri uri = in.getData();
if(uri.getPath().contains("(invalid)")) return;
Core.app.post(() -> Core.app.post(() -> cons.get(new Fi(uri.getPath()){
@Override
public InputStream read(){
try{
return getContentResolver().openInputStream(uri);
}catch(IOException e){
throw new ArcRuntimeException(e);
}
}
@Override
public OutputStream write(boolean append){
try{
return getContentResolver().openOutputStream(uri);
}catch(IOException e){
throw new ArcRuntimeException(e);
}
}
})));
}
});
}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)){
chooser = new FileChooser(title, file -> Structs.contains(extensions, file.extension().toLowerCase()), open, file -> {
if(!open){
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);
}
//Cache will be deleted when mod is removed
if(!modCache.exists() || jar.length() != modCache.length()){
modCacheDir.mkdirs();
jar.copyTo(modCache);
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);
}
modCache.file().setReadOnly();
return loadJar(modCache, parent);
}
}
@Override
public void showFileChooser(FileChooserParams params){
try{
String extension = params.extensions[0];
if(VERSION.SDK_INT >= VERSION_CODES.Q){
Intent intent = new Intent(params.open ? Intent.ACTION_OPEN_DOCUMENT : Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(extension.equals("zip") && !params.open && params.extensions.length == 1 ? "application/zip" : "*/*");
intent.putExtra(Intent.EXTRA_TITLE, params.fileName);
if(params.allowMultiple){
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
}
addResultListener(i -> startActivityForResult(intent, i), (code, in) -> {
if(code == Activity.RESULT_OK && in != null && in.getData() != null){
Uri[] uris;
if(in.getClipData() != null){
uris = new Uri[in.getClipData().getItemCount()];
for(int i = 0; i < uris.length; i++){
uris[i] = in.getClipData().getItemAt(i).getUri();
}
}else{
uris = new Uri[]{in.getData()};
}
if(uris.length == 0 || uris[0].getPath().contains("(invalid)")) return;
Fi[] files = Seq.with(uris).map(uri -> new Fi(uri.getPath()){
@Override
public InputStream read(){
try{
return getContentResolver().openInputStream(uri);
}catch(IOException e){
throw new ArcRuntimeException(e);
}
}
@Override
public OutputStream write(boolean append){
try{
return getContentResolver().openOutputStream(uri, "rwt");
}catch(IOException e){
throw new ArcRuntimeException(e);
}
}
@Override
public Writer writer(boolean append, String charset){
try{
return new OutputStreamWriter(write(append), charset);
}catch(IOException ex){
throw new ArcRuntimeException(ex);
}
}
}).toArray(Fi.class);
Core.app.post(() -> Core.app.post(() -> params.handleChooseResult(files)));
}
});
}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)){
chooser = FileChooser.createFallbackFileChooser(params);
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{
FileChooser.showFallbackFileChooser(params);
}
}catch(Throwable error){
Core.app.post(() -> Vars.ui.showException(error));
}
public void showMultiFileChooser(Cons<Fi> cons, String... extensions){
showFileChooser(true, "@open", cons, extensions);
}
@Override
@@ -219,11 +180,8 @@ public class AndroidLauncher extends AndroidApplication{
}, new AndroidApplicationConfiguration(){{
useImmersiveMode = true;
hideStatusBar = true;
useGL30 = true;
}});
var intent = getIntent();
Events.on(ClientLoadEvent.class, u -> handleIntent(intent));
checkFiles(getIntent());
try{
//new external folder
@@ -240,6 +198,7 @@ public class AndroidLauncher extends AndroidApplication{
Log.err("Failed to delete cached folder", t);
}
//move to internal storage if there's no file indicating that it moved
if(!Core.files.local("files_moved").exists()){
Log.info("Moving files to external storage...");
@@ -281,45 +240,54 @@ public class AndroidLauncher extends AndroidApplication{
}
}
@Override
protected void onNewIntent(Intent intent){
super.onNewIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent){
if(intent == null) return;
private void checkFiles(Intent intent){
try{
Uri uri = intent.getData();
if(uri != null){
File myFile = null;
String scheme = uri.getScheme();
//clear data (not sure if necessary?)
intent.setAction(Intent.ACTION_MAIN);
intent.setData(null);
setIntent(intent);
if("mindustry".equalsIgnoreCase(scheme)){ //open a server URL
String host = uri.getHost();
int port = uri.getPort();
if(host != null && !host.isEmpty()){
if(scheme.equals("file")){
String fileName = uri.getEncodedPath();
myFile = new File(fileName);
}else if(!scheme.equals("content")){
//error
return;
}
boolean save = uri.getPath().endsWith(saveExtension);
boolean map = uri.getPath().endsWith(mapExtension);
InputStream inStream;
if(myFile != null) inStream = new FileInputStream(myFile);
else inStream = getContentResolver().openInputStream(uri);
Core.app.post(() -> Core.app.post(() -> {
if(save){ //open save
System.out.println("Opening save.");
Fi file = Core.files.local("temp-save." + saveExtension);
file.write(inStream, false);
if(SaveIO.isSaveValid(file)){
try{
SaveSlot slot = control.saves.importSave(file);
ui.load.runLoadSave(slot);
}catch(IOException e){
ui.showException("@save.import.fail", e);
}
}else{
ui.showErrorMessage("@save.import.invalid");
}
}else if(map){ //open map
Fi file = Core.files.local("temp-map." + mapExtension);
file.write(inStream, false);
Core.app.post(() -> {
ui.showConfirm(Core.bundle.format("servers.connect.confirm", host), () -> ui.join.connect(host, port != -1 ? port : 6567));
System.out.println("Opening map.");
if(!ui.editor.isShown()){
ui.editor.show();
}
ui.editor.beginEditMap(file);
});
}
}else{ //open a save file
Fi file = Core.files.cache("imported");
file.write(getContentResolver().openInputStream(uri), false);
ClientLauncher.handleFileImport(file);
}
}));
}
}catch(Throwable e){
Log.err(e);
}catch(IOException e){
e.printStackTrace();
}
}
@@ -2,7 +2,9 @@ package mindustry.android;
import android.annotation.*;
import android.os.*;
import android.os.Build.*;
import arc.*;
import arc.backend.android.*;
import com.android.dex.*;
import com.android.dx.cf.direct.*;
import com.android.dx.command.dexer.*;
@@ -11,6 +13,7 @@ import com.android.dx.dex.cf.*;
import com.android.dx.dex.file.DexFile;
import com.android.dx.merge.*;
import dalvik.system.*;
import mindustry.mod.*;
import rhino.*;
import java.io.*;
@@ -156,9 +159,8 @@ public class AndroidRhinoContext{
}catch(IOException e){
e.printStackTrace();
}
var context = (android.content.Context)Core.app;
return new DexClassLoader(dexFile.getPath(), context.getCodeCacheDir().getPath(), null, getParent()).loadClass(name);
android.content.Context context = (android.content.Context)((AndroidApplication)Core.app);
return new DexClassLoader(dexFile.getPath(), VERSION.SDK_INT >= 21 ? context.getCodeCacheDir().getPath() : context.getCacheDir().getAbsolutePath(), null, getParent()).loadClass(name);
}
@Override
@@ -35,10 +35,6 @@ public class Annotations{
@Retention(RetentionPolicy.SOURCE)
public @interface NoSync{}
/** Indicates that a field should be synced, but not serialized */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface NoSerialize{}
/** Indicates that a component field is imported from other components. This means it doesn't actually exist. */
@Target({ElementType.FIELD})
@@ -58,8 +54,6 @@ public class Annotations{
/** 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. */
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. */
@@ -85,11 +79,9 @@ public class Annotations{
@Retention(RetentionPolicy.SOURCE)
public @interface GroupDef{
Class[] value();
Class[] exclude() default {};
boolean collide() default false;
boolean spatial() default false;
boolean mapping() default false;
boolean update() default false;
}
/** Indicates an entity definition. */
@@ -109,8 +101,6 @@ public class Annotations{
boolean genio() default true;
/** Whether I made a massive mistake by merging two different class branches */
boolean legacy() default false;
/** Groups to exclude. */
String[] excludeGroups() default {};
}
/** Indicates an internal interface for entity components. */
@@ -192,16 +182,18 @@ public class Annotations{
/** A set of two booleans, one specifying server and one specifying client. */
public enum Loc{
/** Server only. */
/** Method can only be invoked on the client from the server. */
server(true, false),
/** Client only. */
/** Method can only be invoked on the server from the client. */
client(false, true),
/** Both server and client. */
/** Method can be invoked from anywhere */
both(true, true),
/** Neither server nor client. */
none(false, false);
/** If true, this method can be invoked ON clients FROM servers. */
public final boolean isServer;
/** If true, this method can be invoked ON servers FROM clients. */
public final boolean isClient;
Loc(boolean server, boolean client){
@@ -230,16 +222,16 @@ public class Annotations{
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
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;
/** 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;
/** 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;
/** 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;
/**
@@ -211,7 +211,10 @@ public abstract class BaseProcessor extends AbstractProcessor{
if(round++ >= rounds) return false; //only process 1 round
if(rootDirectory == null){
try{
rootDirectory = new Fi(new File(filer.getResource(StandardLocation.CLASS_OUTPUT, "no", "no").toUri())).parent().parent().parent().parent().parent().parent().parent().parent();
String path = Fi.get(filer.getResource(StandardLocation.CLASS_OUTPUT, "no", "no")
.toUri().toURL().toString().substring(OS.isWindows ? 6 : "file:".length()))
.parent().parent().parent().parent().parent().parent().parent().toString().replace("%20", " ");
rootDirectory = Fi.get(path).parent();
}catch(IOException e){
throw new RuntimeException(e);
}
@@ -229,7 +232,7 @@ public abstract class BaseProcessor extends AbstractProcessor{
@Override
public SourceVersion getSupportedSourceVersion(){
return SourceVersion.RELEASE_17;
return SourceVersion.RELEASE_8;
}
public void process(RoundEnvironment env) throws Exception{
@@ -13,8 +13,6 @@ import mindustry.annotations.util.TypeIOResolver.*;
import javax.lang.model.element.*;
import java.lang.annotation.*;
import static mindustry.annotations.BaseProcessor.instanceOf;
public class EntityIO{
@@ -58,8 +56,7 @@ public class EntityIO{
Seq<FieldSpec> fields = typeFields.select(spec ->
!spec.hasModifier(Modifier.TRANSIENT) &&
!spec.hasModifier(Modifier.STATIC) &&
!spec.hasModifier(Modifier.FINAL)
/* &&
!spec.hasModifier(Modifier.FINAL)/* &&
(spec.type.isPrimitive() || serializer.has(spec.type.toString()))*/);
//sort to keep order
@@ -80,7 +77,7 @@ public class EntityIO{
}
}
void write(MethodSpec.Builder method, boolean write, Seq<Svar> allFields) throws Exception{
void write(MethodSpec.Builder method, boolean write) throws Exception{
this.method = method;
this.write = write;
@@ -92,7 +89,6 @@ public class EntityIO{
st("write.s($L)", revisions.peek().version);
//write uses most recent revision
for(RevisionField field : revisions.peek().fields){
if(fieldHasAnno(field, allFields, NoSerialize.class)) continue;
io(field.type, "this." + field.name, false);
}
}else{
@@ -110,7 +106,6 @@ public class EntityIO{
//add code for reading revision
for(RevisionField field : rev.fields){
if(fieldHasAnno(field, allFields, NoSerialize.class)) continue;
//if the field doesn't exist, the result will be an empty string, it won't get assigned
io(field.type, presentFields.contains(field.name) ? "this." + field.name + " = " : "", false);
}
@@ -123,11 +118,6 @@ public class EntityIO{
}
}
boolean fieldHasAnno(RevisionField field, Seq<Svar> allFields, Class<? extends Annotation> type){
Svar var = allFields.find(s -> s.name().equals(field.name));
return var != null && var.has(type);
}
void writeSync(MethodSpec.Builder method, boolean write, Seq<Svar> allFields) throws Exception{
this.method = method;
this.write = write;
@@ -135,7 +125,8 @@ public class EntityIO{
if(write){
//write uses most recent revision
for(RevisionField field : revisions.peek().fields){
if(fieldHasAnno(field, allFields, NoSync.class)) continue;
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);
}
@@ -238,7 +229,7 @@ public class EntityIO{
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")){
if(write){
s("s", field + " == null ? -1 : " + field + ".id");
s("s", field + ".id");
}else{
st(field + "mindustry.Vars.content.getByID(mindustry.ctype.ContentType.$L, read.s())", BaseProcessor.simpleName(type).toLowerCase().replace("type", ""));
}
@@ -246,8 +237,8 @@ public class EntityIO{
st("$L(write, $L)", network ? serializer.getNetWriter(type, null) : serializer.writers.get(type), field);
}else if(serializer.mutatorReaders.containsKey(type) && !write && !field.replace(" = ", "").contains(" ") && !field.isEmpty()){
st("$L$L(read, $L)", field, serializer.mutatorReaders.get(type), field.replace(" = ", ""));
}else if((serializer.readers.containsKey(type) || (network && serializer.netReaders.containsKey(type))) && !write){
st("$L$L(read)", field, network ? serializer.getNetReader(type, null) : serializer.readers.get(type));
}else if(serializer.readers.containsKey(type) && !write){
st("$L$L(read)", field, serializer.readers.get(type));
}else if(type.endsWith("[]")){ //it's a 1D array
String rawType = type.substring(0, type.length() - 2);
@@ -19,8 +19,6 @@ import javax.annotation.processing.*;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import java.lang.annotation.*;
import java.util.*;
import java.util.regex.*;
@SupportedAnnotationTypes({
"mindustry.annotations.Annotations.EntityDef",
@@ -30,11 +28,6 @@ import java.util.regex.*;
"mindustry.annotations.Annotations.TypeIOHandler"
})
public class EntityProcess extends BaseProcessor{
static final Pattern selfParamPattern = Pattern.compile("this\\.<(.*)>self\\(\\)");
static final Pattern selfPattern = Pattern.compile("self\\(\\)");
static final Pattern yieldPattern = Pattern.compile(" yield ");
static final Pattern missingPattern = Pattern.compile("\\/\\*missing\\*\\/");
Seq<EntityDefinition> definitions = new Seq<>();
Seq<GroupDefinition> groupDefs = new Seq<>();
Seq<Stype> baseComponents;
@@ -83,12 +76,12 @@ public class EntityProcess extends BaseProcessor{
for(Smethod elem : component.methods()){
if(elem.is(Modifier.ABSTRACT) || elem.is(Modifier.NATIVE)) continue;
//get all statements in the method, store them
String body = elem.tree().getBody().toString();
body = selfParamPattern.matcher(body).replaceAll("this"); //fix parameterized self() calls
body = selfPattern.matcher(body).replaceAll("this"); //fix self() calls
body = yieldPattern.matcher(body).replaceAll(""); //fix enhanced switch
body = missingPattern.matcher(body).replaceAll("var"); //fix vars
methodBlocks.put(elem.descString(), body);
methodBlocks.put(elem.descString(), elem.tree().getBody().toString()
.replaceAll("this\\.<(.*)>self\\(\\)", "this") //fix parameterized self() calls
.replaceAll("self\\(\\)", "this") //fix self() calls
.replaceAll(" yield ", "") //fix enchanced switch
.replaceAll("\\/\\*missing\\*\\/", "var") //fix vars
);
}
}
@@ -104,8 +97,6 @@ public class EntityProcess extends BaseProcessor{
//create component interfaces
for(Stype component : allComponents){
TypeSpec.Builder inter = TypeSpec.interfaceBuilder(interfaceName(component))
.addModifiers(Modifier.PUBLIC).addAnnotation(EntityInterface.class);
@@ -125,47 +116,45 @@ public class EntityProcess extends BaseProcessor{
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
for(Smethod method : component.methods()){
//skip private methods, those are for internal use.
if(method.isAny(Modifier.PRIVATE, Modifier.STATIC)) continue;
//add utility methods to interface
for(Smethod method : component.methods()){
//skip private methods, those are for internal use.
if(method.isAny(Modifier.PRIVATE, Modifier.STATIC)) continue;
//keep track of signatures used to prevent dupes
signatures.add(method.e.toString());
//keep track of signatures used to prevent dupes
signatures.add(method.e.toString());
inter.addMethod(MethodSpec.methodBuilder(method.name())
.addJavadoc(method.doc() == null ? "" : method.doc())
.addExceptions(method.thrownt())
.addTypeVariables(method.typeVariables().map(TypeVariableName::get))
.returns(method.ret().toString().equals("void") ? TypeName.VOID : method.retn())
.addParameters(method.params().map(v -> ParameterSpec.builder(v.tname(), v.name())
.build())).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT).build());
inter.addMethod(MethodSpec.methodBuilder(method.name())
.addJavadoc(method.doc() == null ? "" : method.doc())
.addExceptions(method.thrownt())
.addTypeVariables(method.typeVariables().map(TypeVariableName::get))
.returns(method.ret().toString().equals("void") ? TypeName.VOID : method.retn())
.addParameters(method.params().map(v -> ParameterSpec.builder(v.tname(), v.name())
.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
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());
}
//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());
}
//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());
}
}
@@ -240,21 +229,15 @@ public class EntityProcess extends BaseProcessor{
return result;
});
Seq<Stype> exclusions = types(an, GroupDef::exclude).map(stype -> {
Stype result = interfaceToComp(stype);
if(result == null) throw new IllegalArgumentException("Interface " + stype + " does not have an associated component!");
return result;
});
//representative component type
Stype repr = types.first();
String groupType = repr.annotation(Component.class).base() ? baseName(repr) : interfaceName(repr);
String name = group.name();
String name = group.name().startsWith("g") ? group.name().substring(1) : group.name();
boolean collides = an.collide();
groupDefs.add(new GroupDefinition(name,
ClassName.bestGuess(packageName + "." + groupType), types, exclusions, an.spatial(), an.mapping(), collides, an.update()));
ClassName.bestGuess(packageName + "." + groupType), types, an.spatial(), an.mapping(), collides));
TypeSpec.Builder accessor = TypeSpec.interfaceBuilder("IndexableEntity__" + name);
accessor.addMethod(MethodSpec.methodBuilder("setIndex__" + name).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC).addParameter(int.class, "index").returns(void.class).build());
@@ -270,9 +253,7 @@ public class EntityProcess extends BaseProcessor{
//all component classes (not interfaces)
Seq<Stype> components = allComponents(type);
Seq<GroupDefinition> groups = groupDefs.select(g -> (!g.components.isEmpty() && !Structs.contains(ann.excludeGroups(), g.name)
&& !g.components.contains(s -> !components.contains(s)) && !g.excludedComponents.contains(s -> components.contains(s))));
Seq<GroupDefinition> groups = groupDefs.select(g -> (!g.components.isEmpty() && !g.components.contains(s -> !components.contains(s))) || g.manualInclusions.contains(type));
ObjectMap<String, Seq<Smethod>> methods = new ObjectMap<>();
ObjectMap<FieldSpec, Svar> specVariables = new ObjectMap<>();
ObjectSet<String> usedFields = new ObjectSet<>();
@@ -435,34 +416,19 @@ public class EntityProcess extends BaseProcessor{
//add all methods from components
for(ObjectMap.Entry<String, Seq<Smethod>> entry : methods){
//there are multiple @Replace implementations, or multiple non-void implementations.
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)){
//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()));
if(entry.value.contains(m -> m.has(Replace.class))){
//check replacements
if(entry.value.count(m -> m.has(Replace.class)) > 1){
err("Type " + type + " has multiple components replacing method " + entry.key + ".");
}
Smethod base = entry.value.find(m -> m.has(Replace.class));
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())));
@@ -479,7 +445,6 @@ public class EntityProcess extends BaseProcessor{
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(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);
mbuilder.addTypeVariables(first.typeVariables().map(TypeVariableName::get));
mbuilder.returns(first.retn());
@@ -519,7 +484,7 @@ public class EntityProcess extends BaseProcessor{
//SPECIAL CASE: I/O code
//note that serialization is generated even for non-serializing entities for manual usage
if((first.name().equals("read") || first.name().equals("write"))){
io.write(mbuilder, first.name().equals("write"), allFields);
io.write(mbuilder, first.name().equals("write"));
specialIO = true;
}
@@ -572,15 +537,7 @@ public class EntityProcess extends BaseProcessor{
String blockName = elem.up().getSimpleName().toString().toLowerCase().replace("comp", "");
//skip empty blocks
boolean empty = true;
for(int i = 0; i < str.length(); i++){
char c = str.charAt(i);
if(c != '{' && c != '}' && c != '\n' && c != '\t' && c != ' '){
empty = false;
break;
}
}
if(empty){
if(str.replace("{", "").replace("\n", "").replace("}", "").replace("\t", "").replace(" ", "").isEmpty()){
continue;
}
@@ -707,18 +664,13 @@ public class EntityProcess extends BaseProcessor{
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
MethodSpec.Builder groupUpdate = MethodSpec.methodBuilder("update")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
MethodSpec.Builder groupPoolUpdate = MethodSpec.methodBuilder("updatePooling")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
//free everything pooled at the start of each updaet
groupPoolUpdate
groupUpdate
.addStatement("for($T p : freeQueue) $T.free(p)", Poolable.class, Pools.class)
.addStatement("freeQueue.clear()");
groupUpdate.addStatement("updatePooling()");
//method resize
for(GroupDefinition group : groupDefs){
if(group.spatial){
@@ -727,11 +679,7 @@ public class EntityProcess extends BaseProcessor{
}
}
for(GroupDefinition group : groupDefs){
if(group.updates){
groupUpdate.addStatement("$L.update()", group.name);
}
}
groupUpdate.addStatement("all.update()");
for(GroupDefinition group : groupDefs){
if(group.collides){
@@ -740,7 +688,6 @@ public class EntityProcess extends BaseProcessor{
}
groupsBuilder.addMethod(groupResize.build());
groupsBuilder.addMethod(groupPoolUpdate.build());
groupsBuilder.addMethod(groupUpdate.build());
write(groupsBuilder);
@@ -904,6 +851,89 @@ public class EntityProcess extends BaseProcessor{
for(TypeSpec.Builder b : baseClasses){
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);
}
}
@@ -1016,18 +1046,17 @@ public class EntityProcess extends BaseProcessor{
class GroupDefinition{
final String name;
final ClassName baseType;
final Seq<Stype> components, excludedComponents;
final boolean spatial, mapping, collides, updates;
final Seq<Stype> components;
final boolean spatial, mapping, collides;
final ObjectSet<Selement> manualInclusions = new ObjectSet<>();
public GroupDefinition(String name, ClassName bestType, Seq<Stype> components, Seq<Stype> excludedComponents, boolean spatial, boolean mapping, boolean collides, boolean updates){
public GroupDefinition(String name, ClassName bestType, Seq<Stype> components, boolean spatial, boolean mapping, boolean collides){
this.baseType = bestType;
this.components = components;
this.excludedComponents = excludedComponents;
this.name = name;
this.spatial = spatial;
this.mapping = mapping;
this.collides = collides;
this.updates = updates;
}
@Override
@@ -57,9 +57,6 @@ public class AssetsProcess extends BaseProcessor{
ichtype.addField(FieldSpec.builder(ParameterizedTypeName.get(ObjectIntMap.class, String.class),
"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<>();
for(Jval val : icons.get("glyphs").asArray()){
@@ -70,9 +67,7 @@ public class AssetsProcess extends BaseProcessor{
int code = val.getInt("code", 0);
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());
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);
icload.addStatement(name + "Small = mindustry.ui.Fonts.getGlyph(mindustry.ui.Fonts.def, (char)" + code + ")");
@@ -142,19 +137,6 @@ public class AssetsProcess extends BaseProcessor{
.addParameter(int.class, "id")
.returns(Sound.class)
.addStatement("return (Sound)idToSound.get(id, () -> Sounds.none)").build());
type.addMethod(MethodSpec.methodBuilder("registerSound")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(Sound.class, "sound")
.addParameter(int.class, "id")
.returns(void.class)
.addStatement("idToSound.put(id, sound); soundToId.put(sound, id);").build());
type.addMethod(MethodSpec.methodBuilder("unregisterSound")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(Sound.class, "sound")
.returns(void.class)
.addStatement("int id = soundToId.get(sound); if(id != 0){ soundToId.remove(sound); idToSound.remove(id); }").build());
}
HashSet<String> names = new HashSet<>();
@@ -196,11 +178,7 @@ public class AssetsProcess extends BaseProcessor{
}
if(classname.equals("Sounds")){
type.addField(FieldSpec.builder(ClassName.bestGuess(rtype), "none", Modifier.STATIC, Modifier.PUBLIC).initializer("new " + rtype + "()")
.addJavadoc("Does not play anything.").build());
type.addField(FieldSpec.builder(ClassName.bestGuess(rtype), "unset", Modifier.STATIC, Modifier.PUBLIC).initializer("new " + rtype + "()")
.addJavadoc("Used a placeholder value for unset default values. This is usually reassigned in init() of the relevant block or unit. Does not play anything.").build());
type.addField(FieldSpec.builder(ClassName.bestGuess(rtype), "none", Modifier.STATIC, Modifier.PUBLIC).initializer("new " + rtype + "()").build());
}
type.addMethod(loadBegin.build());
@@ -102,7 +102,7 @@ public class StructProcess extends BaseProcessor{
//bools: single bit, needs special case to clear things
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.addStatement("return ($T)(($L & ~(1L << $LL)))", structType, structParam, offset);
setter.endControlFlow();
@@ -56,15 +56,6 @@ public class CallGenerator{
packet.addMethod(writeHandleMethod(ent, true));
}
if(!(ent.where.isClient && ent.where.isServer)){
packet.addMethod( MethodSpec.methodBuilder("allow")
.addModifiers(Modifier.PUBLIC)
.addParameter(boolean.class, "server")
.addAnnotation(Override.class)
.returns(boolean.class)
.addCode("return " + (ent.where.isClient ? "server" : "!server") + ";").build());
}
//register packet
register.addStatement("mindustry.net.Net.registerPacket($L.$L::new)", packageName, ent.packetClassName);
@@ -202,7 +193,7 @@ public class CallGenerator{
builder.addStatement("$L = READ.$L()", varName, pname);
}else{
//else, try and find a serializer
String ser = serializer.getNetReader(typeName.replace("mindustry.gen.", ""), SerializerResolver.locate(ent.element.e, var.mirror(), false));
String ser = serializer.readers.get(typeName.replace("mindustry.gen.", ""), SerializerResolver.locate(ent.element.e, var.mirror(), false));
if(ser == null){ //make sure a serializer exists!
BaseProcessor.err("No read method to read class type '" + typeName + "' in method " + ent.targetMethod + "; " + serializer.readers, var);
@@ -28,10 +28,6 @@ public class Stype extends Selement<TypeElement>{
return interfaces().flatMap(s -> s.allInterfaces().add(s)).distinct();
}
public boolean isInterface(){
return e.getKind() == ElementKind.INTERFACE;
}
public Seq<Stype> superclasses(){
return Seq.with(BaseProcessor.typeu.directSupertypes(mirror())).map(Stype::of);
}
@@ -16,7 +16,7 @@ public class TypeIOResolver{
* Maps fully qualified class names to their serializers.
*/
public static ClassSerializer resolve(BaseProcessor processor){
ClassSerializer out = new ClassSerializer(new ObjectMap<>(), new ObjectMap<>(), new ObjectMap<>(), new ObjectMap<>(), new ObjectMap<>());
ClassSerializer out = new ClassSerializer(new ObjectMap<>(), new ObjectMap<>(), new ObjectMap<>(), new ObjectMap<>());
for(Stype type : processor.types(TypeIOHandler.class)){
//look at all TypeIOHandler methods
Seq<Smethod> methods = type.methods();
@@ -27,11 +27,11 @@ public class TypeIOResolver{
if(params.size == 2 && params.first().tname().toString().equals("arc.util.io.Writes")){
//Net suffix indicates that this should only be used for sync operations
ObjectMap<String, String> targetMap = meth.name().endsWith("Net") ? out.netWriters : out.writers;
targetMap.put(fix(params.get(1).tname().toString()), type.fullName() + "." + meth.name());
}else if(params.size == 1 && params.first().tname().toString().equals("arc.util.io.Reads") && !meth.isVoid()){ //1 param, one is reader, returns type
//Net suffix indicates that this should only be used for sync operations
ObjectMap<String, String> targetMap = meth.name().endsWith("Net") ? out.netReaders : out.readers;
targetMap.put(fix(meth.retn().toString()), type.fullName() + "." + meth.name());
}else if(params.size == 1 && params.first().tname().toString().equals("arc.util.io.Reads") && !meth.isVoid()){
//1 param, one is reader, returns type
out.readers.put(fix(meth.retn().toString()), type.fullName() + "." + meth.name());
}else if(params.size == 2 && params.first().tname().toString().equals("arc.util.io.Reads") && !meth.isVoid() && meth.ret().equals(meth.params().get(1).mirror())){
//2 params, one is reader, other is type, returns type - these are made to reduce garbage allocated
out.mutatorReaders.put(fix(meth.retn().toString()), type.fullName() + "." + meth.name());
@@ -50,22 +50,17 @@ public class TypeIOResolver{
/** Information about read/write methods for class types. */
public static class ClassSerializer{
public final ObjectMap<String, String> writers, readers, mutatorReaders, netWriters, netReaders;
public final ObjectMap<String, String> writers, readers, mutatorReaders, netWriters;
public ClassSerializer(ObjectMap<String, String> writers, ObjectMap<String, String> readers, ObjectMap<String, String> mutatorReaders, ObjectMap<String, String> netWriters, ObjectMap<String, String> netReaders){
public ClassSerializer(ObjectMap<String, String> writers, ObjectMap<String, String> readers, ObjectMap<String, String> mutatorReaders, ObjectMap<String, String> netWriters){
this.writers = writers;
this.readers = readers;
this.mutatorReaders = mutatorReaders;
this.netWriters = netWriters;
this.netReaders = netReaders;
}
public String getNetWriter(String type, String fallback){
return netWriters.get(type, writers.get(type, fallback));
}
public String getNetReader(String type, String fallback){
return netReaders.get(type, readers.get(type, fallback));
}
}
}
@@ -6,7 +6,6 @@ atrax=1
beta=30
block=2
corvus=24
dummy=49
elude=45
flare=3
gamma=31
@@ -20,7 +19,6 @@ mindustry.entities.comp.DecalComp=8
mindustry.entities.comp.EffectStateComp=9
mindustry.entities.comp.FireComp=10
mindustry.entities.comp.LaunchCoreComp=11
mindustry.entities.comp.LocationPingComp=48
mindustry.entities.comp.PlayerComp=12
mindustry.entities.comp.PosTeam=27
mindustry.entities.comp.PosTeamDef=28
@@ -1 +0,0 @@
{version:2,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:selectedBlock,type:mindustry.world.Block},{name:selectedRotation,type:int},{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}]}
@@ -1 +0,0 @@
{version:1,fields:[{name:flags,type:byte},{name:fontSize,type:float},{name:parent,type:mindustry.gen.Posc},{name:text,type:java.lang.String},{name:x,type:float},{name:y,type:float},{name:z,type:float}]}
@@ -1 +0,0 @@
{version:4,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:5,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:4,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:5,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:4,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:5,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:8,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:9,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:8,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:9,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:building,type:Building},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:1,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:2,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:8,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:9,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:4,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:5,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:1,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:2,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:8,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:baseRotation,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:9,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:baseRotation,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:2,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:building,type:Building},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:payloads,type:arc.struct.Seq<mindustry.world.blocks.payloads.Payload>},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:3,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:building,type:Building},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:payloads,type:arc.struct.Seq<mindustry.world.blocks.payloads.Payload>},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:6,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:payloads,type:arc.struct.Seq<mindustry.world.blocks.payloads.Payload>},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:7,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:payloads,type:arc.struct.Seq<mindustry.world.blocks.payloads.Payload>},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:2,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:lifetime,type:float},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:time,type:float},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:3,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:lifetime,type:float},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:time,type:float},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:7,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:8,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:6,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:baseRotation,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:7,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:baseRotation,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:6,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:payloads,type:arc.struct.Seq<mindustry.world.blocks.payloads.Payload>},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:7,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:payloads,type:arc.struct.Seq<mindustry.world.blocks.payloads.Payload>},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:6,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:7,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:4,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:baseRotation,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:5,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:baseRotation,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:7,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:payloads,type:arc.struct.Seq<mindustry.world.blocks.payloads.Payload>},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:8,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:payloads,type:arc.struct.Seq<mindustry.world.blocks.payloads.Payload>},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:4,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:baseRotation,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:5,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:baseRotation,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:8,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:9,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:7,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:8,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:1,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:2,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:4,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:ammo,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
@@ -1 +0,0 @@
{version:5,fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
+36 -109
View File
@@ -1,10 +1,8 @@
import java.text.SimpleDateFormat
buildscript{
ext{
arcHash = property("archash")
localArc = new File(rootDir.parent, 'Arc').exists() && !project.hasProperty("noLocalArc")
localArc = !project.hasProperty("release") && new File(rootDir.parent, 'Arc').exists() && !project.hasProperty("noLocalArc")
arcModule = { String name ->
//skip to last submodule
@@ -12,12 +10,13 @@ buildscript{
return "com.github.Anuken${localArc ? "" : ".Arc"}:$name:$arcHash"
}
}
repositories{
mavenLocal()
mavenCentral()
google()
maven{ url = 'https://jitpack.io' }
maven{ url "https://oss.sonatype.org/content/repositories/snapshots/" }
maven{ url 'https://jitpack.io' }
}
dependencies{
@@ -27,23 +26,23 @@ buildscript{
}
plugins{
id "org.jetbrains.kotlin.jvm" version "2.3.20"
id "org.jetbrains.kotlin.kapt" version "2.3.20"
id "org.jetbrains.kotlin.jvm" version "1.6.0"
id "org.jetbrains.kotlin.kapt" version "1.6.0"
}
allprojects{
apply plugin: 'maven-publish'
version = project.hasProperty("packageVersion") ? project.getProperty("packageVersion") : 'release'
group = 'com.github.Anuken'
ext{
versionNumber = '8'
versionNumber = '7'
if(!project.hasProperty("versionModifier")) versionModifier = 'release'
if(!project.hasProperty("versionType")) versionType = 'official'
appName = 'Mindustry'
steamworksVersion = '185e76cfe630a6f0e1dd7b9bf57be41f6064a1f2'
rhinoVersion = '32395f942976a6d1d21ec9877664554cd5762306'
steamworksVersion = '0b86023401880bb5e586bc404bedbaae9b1f1c94'
rhinoVersion = '73a812444ac388ac2d94013b5cadc8f70b7ea027'
loadVersionProps = {
return new Properties().with{p -> p.load(file('../core/assets/version.properties').newReader()); return p }
@@ -77,8 +76,7 @@ allprojects{
}
hasSprites = {
def file = new File(rootDir, "core/build/last_pack_version")
return new File(rootDir, "core/assets/sprites/sprites.aatls").exists() && file.exists() && file.text == versionNumber
return new File(rootDir, "core/assets/sprites/sprites.aatls").exists()
}
getModifierString = {
@@ -91,10 +89,6 @@ allprojects{
return project.getProperties()["buildversion"]
}
getCommitHash = {
return 'git rev-parse --verify --short HEAD'.execute().text.trim()
}
getPackage = {
return project.ext.mainClassName.substring(0, project.ext.mainClassName.indexOf("desktop") - 1)
}
@@ -113,11 +107,11 @@ allprojects{
def output = 'en\n'
def bundles = new File(project(':core').projectDir, 'assets/bundles/')
bundles.list().sort().each{ name ->
if(name == "bundle.properties" || name == "global.properties") return
if(name == "bundle.properties") return
output += name.substring("bundle".length() + 1, name.lastIndexOf('.')) + "\n"
}
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().findAll{f -> f.endsWith(".msch")}.join("\n")
new File(project(':core').projectDir, 'assets/basepartnames').text = new File(project(':core').projectDir, 'assets/baseparts/').list().sort().join("\n")
}
writeVersion = {
@@ -139,11 +133,6 @@ allprojects{
props["number"] = versionNumber
props["modifier"] = versionModifier
props["build"] = buildid
props["buildDate"] = new SimpleDateFormat("MMMM d, yyyy HH:mm a", Locale.getDefault()).format(new Date())
props["commitHash"] = "unknown"
if(project.hasProperty("showCommitHash")){
props["commitHash"] = getCommitHash()
}
props.store(pfile.newWriter(), "Autogenerated file. Do not modify.")
}
@@ -181,8 +170,9 @@ allprojects{
repositories{
mavenLocal()
mavenCentral()
maven{ url = "https://central.sonatype.com/repository/maven-snapshots" }
maven{ url = 'https://jitpack.io' }
maven{ url "https://oss.sonatype.org/content/repositories/snapshots/" }
maven{ url "https://oss.sonatype.org/content/repositories/releases/" }
maven{ url 'https://jitpack.io' }
}
task clearCache{
@@ -194,7 +184,7 @@ allprojects{
}
tasks.withType(JavaCompile){
targetCompatibility = JavaVersion.VERSION_17
targetCompatibility = 8
sourceCompatibility = JavaVersion.VERSION_17
options.encoding = "UTF-8"
options.compilerArgs += ["-Xlint:deprecation"]
@@ -215,38 +205,22 @@ allprojects{
configure(project(":annotations")){
tasks.withType(JavaCompile){
targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = 8
sourceCompatibility = 8
options.fork = true
options.compilerArgs.addAll([
"--add-exports", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
"--add-exports", "java.base/sun.reflect.annotation=ALL-UNNAMED"
])
}
}
//compile with java 8 compatibility for everything except the annotation project
configure(subprojects - project(":annotations") - project(":tests")){
configure(subprojects - project(":annotations")){
tasks.withType(JavaCompile){
options.compilerArgs.addAll(['--release', '17'])
options.compilerArgs.addAll(['--release', '8'])
}
tasks.withType(Javadoc){
options{
addStringOption('Xdoclint:none', '-quiet')
addStringOption('-release', '17')
addStringOption('-release', '16')
encoding('UTF-8')
}
}
@@ -259,10 +233,7 @@ project(":desktop"){
dependencies{
implementation project(":core")
implementation arcModule("extensions:profiling")
implementation arcModule("extensions:discord")
implementation arcModule("natives:natives-filedialogs")
implementation arcModule("extensions:filedialogs")
implementation arcModule("natives:natives-desktop")
implementation arcModule("natives:natives-freetype-desktop")
@@ -281,8 +252,8 @@ project(":core"){
kapt{
javacOptions{
option("-source", "17")
option("-target", "17")
option("-source", "16")
option("-target", "1.8")
}
}
@@ -320,61 +291,29 @@ project(":core"){
}
}
task depsJar(type: Jar, dependsOn: [classes, ":server:dist"]){
archiveClassifier = 'deps'
from files(sourceSets.main.output.classesDirs)
from{ configurations.runtimeClasspath.collect{ it.isDirectory() ? it : zipTree(it) } }
from zipTree("../server/build/libs/server-release.jar")
from files("src/")
from files("../../Arc/natives/natives-desktop/libs")
from files("../../Arc/arc-core/src/")
from files("../../Arc/extensions/arcnet/src/")
from files("../../Arc/extensions/g3d/src/")
from files("../../Arc/extensions/flabel/src/")
from files("../../Arc/extensions/freetype/src/")
from files("../server/src/")
exclude("META-INF/**")
exclude("vfxshaders/**")
exclude("maps/**")
exclude("baseparts/**")
exclude("vfxshaders/**")
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
task sourcesJar(type: Jar, dependsOn: classes){
archiveClassifier = 'sources'
from sourceSets.main.allSource
}
task assetsJar(type: Jar){
dependsOn ":tools:pack"
task assetsJar(type: Jar, dependsOn: ":tools:pack"){
archiveClassifier = 'assets'
into('assets'){
from 'assets'
exclude 'config', 'cache', 'music', 'sounds', 'sprites/fallback'
}
into('assets-raw/sprites'){
from 'assets-raw/sprites_out'
include '**/*.png'
from files("assets"){
exclude "config", "cache", "music", "sounds"
}
}
task musicJar(type: Jar){
archiveClassifier = 'music'
into('assets'){
from 'assets'
include '**/*.ogg', '**/*.mp3'
from files("assets"){
include "music/*", "sounds/*"
}
}
dependencies{
compileJava.dependsOn(preGen)
api "at.yawk.lz4:lz4-java:1.10.2"
api "org.lz4:lz4-java:1.8.0"
api arcModule("arc-core")
api arcModule("extensions:flabel")
api arcModule("extensions:freetype")
@@ -385,6 +324,7 @@ project(":core"){
if(localArc && debugged()) api arcModule("extensions:recorder")
if(localArc) api arcModule(":extensions:packer")
annotationProcessor 'com.github.Anuken:jabel:0.9.0'
compileOnly project(":annotations")
if(!project.hasProperty("noKapt")) kapt project(":annotations")
}
@@ -415,6 +355,7 @@ project(":core"){
//these are completely unnecessary
tasks.kaptGenerateStubsKotlin.onlyIf{ false }
tasks.compileKotlin.onlyIf{ false }
tasks.inspectClassesForKotlinIC.onlyIf{ false }
}
//comp** classes are only used for code generation
@@ -429,7 +370,6 @@ project(":server"){
dependencies{
implementation project(":core")
implementation arcModule("backends:backend-headless")
implementation 'org.jline:jline:4.0.0'
}
}
@@ -443,19 +383,11 @@ project(":tests"){
testImplementation arcModule("backends:backend-headless")
testImplementation "org.json:json:20230618"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.7.1"
testRuntimeOnly "org.junit.platform:junit-platform-launcher"
}
tasks.withType(JavaCompile){
targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_17
options.compilerArgs.addAll(['--release', '17'])
}
test{
//fork every test so mods don't interact with each other
forkEvery = 1
jvmArgs = ["-XX:+HeapDumpOnOutOfMemoryError"]
useJUnitPlatform()
workingDir = new File("../core/assets")
testLogging{
@@ -474,9 +406,6 @@ project(":tools"){
implementation arcModule("natives:natives-desktop")
implementation arcModule("natives:natives-freetype-desktop")
implementation arcModule("backends:backend-headless")
implementation "com.google.guava:guava:33.3.1-jre"
implementation 'com.github.javaparser:javaparser-core:3.28.2'
}
}
@@ -499,6 +428,9 @@ configure([":core", ":server"].collect{project(it)}){
publications{
maven(MavenPublication){
from components.java
if(project.name == "core"){
artifact(tasks.named("assetsJar"))
}
}
}
}
@@ -519,6 +451,7 @@ task deployAll{
dependsOn cleanDeployOutput
dependsOn "desktop:packrLinux64"
dependsOn "desktop:packrWindows64"
dependsOn "desktop:packrWindows32"
dependsOn "desktop:packrMacOS"
if(versionModifier != "steam"){
dependsOn "server:deploy"
@@ -534,9 +467,3 @@ task resolveDependencies{
}
}
}
if(System.getProperty("compileMethodParams") != null){
project(":core").tasks.withType(JavaCompile).configureEach{
options.compilerArgs << '-parameters'
}
}
+7 -195
View File
@@ -308,6 +308,12 @@
"discord-logo-white"
]
},
{
"uid": "dd6df3b754e8d0dbf09d9b24088f0442",
"css": "box",
"code": 59422,
"src": "iconic"
},
{
"uid": "f08ba9f0e8f9b426a92efcaa59c1e932",
"css": "reddit-alien",
@@ -1063,200 +1069,6 @@
"search": [
"planet"
]
},
{
"uid": "ec43f846ca53702f30310718d627f2b5",
"css": "arrow-note",
"code": 59444,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M50 0L0 50V150 250 350 450 550 650L50 700H150 250 350 450 550 650 750L800 750 750 800 700 850V950L750 1000H850L900 950 950 900 1000 850 1050 800 1100 750 1150 700 1200 650V550L1150 500 1100 450 1050 400 1000 350 950 300 900 250 850 200H750L700 250V350L750 400 800 450 750 500H650 550 450 350 250L200 450V350 250 150 50L150 0Z",
"width": 1200
},
"search": [
"arrow-note"
]
},
{
"uid": "9ea0a737ccc45d6c510dcbae56058849",
"css": "music",
"code": 59402,
"src": "fontawesome"
},
{
"uid": "81db033e704eb7c586a365559d7c0f36",
"css": "file-audio",
"code": 61895,
"src": "fontawesome"
},
{
"uid": "e44601720c64e6bb6a2d5cba6b0c588c",
"css": "volume-off",
"code": 59404,
"src": "fontawesome"
},
{
"uid": "fee6e00f36e8ca8ef3e4a62caa213bf6",
"css": "volume-down",
"code": 59423,
"src": "fontawesome"
},
{
"uid": "76857a03fbaa6857fe063b6c25aa98ed",
"css": "volume-up",
"code": 59424,
"src": "fontawesome"
},
{
"uid": "3db5347bd219f3bce6025780f5d9ef45",
"css": "tag",
"code": 59425,
"src": "fontawesome"
},
{
"uid": "a3f89e106175a5c5c4e9738870b12e55",
"css": "tags",
"code": 59432,
"src": "fontawesome"
},
{
"uid": "69bd9dfe950d07d910f39c6988364e49",
"css": "battery-4",
"code": 62016,
"src": "fontawesome"
},
{
"uid": "7ab4ec24078133effbc4e923b65ef835",
"css": "battery-3",
"code": 62017,
"src": "fontawesome"
},
{
"uid": "e430a168b45ba40aa988ca1a5034a553",
"css": "battery-2",
"code": 62018,
"src": "fontawesome"
},
{
"uid": "99f4ea74adc24022106932d84a49b055",
"css": "battery-1",
"code": 62019,
"src": "fontawesome"
},
{
"uid": "9983d2489e028d659d196915a25ef3ca",
"css": "battery-0",
"code": 62020,
"src": "fontawesome"
},
{
"uid": "26613a2e6bc41593c54bead46f8c8ee3",
"css": "file-code",
"code": 61897,
"src": "fontawesome"
},
{
"uid": "dd69d9aa589ea7bc0a82a3fe67039f4b",
"css": "file-video",
"code": 61896,
"src": "fontawesome"
},
{
"uid": "e80ae555c1413a4ec18b33fb348b4049",
"css": "file-archive",
"code": 61894,
"src": "fontawesome"
},
{
"uid": "c95735c17a10af81448c7fed98a04546",
"css": "folder-open",
"code": 59438,
"src": "fontawesome"
},
{
"uid": "b091a8bd0fdade174951f17d936f51e4",
"css": "folder-empty",
"code": 61716,
"src": "fontawesome"
},
{
"uid": "6533bdc16ab201eb3f3b27ce989cab33",
"css": "folder-open-empty",
"code": 61717,
"src": "fontawesome"
},
{
"uid": "c7a75a25880928fe76981a53eca4f926",
"css": "inbox",
"code": 59439,
"src": "fontawesome"
},
{
"uid": "6fe95ffc3c807e62647d4f814a96e0d7",
"css": "sitemap",
"code": 61672,
"src": "fontawesome"
},
{
"uid": "2c413e78faf1d6631fd7b094d14c2253",
"css": "cloud",
"code": 59441,
"src": "fontawesome"
},
{
"uid": "de2fc7a5c986ab8c622f63455d7cf814",
"css": "upload-cloud",
"code": 61678,
"src": "fontawesome"
},
{
"uid": "f5999a012fc3752386635ec02a858447",
"css": "download-cloud",
"code": 61677,
"src": "fontawesome"
},
{
"uid": "e7a4824e79002c2d8ce3047b2da8b5a2",
"css": "rain",
"code": 59442,
"src": "typicons"
},
{
"uid": "ac10a3a3077eab1b9d66509eb25fb9b4",
"css": "cloud-sun",
"code": 59448,
"src": "typicons"
},
{
"uid": "6f200a5a449259c51cd0ccf56b4c1e93",
"css": "drizzle",
"code": 59452,
"src": "typicons"
},
{
"uid": "3cac7625657b10cf706e7f332749c912",
"css": "cloud-flash",
"code": 59456,
"src": "typicons"
},
{
"uid": "364cc69fb3edb8fb21caa6a2c0489589",
"css": "cloud-wind",
"code": 59457,
"src": "typicons"
},
{
"uid": "cc05df515bebe11df3ada0a5910a8f6d",
"css": "box",
"code": 59422,
"src": "entypo"
},
{
"uid": "d5f287ad8e3a2db8f808bc09980ec3dc",
"css": "chart-alt",
"code": 59459,
"src": "typicons"
}
]
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 411 B

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: 8.0 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.

After

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 966 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 B

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 393 B

After

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 428 B

After

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 505 B

After

Width:  |  Height:  |  Size: 633 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 B

After

Width:  |  Height:  |  Size: 426 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 B

After

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 251 B

After

Width:  |  Height:  |  Size: 149 B

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