21
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,12 +1,25 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help fix an issue.
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Platform**: (Android/iOS/Mac/Windows/Linux)
|
||||
**Platform**: *Android/iOS/Mac/Windows/Linux*
|
||||
|
||||
**Build**: (The build number under the title in the main menu. Required.)
|
||||
**Build**: *The build number under the title in the main menu. Required.*
|
||||
|
||||
**Issue**: (Explain your issue in detail.)
|
||||
**Issue**: *Explain your issue in detail.*
|
||||
|
||||
**Steps to reproduce**: (How you happened across the issue, and what you were doing at the time.)
|
||||
**Steps to reproduce**: *How you happened across the issue, and what you were doing at the time.*
|
||||
|
||||
**Link to mod(s) used, if applicable**: *The mod repositories or zip files that are related to the issue.*
|
||||
|
||||
**Crash report, if applicable**: *The contents of relevant crash report files.*
|
||||
|
||||
---
|
||||
|
||||
*Place an X (no spaces) between the brackets to confirm that you have read the line below.*
|
||||
- [ ] **I have searched the closed and open issues to make sure that this problem has not already been reported.**
|
||||
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Feature request
|
||||
url: https://github.com/Anuken/Mindustry-Suggestions/issues/new/choose
|
||||
about: Do not make a new issue for feature requests! Instead, post it in suggestions repository.
|
||||
- name: Question
|
||||
url: https://discord.com/invite/mindustry
|
||||
about: Questions about the game should be asked in the Discord, not in the issue tracker.
|
||||
7
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,7 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
**Do not make a new issue for feature requests!** Instead, post it in #545.
|
||||
85
.github/Mindustry-CodeStyle-IJ.xml
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
<code_scheme name="Default" version="173">
|
||||
<option name="AUTODETECT_INDENTS" value="false" />
|
||||
<option name="RIGHT_MARGIN" value="300" />
|
||||
<GroovyCodeStyleSettings>
|
||||
<option name="SPACE_BEFORE_CLOSURE_LBRACE" value="false" />
|
||||
</GroovyCodeStyleSettings>
|
||||
<JavaCodeStyleSettings>
|
||||
<option name="SPACE_AROUND_TYPE_BOUNDS_IN_TYPE_PARAMETERS" value="false" />
|
||||
<option name="INSERT_INNER_CLASS_IMPORTS" value="true" />
|
||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="1" />
|
||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="2" />
|
||||
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
|
||||
<value>
|
||||
<package name="java.awt" withSubpackages="false" static="false" />
|
||||
<package name="mindustry" withSubpackages="false" static="true" />
|
||||
<package name="javax.swing" withSubpackages="false" static="false" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
|
||||
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
|
||||
<option name="JD_ADD_BLANK_AFTER_DESCRIPTION" value="false" />
|
||||
<option name="JD_DO_NOT_WRAP_ONE_LINE_COMMENTS" value="true" />
|
||||
<option name="JD_KEEP_EMPTY_PARAMETER" value="false" />
|
||||
<option name="JD_KEEP_EMPTY_EXCEPTION" value="false" />
|
||||
<option name="JD_KEEP_EMPTY_RETURN" value="false" />
|
||||
</JavaCodeStyleSettings>
|
||||
<Properties>
|
||||
<option name="SPACES_AROUND_KEY_VALUE_DELIMITER" value="true" />
|
||||
<option name="KEEP_BLANK_LINES" value="true" />
|
||||
</Properties>
|
||||
<codeStyleSettings language="Groovy">
|
||||
<option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_TRY_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_SYNCHRONIZED_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_CLASS_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_METHOD_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_IF_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_ELSE_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_FOR_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_DO_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_SWITCH_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_TRY_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_FINALLY_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_SYNCHRONIZED_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_ELSE_KEYWORD" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_KEYWORD" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_KEYWORD" value="false" />
|
||||
<option name="SPACE_BEFORE_FINALLY_KEYWORD" value="false" />
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JAVA">
|
||||
<option name="SPACE_AFTER_TYPE_CAST" value="false" />
|
||||
<option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_TRY_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_SYNCHRONIZED_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_CLASS_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_METHOD_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_IF_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_ELSE_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_FOR_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_DO_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_SWITCH_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_TRY_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_FINALLY_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_SYNCHRONIZED_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_ELSE_KEYWORD" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_KEYWORD" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_KEYWORD" value="false" />
|
||||
<option name="SPACE_BEFORE_FINALLY_KEYWORD" value="false" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="0" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
21
.github/workflows/gradle.yml
vendored
@@ -3,15 +3,26 @@ name: Java CI
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
buildJava8:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Set up JDK 1.8
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Run unit tests with gradle
|
||||
java-version: 8
|
||||
- name: Run unit tests with gradle and Java 8
|
||||
run: ./gradlew test
|
||||
|
||||
buildJava14:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Set up JDK 14
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 14
|
||||
- name: Run unit tests with gradle and Java 14
|
||||
run: ./gradlew test
|
||||
|
||||
21
.gitignore
vendored
@@ -10,6 +10,7 @@ logs/
|
||||
/desktop/mindustry-saves/
|
||||
/desktop/mindustry-maps/
|
||||
/desktop/gifexport/
|
||||
/gifs/
|
||||
/core/lib/
|
||||
/ios/assets/
|
||||
/core/assets-raw/sprites/generated/
|
||||
@@ -18,29 +19,30 @@ logs/
|
||||
/annotations/out/
|
||||
/net/build/
|
||||
/tools/build/
|
||||
/core/build/
|
||||
/tests/build/
|
||||
/server/build/
|
||||
changelog
|
||||
saves/
|
||||
/core/assets-raw/fontgen/out/
|
||||
core/assets/saves/
|
||||
/core/assets/saves/
|
||||
steam_appid.txt
|
||||
/test_files/
|
||||
/annotations/build/
|
||||
/android/assets/mindustry-maps/
|
||||
/android/assets/mindustry-saves/
|
||||
/core/assets/gifexport/
|
||||
/annotations/src/main/resources/META-INF/services
|
||||
/core/assets/version.properties
|
||||
/core/assets/locales
|
||||
/ios/src/io/anuke/mindustry/gen/
|
||||
/core/src/io/anuke/mindustry/gen/
|
||||
/ios/src/mindustry/gen/
|
||||
/core/src/mindustry/gen/
|
||||
ios/robovm.properties
|
||||
packr-out/
|
||||
config/
|
||||
changelog
|
||||
*.gif
|
||||
/core/assets/saves/
|
||||
|
||||
/core/assets/basepartnames
|
||||
version.properties
|
||||
|
||||
.attach_*
|
||||
@@ -55,15 +57,6 @@ crash-report-*
|
||||
## Robovm
|
||||
/ios/robovm-build/
|
||||
|
||||
## GWT
|
||||
/html/war/
|
||||
/html/gwt-unitCache/
|
||||
.apt_generated/
|
||||
.gwt/
|
||||
gwt-unitCache/
|
||||
www-test/
|
||||
.gwt-tmp/
|
||||
|
||||
## Android Studio and Intellij and Android in general
|
||||
/android/libs/armeabi/
|
||||
/android/libs/armeabi-v7a/
|
||||
|
||||
12
.travis.yml
@@ -1,16 +1,16 @@
|
||||
jdk:
|
||||
- openjdk8
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
android:
|
||||
components:
|
||||
- android-29
|
||||
- build-tools-29.0.2
|
||||
- build-tools-29.0.3
|
||||
|
||||
script:
|
||||
- git clone --depth=1 --branch=master https://github.com/Anuken/MindustryBuilds ../MindustryBuilds
|
||||
- cd ../MindustryBuilds
|
||||
- echo ${TRAVIS_TAG}
|
||||
- if [ -n "$TRAVIS_TAG" ]; then echo versionName=4-fdroid-${TRAVIS_TAG:1}$'\n'versionCode=${TRAVIS_TAG:1} > version_fdroid.txt; git add .; git commit -m "Updating to build ${TRAVIS_TAG}"; fi
|
||||
- if [ -n "$TRAVIS_TAG" ]; then echo versionName=5-fdroid-${TRAVIS_TAG:1}$'\n'versionCode=${TRAVIS_TAG:1} > version_fdroid.txt; git add .; git commit -m "Updating to build ${TRAVIS_TAG}"; fi
|
||||
- git tag ${TRAVIS_BUILD_NUMBER}
|
||||
- git config --global user.name "Build Uploader"
|
||||
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then git push https://Anuken:${GH_PUSH_TOKEN}@github.com/Anuken/MindustryBuilds ${TRAVIS_BUILD_NUMBER}; git push https://Anuken:${GH_PUSH_TOKEN}@github.com/Anuken/MindustryBuilds; fi
|
||||
@@ -20,6 +20,12 @@ script:
|
||||
- "./gradlew test"
|
||||
- "./gradlew desktop:dist -Pbuildversion=${TRAVIS_TAG:1}"
|
||||
- "./gradlew server:dist -Pbuildversion=${TRAVIS_TAG:1}"
|
||||
- "./gradlew core:javadoc"
|
||||
- cd ../
|
||||
- git clone --depth=1 https://github.com/MindustryGame/docs.git
|
||||
- cp -a Mindustry/core/build/docs/javadoc/. docs/
|
||||
- cd docs
|
||||
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then git add .; git commit -m "Update ${TRAVIS_BUILD_NUMBER}"; git push https://Anuken:${GH_PUSH_TOKEN}@github.com/MindustryGame/docs; fi
|
||||
deploy:
|
||||
- provider: releases
|
||||
skip_cleanup: true
|
||||
|
||||
70
CONTRIBUTING.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Contributing
|
||||
|
||||
This is for code contributions. For translations, see [TRANSLATING](TRANSLATING.md).
|
||||
|
||||
## 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) (preferred method) or via e-mail (*anukendev@gmail.com*).
|
||||
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.
|
||||
|
||||
|
||||
## Style Guidelines
|
||||
|
||||
#### Follow the formatting guidelines.
|
||||
This means:
|
||||
- No spaces around parentheses: `if(condition){`, `SomeType s = (SomeType)object`
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
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).
|
||||
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.
|
||||
|
||||
The same applies to any class *outside* of the standard `java.[n]io` / `java.net` / `java.util` packages: Most of them are not supported.
|
||||
`java.awt` is one of these packages: do not use it, ever. It is not supported on any platform, even desktop - the entire package is removed during JRE minimization.
|
||||
In general, if you are using IntelliJ, you should be warned about platform incompatiblities.
|
||||
|
||||
|
||||
#### Use `arc` collections and classes when possible.
|
||||
Instead of using `java.util.List`, `java.util.HashMap`, and other standard Java collections, use `Seq`, `ObjectMap` and other equivalents from `arc.struct`.
|
||||
Why? Because that's what the rest of the codebase uses, and the standard collections have a lot of cruft and usability issues associated with them.
|
||||
In the rare case that concurrency is required, you may use the standard Java classes for that purpose (e.g. `CopyOnWriteArrayList`).
|
||||
|
||||
What you'll usually need to change:
|
||||
- `HashSet` -> `ObjectSet`
|
||||
- `HashMap` -> `ObjectMap`
|
||||
- `List` / `ArrayList` / `Stack` -> `Seq`
|
||||
- `java.util.Queue` -> `arc.struct.Queue`
|
||||
- *Many others*
|
||||
|
||||
|
||||
#### 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.
|
||||
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.
|
||||
27
README.md
@@ -1,29 +1,36 @@
|
||||

|
||||

|
||||
|
||||
[](https://travis-ci.org/Anuken/Mindustry)
|
||||
[](https://discord.gg/mindustry)
|
||||
[](https://discord.gg/mindustry)
|
||||
|
||||
A sandbox tower defense game written in Java.
|
||||
|
||||
_[Trello Board](https://trello.com/b/aE2tcUwF/mindustry-40-plans)_
|
||||
_[Wiki](https://mindustrygame.github.io/wiki)_
|
||||
_[Wiki](https://mindustrygame.github.io/wiki)_
|
||||
_[Javadoc](https://mindustrygame.github.io/docs/)_
|
||||
|
||||
### Contributing
|
||||
|
||||
See [CONTRIBUTING](CONTRIBUTING.md).
|
||||
|
||||
### Building
|
||||
|
||||
Bleeding-edge live builds are generated automatically for every commit. You can see them [here](https://github.com/Anuken/MindustryBuilds/releases). Old builds might still be on [jenkins](https://jenkins.hellomouse.net/job/mindustry/).
|
||||
|
||||
If you'd rather compile on your own, follow these instructions.
|
||||
First, make sure you have [Java 8](https://www.java.com/en/download/) and [JDK 8](https://adoptopenjdk.net/) installed. Open a terminal in the root directory, `cd` to the Mindustry folder and run the following commands:
|
||||
First, make sure you have [JDK 14](https://adoptopenjdk.net/) installed. Open a terminal in the root directory, `cd` to the Mindustry folder and run the following commands:
|
||||
|
||||
#### Windows
|
||||
|
||||
_Running:_ `gradlew desktop:run`
|
||||
_Building:_ `gradlew desktop:dist`
|
||||
_Running:_ `gradlew.bat desktop:run`
|
||||
_Building:_ `gradlew.bat desktop:dist`
|
||||
_Sprite Packing:_ `gradlew.bat tools:pack`
|
||||
|
||||
#### Linux/Mac OS
|
||||
|
||||
_Running:_ `./gradlew desktop:run`
|
||||
_Building:_ `./gradlew desktop:dist`
|
||||
_Building:_ `./gradlew desktop:dist`
|
||||
_Sprite Packing:_ `./gradlew tools:pack`
|
||||
|
||||
#### Server
|
||||
|
||||
@@ -34,7 +41,7 @@ Server builds are bundled with each released build (in Releases). If you'd rathe
|
||||
1. Install the Android SDK [here.](https://developer.android.com/studio#downloads) Make sure you're downloading the "Command line tools only", as Android Studio is not required.
|
||||
2. Create a file named `local.properties` inside the Mindustry directory, with its contents looking like this: `sdk.dir=<Path to Android SDK you just downloaded, without these bracket>`. For example, if you're on Windows and installed the tools to C:\\tools, your local.properties would contain `sdk.dir=C:\\tools` (*note the double backslashes are required instead of single ones!*).
|
||||
3. Run `gradlew android:assembleDebug` (or `./gradlew` if on linux/mac). This will create an unsigned APK in `android/build/outputs/apk`.
|
||||
4. (Optional) To debug the application on a connected phone, do `gradlew android:installDebug android:run`. It is **highly recommended** to use IntelliJ for this instead, however.
|
||||
4. (Optional) To debug the application on a connected phone, do `gradlew android:installDebug android:run`. It is **highly recommended** to use IntelliJ for this instead.
|
||||
|
||||
##### Troubleshooting
|
||||
|
||||
@@ -45,6 +52,10 @@ If the terminal returns `Permission denied` or `Command not found` on Mac/Linux,
|
||||
Gradle may take up to several minutes to download files. Be patient. <br>
|
||||
After building, the output .JAR file should be in `/desktop/build/libs/Mindustry.jar` for desktop builds, and in `/server/build/libs/server-release.jar` for server builds.
|
||||
|
||||
### Feature Requests
|
||||
|
||||
Post feature requests and feedback [here](https://github.com/Anuken/Mindustry-Suggestions/issues/new/choose).
|
||||
|
||||
### Downloads
|
||||
|
||||
[<img src="https://static.itch.io/images/badge.svg"
|
||||
|
||||
26
SERVERLIST.md
Normal file
@@ -0,0 +1,26 @@
|
||||
### 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.json) in this repository.
|
||||
|
||||
You may want to add your server to this list. The steps for getting this done are as follows:
|
||||
|
||||
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. **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.
|
||||
3. **Get some good maps.** *(optional, but highly recommended)*. Add some maps to your server and set the map rotation to custom-only. You can get maps from the Steam workshop by subscribing and exporting them; using the `#maps` channel on Discord is also an option.
|
||||
4. **Check your server configuration.** *(optional)* I would recommend adding a message rate limit of 1 second (`config messageRateLimit 1`), and disabling connect/disconnect messages to reduce spam (`config showConnectMessages false`).
|
||||
5. 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.json), then add a JSON object with a single key, indicating your server address.
|
||||
For example, if your server address is `google.com`, you would add a comma after the last entry and insert:
|
||||
```json
|
||||
{
|
||||
"address": "google.com"
|
||||
}
|
||||
```
|
||||
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.
|
||||
@@ -3,6 +3,7 @@
|
||||
package="io.anuke.mindustry">
|
||||
|
||||
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
|
||||
<uses-feature android:name="android.hardware.type.pc" android:required="false" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
@@ -13,12 +14,13 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:isGame="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:appCategory="game"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/GdxTheme" android:fullBackupContent="@xml/backup_rules">
|
||||
android:theme="@style/ArcTheme" android:fullBackupContent="@xml/backup_rules">
|
||||
<meta-data android:name="android.max_aspect" android:value="2.1"/>
|
||||
<activity
|
||||
android:name="io.anuke.mindustry.AndroidLauncher"
|
||||
android:name="mindustry.android.AndroidLauncher"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="user"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout">
|
||||
|
||||
@@ -8,6 +8,8 @@ buildscript{
|
||||
}
|
||||
|
||||
dependencies{
|
||||
//IMPORTANT NOTICE: any version of the plugin after 3.4.1 will break builds for every API level < 24, perhaps even higher.
|
||||
//it appears abstract methods don't get desugared properly (if at all)
|
||||
classpath 'com.android.tools.build:gradle:3.4.1'
|
||||
}
|
||||
}
|
||||
@@ -19,25 +21,18 @@ configurations{ natives }
|
||||
repositories{
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven{
|
||||
url "https://maven.google.com"
|
||||
}
|
||||
maven{ url "https://maven.google.com" }
|
||||
}
|
||||
|
||||
dependencies{
|
||||
implementation project(":core")
|
||||
|
||||
implementation arcModule("backends:backend-android")
|
||||
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi"
|
||||
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a"
|
||||
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-arm64-v8a"
|
||||
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86"
|
||||
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86_64"
|
||||
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi"
|
||||
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi-v7a"
|
||||
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-arm64-v8a"
|
||||
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86"
|
||||
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86_64"
|
||||
implementation 'com.jakewharton.android.repackaged:dalvik-dx:9.0.0_r3'
|
||||
|
||||
natives "com.github.Anuken.Arc:natives-android:${getArcHash()}"
|
||||
natives "com.github.Anuken.Arc:natives-freetype-android:${getArcHash()}"
|
||||
natives "com.github.Anuken.Arc:natives-box2d-android:${getArcHash()}"
|
||||
}
|
||||
|
||||
task deploy(type: Copy){
|
||||
@@ -49,7 +44,7 @@ task deploy(type: Copy){
|
||||
}
|
||||
|
||||
android{
|
||||
buildToolsVersion '29.0.2'
|
||||
buildToolsVersion '29.0.3'
|
||||
compileSdkVersion 29
|
||||
sourceSets{
|
||||
main{
|
||||
@@ -69,7 +64,7 @@ android{
|
||||
}
|
||||
|
||||
defaultConfig{
|
||||
Properties props = new Properties().with{p -> p.load(file('../core/assets/version.properties').newReader()); return p }
|
||||
Properties props = loadVersionProps()
|
||||
Integer vcode = props['androidBuildCode']?.toInteger() ?: 1
|
||||
def versionNameResult = "$versionNumber-$versionType-${getBuildVersion().replace(" ", "-")}"
|
||||
|
||||
@@ -118,25 +113,11 @@ android{
|
||||
// the natives configuration, and extracts them to the proper libs/ folders
|
||||
// so they get packed with the APK.
|
||||
task copyAndroidNatives(){
|
||||
file("libs/armeabi/").mkdirs()
|
||||
file("libs/armeabi-v7a/").mkdirs()
|
||||
file("libs/arm64-v8a/").mkdirs()
|
||||
file("libs/x86_64/").mkdirs()
|
||||
file("libs/x86/").mkdirs()
|
||||
|
||||
configurations.natives.files.each{ jar ->
|
||||
def outputDir = null
|
||||
if(jar.name.endsWith("natives-arm64-v8a.jar")) outputDir = file("libs/arm64-v8a")
|
||||
if(jar.name.endsWith("natives-armeabi-v7a.jar")) outputDir = file("libs/armeabi-v7a")
|
||||
if(jar.name.endsWith("natives-armeabi.jar")) outputDir = file("libs/armeabi")
|
||||
if(jar.name.endsWith("natives-x86_64.jar")) outputDir = file("libs/x86_64")
|
||||
if(jar.name.endsWith("natives-x86.jar")) outputDir = file("libs/x86")
|
||||
if(outputDir != null){
|
||||
copy{
|
||||
from zipTree(jar)
|
||||
into outputDir
|
||||
include "*.so"
|
||||
}
|
||||
copy{
|
||||
from zipTree(jar)
|
||||
into file("libs/")
|
||||
include "**"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,5 +141,5 @@ task run(type: Exec){
|
||||
}
|
||||
|
||||
def adb = path + "/platform-tools/adb"
|
||||
commandLine "$adb", 'shell', 'am', 'start', '-n', 'io.anuke.mindustry/io.anuke.mindustry.AndroidLauncher'
|
||||
}
|
||||
commandLine "$adb", 'shell', 'am', 'start', '-n', 'io.anuke.mindustry/mindustry.android.AndroidLauncher'
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# To enable ProGuard in your project, edit project.properties
|
||||
# to define the proguard.config property as described in that file.
|
||||
#
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in ${sdk.dir}/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the ProGuard
|
||||
# include property in project.properties.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
-verbose
|
||||
-verbose
|
||||
-ignorewarnings
|
||||
-keep class io.anuke.mindustry.game.Rules
|
||||
-keep class io.anuke.mindustry.desktop.DesktopLauncher
|
||||
-keepclasseswithmembers public class * {
|
||||
public static void main(java.lang.String[]);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
# Project target.
|
||||
target=android-19
|
||||
@@ -2,4 +2,4 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
</adaptive-icon>
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
</adaptive-icon>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="GdxTheme" parent="android:Theme.Material.NoActionBar">
|
||||
<style name="ArcTheme" parent="android:Theme.Material.NoActionBar">
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||
<item name="android:windowAnimationStyle">@android:style/Animation</item>
|
||||
@@ -8,4 +8,4 @@
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
<string name="app_name">Mindustry</string>
|
||||
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<resources>
|
||||
|
||||
<style name="GdxTheme" parent="android:Theme">
|
||||
<style name="ArcTheme" parent="android:Theme">
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||
<item name="android:windowAnimationStyle">@android:style/Animation</item>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<full-backup-content>
|
||||
|
||||
</full-backup-content>
|
||||
</full-backup-content>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.anuke.mindustry;
|
||||
package mindustry.android;
|
||||
|
||||
import android.*;
|
||||
import android.app.*;
|
||||
@@ -9,23 +9,26 @@ import android.os.Build.*;
|
||||
import android.os.*;
|
||||
import android.provider.Settings.*;
|
||||
import android.telephony.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.backends.android.surfaceview.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.serialization.*;
|
||||
import io.anuke.mindustry.game.Saves.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.mod.*;
|
||||
import io.anuke.mindustry.ui.dialogs.*;
|
||||
import arc.*;
|
||||
import arc.backend.android.*;
|
||||
import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import arc.util.serialization.*;
|
||||
import mindustry.*;
|
||||
import mindustry.game.Saves.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.net.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.System;
|
||||
import java.lang.Thread.*;
|
||||
import java.util.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
|
||||
public class AndroidLauncher extends AndroidApplication{
|
||||
public static final int PERMISSION_REQUEST_CODE = 1;
|
||||
@@ -35,6 +38,20 @@ public class AndroidLauncher extends AndroidApplication{
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState){
|
||||
UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
|
||||
Thread.setDefaultUncaughtExceptionHandler((thread, error) -> {
|
||||
CrashSender.log(error);
|
||||
|
||||
//try to forward exception to system handler
|
||||
if(handler != null){
|
||||
handler.uncaughtException(thread, error);
|
||||
}else{
|
||||
error.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
if(doubleScaleTablets && isTablet(this.getContext())){
|
||||
Scl.setAddition(0.5f);
|
||||
@@ -48,40 +65,27 @@ public class AndroidLauncher extends AndroidApplication{
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUUID(){
|
||||
try{
|
||||
String s = Secure.getString(getContext().getContentResolver(), Secure.ANDROID_ID);
|
||||
int len = s.length();
|
||||
byte[] data = new byte[len / 2];
|
||||
for(int i = 0; i < len; i += 2){
|
||||
data[i / 2] = (byte)((Character.digit(s.charAt(i), 16) << 4)
|
||||
+ Character.digit(s.charAt(i + 1), 16));
|
||||
}
|
||||
String result = new String(Base64Coder.encode(data));
|
||||
if(result.equals("AAAAAAAAAOA=")) throw new RuntimeException("Bad UUID.");
|
||||
return result;
|
||||
}catch(Exception e){
|
||||
return super.getUUID();
|
||||
}
|
||||
public rhino.Context getScriptContext(){
|
||||
return AndroidRhinoContext.enter(getContext().getCacheDir());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shareFile(FileHandle file){
|
||||
public void shareFile(Fi file){
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showFileChooser(boolean open, String extension, Consumer<FileHandle> cons){
|
||||
public void showFileChooser(boolean open, String extension, Cons<Fi> cons){
|
||||
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") ? "application/zip" : "*/*");
|
||||
intent.setType(extension.equals("zip") && !open ? "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.accept(new FileHandle(uri.getPath()){
|
||||
Core.app.post(() -> Core.app.post(() -> cons.get(new Fi(uri.getPath()){
|
||||
@Override
|
||||
public InputStream read(){
|
||||
try{
|
||||
@@ -104,11 +108,11 @@ public class AndroidLauncher extends AndroidApplication{
|
||||
});
|
||||
}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(open ? "$open" : "$save", file -> file.extension().equalsIgnoreCase(extension), open, file -> {
|
||||
chooser = new FileChooser(open ? "@open" : "@save", file -> file.extension().equalsIgnoreCase(extension), open, file -> {
|
||||
if(!open){
|
||||
cons.accept(file.parent().child(file.nameWithoutExtension() + "." + extension));
|
||||
cons.get(file.parent().child(file.nameWithoutExtension() + "." + extension));
|
||||
}else{
|
||||
cons.accept(file);
|
||||
cons.get(file);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -137,11 +141,35 @@ public class AndroidLauncher extends AndroidApplication{
|
||||
|
||||
}, new AndroidApplicationConfiguration(){{
|
||||
useImmersiveMode = true;
|
||||
depth = 0;
|
||||
hideStatusBar = true;
|
||||
errorHandler = ModCrashHandler::handle;
|
||||
stencil = 8;
|
||||
}});
|
||||
checkFiles(getIntent());
|
||||
|
||||
|
||||
//new external folder
|
||||
Fi data = Core.files.absolute(getContext().getExternalFilesDir(null).getAbsolutePath());
|
||||
Core.settings.setDataDirectory(data);
|
||||
|
||||
//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...");
|
||||
|
||||
try{
|
||||
//current local storage folder
|
||||
Fi src = Core.files.absolute(Core.files.getLocalStoragePath());
|
||||
for(Fi fi : src.list()){
|
||||
fi.copyTo(data);
|
||||
}
|
||||
//create marker
|
||||
Core.files.local("files_moved").writeString("files moved to " + data);
|
||||
Core.files.local("files_moved_103").writeString("files moved again");
|
||||
Log.info("Files moved.");
|
||||
}catch(Throwable t){
|
||||
Log.err("Failed to move files!");
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -181,20 +209,20 @@ public class AndroidLauncher extends AndroidApplication{
|
||||
Core.app.post(() -> Core.app.post(() -> {
|
||||
if(save){ //open save
|
||||
System.out.println("Opening save.");
|
||||
FileHandle file = Core.files.local("temp-save." + saveExtension);
|
||||
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);
|
||||
ui.showException("@save.import.fail", e);
|
||||
}
|
||||
}else{
|
||||
ui.showErrorMessage("$save.import.invalid");
|
||||
ui.showErrorMessage("@save.import.invalid");
|
||||
}
|
||||
}else if(map){ //open map
|
||||
FileHandle file = Core.files.local("temp-map." + mapExtension);
|
||||
Fi file = Core.files.local("temp-map." + mapExtension);
|
||||
file.write(inStream, false);
|
||||
Core.app.post(() -> {
|
||||
System.out.println("Opening map.");
|
||||
227
android/src/mindustry/android/AndroidRhinoContext.java
Normal file
@@ -0,0 +1,227 @@
|
||||
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.*;
|
||||
import com.android.dx.dex.*;
|
||||
import com.android.dx.dex.cf.*;
|
||||
import com.android.dx.dex.file.DexFile;
|
||||
import com.android.dx.merge.*;
|
||||
import dalvik.system.*;
|
||||
import rhino.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.*;
|
||||
|
||||
/**
|
||||
* Helps to prepare a Rhino Context for usage on android.
|
||||
* @author F43nd1r
|
||||
* @since 11.01.2016
|
||||
*/
|
||||
public class AndroidRhinoContext{
|
||||
|
||||
/**
|
||||
* call this instead of {@link Context#enter()}
|
||||
* @return a context prepared for android
|
||||
*/
|
||||
public static Context enter(File cacheDirectory){
|
||||
if(!SecurityController.hasGlobal())
|
||||
SecurityController.initGlobal(new SecurityController(){
|
||||
@Override
|
||||
public GeneratedClassLoader createClassLoader(ClassLoader classLoader, Object o){
|
||||
return Context.getCurrentContext().createClassLoader(classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDynamicSecurityDomain(Object o){
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
AndroidContextFactory factory;
|
||||
if(!ContextFactory.hasExplicitGlobal()){
|
||||
factory = new AndroidContextFactory(cacheDirectory);
|
||||
ContextFactory.getGlobalSetter().setContextFactoryGlobal(factory);
|
||||
}else if(!(ContextFactory.getGlobal() instanceof AndroidContextFactory)){
|
||||
throw new IllegalStateException("Cannot initialize factory for Android Rhino: There is already another factory");
|
||||
}else{
|
||||
factory = (AndroidContextFactory)ContextFactory.getGlobal();
|
||||
}
|
||||
|
||||
return factory.enterContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the classLoader used is correct
|
||||
* @author F43nd1r
|
||||
* @since 11.01.2016
|
||||
*/
|
||||
public static class AndroidContextFactory extends ContextFactory{
|
||||
private final File cacheDirectory;
|
||||
|
||||
/**
|
||||
* Create a new factory. It will cache generated code in the given directory
|
||||
* @param cacheDirectory the cache directory
|
||||
*/
|
||||
public AndroidContextFactory(File cacheDirectory){
|
||||
this.cacheDirectory = cacheDirectory;
|
||||
initApplicationClassLoader(createClassLoader(AndroidContextFactory.class.getClassLoader()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ClassLoader which is able to deal with bytecode
|
||||
* @param parent the parent of the create classloader
|
||||
* @return a new ClassLoader
|
||||
*/
|
||||
@Override
|
||||
public BaseAndroidClassLoader createClassLoader(ClassLoader parent){
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
|
||||
return new InMemoryAndroidClassLoader(parent);
|
||||
}
|
||||
return new FileAndroidClassLoader(parent, cacheDirectory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onContextReleased(final Context cx){
|
||||
super.onContextReleased(cx);
|
||||
((BaseAndroidClassLoader)cx.getApplicationClassLoader()).reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles java bytecode to dex bytecode and loads it
|
||||
* @author F43nd1r
|
||||
* @since 11.01.2016
|
||||
*/
|
||||
abstract static class BaseAndroidClassLoader extends ClassLoader implements GeneratedClassLoader{
|
||||
|
||||
public BaseAndroidClassLoader(ClassLoader parent){
|
||||
super(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> defineClass(String name, byte[] data){
|
||||
try{
|
||||
DexOptions dexOptions = new DexOptions();
|
||||
DexFile dexFile = new DexFile(dexOptions);
|
||||
DirectClassFile classFile = new DirectClassFile(data, name.replace('.', '/') + ".class", true);
|
||||
classFile.setAttributeFactory(StdAttributeFactory.THE_ONE);
|
||||
classFile.getMagic();
|
||||
DxContext context = new DxContext();
|
||||
dexFile.add(CfTranslator.translate(context, classFile, null, new CfOptions(), dexOptions, dexFile));
|
||||
Dex dex = new Dex(dexFile.toDex(null, false));
|
||||
Dex oldDex = getLastDex();
|
||||
if(oldDex != null){
|
||||
dex = new DexMerger(new Dex[]{dex, oldDex}, CollisionPolicy.KEEP_FIRST, context).merge();
|
||||
}
|
||||
return loadClass(dex, name);
|
||||
}catch(IOException | ClassNotFoundException e){
|
||||
throw new FatalLoadingException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Class<?> loadClass(Dex dex, String name) throws ClassNotFoundException;
|
||||
|
||||
protected abstract Dex getLastDex();
|
||||
|
||||
protected abstract void reset();
|
||||
|
||||
@Override
|
||||
public void linkClass(Class<?> aClass){}
|
||||
|
||||
@Override
|
||||
public Class<?> loadClass(String name, boolean resolve)
|
||||
throws ClassNotFoundException{
|
||||
Class<?> loadedClass = findLoadedClass(name);
|
||||
if(loadedClass == null){
|
||||
Dex dex = getLastDex();
|
||||
if(dex != null){
|
||||
loadedClass = loadClass(dex, name);
|
||||
}
|
||||
if(loadedClass == null){
|
||||
loadedClass = getParent().loadClass(name);
|
||||
}
|
||||
}
|
||||
return loadedClass;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Might be thrown in any Rhino method that loads bytecode if the loading failed. */
|
||||
public static class FatalLoadingException extends RuntimeException{
|
||||
FatalLoadingException(Throwable t){
|
||||
super("Failed to define class", t);
|
||||
}
|
||||
}
|
||||
|
||||
static class FileAndroidClassLoader extends BaseAndroidClassLoader{
|
||||
private static int instanceCounter = 0;
|
||||
private final File dexFile;
|
||||
|
||||
public FileAndroidClassLoader(ClassLoader parent, File cacheDir){
|
||||
super(parent);
|
||||
int id = instanceCounter++;
|
||||
dexFile = new File(cacheDir, id + ".dex");
|
||||
cacheDir.mkdirs();
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(Dex dex, String name) throws ClassNotFoundException{
|
||||
try{
|
||||
dex.writeTo(dexFile);
|
||||
}catch(IOException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
android.content.Context context = ((AndroidApplication) Core.app).getContext();
|
||||
return new DexClassLoader(dexFile.getPath(), VERSION.SDK_INT >= 21 ? context.getCodeCacheDir().getPath() : context.getCacheDir().getAbsolutePath(), null, getParent()).loadClass(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dex getLastDex(){
|
||||
if(dexFile.exists()){
|
||||
try{
|
||||
return new Dex(dexFile);
|
||||
}catch(IOException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reset(){
|
||||
dexFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
static class InMemoryAndroidClassLoader extends BaseAndroidClassLoader{
|
||||
private Dex last;
|
||||
|
||||
public InMemoryAndroidClassLoader(ClassLoader parent){
|
||||
super(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(Dex dex, String name) throws ClassNotFoundException{
|
||||
last = dex;
|
||||
return new InMemoryDexClassLoader(ByteBuffer.wrap(dex.getBytes()), getParent()).loadClass(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dex getLastDex(){
|
||||
return last;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reset(){
|
||||
last = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,2 @@
|
||||
apply plugin: "java"
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
sourceSets.main.java.srcDirs = ["src/main/java/"]
|
||||
sourceSets.main.resources.srcDirs = ["src/main/resources/"]
|
||||
|
||||
sourceSets.main.resources.srcDirs = ["src/main/resources/"]
|
||||
@@ -1,141 +0,0 @@
|
||||
package io.anuke.annotations;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
public class Annotations{
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface StyleDefaults {
|
||||
}
|
||||
|
||||
/** Indicates that a method should always call its super version. */
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface CallSuper{
|
||||
|
||||
}
|
||||
|
||||
/** Annotation that allows overriding CallSuper annotation. To be used on method that overrides method with CallSuper annotation from parent class.*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface OverrideCallSuper {
|
||||
}
|
||||
|
||||
/** Marks a class as serializable. */
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Serialize{
|
||||
|
||||
}
|
||||
|
||||
/** Marks a class as a special value type struct. Class name must end in 'Struct'. */
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Struct{
|
||||
|
||||
}
|
||||
|
||||
/** Marks a field of a struct. Optional. */
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface StructField{
|
||||
/** Size of a struct field in bits. Not valid on booleans or floating point numbers. */
|
||||
int value();
|
||||
}
|
||||
|
||||
public enum PacketPriority{
|
||||
/** Gets put in a queue and processed if not connected. */
|
||||
normal,
|
||||
/** Gets handled immediately, regardless of connection status. */
|
||||
high,
|
||||
/** Does not get handled unless client is connected. */
|
||||
low
|
||||
}
|
||||
|
||||
/** A set of two booleans, one specifying server and one specifying client. */
|
||||
public enum Loc{
|
||||
/** Method can only be invoked on the client from the server. */
|
||||
server(true, false),
|
||||
/** Method can only be invoked on the server from the client. */
|
||||
client(false, true),
|
||||
/** 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){
|
||||
this.isServer = server;
|
||||
this.isClient = client;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Variant{
|
||||
/** Method can only be invoked targeting one player. */
|
||||
one(true, false),
|
||||
/** Method can only be invoked targeting all players. */
|
||||
all(false, true),
|
||||
/** Method targets both one player and all players. */
|
||||
both(true, true);
|
||||
|
||||
public final boolean isOne, isAll;
|
||||
|
||||
Variant(boolean isOne, boolean isAll){
|
||||
this.isOne = isOne;
|
||||
this.isAll = isAll;
|
||||
}
|
||||
}
|
||||
|
||||
/** Marks a method as invokable remotely across a server/client connection. */
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Remote{
|
||||
/** 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. */
|
||||
Variant variants() default Variant.all;
|
||||
|
||||
/** The local locations where this method is called locally, when invoked. */
|
||||
Loc called() default Loc.none;
|
||||
|
||||
/** Whether to forward this packet to all other clients upon recieval. Client only. */
|
||||
boolean forward() default false;
|
||||
|
||||
/**
|
||||
* Whether the packet for this method is sent with UDP instead of TCP.
|
||||
* UDP is faster, but is prone to packet loss and duplication.
|
||||
*/
|
||||
boolean unreliable() default false;
|
||||
|
||||
/** Priority of this event. */
|
||||
PacketPriority priority() default PacketPriority.normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that this method will be used to write classes of the type returned by {@link #value()}.<br>
|
||||
* This method must return void and have two parameters, the first being of type {@link java.nio.ByteBuffer} and the second
|
||||
* being the type returned by {@link #value()}.
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface WriteClass{
|
||||
Class<?> value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that this method will be used to read classes of the type returned by {@link #value()}. <br>
|
||||
* This method must return the type returned by {@link #value()},
|
||||
* and have one parameter, being of type {@link java.nio.ByteBuffer}.
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface ReadClass{
|
||||
Class<?> value();
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
package io.anuke.annotations;
|
||||
|
||||
import com.squareup.javapoet.*;
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.*;
|
||||
import javax.lang.model.element.*;
|
||||
import javax.tools.Diagnostic.*;
|
||||
import javax.tools.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||
@SupportedAnnotationTypes("io.anuke.annotations.Annotations.StyleDefaults")
|
||||
public class AssetsAnnotationProcessor extends AbstractProcessor{
|
||||
/** Name of the base package to put all the generated classes. */
|
||||
private static final String packageName = "io.anuke.mindustry.gen";
|
||||
private String path;
|
||||
private int round;
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment processingEnv){
|
||||
super.init(processingEnv);
|
||||
//put all relevant utils into utils class
|
||||
Utils.typeUtils = processingEnv.getTypeUtils();
|
||||
Utils.elementUtils = processingEnv.getElementUtils();
|
||||
Utils.filer = processingEnv.getFiler();
|
||||
Utils.messager = processingEnv.getMessager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
||||
if(round++ != 0) return false; //only process 1 round
|
||||
|
||||
try{
|
||||
path = Paths.get(Utils.filer.createResource(StandardLocation.CLASS_OUTPUT, "no", "no")
|
||||
.toUri().toURL().toString().substring(System.getProperty("os.name").contains("Windows") ? 6 : "file:".length()))
|
||||
.getParent().getParent().getParent().getParent().getParent().getParent().toString();
|
||||
path = path.replace("%20", " ");
|
||||
|
||||
processSounds("Sounds", path + "/assets/sounds", "io.anuke.arc.audio.Sound");
|
||||
processSounds("Musics", path + "/assets/music", "io.anuke.arc.audio.Music");
|
||||
processUI(roundEnv.getElementsAnnotatedWith(StyleDefaults.class));
|
||||
|
||||
return true;
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
void processUI(Set<? extends Element> elements) throws Exception{
|
||||
String[] iconSizes = {"small", "smaller", "tiny"};
|
||||
|
||||
TypeSpec.Builder type = TypeSpec.classBuilder("Tex").addModifiers(Modifier.PUBLIC);
|
||||
TypeSpec.Builder ictype = TypeSpec.classBuilder("Icon").addModifiers(Modifier.PUBLIC);
|
||||
MethodSpec.Builder load = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
MethodSpec.Builder loadStyles = MethodSpec.methodBuilder("loadStyles").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
MethodSpec.Builder icload = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
String resources = path + "/assets-raw/sprites/ui";
|
||||
Files.walk(Paths.get(resources)).forEach(p -> {
|
||||
if(Files.isDirectory(p) || p.getFileName().toString().equals(".DS_Store")) return;
|
||||
|
||||
String filename = p.getFileName().toString();
|
||||
filename = filename.substring(0, filename.indexOf("."));
|
||||
|
||||
ArrayList<String> names = new ArrayList<>();
|
||||
names.add("");
|
||||
if(filename.contains("icon")){
|
||||
names.addAll(Arrays.asList(iconSizes));
|
||||
}
|
||||
|
||||
for(String suffix : names){
|
||||
suffix = suffix.isEmpty() ? "" : "-" + suffix;
|
||||
|
||||
String sfilen = filename + suffix;
|
||||
String dtype = p.getFileName().toString().endsWith(".9.png") ? "io.anuke.arc.scene.style.NinePatchDrawable" : "io.anuke.arc.scene.style.TextureRegionDrawable";
|
||||
|
||||
String varname = capitalize(sfilen);
|
||||
TypeSpec.Builder ttype = type;
|
||||
MethodSpec.Builder tload = load;
|
||||
if(varname.startsWith("icon")){
|
||||
varname = varname.substring("icon".length());
|
||||
varname = Character.toLowerCase(varname.charAt(0)) + varname.substring(1);
|
||||
ttype = ictype;
|
||||
tload = icload;
|
||||
if(SourceVersion.isKeyword(varname)) varname += "i";
|
||||
}
|
||||
|
||||
if(SourceVersion.isKeyword(varname)) varname += "s";
|
||||
|
||||
ttype.addField(ClassName.bestGuess(dtype), varname, Modifier.STATIC, Modifier.PUBLIC);
|
||||
tload.addStatement(varname + " = ("+dtype+")io.anuke.arc.Core.atlas.drawable($S)", sfilen);
|
||||
}
|
||||
});
|
||||
|
||||
for(Element elem : elements){
|
||||
TypeElement t = (TypeElement)elem;
|
||||
t.getEnclosedElements().stream().filter(e -> e.getKind() == ElementKind.FIELD).forEach(field -> {
|
||||
String fname = field.getSimpleName().toString();
|
||||
if(fname.startsWith("default")){
|
||||
loadStyles.addStatement("io.anuke.arc.Core.scene.addStyle(" + field.asType().toString() + ".class, io.anuke.mindustry.ui.Styles." + fname + ")");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ictype.addMethod(icload.build());
|
||||
JavaFile.builder(packageName, ictype.build()).build().writeTo(Utils.filer);
|
||||
|
||||
type.addMethod(load.build());
|
||||
type.addMethod(loadStyles.build());
|
||||
JavaFile.builder(packageName, type.build()).build().writeTo(Utils.filer);
|
||||
}
|
||||
|
||||
void processSounds(String classname, String path, String rtype) throws Exception{
|
||||
TypeSpec.Builder type = TypeSpec.classBuilder(classname).addModifiers(Modifier.PUBLIC);
|
||||
MethodSpec.Builder dispose = MethodSpec.methodBuilder("dispose").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
MethodSpec.Builder loadBegin = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
|
||||
HashSet<String> names = new HashSet<>();
|
||||
Files.list(Paths.get(path)).forEach(p -> {
|
||||
String fname = p.getFileName().toString();
|
||||
String name = p.getFileName().toString();
|
||||
name = name.substring(0, name.indexOf("."));
|
||||
|
||||
if(names.contains(name)){
|
||||
Utils.messager.printMessage(Kind.ERROR, "Duplicate file name: " + p.toString() + "!");
|
||||
}else{
|
||||
names.add(name);
|
||||
}
|
||||
|
||||
if(SourceVersion.isKeyword(name)){
|
||||
name = name + "s";
|
||||
}
|
||||
|
||||
String filepath = path.substring(path.lastIndexOf("/") + 1) + "/" + fname;
|
||||
|
||||
String filename = "io.anuke.arc.Core.app.getType() != io.anuke.arc.Application.ApplicationType.iOS ? \"" + filepath + "\" : \"" + filepath.replace(".ogg", ".mp3")+"\"";
|
||||
|
||||
loadBegin.addStatement("io.anuke.arc.Core.assets.load("+filename +", "+rtype+".class).loaded = a -> " + name + " = ("+rtype+")a", filepath, filepath.replace(".ogg", ".mp3"));
|
||||
|
||||
dispose.addStatement("io.anuke.arc.Core.assets.unload(" + filename + ")");
|
||||
dispose.addStatement(name + " = null");
|
||||
type.addField(FieldSpec.builder(ClassName.bestGuess(rtype), name, Modifier.STATIC, Modifier.PUBLIC).initializer("new io.anuke.arc.audio.mock.Mock" + rtype.substring(rtype.lastIndexOf(".") + 1)+ "()").build());
|
||||
});
|
||||
|
||||
if(classname.equals("Sounds")){
|
||||
type.addField(FieldSpec.builder(ClassName.bestGuess(rtype), "none", Modifier.STATIC, Modifier.PUBLIC).initializer("new io.anuke.arc.audio.mock.Mock" + rtype.substring(rtype.lastIndexOf(".") + 1)+ "()").build());
|
||||
}
|
||||
|
||||
type.addMethod(loadBegin.build());
|
||||
type.addMethod(dispose.build());
|
||||
JavaFile.builder(packageName, type.build()).build().writeTo(Utils.filer);
|
||||
}
|
||||
|
||||
static String capitalize(String s){
|
||||
StringBuilder result = new StringBuilder(s.length());
|
||||
|
||||
for(int i = 0; i < s.length(); i++){
|
||||
char c = s.charAt(i);
|
||||
if(c != '_' && c != '-'){
|
||||
if(i > 0 && (s.charAt(i - 1) == '_' || s.charAt(i - 1) == '-')){
|
||||
result.append(Character.toUpperCase(c));
|
||||
}else{
|
||||
result.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package io.anuke.annotations;
|
||||
|
||||
import com.sun.source.util.*;
|
||||
import com.sun.tools.javac.tree.*;
|
||||
import com.sun.tools.javac.tree.JCTree.*;
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.*;
|
||||
import javax.lang.model.element.*;
|
||||
import javax.tools.Diagnostic.*;
|
||||
import java.util.*;
|
||||
|
||||
@SupportedAnnotationTypes({"java.lang.Override"})
|
||||
public class CallSuperAnnotationProcessor extends AbstractProcessor{
|
||||
private Trees trees;
|
||||
|
||||
@Override
|
||||
public void init(ProcessingEnvironment pe){
|
||||
super.init(pe);
|
||||
trees = Trees.instance(pe);
|
||||
}
|
||||
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
||||
for(Element e : roundEnv.getElementsAnnotatedWith(Override.class)){
|
||||
if(e.getAnnotation(OverrideCallSuper.class) != null) return false;
|
||||
|
||||
CodeAnalyzerTreeScanner codeScanner = new CodeAnalyzerTreeScanner();
|
||||
codeScanner.setMethodName(e.getSimpleName().toString());
|
||||
|
||||
TreePath tp = trees.getPath(e.getEnclosingElement());
|
||||
codeScanner.scan(tp, trees);
|
||||
|
||||
if(codeScanner.isCallSuperUsed()){
|
||||
List list = codeScanner.getMethod().getBody().getStatements();
|
||||
|
||||
if(!doesCallSuper(list, codeScanner.getMethodName())){
|
||||
processingEnv.getMessager().printMessage(Kind.ERROR, "Overriding method '" + codeScanner.getMethodName() + "' must explicitly call super method from its parent class.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean doesCallSuper(List list, String methodName){
|
||||
for(Object object : list){
|
||||
if(object instanceof JCTree.JCExpressionStatement){
|
||||
JCTree.JCExpressionStatement expr = (JCExpressionStatement)object;
|
||||
String exprString = expr.toString();
|
||||
if(exprString.startsWith("super." + methodName) && exprString.endsWith(");")) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceVersion getSupportedSourceVersion(){
|
||||
return SourceVersion.RELEASE_8;
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
package io.anuke.annotations;
|
||||
|
||||
import com.sun.source.tree.*;
|
||||
import com.sun.source.util.TreePathScanner;
|
||||
import com.sun.source.util.Trees;
|
||||
import com.sun.tools.javac.code.Scope;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import com.sun.tools.javac.code.Symbol.MethodSymbol;
|
||||
import com.sun.tools.javac.code.Type.ClassType;
|
||||
import com.sun.tools.javac.tree.JCTree.JCIdent;
|
||||
import com.sun.tools.javac.tree.JCTree.JCTypeApply;
|
||||
import io.anuke.annotations.Annotations.CallSuper;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
class CodeAnalyzerTreeScanner extends TreePathScanner<Object, Trees> {
|
||||
private String methodName;
|
||||
private MethodTree method;
|
||||
private boolean callSuperUsed;
|
||||
|
||||
@Override
|
||||
public Object visitClass (ClassTree classTree, Trees trees) {
|
||||
Tree extendTree = classTree.getExtendsClause();
|
||||
|
||||
if (extendTree instanceof JCTypeApply) { //generic classes case
|
||||
JCTypeApply generic = (JCTypeApply) extendTree;
|
||||
extendTree = generic.clazz;
|
||||
}
|
||||
|
||||
if (extendTree instanceof JCIdent) {
|
||||
JCIdent tree = (JCIdent) extendTree;
|
||||
Scope members = tree.sym.members();
|
||||
|
||||
if (checkScope(members))
|
||||
return super.visitClass(classTree, trees);
|
||||
|
||||
if (checkSuperTypes((ClassType) tree.type))
|
||||
return super.visitClass(classTree, trees);
|
||||
|
||||
}
|
||||
callSuperUsed = false;
|
||||
|
||||
return super.visitClass(classTree, trees);
|
||||
}
|
||||
|
||||
public boolean checkSuperTypes (ClassType type) {
|
||||
if (type.supertype_field != null && type.supertype_field.tsym != null) {
|
||||
if (checkScope(type.supertype_field.tsym.members()))
|
||||
return true;
|
||||
else
|
||||
return checkSuperTypes((ClassType) type.supertype_field);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean checkScope (Scope members) {
|
||||
Iterable<Symbol> it;
|
||||
try{
|
||||
it = (Iterable<Symbol>)members.getClass().getMethod("getElements").invoke(members);
|
||||
}catch(Throwable t){
|
||||
try{
|
||||
it = (Iterable<Symbol>)members.getClass().getMethod("getSymbols").invoke(members);
|
||||
}catch(Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
for (Symbol s : it) {
|
||||
if (s instanceof MethodSymbol) {
|
||||
MethodSymbol ms = (MethodSymbol) s;
|
||||
|
||||
if (ms.getSimpleName().toString().equals(methodName)) {
|
||||
Annotation annotation = ms.getAnnotation(CallSuper.class);
|
||||
if (annotation != null) {
|
||||
callSuperUsed = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitMethod (MethodTree methodTree, Trees trees) {
|
||||
if (methodTree.getName().toString().equals(methodName))
|
||||
method = methodTree;
|
||||
|
||||
return super.visitMethod(methodTree, trees);
|
||||
}
|
||||
|
||||
public void setMethodName (String methodName) {
|
||||
this.methodName = methodName;
|
||||
}
|
||||
|
||||
public String getMethodName () {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
public MethodTree getMethod () {
|
||||
return method;
|
||||
}
|
||||
|
||||
public boolean isCallSuperUsed () {
|
||||
return callSuperUsed;
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package io.anuke.annotations;
|
||||
|
||||
import io.anuke.annotations.Annotations.ReadClass;
|
||||
import io.anuke.annotations.Annotations.WriteClass;
|
||||
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.type.MirroredTypeException;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class finds reader and writer methods annotated by the {@link io.anuke.annotations.Annotations.WriteClass}
|
||||
* and {@link io.anuke.annotations.Annotations.ReadClass} annotations.
|
||||
*/
|
||||
public class IOFinder{
|
||||
|
||||
/**
|
||||
* Finds all class serializers for all types and returns them. Logs errors when necessary.
|
||||
* Maps fully qualified class names to their serializers.
|
||||
*/
|
||||
public HashMap<String, ClassSerializer> findSerializers(RoundEnvironment env){
|
||||
HashMap<String, ClassSerializer> result = new HashMap<>();
|
||||
|
||||
//get methods with the types
|
||||
Set<? extends Element> writers = env.getElementsAnnotatedWith(WriteClass.class);
|
||||
Set<? extends Element> readers = env.getElementsAnnotatedWith(ReadClass.class);
|
||||
|
||||
//look for writers first
|
||||
for(Element writer : writers){
|
||||
WriteClass writean = writer.getAnnotation(WriteClass.class);
|
||||
String typeName = getValue(writean);
|
||||
|
||||
//make sure there's only one read method
|
||||
if(readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).count() > 1){
|
||||
Utils.messager.printMessage(Kind.ERROR, "Multiple writer methods for type '" + typeName + "'", writer);
|
||||
}
|
||||
|
||||
//make sure there's only one write method
|
||||
long count = readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).count();
|
||||
if(count == 0){
|
||||
Utils.messager.printMessage(Kind.ERROR, "Writer method does not have an accompanying reader: ", writer);
|
||||
}else if(count > 1){
|
||||
Utils.messager.printMessage(Kind.ERROR, "Writer method has multiple reader for type: ", writer);
|
||||
}
|
||||
|
||||
Element reader = readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).findFirst().get();
|
||||
|
||||
//add to result list
|
||||
result.put(typeName, new ClassSerializer(Utils.getMethodName(reader), Utils.getMethodName(writer), typeName));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getValue(WriteClass write){
|
||||
try{
|
||||
Class<?> type = write.value();
|
||||
return type.getName();
|
||||
}catch(MirroredTypeException e){
|
||||
return e.getTypeMirror().toString();
|
||||
}
|
||||
}
|
||||
|
||||
private String getValue(ReadClass read){
|
||||
try{
|
||||
Class<?> type = read.value();
|
||||
return type.getName();
|
||||
}catch(MirroredTypeException e){
|
||||
return e.getTypeMirror().toString();
|
||||
}
|
||||
}
|
||||
|
||||
/** Information about read/write methods for a specific class type. */
|
||||
public static class ClassSerializer{
|
||||
/** Fully qualified method name of the reader. */
|
||||
public final String readMethod;
|
||||
/** Fully qualified method name of the writer. */
|
||||
public final String writeMethod;
|
||||
/** Fully qualified class type name. */
|
||||
public final String classType;
|
||||
|
||||
public ClassSerializer(String readMethod, String writeMethod, String classType){
|
||||
this.readMethod = readMethod;
|
||||
this.writeMethod = writeMethod;
|
||||
this.classType = classType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
package io.anuke.annotations;
|
||||
|
||||
import com.squareup.javapoet.*;
|
||||
import io.anuke.annotations.Annotations.Loc;
|
||||
import io.anuke.annotations.Annotations.Remote;
|
||||
import io.anuke.annotations.IOFinder.ClassSerializer;
|
||||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.*;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/** The annotation processor for generating remote method call code. */
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||
@SupportedAnnotationTypes({
|
||||
"io.anuke.annotations.Annotations.Remote",
|
||||
"io.anuke.annotations.Annotations.WriteClass",
|
||||
"io.anuke.annotations.Annotations.ReadClass",
|
||||
})
|
||||
public class RemoteMethodAnnotationProcessor extends AbstractProcessor{
|
||||
/** Maximum size of each event packet. */
|
||||
public static final int maxPacketSize = 4096;
|
||||
/** Warning on top of each autogenerated file. */
|
||||
public static final String autogenWarning = "Autogenerated file. Do not modify!\n";
|
||||
/** Name of the base package to put all the generated classes. */
|
||||
private static final String packageName = "io.anuke.mindustry.gen";
|
||||
|
||||
/** Name of class that handles reading and invoking packets on the server. */
|
||||
private static final String readServerName = "RemoteReadServer";
|
||||
/** Name of class that handles reading and invoking packets on the client. */
|
||||
private static final String readClientName = "RemoteReadClient";
|
||||
/** Simple class name of generated class name. */
|
||||
private static final String callLocation = "Call";
|
||||
|
||||
/** Processing round number. */
|
||||
private int round;
|
||||
|
||||
//class serializers
|
||||
private HashMap<String, ClassSerializer> serializers;
|
||||
//all elements with the Remote annotation
|
||||
private Set<? extends Element> elements;
|
||||
//map of all classes to generate by name
|
||||
private HashMap<String, ClassEntry> classMap;
|
||||
//list of all method entries
|
||||
private ArrayList<MethodEntry> methods;
|
||||
//list of all method entries
|
||||
private ArrayList<ClassEntry> classes;
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment processingEnv){
|
||||
super.init(processingEnv);
|
||||
//put all relevant utils into utils class
|
||||
Utils.typeUtils = processingEnv.getTypeUtils();
|
||||
Utils.elementUtils = processingEnv.getElementUtils();
|
||||
Utils.filer = processingEnv.getFiler();
|
||||
Utils.messager = processingEnv.getMessager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
||||
if(round > 1) return false; //only process 2 rounds
|
||||
|
||||
round++;
|
||||
|
||||
try{
|
||||
|
||||
//round 1: find all annotations, generate *writers*
|
||||
if(round == 1){
|
||||
//get serializers
|
||||
serializers = new IOFinder().findSerializers(roundEnv);
|
||||
//last method ID used
|
||||
int lastMethodID = 0;
|
||||
//find all elements with the Remote annotation
|
||||
elements = roundEnv.getElementsAnnotatedWith(Remote.class);
|
||||
//map of all classes to generate by name
|
||||
classMap = new HashMap<>();
|
||||
//list of all method entries
|
||||
methods = new ArrayList<>();
|
||||
//list of all method entries
|
||||
classes = new ArrayList<>();
|
||||
|
||||
List<Element> orderedElements = new ArrayList<>(elements);
|
||||
orderedElements.sort(Comparator.comparing(Object::toString));
|
||||
|
||||
//create methods
|
||||
for(Element element : orderedElements){
|
||||
Remote annotation = element.getAnnotation(Remote.class);
|
||||
|
||||
//check for static
|
||||
if(!element.getModifiers().contains(Modifier.STATIC) || !element.getModifiers().contains(Modifier.PUBLIC)){
|
||||
Utils.messager.printMessage(Kind.ERROR, "All @Remote methods must be public and static: ", element);
|
||||
}
|
||||
|
||||
//can't generate none methods
|
||||
if(annotation.targets() == Loc.none){
|
||||
Utils.messager.printMessage(Kind.ERROR, "A @Remote method's targets() cannot be equal to 'none':", element);
|
||||
}
|
||||
|
||||
//get and create class entry if needed
|
||||
if(!classMap.containsKey(callLocation)){
|
||||
ClassEntry clas = new ClassEntry(callLocation);
|
||||
classMap.put(callLocation, clas);
|
||||
classes.add(clas);
|
||||
}
|
||||
|
||||
ClassEntry entry = classMap.get(callLocation);
|
||||
|
||||
//create and add entry
|
||||
MethodEntry method = new MethodEntry(entry.name, Utils.getMethodName(element), annotation.targets(), annotation.variants(),
|
||||
annotation.called(), annotation.unreliable(), annotation.forward(), lastMethodID++, (ExecutableElement)element, annotation.priority());
|
||||
|
||||
entry.methods.add(method);
|
||||
methods.add(method);
|
||||
}
|
||||
|
||||
//create read/write generators
|
||||
RemoteWriteGenerator writegen = new RemoteWriteGenerator(serializers);
|
||||
|
||||
//generate the methods to invoke (write)
|
||||
writegen.generateFor(classes, packageName);
|
||||
|
||||
return true;
|
||||
}else if(round == 2){ //round 2: generate all *readers*
|
||||
RemoteReadGenerator readgen = new RemoteReadGenerator(serializers);
|
||||
|
||||
//generate server readers
|
||||
readgen.generateFor(methods.stream().filter(method -> method.where.isClient).collect(Collectors.toList()), readServerName, packageName, true);
|
||||
//generate client readers
|
||||
readgen.generateFor(methods.stream().filter(method -> method.where.isServer).collect(Collectors.toList()), readClientName, packageName, false);
|
||||
|
||||
//create class for storing unique method hash
|
||||
TypeSpec.Builder hashBuilder = TypeSpec.classBuilder("MethodHash").addModifiers(Modifier.PUBLIC);
|
||||
hashBuilder.addJavadoc(autogenWarning);
|
||||
hashBuilder.addField(FieldSpec.builder(int.class, "HASH", Modifier.STATIC, Modifier.PUBLIC, Modifier.FINAL)
|
||||
.initializer("$1L", Objects.hash(methods)).build());
|
||||
|
||||
//build and write resulting hash class
|
||||
TypeSpec spec = hashBuilder.build();
|
||||
JavaFile.builder(packageName, spec).build().writeTo(Utils.filer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package io.anuke.annotations;
|
||||
|
||||
import com.squareup.javapoet.*;
|
||||
import io.anuke.annotations.Annotations.Serialize;
|
||||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.*;
|
||||
import javax.lang.model.util.ElementFilter;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||
@SupportedAnnotationTypes("io.anuke.annotations.Annotations.Serialize")
|
||||
public class SerializeAnnotationProcessor extends AbstractProcessor{
|
||||
/** Target class name. */
|
||||
private static final String className = "Serialization";
|
||||
/** Name of the base package to put all the generated classes. */
|
||||
private static final String packageName = "io.anuke.mindustry.gen";
|
||||
|
||||
private int round;
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment processingEnv){
|
||||
super.init(processingEnv);
|
||||
//put all relevant utils into utils class
|
||||
Utils.typeUtils = processingEnv.getTypeUtils();
|
||||
Utils.elementUtils = processingEnv.getElementUtils();
|
||||
Utils.filer = processingEnv.getFiler();
|
||||
Utils.messager = processingEnv.getMessager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
||||
if(round++ != 0) return false; //only process 1 round
|
||||
|
||||
try{
|
||||
Set<TypeElement> elements = ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(Serialize.class));
|
||||
|
||||
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
|
||||
classBuilder.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "\"unchecked\"").build());
|
||||
classBuilder.addJavadoc(RemoteMethodAnnotationProcessor.autogenWarning);
|
||||
|
||||
|
||||
MethodSpec.Builder method = MethodSpec.methodBuilder("init").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
|
||||
for(TypeElement elem : elements){
|
||||
TypeName type = TypeName.get(elem.asType());
|
||||
String simpleTypeName = type.toString().substring(type.toString().lastIndexOf('.') + 1);
|
||||
|
||||
TypeSpec.Builder serializer = TypeSpec.anonymousClassBuilder("")
|
||||
.addSuperinterface(ParameterizedTypeName.get(
|
||||
ClassName.bestGuess("io.anuke.arc.Settings.TypeSerializer"), type));
|
||||
|
||||
MethodSpec.Builder writeMethod = MethodSpec.methodBuilder("write")
|
||||
.returns(void.class)
|
||||
.addParameter(DataOutput.class, "stream")
|
||||
.addParameter(type, "object")
|
||||
.addException(IOException.class)
|
||||
.addModifiers(Modifier.PUBLIC);
|
||||
|
||||
MethodSpec.Builder readMethod = MethodSpec.methodBuilder("read")
|
||||
.returns(type)
|
||||
.addParameter(DataInput.class, "stream")
|
||||
.addException(IOException.class)
|
||||
.addModifiers(Modifier.PUBLIC);
|
||||
|
||||
readMethod.addStatement("$L object = new $L()", type, type);
|
||||
|
||||
List<VariableElement> fields = ElementFilter.fieldsIn(Utils.elementUtils.getAllMembers(elem));
|
||||
for(VariableElement field : fields){
|
||||
if(field.getModifiers().contains(Modifier.STATIC) || field.getModifiers().contains(Modifier.TRANSIENT) || field.getModifiers().contains(Modifier.PRIVATE))
|
||||
continue;
|
||||
|
||||
String name = field.getSimpleName().toString();
|
||||
String typeName = Utils.typeUtils.erasure(field.asType()).toString().replace('$', '.');
|
||||
String capName = Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1);
|
||||
|
||||
if(field.asType().getKind().isPrimitive()){
|
||||
writeMethod.addStatement("stream.write" + capName + "(object." + name + ")");
|
||||
readMethod.addStatement("object." + name + "= stream.read" + capName + "()");
|
||||
}else{
|
||||
writeMethod.addStatement("io.anuke.arc.Core.settings.getSerializer(" + typeName + ".class).write(stream, object." + name + ")");
|
||||
readMethod.addStatement("object." + name + " = (" + typeName + ")io.anuke.arc.Core.settings.getSerializer(" + typeName + ".class).read(stream)");
|
||||
}
|
||||
}
|
||||
|
||||
readMethod.addStatement("return object");
|
||||
|
||||
serializer.addMethod(writeMethod.build());
|
||||
serializer.addMethod(readMethod.build());
|
||||
|
||||
method.addStatement("io.anuke.arc.Core.settings.setSerializer($N, $L)", Utils.elementUtils.getBinaryName(elem).toString().replace('$', '.') + ".class", serializer.build());
|
||||
|
||||
name(writeMethod, "write" + simpleTypeName);
|
||||
name(readMethod, "read" + simpleTypeName);
|
||||
|
||||
writeMethod.addModifiers(Modifier.STATIC);
|
||||
readMethod.addModifiers(Modifier.STATIC);
|
||||
|
||||
classBuilder.addMethod(writeMethod.build());
|
||||
classBuilder.addMethod(readMethod.build());
|
||||
}
|
||||
|
||||
classBuilder.addMethod(method.build());
|
||||
|
||||
//write result
|
||||
JavaFile.builder(packageName, classBuilder.build()).build().writeTo(Utils.filer);
|
||||
|
||||
return true;
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void name(MethodSpec.Builder builder, String name){
|
||||
try{
|
||||
Field field = builder.getClass().getDeclaredField("name");
|
||||
field.setAccessible(true);
|
||||
field.set(builder, name);
|
||||
}catch(Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
package io.anuke.annotations;
|
||||
|
||||
import com.squareup.javapoet.*;
|
||||
import io.anuke.annotations.Annotations.Struct;
|
||||
import io.anuke.annotations.Annotations.StructField;
|
||||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.*;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.util.ElementFilter;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Generates ""value types"" classes that are packed into integer primitives of the most aproppriate size.
|
||||
* It would be nice if Java didn't make crazy hacks like this necessary.
|
||||
*/
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||
@SupportedAnnotationTypes({
|
||||
"io.anuke.annotations.Annotations.Struct"
|
||||
})
|
||||
public class StructAnnotationProcessor extends AbstractProcessor{
|
||||
/** Name of the base package to put all the generated classes. */
|
||||
private static final String packageName = "io.anuke.mindustry.gen";
|
||||
private int round;
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment processingEnv){
|
||||
super.init(processingEnv);
|
||||
//put all relevant utils into utils class
|
||||
Utils.typeUtils = processingEnv.getTypeUtils();
|
||||
Utils.elementUtils = processingEnv.getElementUtils();
|
||||
Utils.filer = processingEnv.getFiler();
|
||||
Utils.messager = processingEnv.getMessager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
||||
if(round++ != 0) return false; //only process 1 round
|
||||
|
||||
try{
|
||||
Set<TypeElement> elements = ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(Struct.class));
|
||||
|
||||
for(TypeElement elem : elements){
|
||||
|
||||
if(!elem.getSimpleName().toString().endsWith("Struct")){
|
||||
Utils.messager.printMessage(Kind.ERROR, "All classes annotated with @Struct must have their class names end in 'Struct'.", elem);
|
||||
continue;
|
||||
}
|
||||
|
||||
String structName = elem.getSimpleName().toString().substring(0, elem.getSimpleName().toString().length() - "Struct".length());
|
||||
String structParam = structName.toLowerCase();
|
||||
|
||||
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(structName)
|
||||
.addModifiers(Modifier.FINAL, Modifier.PUBLIC);
|
||||
|
||||
try{
|
||||
List<VariableElement> variables = ElementFilter.fieldsIn(elem.getEnclosedElements());
|
||||
int structSize = variables.stream().mapToInt(StructAnnotationProcessor::varSize).sum();
|
||||
int structTotalSize = (structSize <= 8 ? 8 : structSize <= 16 ? 16 : structSize <= 32 ? 32 : 64);
|
||||
|
||||
if(variables.size() == 0){
|
||||
Utils.messager.printMessage(Kind.ERROR, "making a struct with no fields is utterly pointles.", elem);
|
||||
continue;
|
||||
}
|
||||
|
||||
//obtain type which will be stored
|
||||
Class<?> structType = typeForSize(structSize);
|
||||
|
||||
//[constructor] get(fields...) : structType
|
||||
MethodSpec.Builder constructor = MethodSpec.methodBuilder("get")
|
||||
.addModifiers(Modifier.STATIC, Modifier.PUBLIC)
|
||||
.returns(structType);
|
||||
|
||||
StringBuilder cons = new StringBuilder();
|
||||
StringBuilder doc = new StringBuilder();
|
||||
doc.append("Bits used: ").append(structSize).append(" / ").append(structTotalSize).append("\n");
|
||||
|
||||
int offset = 0;
|
||||
for(VariableElement var : variables){
|
||||
int size = varSize(var);
|
||||
TypeName varType = TypeName.get(var.asType());
|
||||
String varName = var.getSimpleName().toString();
|
||||
|
||||
//add val param to constructor
|
||||
constructor.addParameter(varType, varName);
|
||||
|
||||
//[get] field(structType) : fieldType
|
||||
MethodSpec.Builder getter = MethodSpec.methodBuilder(var.getSimpleName().toString())
|
||||
.addModifiers(Modifier.STATIC, Modifier.PUBLIC)
|
||||
.returns(varType)
|
||||
.addParameter(structType, structParam);
|
||||
//[set] field(structType, fieldType) : structType
|
||||
MethodSpec.Builder setter = MethodSpec.methodBuilder(var.getSimpleName().toString())
|
||||
.addModifiers(Modifier.STATIC, Modifier.PUBLIC)
|
||||
.returns(structType)
|
||||
.addParameter(structType, structParam).addParameter(varType, "value");
|
||||
|
||||
//[getter]
|
||||
if(varType == TypeName.BOOLEAN){
|
||||
//bools: single bit, is simplified
|
||||
getter.addStatement("return ($L & (1L << $L)) != 0", structParam, offset);
|
||||
}else if(varType == TypeName.FLOAT){
|
||||
//floats: need conversion
|
||||
getter.addStatement("return Float.intBitsToFloat((int)(($L >>> $L) & $L))", structParam, offset, bitString(size, structTotalSize));
|
||||
}else{
|
||||
//bytes, shorts, chars, ints
|
||||
getter.addStatement("return ($T)(($L >>> $L) & $L)", varType, structParam, offset, bitString(size, structTotalSize));
|
||||
}
|
||||
|
||||
//[setter] + [constructor building]
|
||||
if(varType == TypeName.BOOLEAN){
|
||||
cons.append(" | (").append(varName).append(" ? ").append("1L << ").append(offset).append("L : 0)");
|
||||
|
||||
//bools: single bit, needs special case to clear things
|
||||
setter.beginControlFlow("if(value)");
|
||||
setter.addStatement("return ($T)(($L & ~(1L << $LL)))", structType, structParam, offset);
|
||||
setter.nextControlFlow("else");
|
||||
setter.addStatement("return ($T)(($L & ~(1L << $LL)) | (1L << $LL))", structType, structParam, offset, offset);
|
||||
setter.endControlFlow();
|
||||
}else if(varType == TypeName.FLOAT){
|
||||
cons.append(" | (").append("(").append(structType).append(")").append("Float.floatToIntBits(").append(varName).append(") << ").append(offset).append("L)");
|
||||
|
||||
//floats: need conversion
|
||||
setter.addStatement("return ($T)(($L & $L) | (($T)Float.floatToIntBits(value) << $LL))", structType, structParam, bitString(offset, size, structTotalSize), structType, offset);
|
||||
}else{
|
||||
cons.append(" | (((").append(structType).append(")").append(varName).append(" << ").append(offset).append("L)").append(" & ").append(bitString(offset, size, structTotalSize)).append(")");
|
||||
|
||||
//bytes, shorts, chars, ints
|
||||
setter.addStatement("return ($T)(($L & $L) | (($T)value << $LL))", structType, structParam, bitString(offset, size, structTotalSize), structType, offset);
|
||||
}
|
||||
|
||||
doc.append("<br> ").append(varName).append(" [").append(offset).append("..").append(size + offset).append("]\n");
|
||||
|
||||
//add finished methods
|
||||
classBuilder.addMethod(getter.build());
|
||||
classBuilder.addMethod(setter.build());
|
||||
|
||||
offset += size;
|
||||
}
|
||||
|
||||
classBuilder.addJavadoc(doc.toString());
|
||||
|
||||
//add constructor final statement + add to class and build
|
||||
constructor.addStatement("return ($T)($L)", structType, cons.toString().substring(3));
|
||||
classBuilder.addMethod(constructor.build());
|
||||
|
||||
JavaFile.builder(packageName, classBuilder.build()).build().writeTo(Utils.filer);
|
||||
}catch(IllegalArgumentException e){
|
||||
e.printStackTrace();
|
||||
Utils.messager.printMessage(Kind.ERROR, e.getMessage(), elem);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static String bitString(int offset, int size, int totalSize){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for(int i = 0; i < offset; i++) builder.append('0');
|
||||
for(int i = 0; i < size; i++) builder.append('1');
|
||||
for(int i = 0; i < totalSize - size - offset; i++) builder.append('0');
|
||||
return "0b" + builder.reverse().toString() + "L";
|
||||
}
|
||||
|
||||
static String bitString(int size, int totalSize){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for(int i = 0; i < size; i++) builder.append('1');
|
||||
for(int i = 0; i < totalSize - size; i++) builder.append('0');
|
||||
return "0b" + builder.reverse().toString() + "L";
|
||||
}
|
||||
|
||||
static int varSize(VariableElement var) throws IllegalArgumentException{
|
||||
if(!var.asType().getKind().isPrimitive()){
|
||||
throw new IllegalArgumentException("All struct fields must be primitives: " + var);
|
||||
}
|
||||
|
||||
StructField an = var.getAnnotation(StructField.class);
|
||||
if(var.asType().getKind() == TypeKind.BOOLEAN && an != null && an.value() != 1){
|
||||
throw new IllegalArgumentException("Booleans can only be one bit long... why would you do this?");
|
||||
}
|
||||
|
||||
if(var.asType().getKind() == TypeKind.FLOAT && an != null && an.value() != 32){
|
||||
throw new IllegalArgumentException("Float size can't be changed. Very sad.");
|
||||
}
|
||||
|
||||
return an == null ? typeSize(var.asType().getKind()) : an.value();
|
||||
}
|
||||
|
||||
static Class<?> typeForSize(int size) throws IllegalArgumentException{
|
||||
if(size <= 8){
|
||||
return byte.class;
|
||||
}else if(size <= 16){
|
||||
return short.class;
|
||||
}else if(size <= 32){
|
||||
return int.class;
|
||||
}else if(size <= 64){
|
||||
return long.class;
|
||||
}
|
||||
throw new IllegalArgumentException("Too many fields, must fit in 64 bits. Curent size: " + size);
|
||||
}
|
||||
|
||||
/** returns a type's element size in bits. */
|
||||
static int typeSize(TypeKind kind) throws IllegalArgumentException{
|
||||
switch(kind){
|
||||
case BOOLEAN:
|
||||
return 1;
|
||||
case BYTE:
|
||||
return 8;
|
||||
case SHORT:
|
||||
return 16;
|
||||
case FLOAT:
|
||||
case CHAR:
|
||||
case INT:
|
||||
return 32;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid type kind: " + kind + ". Note that doubles and longs are not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package io.anuke.annotations;
|
||||
|
||||
import javax.annotation.processing.Filer;
|
||||
import javax.annotation.processing.Messager;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
|
||||
public class Utils{
|
||||
public static Types typeUtils;
|
||||
public static Elements elementUtils;
|
||||
public static Filer filer;
|
||||
public static Messager messager;
|
||||
|
||||
public static String getMethodName(Element element){
|
||||
return ((TypeElement)element.getEnclosingElement()).getQualifiedName().toString() + "." + element.getSimpleName();
|
||||
}
|
||||
|
||||
public static boolean isPrimitive(String type){
|
||||
return type.equals("boolean") || type.equals("byte") || type.equals("short") || type.equals("int")
|
||||
|| type.equals("long") || type.equals("float") || type.equals("double") || type.equals("char");
|
||||
}
|
||||
}
|
||||
256
annotations/src/main/java/mindustry/annotations/Annotations.java
Normal file
@@ -0,0 +1,256 @@
|
||||
package mindustry.annotations;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
public class Annotations{
|
||||
//region entity interfaces
|
||||
|
||||
/** Indicates that a method overrides other methods. */
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Replace{
|
||||
}
|
||||
|
||||
/** Indicates that a method should be final in all implementing classes. */
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Final{
|
||||
}
|
||||
|
||||
/** Indicates that a field will be interpolated when synced. */
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface SyncField{
|
||||
/** If true, the field will be linearly interpolated. If false, it will be interpolated as an angle. */
|
||||
boolean value();
|
||||
/** If true, the field is clamped to 0-1. */
|
||||
boolean clamped() default false;
|
||||
}
|
||||
|
||||
/** Indicates that a field will not be read from the server when syncing the local player state. */
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface SyncLocal{
|
||||
|
||||
}
|
||||
|
||||
/** Indicates that a component field is imported from other components. This means it doesn't actually exist. */
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Import{
|
||||
}
|
||||
|
||||
/** Indicates that a component field is read-only. */
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface ReadOnly{
|
||||
}
|
||||
|
||||
/** Indicates multiple inheritance on a component type. */
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Component{
|
||||
/** 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;
|
||||
}
|
||||
|
||||
/** Indicates that a method is implemented by the annotation processor. */
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface InternalImpl{
|
||||
}
|
||||
|
||||
/** Indicates priority of a method in an entity. Methods with higher priority are done last. */
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface MethodPriority{
|
||||
float value();
|
||||
}
|
||||
|
||||
/** Indicates that a component def is present on all entities. */
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface BaseComponent{
|
||||
}
|
||||
|
||||
/** Creates a group that only examines entities that have all the components listed. */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface GroupDef{
|
||||
Class[] value();
|
||||
boolean collide() default false;
|
||||
boolean spatial() default false;
|
||||
boolean mapping() default false;
|
||||
}
|
||||
|
||||
/** Indicates an entity definition. */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface EntityDef{
|
||||
/** List of component interfaces */
|
||||
Class[] value();
|
||||
/** Whether the class is final */
|
||||
boolean isFinal() default true;
|
||||
/** If true, entities are recycled. */
|
||||
boolean pooled() default false;
|
||||
/** Whether to serialize (makes the serialize method return this value).
|
||||
* If true, this entity is automatically put into save files.
|
||||
* If false, no serialization code is generated at all. */
|
||||
boolean serialize() default true;
|
||||
/** Whether to generate IO code. This is for advanced usage only. */
|
||||
boolean genio() default true;
|
||||
}
|
||||
|
||||
/** Indicates an internal interface for entity components. */
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface EntityInterface{
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region misc. utility
|
||||
|
||||
/** Automatically loads block regions annotated with this. */
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Load{
|
||||
/**
|
||||
* The region name to load. Variables can be used:
|
||||
* "@" -> block name
|
||||
* "$size" -> block size
|
||||
* "#" "#1" "#2" -> index number, for arrays
|
||||
* */
|
||||
String value();
|
||||
/** 1D Array length, if applicable. */
|
||||
int length() default 1;
|
||||
/** 2D array lengths. */
|
||||
int[] lengths() default {};
|
||||
/** Fallback string used to replace "@" (the block name) if the region isn't found. */
|
||||
String fallback() default "error";
|
||||
}
|
||||
|
||||
/** Registers a statement for auto serialization. */
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface RegisterStatement{
|
||||
String value();
|
||||
}
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface StyleDefaults{
|
||||
}
|
||||
|
||||
/** Indicates that a method should always call its super version. */
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface CallSuper{
|
||||
|
||||
}
|
||||
|
||||
/** Annotation that allows overriding CallSuper annotation. To be used on method that overrides method with CallSuper annotation from parent class. */
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface OverrideCallSuper{
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region struct
|
||||
|
||||
/** Marks a class as a special value type struct. Class name must end in 'Struct'. */
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Struct{
|
||||
|
||||
}
|
||||
|
||||
/** Marks a field of a struct. Optional. */
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface StructField{
|
||||
/** Size of a struct field in bits. Not valid on booleans or floating point numbers. */
|
||||
int value();
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region remote
|
||||
|
||||
public enum PacketPriority{
|
||||
/** Gets put in a queue and processed if not connected. */
|
||||
normal,
|
||||
/** Gets handled immediately, regardless of connection status. */
|
||||
high,
|
||||
/** Does not get handled unless client is connected. */
|
||||
low
|
||||
}
|
||||
|
||||
/** A set of two booleans, one specifying server and one specifying client. */
|
||||
public enum Loc{
|
||||
/** Method can only be invoked on the client from the server. */
|
||||
server(true, false),
|
||||
/** Method can only be invoked on the server from the client. */
|
||||
client(false, true),
|
||||
/** 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){
|
||||
this.isServer = server;
|
||||
this.isClient = client;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Variant{
|
||||
/** Method can only be invoked targeting one player. */
|
||||
one(true, false),
|
||||
/** Method can only be invoked targeting all players. */
|
||||
all(false, true),
|
||||
/** Method targets both one player and all players. */
|
||||
both(true, true);
|
||||
|
||||
public final boolean isOne, isAll;
|
||||
|
||||
Variant(boolean isOne, boolean isAll){
|
||||
this.isOne = isOne;
|
||||
this.isAll = isAll;
|
||||
}
|
||||
}
|
||||
|
||||
/** Marks a method as invokable remotely across a server/client connection. */
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Remote{
|
||||
/** 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. */
|
||||
Variant variants() default Variant.all;
|
||||
|
||||
/** The local locations where this method is called locally, when invoked. */
|
||||
Loc called() default Loc.none;
|
||||
|
||||
/** Whether to forward this packet to all other clients upon receival. Client only. */
|
||||
boolean forward() default false;
|
||||
|
||||
/**
|
||||
* Whether the packet for this method is sent with UDP instead of TCP.
|
||||
* UDP is faster, but is prone to packet loss and duplication.
|
||||
*/
|
||||
boolean unreliable() default false;
|
||||
|
||||
/** Priority of this event. */
|
||||
PacketPriority priority() default PacketPriority.normal;
|
||||
}
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface TypeIOHandler{
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
package mindustry.annotations;
|
||||
|
||||
import arc.files.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.Log;
|
||||
import arc.util.Log.*;
|
||||
import arc.util.*;
|
||||
import com.squareup.javapoet.*;
|
||||
import com.sun.source.util.*;
|
||||
import com.sun.tools.javac.model.*;
|
||||
import com.sun.tools.javac.processing.*;
|
||||
import com.sun.tools.javac.tree.*;
|
||||
import com.sun.tools.javac.util.*;
|
||||
import mindustry.annotations.util.*;
|
||||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.*;
|
||||
import javax.lang.model.element.*;
|
||||
import javax.lang.model.type.*;
|
||||
import javax.lang.model.util.*;
|
||||
import javax.tools.Diagnostic.*;
|
||||
import javax.tools.*;
|
||||
import java.io.*;
|
||||
import java.lang.annotation.*;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||
public abstract class BaseProcessor extends AbstractProcessor{
|
||||
/** Name of the base package to put all the generated classes. */
|
||||
public static final String packageName = "mindustry.gen";
|
||||
|
||||
public static Types typeu;
|
||||
public static JavacElements elementu;
|
||||
public static Filer filer;
|
||||
public static Messager messager;
|
||||
public static Trees trees;
|
||||
public static TreeMaker maker;
|
||||
|
||||
protected int round;
|
||||
protected int rounds = 1;
|
||||
protected RoundEnvironment env;
|
||||
protected Fi rootDirectory;
|
||||
|
||||
protected Context context;
|
||||
|
||||
public static String getMethodName(Element element){
|
||||
return ((TypeElement)element.getEnclosingElement()).getQualifiedName().toString() + "." + element.getSimpleName();
|
||||
}
|
||||
|
||||
public static boolean isPrimitive(String type){
|
||||
return type.equals("boolean") || type.equals("byte") || type.equals("short") || type.equals("int")
|
||||
|| type.equals("long") || type.equals("float") || type.equals("double") || type.equals("char");
|
||||
}
|
||||
|
||||
public static boolean instanceOf(String type, String other){
|
||||
TypeElement a = elementu.getTypeElement(type);
|
||||
TypeElement b = elementu.getTypeElement(other);
|
||||
return a != null && b != null && typeu.isSubtype(a.asType(), b.asType());
|
||||
}
|
||||
|
||||
public static String getDefault(String value){
|
||||
switch(value){
|
||||
case "float":
|
||||
case "double":
|
||||
case "int":
|
||||
case "long":
|
||||
case "short":
|
||||
case "char":
|
||||
case "byte":
|
||||
return "0";
|
||||
case "boolean":
|
||||
return "false";
|
||||
default:
|
||||
return "null";
|
||||
}
|
||||
}
|
||||
|
||||
//in bytes
|
||||
public static int typeSize(String kind){
|
||||
switch(kind){
|
||||
case "boolean":
|
||||
case "byte":
|
||||
return 1;
|
||||
case "short":
|
||||
return 2;
|
||||
case "float":
|
||||
case "char":
|
||||
case "int":
|
||||
return 4;
|
||||
case "long":
|
||||
return 8;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid primitive type: " + kind + "");
|
||||
}
|
||||
}
|
||||
|
||||
public static String simpleName(String str){
|
||||
return str.contains(".") ? str.substring(str.lastIndexOf('.') + 1) : str;
|
||||
}
|
||||
|
||||
public static TypeName tname(String pack, String simple){
|
||||
return ClassName.get(pack, simple );
|
||||
}
|
||||
|
||||
public static TypeName tname(String name){
|
||||
if(!name.contains(".")) return ClassName.get(packageName, name);
|
||||
|
||||
String pack = name.substring(0, name.lastIndexOf("."));
|
||||
String simple = name.substring(name.lastIndexOf(".") + 1);
|
||||
return ClassName.get(pack, simple);
|
||||
}
|
||||
|
||||
public static TypeName tname(Class<?> c){
|
||||
return ClassName.get(c).box();
|
||||
}
|
||||
|
||||
public static TypeVariableName getTVN(TypeParameterElement element) {
|
||||
String name = element.getSimpleName().toString();
|
||||
List<? extends TypeMirror> boundsMirrors = element.getBounds();
|
||||
|
||||
List<TypeName> boundsTypeNames = new ArrayList<>();
|
||||
for (TypeMirror typeMirror : boundsMirrors) {
|
||||
boundsTypeNames.add(TypeName.get(typeMirror));
|
||||
}
|
||||
|
||||
return TypeVariableName.get(name, boundsTypeNames.toArray(new TypeName[0]));
|
||||
}
|
||||
|
||||
public static void write(TypeSpec.Builder builder) throws Exception{
|
||||
write(builder, null);
|
||||
}
|
||||
|
||||
public static void write(TypeSpec.Builder builder, Seq<String> imports) throws Exception{
|
||||
JavaFile file = JavaFile.builder(packageName, builder.build()).skipJavaLangImports(true).build();
|
||||
|
||||
if(imports != null){
|
||||
String rawSource = file.toString();
|
||||
Seq<String> result = new Seq<>();
|
||||
for (String s : rawSource.split("\n", -1)) {
|
||||
result.add(s);
|
||||
if (s.startsWith("package ")) {
|
||||
result.add("");
|
||||
for (String i : imports) {
|
||||
result.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String out = result.toString("\n");
|
||||
JavaFileObject object = filer.createSourceFile(file.packageName + "." + file.typeSpec.name, file.typeSpec.originatingElements.toArray(new Element[0]));
|
||||
OutputStream stream = object.openOutputStream();
|
||||
stream.write(out.getBytes());
|
||||
stream.close();
|
||||
}else{
|
||||
file.writeTo(filer);
|
||||
}
|
||||
}
|
||||
|
||||
public Seq<Selement> elements(Class<? extends Annotation> type){
|
||||
return Seq.with(env.getElementsAnnotatedWith(type)).map(Selement::new);
|
||||
}
|
||||
|
||||
public Seq<Stype> types(Class<? extends Annotation> type){
|
||||
return Seq.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof TypeElement)
|
||||
.map(e -> new Stype((TypeElement)e));
|
||||
}
|
||||
|
||||
public Seq<Svar> fields(Class<? extends Annotation> type){
|
||||
return Seq.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof VariableElement)
|
||||
.map(e -> new Svar((VariableElement)e));
|
||||
}
|
||||
|
||||
public Seq<Smethod> methods(Class<? extends Annotation> type){
|
||||
return Seq.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof ExecutableElement)
|
||||
.map(e -> new Smethod((ExecutableElement)e));
|
||||
}
|
||||
|
||||
public static void err(String message){
|
||||
messager.printMessage(Kind.ERROR, message);
|
||||
Log.err("[CODEGEN ERROR] " +message);
|
||||
}
|
||||
|
||||
public static void err(String message, Element elem){
|
||||
messager.printMessage(Kind.ERROR, message, elem);
|
||||
Log.err("[CODEGEN ERROR] " + message + ": " + elem);
|
||||
}
|
||||
|
||||
public void err(String message, Selement elem){
|
||||
err(message, elem.e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment env){
|
||||
super.init(env);
|
||||
|
||||
JavacProcessingEnvironment javacProcessingEnv = (JavacProcessingEnvironment)env;
|
||||
|
||||
trees = Trees.instance(env);
|
||||
typeu = env.getTypeUtils();
|
||||
elementu = javacProcessingEnv.getElementUtils();
|
||||
filer = env.getFiler();
|
||||
messager = env.getMessager();
|
||||
context = ((JavacProcessingEnvironment)env).getContext();
|
||||
maker = TreeMaker.instance(javacProcessingEnv.getContext());
|
||||
|
||||
Log.setLogLevel(LogLevel.info);
|
||||
|
||||
if(System.getProperty("debug") != null){
|
||||
Log.setLogLevel(LogLevel.debug);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
||||
if(round++ >= rounds) return false; //only process 1 round
|
||||
if(rootDirectory == null){
|
||||
try{
|
||||
String path = Fi.get(filer.getResource(StandardLocation.CLASS_OUTPUT, "no", "no")
|
||||
.toUri().toURL().toString().substring(OS.isWindows ? 6 : "file:".length()))
|
||||
.parent().parent().parent().parent().parent().parent().parent().toString().replace("%20", " ");
|
||||
rootDirectory = Fi.get(path);
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
this.env = roundEnv;
|
||||
try{
|
||||
process(roundEnv);
|
||||
}catch(Throwable e){
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceVersion getSupportedSourceVersion(){
|
||||
return SourceVersion.RELEASE_8;
|
||||
}
|
||||
|
||||
public void process(RoundEnvironment env) throws Exception{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
package mindustry.annotations.entity;
|
||||
|
||||
import arc.files.*;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.serialization.*;
|
||||
import com.squareup.javapoet.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.annotations.*;
|
||||
import mindustry.annotations.util.*;
|
||||
import mindustry.annotations.util.TypeIOResolver.*;
|
||||
|
||||
import javax.lang.model.element.*;
|
||||
|
||||
import static mindustry.annotations.BaseProcessor.instanceOf;
|
||||
|
||||
public class EntityIO{
|
||||
final static Json json = new Json();
|
||||
//suffixes for sync fields
|
||||
final static String targetSuf = "_TARGET_", lastSuf = "_LAST_";
|
||||
//replacements after refactoring
|
||||
final static StringMap replacements = StringMap.of("mindustry.entities.units.BuildRequest", "mindustry.entities.units.BuildPlan");
|
||||
|
||||
final ClassSerializer serializer;
|
||||
final String name;
|
||||
final TypeSpec.Builder type;
|
||||
final Fi directory;
|
||||
final Seq<Revision> revisions = new Seq<>();
|
||||
|
||||
boolean write;
|
||||
MethodSpec.Builder method;
|
||||
ObjectSet<String> presentFields = new ObjectSet<>();
|
||||
|
||||
EntityIO(String name, TypeSpec.Builder type, Seq<FieldSpec> typeFields, ClassSerializer serializer, Fi directory){
|
||||
this.directory = directory;
|
||||
this.type = type;
|
||||
this.serializer = serializer;
|
||||
this.name = name;
|
||||
|
||||
directory.mkdirs();
|
||||
|
||||
//load old revisions
|
||||
for(Fi fi : directory.list()){
|
||||
revisions.add(json.fromJson(Revision.class, fi));
|
||||
}
|
||||
|
||||
//next revision to be used
|
||||
int nextRevision = revisions.isEmpty() ? 0 : revisions.max(r -> r.version).version + 1;
|
||||
|
||||
//resolve preferred field order based on fields that fit
|
||||
Seq<FieldSpec> fields = typeFields.select(spec ->
|
||||
!spec.hasModifier(Modifier.TRANSIENT) &&
|
||||
!spec.hasModifier(Modifier.STATIC) &&
|
||||
!spec.hasModifier(Modifier.FINAL)/* &&
|
||||
(spec.type.isPrimitive() || serializer.has(spec.type.toString()))*/);
|
||||
|
||||
//sort to keep order
|
||||
fields.sortComparing(f -> f.name);
|
||||
|
||||
//keep track of fields present in the entity
|
||||
presentFields.addAll(fields.map(f -> f.name));
|
||||
|
||||
//add new revision if it doesn't match or there are no revisions
|
||||
if(revisions.isEmpty() || !revisions.peek().equal(fields)){
|
||||
revisions.add(new Revision(nextRevision,
|
||||
fields.map(f -> new RevisionField(f.name, f.type.toString(),
|
||||
f.type.isPrimitive() ? BaseProcessor.typeSize(f.type.toString()) : -1))));
|
||||
//write revision
|
||||
directory.child(nextRevision + ".json").writeString(json.toJson(revisions.peek()));
|
||||
}
|
||||
}
|
||||
|
||||
void write(MethodSpec.Builder method, boolean write) throws Exception{
|
||||
this.method = method;
|
||||
this.write = write;
|
||||
|
||||
//subclasses *have* to call this method
|
||||
method.addAnnotation(CallSuper.class);
|
||||
|
||||
if(write){
|
||||
//write short revision
|
||||
st("write.s($L)", revisions.peek().version);
|
||||
//write uses most recent revision
|
||||
for(RevisionField field : revisions.peek().fields){
|
||||
io(field.type, "this." + field.name);
|
||||
}
|
||||
}else{
|
||||
//read revision
|
||||
st("short REV = read.s()");
|
||||
|
||||
for(int i = 0; i < revisions.size; i++){
|
||||
//check for the right revision
|
||||
Revision rev = revisions.get(i);
|
||||
if(i == 0){
|
||||
cont("if(REV == $L)", rev.version);
|
||||
}else{
|
||||
ncont("else if(REV == $L)", rev.version);
|
||||
}
|
||||
|
||||
//add code for reading revision
|
||||
for(RevisionField field : rev.fields){
|
||||
//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 + " = " : "");
|
||||
}
|
||||
}
|
||||
|
||||
//throw exception on illegal revisions
|
||||
ncont("else");
|
||||
st("throw new IllegalArgumentException(\"Unknown revision '\" + REV + \"' for entity type '" + name + "'\")");
|
||||
econt();
|
||||
}
|
||||
}
|
||||
|
||||
void writeSync(MethodSpec.Builder method, boolean write, Seq<Svar> syncFields, Seq<Svar> allFields) throws Exception{
|
||||
this.method = method;
|
||||
this.write = write;
|
||||
|
||||
if(write){
|
||||
//write uses most recent revision
|
||||
for(RevisionField field : revisions.peek().fields){
|
||||
io(field.type, "this." + field.name);
|
||||
}
|
||||
}else{
|
||||
Revision rev = revisions.peek();
|
||||
|
||||
//base read code
|
||||
st("if(lastUpdated != 0) updateSpacing = $T.timeSinceMillis(lastUpdated)", Time.class);
|
||||
st("lastUpdated = $T.millis()", Time.class);
|
||||
st("boolean islocal = isLocal()");
|
||||
|
||||
//add code for reading revision
|
||||
for(RevisionField field : rev.fields){
|
||||
Svar var = allFields.find(s -> s.name().equals(field.name));
|
||||
boolean sf = var.has(SyncField.class), sl = var.has(SyncLocal.class);
|
||||
|
||||
if(sl) cont("if(!islocal)");
|
||||
|
||||
if(sf){
|
||||
st(field.name + lastSuf + " = this." + field.name);
|
||||
}
|
||||
|
||||
io(field.type, "this." + (sf ? field.name + targetSuf : field.name) + " = ");
|
||||
|
||||
if(sl){
|
||||
ncont("else" );
|
||||
|
||||
io(field.type, "");
|
||||
|
||||
econt();
|
||||
}
|
||||
}
|
||||
|
||||
st("afterSync()");
|
||||
}
|
||||
}
|
||||
|
||||
void writeSyncManual(MethodSpec.Builder method, boolean write, Seq<Svar> syncFields) throws Exception{
|
||||
this.method = method;
|
||||
this.write = write;
|
||||
|
||||
if(write){
|
||||
for(Svar field : syncFields){
|
||||
st("buffer.put(this.$L)", field.name());
|
||||
}
|
||||
}else{
|
||||
//base read code
|
||||
st("if(lastUpdated != 0) updateSpacing = $T.timeSinceMillis(lastUpdated)", Time.class);
|
||||
st("lastUpdated = $T.millis()", Time.class);
|
||||
|
||||
//just read the field
|
||||
for(Svar field : syncFields){
|
||||
//last
|
||||
st("this.$L = this.$L", field.name() + lastSuf, field.name());
|
||||
//assign target
|
||||
st("this.$L = buffer.get()", field.name() + targetSuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void writeInterpolate(MethodSpec.Builder method, Seq<Svar> fields) throws Exception{
|
||||
this.method = method;
|
||||
|
||||
cont("if(lastUpdated != 0 && updateSpacing != 0)");
|
||||
|
||||
//base calculations
|
||||
st("float timeSinceUpdate = Time.timeSinceMillis(lastUpdated)");
|
||||
st("float alpha = Math.min(timeSinceUpdate / updateSpacing, 2f)");
|
||||
|
||||
//write interpolated data, using slerp / lerp
|
||||
for(Svar field : fields){
|
||||
String name = field.name(), targetName = name + targetSuf, lastName = name + lastSuf;
|
||||
st("$L = $L($T.$L($L, $L, alpha))", name, field.annotation(SyncField.class).clamped() ? "arc.math.Mathf.clamp" : "", Mathf.class, field.annotation(SyncField.class).value() ? "lerp" : "slerp", lastName, targetName);
|
||||
}
|
||||
|
||||
ncont("else if(lastUpdated != 0)"); //check if no meaningful data has arrived yet
|
||||
|
||||
//write values directly to targets
|
||||
for(Svar field : fields){
|
||||
String name = field.name(), targetName = name + targetSuf;
|
||||
st("$L = $L", name, targetName);
|
||||
}
|
||||
|
||||
econt();
|
||||
}
|
||||
|
||||
private void io(String type, String field) throws Exception{
|
||||
type = type.replace("mindustry.gen.", "");
|
||||
type = replacements.get(type, type);
|
||||
|
||||
if(BaseProcessor.isPrimitive(type)){
|
||||
s(type.equals("boolean") ? "bool" : type.charAt(0) + "", field);
|
||||
}else if(instanceOf(type, "mindustry.ctype.Content")){
|
||||
if(write){
|
||||
s("s", field + ".id");
|
||||
}else{
|
||||
st(field + "mindustry.Vars.content.getByID(mindustry.ctype.ContentType.$L, read.s())", BaseProcessor.simpleName(type).toLowerCase().replace("type", ""));
|
||||
}
|
||||
}else if(serializer.writers.containsKey(type) && write){
|
||||
st("$L(write, $L)", 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) && !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);
|
||||
|
||||
if(write){
|
||||
s("i", field + ".length");
|
||||
cont("for(int INDEX = 0; INDEX < $L.length; INDEX ++)", field);
|
||||
io(rawType, field + "[INDEX]");
|
||||
}else{
|
||||
String fieldName = field.replace(" = ", "").replace("this.", "");
|
||||
String lenf = fieldName + "_LENGTH";
|
||||
s("i", "int " + lenf + " = ");
|
||||
if(!field.isEmpty()){
|
||||
st("$Lnew $L[$L]", field, type.replace("[]", ""), lenf);
|
||||
}
|
||||
cont("for(int INDEX = 0; INDEX < $L; INDEX ++)", lenf);
|
||||
io(rawType, field.replace(" = ", "[INDEX] = "));
|
||||
}
|
||||
|
||||
econt();
|
||||
}else if(type.startsWith("arc.struct") && type.contains("<")){ //it's some type of data structure
|
||||
String struct = type.substring(0, type.indexOf("<"));
|
||||
String generic = type.substring(type.indexOf("<") + 1, type.indexOf(">"));
|
||||
|
||||
if(struct.equals("arc.struct.Queue") || struct.equals("arc.struct.Seq")){
|
||||
if(write){
|
||||
s("i", field + ".size");
|
||||
cont("for(int INDEX = 0; INDEX < $L.size; INDEX ++)", field);
|
||||
io(generic, field + ".get(INDEX)");
|
||||
}else{
|
||||
String fieldName = field.replace(" = ", "").replace("this.", "");
|
||||
String lenf = fieldName + "_LENGTH";
|
||||
s("i", "int " + lenf + " = ");
|
||||
if(!field.isEmpty()){
|
||||
st("$L.clear()", field.replace(" = ", ""));
|
||||
}
|
||||
cont("for(int INDEX = 0; INDEX < $L; INDEX ++)", lenf);
|
||||
io(generic, field.replace(" = ", "_ITEM = ").replace("this.", generic + " "));
|
||||
if(!field.isEmpty()){
|
||||
String temp = field.replace(" = ", "_ITEM").replace("this.", "");
|
||||
st("if($L != null) $L.add($L)", temp, field.replace(" = ", ""), temp);
|
||||
}
|
||||
}
|
||||
|
||||
econt();
|
||||
}else{
|
||||
Log.warn("Missing serialization code for collection '@' in '@'", type, name);
|
||||
}
|
||||
}else{
|
||||
Log.warn("Missing serialization code for type '@' in '@'", type, name);
|
||||
}
|
||||
}
|
||||
|
||||
private void cont(String text, Object... fmt){
|
||||
method.beginControlFlow(text, fmt);
|
||||
}
|
||||
|
||||
private void econt(){
|
||||
method.endControlFlow();
|
||||
}
|
||||
|
||||
private void ncont(String text, Object... fmt){
|
||||
method.nextControlFlow(text, fmt);
|
||||
}
|
||||
|
||||
private void st(String text, Object... args){
|
||||
method.addStatement(text, args);
|
||||
}
|
||||
|
||||
private void s(String type, String field){
|
||||
if(write){
|
||||
method.addStatement("write.$L($L)", type, field);
|
||||
}else{
|
||||
method.addStatement("$Lread.$L()", field, type);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Revision{
|
||||
int version;
|
||||
Seq<RevisionField> fields;
|
||||
|
||||
Revision(int version, Seq<RevisionField> fields){
|
||||
this.version = version;
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
Revision(){}
|
||||
|
||||
/** @return whether these two revisions are compatible */
|
||||
boolean equal(Seq<FieldSpec> specs){
|
||||
if(fields.size != specs.size) return false;
|
||||
|
||||
for(int i = 0; i < fields.size; i++){
|
||||
RevisionField field = fields.get(i);
|
||||
FieldSpec spec = specs.get(i);
|
||||
//TODO when making fields, their primitive size may be overwritten by an annotation; check for that
|
||||
if(!(field.type.equals(spec.type.toString()) && (!spec.type.isPrimitive() || BaseProcessor.typeSize(spec.type.toString()) == field.size))){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RevisionField{
|
||||
String name, type;
|
||||
int size; //in bytes
|
||||
|
||||
RevisionField(String name, String type, int size){
|
||||
this.name = name;
|
||||
this.size = size;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
RevisionField(){}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,937 @@
|
||||
package mindustry.annotations.entity;
|
||||
|
||||
import arc.*;
|
||||
import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import arc.util.pooling.Pool.*;
|
||||
import arc.util.pooling.*;
|
||||
import com.squareup.javapoet.*;
|
||||
import com.squareup.javapoet.TypeSpec.*;
|
||||
import com.sun.source.tree.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.annotations.*;
|
||||
import mindustry.annotations.util.*;
|
||||
import mindustry.annotations.util.TypeIOResolver.*;
|
||||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.element.*;
|
||||
import javax.lang.model.type.*;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@SupportedAnnotationTypes({
|
||||
"mindustry.annotations.Annotations.EntityDef",
|
||||
"mindustry.annotations.Annotations.GroupDef",
|
||||
"mindustry.annotations.Annotations.EntityInterface",
|
||||
"mindustry.annotations.Annotations.BaseComponent",
|
||||
"mindustry.annotations.Annotations.TypeIOHandler"
|
||||
})
|
||||
public class EntityProcess extends BaseProcessor{
|
||||
Seq<EntityDefinition> definitions = new Seq<>();
|
||||
Seq<GroupDefinition> groupDefs = new Seq<>();
|
||||
Seq<Stype> baseComponents;
|
||||
ObjectMap<String, Stype> componentNames = new ObjectMap<>();
|
||||
ObjectMap<Stype, Seq<Stype>> componentDependencies = new ObjectMap<>();
|
||||
ObjectMap<Selement, Seq<Stype>> defComponents = new ObjectMap<>();
|
||||
ObjectMap<String, String> varInitializers = new ObjectMap<>();
|
||||
ObjectMap<String, String> methodBlocks = new ObjectMap<>();
|
||||
ObjectMap<Stype, ObjectSet<Stype>> baseClassDeps = new ObjectMap<>();
|
||||
ObjectSet<String> imports = new ObjectSet<>();
|
||||
Seq<Selement> allGroups = new Seq<>();
|
||||
Seq<Selement> allDefs = new Seq<>();
|
||||
Seq<Stype> allInterfaces = new Seq<>();
|
||||
Seq<TypeSpec.Builder> baseClasses = new Seq<>();
|
||||
ClassSerializer serializer;
|
||||
|
||||
{
|
||||
rounds = 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(RoundEnvironment env) throws Exception{
|
||||
allGroups.addAll(elements(GroupDef.class));
|
||||
allDefs.addAll(elements(EntityDef.class));
|
||||
allInterfaces.addAll(types(EntityInterface.class));
|
||||
|
||||
//round 1: generate component interfaces
|
||||
if(round == 1){
|
||||
serializer = TypeIOResolver.resolve(this);
|
||||
baseComponents = types(BaseComponent.class);
|
||||
Seq<Stype> allComponents = types(Component.class);
|
||||
|
||||
//store code
|
||||
for(Stype component : allComponents){
|
||||
for(Svar f : component.fields()){
|
||||
VariableTree tree = f.tree();
|
||||
|
||||
//add initializer if it exists
|
||||
if(tree.getInitializer() != null){
|
||||
String init = tree.getInitializer().toString();
|
||||
varInitializers.put(f.descString(), init);
|
||||
}
|
||||
}
|
||||
|
||||
for(Smethod elem : component.methods()){
|
||||
if(elem.is(Modifier.ABSTRACT) || elem.is(Modifier.NATIVE)) continue;
|
||||
//get all statements in the method, store them
|
||||
methodBlocks.put(elem.descString(), elem.tree().getBody().toString());
|
||||
}
|
||||
}
|
||||
|
||||
//store components
|
||||
for(Stype type : allComponents){
|
||||
componentNames.put(type.name(), type);
|
||||
}
|
||||
|
||||
//add component imports
|
||||
for(Stype comp : allComponents){
|
||||
imports.addAll(getImports(comp.e));
|
||||
}
|
||||
|
||||
//create component interfaces
|
||||
for(Stype component : allComponents){
|
||||
TypeSpec.Builder inter = TypeSpec.interfaceBuilder(interfaceName(component))
|
||||
.addModifiers(Modifier.PUBLIC).addAnnotation(EntityInterface.class);
|
||||
|
||||
inter.addJavadoc("Interface for {@link $L}", component.fullName());
|
||||
|
||||
//implement extra interfaces these components may have, e.g. position
|
||||
for(Stype extraInterface : component.interfaces().select(i -> !isCompInterface(i))){
|
||||
//javapoet completely chokes on this if I add `addSuperInterface` or create the type name with TypeName.get
|
||||
inter.superinterfaces.add(tname(extraInterface.fullName()));
|
||||
}
|
||||
|
||||
//implement super interfaces
|
||||
Seq<Stype> depends = getDependencies(component);
|
||||
for(Stype type : depends){
|
||||
inter.addSuperinterface(ClassName.get(packageName, interfaceName(type)));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
//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());
|
||||
}
|
||||
|
||||
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")).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")).map(AnnotationSpec::get)).build()).build());
|
||||
}
|
||||
}
|
||||
|
||||
write(inter);
|
||||
|
||||
//generate base class if necessary
|
||||
//SPECIAL CASE: components with EntityDefs don't get a base class! the generated class becomes the base class itself
|
||||
if(component.annotation(Component.class).base()){
|
||||
|
||||
Seq<Stype> deps = depends.copy().and(component);
|
||||
baseClassDeps.get(component, ObjectSet::new).addAll(deps);
|
||||
|
||||
//do not generate base classes when the component will generate one itself
|
||||
if(!component.has(EntityDef.class)){
|
||||
TypeSpec.Builder base = TypeSpec.classBuilder(baseName(component)).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT);
|
||||
|
||||
//go through all the fields.
|
||||
for(Stype type : deps){
|
||||
//add public fields
|
||||
for(Svar field : type.fields().select(e -> !e.is(Modifier.STATIC) && !e.is(Modifier.PRIVATE) && !e.has(Import.class) && !e.has(ReadOnly.class))){
|
||||
FieldSpec.Builder builder = FieldSpec.builder(field.tname(),field.name(), Modifier.PUBLIC);
|
||||
|
||||
//keep transience
|
||||
if(field.is(Modifier.TRANSIENT)) builder.addModifiers(Modifier.TRANSIENT);
|
||||
//keep all annotations
|
||||
builder.addAnnotations(field.annotations().map(AnnotationSpec::get));
|
||||
|
||||
//add initializer if it exists
|
||||
if(varInitializers.containsKey(field.descString())){
|
||||
builder.initializer(varInitializers.get(field.descString()));
|
||||
}
|
||||
|
||||
base.addField(builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
//add interfaces
|
||||
for(Stype type : deps){
|
||||
base.addSuperinterface(tname(packageName, interfaceName(type)));
|
||||
}
|
||||
|
||||
//add to queue to be written later
|
||||
baseClasses.add(base);
|
||||
}
|
||||
}
|
||||
|
||||
//LOGGING
|
||||
|
||||
Log.debug("&gGenerating interface for " + component.name());
|
||||
|
||||
for(TypeName tn : inter.superinterfaces){
|
||||
Log.debug("&g> &lbimplements @", simpleName(tn.toString()));
|
||||
}
|
||||
|
||||
//log methods generated
|
||||
for(MethodSpec spec : inter.methodSpecs){
|
||||
Log.debug("&g> > &c@ @(@)", simpleName(spec.returnType.toString()), spec.name, Seq.with(spec.parameters).toString(", ", p -> simpleName(p.type.toString()) + " " + p.name));
|
||||
}
|
||||
|
||||
Log.debug("");
|
||||
}
|
||||
|
||||
}else if(round == 2){ //round 2: get component classes and generate interfaces for them
|
||||
|
||||
//parse groups
|
||||
//this needs to be done before the entity interfaces are generated, as the entity classes need to know which groups to add themselves to
|
||||
for(Selement<?> group : allGroups){
|
||||
GroupDef an = group.annotation(GroupDef.class);
|
||||
Seq<Stype> types = types(an, GroupDef::value).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);
|
||||
|
||||
boolean collides = an.collide();
|
||||
groupDefs.add(new GroupDefinition(group.name().startsWith("g") ? group.name().substring(1) : group.name(),
|
||||
ClassName.bestGuess(packageName + "." + groupType), types, an.spatial(), an.mapping(), collides));
|
||||
}
|
||||
|
||||
ObjectMap<String, Selement> usedNames = new ObjectMap<>();
|
||||
ObjectMap<Selement, ObjectSet<String>> extraNames = new ObjectMap<>();
|
||||
|
||||
//look at each definition
|
||||
for(Selement<?> type : allDefs){
|
||||
EntityDef ann = type.annotation(EntityDef.class);
|
||||
boolean isFinal = ann.isFinal();
|
||||
|
||||
//all component classes (not interfaces)
|
||||
Seq<Stype> components = allComponents(type);
|
||||
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<>();
|
||||
|
||||
//make sure there's less than 2 base classes
|
||||
Seq<Stype> baseClasses = components.select(s -> s.annotation(Component.class).base());
|
||||
if(baseClasses.size > 2){
|
||||
err("No entity may have more than 2 base classes. Base classes: " + baseClasses, type);
|
||||
}
|
||||
|
||||
//get base class type name for extension
|
||||
Stype baseClassType = baseClasses.any() ? baseClasses.first() : null;
|
||||
@Nullable TypeName baseClass = baseClasses.any() ? tname(packageName + "." + baseName(baseClassType)) : null;
|
||||
//whether the main class is the base itself
|
||||
boolean typeIsBase = baseClassType != null && type.has(Component.class) && type.annotation(Component.class).base();
|
||||
|
||||
if(type.isType() && (!type.name().endsWith("Def") && !type.name().endsWith("Comp"))){
|
||||
err("All entity def names must end with 'Def'/'Comp'", type.e);
|
||||
}
|
||||
|
||||
String name = type.isType() ?
|
||||
type.name().replace("Def", "").replace("Comp", "") :
|
||||
createName(type);
|
||||
|
||||
//check for type name conflicts
|
||||
if(!typeIsBase && baseClass != null && name.equals(baseName(baseClassType))){
|
||||
name += "Entity";
|
||||
}
|
||||
|
||||
//skip double classes
|
||||
if(usedNames.containsKey(name)){
|
||||
extraNames.get(usedNames.get(name), ObjectSet::new).add(type.name());
|
||||
continue;
|
||||
}
|
||||
|
||||
usedNames.put(name, type);
|
||||
extraNames.get(type, ObjectSet::new).add(name);
|
||||
if(!type.isType()){
|
||||
extraNames.get(type, ObjectSet::new).add(type.name());
|
||||
}
|
||||
|
||||
TypeSpec.Builder builder = TypeSpec.classBuilder(name).addModifiers(Modifier.PUBLIC);
|
||||
|
||||
if(isFinal && !typeIsBase) builder.addModifiers(Modifier.FINAL);
|
||||
|
||||
//add serialize() boolean
|
||||
builder.addMethod(MethodSpec.methodBuilder("serialize").addModifiers(Modifier.PUBLIC).returns(boolean.class).addStatement("return " + ann.serialize()).build());
|
||||
|
||||
//all SyncField fields
|
||||
Seq<Svar> syncedFields = new Seq<>();
|
||||
Seq<Svar> allFields = new Seq<>();
|
||||
Seq<FieldSpec> allFieldSpecs = new Seq<>();
|
||||
|
||||
boolean isSync = components.contains(s -> s.name().contains("Sync"));
|
||||
|
||||
//add all components
|
||||
for(Stype comp : components){
|
||||
//whether this component's fields are defined in the base class
|
||||
boolean isShadowed = baseClass != null && !typeIsBase && baseClassDeps.get(baseClassType).contains(comp);
|
||||
|
||||
//write fields to the class; ignoring transient/imported ones
|
||||
Seq<Svar> fields = comp.fields().select(f -> !f.has(Import.class));
|
||||
for(Svar f : fields){
|
||||
if(!usedFields.add(f.name())){
|
||||
err("Field '" + f.name() + "' of component '" + comp.name() + "' redefines a field in entity '" + type.name() + "'");
|
||||
continue;
|
||||
}
|
||||
|
||||
FieldSpec.Builder fbuilder = FieldSpec.builder(f.tname(), f.name());
|
||||
//keep statics/finals
|
||||
if(f.is(Modifier.STATIC)){
|
||||
fbuilder.addModifiers(Modifier.STATIC);
|
||||
if(f.is(Modifier.FINAL)) fbuilder.addModifiers(Modifier.FINAL);
|
||||
}
|
||||
//add transient modifier for serialization
|
||||
if(f.is(Modifier.TRANSIENT)){
|
||||
fbuilder.addModifiers(Modifier.TRANSIENT);
|
||||
}
|
||||
|
||||
//add initializer if it exists
|
||||
if(varInitializers.containsKey(f.descString())){
|
||||
fbuilder.initializer(varInitializers.get(f.descString()));
|
||||
}
|
||||
|
||||
fbuilder.addModifiers(f.has(ReadOnly.class) ? Modifier.PROTECTED : Modifier.PUBLIC);
|
||||
fbuilder.addAnnotations(f.annotations().map(AnnotationSpec::get));
|
||||
FieldSpec spec = fbuilder.build();
|
||||
|
||||
//whether this field would be added to the superclass
|
||||
boolean isVisible = !f.is(Modifier.STATIC) && !f.is(Modifier.PRIVATE) && !f.has(ReadOnly.class);
|
||||
|
||||
//add the field only if it isn't visible or it wasn't implemented by the base class
|
||||
if(!isShadowed || !isVisible){
|
||||
builder.addField(spec);
|
||||
}
|
||||
|
||||
specVariables.put(spec, f);
|
||||
|
||||
allFieldSpecs.add(spec);
|
||||
allFields.add(f);
|
||||
|
||||
//add extra sync fields
|
||||
if(f.has(SyncField.class) && isSync){
|
||||
if(!f.tname().toString().equals("float")) err("All SyncFields must be of type float", f);
|
||||
|
||||
syncedFields.add(f);
|
||||
|
||||
//a synced field has 3 values:
|
||||
//- target state
|
||||
//- last state
|
||||
//- current state (the field itself, will be written to)
|
||||
|
||||
//target
|
||||
builder.addField(FieldSpec.builder(float.class, f.name() + EntityIO.targetSuf).addModifiers(Modifier.TRANSIENT, Modifier.PRIVATE).build());
|
||||
|
||||
//last
|
||||
builder.addField(FieldSpec.builder(float.class, f.name() + EntityIO.lastSuf).addModifiers(Modifier.TRANSIENT, Modifier.PRIVATE).build());
|
||||
}
|
||||
}
|
||||
|
||||
//get all methods from components
|
||||
for(Smethod elem : comp.methods()){
|
||||
methods.get(elem.toString(), Seq::new).add(elem);
|
||||
}
|
||||
}
|
||||
|
||||
syncedFields.sortComparing(Selement::name);
|
||||
|
||||
//override toString method
|
||||
builder.addMethod(MethodSpec.methodBuilder("toString")
|
||||
.addAnnotation(Override.class)
|
||||
.returns(String.class)
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addStatement("return $S + $L", name + "#", "id").build());
|
||||
|
||||
EntityIO io = new EntityIO(type.name(), builder, allFieldSpecs, serializer, rootDirectory.child("annotations/src/main/resources/revisions").child(name));
|
||||
//entities with no sync comp and no serialization gen no code
|
||||
boolean hasIO = ann.genio() && (components.contains(s -> s.name().contains("Sync")) || ann.serialize());
|
||||
|
||||
//add all methods from components
|
||||
for(ObjectMap.Entry<String, Seq<Smethod>> entry : methods){
|
||||
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(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(Selement::name)));
|
||||
|
||||
//representative method
|
||||
Smethod first = entry.value.first();
|
||||
|
||||
//skip internal impl
|
||||
if(first.has(InternalImpl.class)){
|
||||
continue;
|
||||
}
|
||||
|
||||
//build method using same params/returns
|
||||
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.is(Modifier.STATIC)) mbuilder.addModifiers(Modifier.STATIC);
|
||||
mbuilder.addTypeVariables(first.typeVariables().map(TypeVariableName::get));
|
||||
mbuilder.returns(first.retn());
|
||||
mbuilder.addExceptions(first.thrownt());
|
||||
|
||||
for(Svar var : first.params()){
|
||||
mbuilder.addParameter(var.tname(), var.name());
|
||||
}
|
||||
|
||||
//only write the block if it's a void method with several entries
|
||||
boolean writeBlock = first.ret().toString().equals("void") && entry.value.size > 1;
|
||||
|
||||
if((entry.value.first().is(Modifier.ABSTRACT) || entry.value.first().is(Modifier.NATIVE)) && entry.value.size == 1 && !entry.value.first().has(InternalImpl.class)){
|
||||
err(entry.value.first().up().getSimpleName() + "#" + entry.value.first() + " is an abstract method and must be implemented in some component", type);
|
||||
}
|
||||
|
||||
//SPECIAL CASE: inject group add/remove code
|
||||
if(first.name().equals("add") || first.name().equals("remove")){
|
||||
mbuilder.addStatement("if(added == $L) return", first.name().equals("add"));
|
||||
|
||||
for(GroupDefinition def : groups){
|
||||
//remove/add from each group, assume imported
|
||||
mbuilder.addStatement("Groups.$L.$L(this)", def.name, first.name());
|
||||
}
|
||||
}
|
||||
|
||||
if(hasIO){
|
||||
//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"));
|
||||
}
|
||||
|
||||
//SPECIAL CASE: sync I/O code
|
||||
if((first.name().equals("readSync") || first.name().equals("writeSync"))){
|
||||
io.writeSync(mbuilder, first.name().equals("writeSync"), syncedFields, allFields);
|
||||
}
|
||||
|
||||
//SPECIAL CASE: sync I/O code for writing to/from a manual buffer
|
||||
if((first.name().equals("readSyncManual") || first.name().equals("writeSyncManual"))){
|
||||
io.writeSyncManual(mbuilder, first.name().equals("writeSyncManual"), syncedFields);
|
||||
}
|
||||
|
||||
//SPECIAL CASE: interpolate method implementation
|
||||
if(first.name().equals("interpolate")){
|
||||
io.writeInterpolate(mbuilder, syncedFields);
|
||||
}
|
||||
|
||||
//SPECIAL CASE: method to snap to target position after being read for the first time
|
||||
if(first.name().equals("snapSync")){
|
||||
mbuilder.addStatement("updateSpacing = 16");
|
||||
mbuilder.addStatement("lastUpdated = $T.millis()", Time.class);
|
||||
for(Svar field : syncedFields){
|
||||
//reset last+current state to target position
|
||||
mbuilder.addStatement("$L = $L", field.name() + EntityIO.lastSuf, field.name() + EntityIO.targetSuf);
|
||||
mbuilder.addStatement("$L = $L", field.name(), field.name() + EntityIO.targetSuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(Smethod elem : entry.value){
|
||||
String descStr = elem.descString();
|
||||
|
||||
if(elem.is(Modifier.ABSTRACT) || elem.is(Modifier.NATIVE) || !methodBlocks.containsKey(descStr)) continue;
|
||||
|
||||
//get all statements in the method, copy them over
|
||||
String str = methodBlocks.get(descStr);
|
||||
//name for code blocks in the methods
|
||||
String blockName = elem.up().getSimpleName().toString().toLowerCase().replace("comp", "");
|
||||
|
||||
//skip empty blocks
|
||||
if(str.replace("{", "").replace("\n", "").replace("}", "").replace("\t", "").replace(" ", "").isEmpty()){
|
||||
continue;
|
||||
}
|
||||
|
||||
//wrap scope to prevent variable leakage
|
||||
if(writeBlock){
|
||||
//replace return; with block break
|
||||
str = str.replace("return;", "break " + blockName + ";");
|
||||
mbuilder.addCode(blockName + ": {\n");
|
||||
}
|
||||
|
||||
//trim block
|
||||
str = str.substring(2, str.length() - 1);
|
||||
|
||||
//make sure to remove braces here
|
||||
mbuilder.addCode(str);
|
||||
|
||||
//end scope
|
||||
if(writeBlock) mbuilder.addCode("}\n");
|
||||
}
|
||||
|
||||
//add free code to remove methods - always at the end
|
||||
//this only gets called next frame.
|
||||
if(first.name().equals("remove") && ann.pooled()){
|
||||
mbuilder.addStatement("$T.app.post(() -> $T.free(this))", Core.class, Pools.class);
|
||||
}
|
||||
|
||||
builder.addMethod(mbuilder.build());
|
||||
}
|
||||
|
||||
//add pool reset method and implement Poolable
|
||||
if(ann.pooled()){
|
||||
builder.addSuperinterface(Poolable.class);
|
||||
//implement reset()
|
||||
MethodSpec.Builder resetBuilder = MethodSpec.methodBuilder("reset").addModifiers(Modifier.PUBLIC);
|
||||
for(FieldSpec spec : allFieldSpecs){
|
||||
@Nullable Svar variable = specVariables.get(spec);
|
||||
if(variable != null && variable.isAny(Modifier.STATIC, Modifier.FINAL)) continue;
|
||||
String desc = variable.descString();
|
||||
|
||||
if(spec.type.isPrimitive()){
|
||||
//set to primitive default
|
||||
resetBuilder.addStatement("$L = $L", spec.name, variable != null && varInitializers.containsKey(desc) ? varInitializers.get(desc) : getDefault(spec.type.toString()));
|
||||
}else{
|
||||
//set to default null
|
||||
if(!varInitializers.containsKey(desc)){
|
||||
resetBuilder.addStatement("$L = null", spec.name);
|
||||
} //else... TODO reset if poolable
|
||||
}
|
||||
}
|
||||
|
||||
builder.addMethod(resetBuilder.build());
|
||||
}
|
||||
|
||||
//make constructor private
|
||||
builder.addMethod(MethodSpec.constructorBuilder().addModifiers(Modifier.PROTECTED).build());
|
||||
|
||||
//add create() method
|
||||
builder.addMethod(MethodSpec.methodBuilder("create").addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.returns(tname(packageName + "." + name))
|
||||
.addStatement(ann.pooled() ? "return Pools.obtain($L.class, " +name +"::new)" : "return new $L()", name).build());
|
||||
|
||||
definitions.add(new EntityDefinition(packageName + "." + name, builder, type, typeIsBase ? null : baseClass, components, groups, allFieldSpecs));
|
||||
}
|
||||
|
||||
//generate groups
|
||||
TypeSpec.Builder groupsBuilder = TypeSpec.classBuilder("Groups").addModifiers(Modifier.PUBLIC);
|
||||
MethodSpec.Builder groupInit = MethodSpec.methodBuilder("init").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
for(GroupDefinition group : groupDefs){
|
||||
//class names for interface/group
|
||||
ClassName itype = group.baseType;
|
||||
ClassName groupc = ClassName.bestGuess("mindustry.entities.EntityGroup");
|
||||
|
||||
//add field...
|
||||
groupsBuilder.addField(ParameterizedTypeName.get(
|
||||
ClassName.bestGuess("mindustry.entities.EntityGroup"), itype), group.name, Modifier.PUBLIC, Modifier.STATIC);
|
||||
|
||||
groupInit.addStatement("$L = new $T<>($L.class, $L, $L)", group.name, groupc, itype, group.spatial, group.mapping);
|
||||
}
|
||||
|
||||
//write the groups
|
||||
groupsBuilder.addMethod(groupInit.build());
|
||||
|
||||
MethodSpec.Builder groupClear = MethodSpec.methodBuilder("clear").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
for(GroupDefinition group : groupDefs){
|
||||
groupClear.addStatement("$L.clear()", group.name);
|
||||
}
|
||||
|
||||
//write clear
|
||||
groupsBuilder.addMethod(groupClear.build());
|
||||
|
||||
//add method for resizing all necessary groups
|
||||
MethodSpec.Builder groupResize = MethodSpec.methodBuilder("resize")
|
||||
.addParameter(TypeName.FLOAT, "x").addParameter(TypeName.FLOAT, "y").addParameter(TypeName.FLOAT, "w").addParameter(TypeName.FLOAT, "h")
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
|
||||
MethodSpec.Builder groupUpdate = MethodSpec.methodBuilder("update")
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
|
||||
//method resize
|
||||
for(GroupDefinition group : groupDefs){
|
||||
if(group.spatial){
|
||||
groupResize.addStatement("$L.resize(x, y, w, h)", group.name);
|
||||
groupUpdate.addStatement("$L.updatePhysics()", group.name);
|
||||
}
|
||||
}
|
||||
|
||||
groupUpdate.addStatement("all.update()");
|
||||
|
||||
for(GroupDefinition group : groupDefs){
|
||||
if(group.collides){
|
||||
groupUpdate.addStatement("$L.collide()", group.name);
|
||||
}
|
||||
}
|
||||
|
||||
groupsBuilder.addMethod(groupResize.build());
|
||||
groupsBuilder.addMethod(groupUpdate.build());
|
||||
|
||||
write(groupsBuilder);
|
||||
|
||||
//load map of sync IDs
|
||||
StringMap map = new StringMap();
|
||||
Fi idProps = rootDirectory.child("annotations/src/main/resources/classids.properties");
|
||||
if(!idProps.exists()) idProps.writeString("");
|
||||
PropertiesUtils.load(map, idProps.reader());
|
||||
//next ID to be used in generation
|
||||
Integer max = map.values().toSeq().map(Integer::parseInt).max(i -> i);
|
||||
int maxID = max == null ? 0 : max + 1;
|
||||
|
||||
//assign IDs
|
||||
definitions.sort(Structs.comparing(t -> t.naming.toString()));
|
||||
for(EntityDefinition def : definitions){
|
||||
String name = def.naming.fullName();
|
||||
if(map.containsKey(name)){
|
||||
def.classID = map.getInt(name);
|
||||
}else{
|
||||
def.classID = maxID++;
|
||||
map.put(name, def.classID + "");
|
||||
}
|
||||
}
|
||||
|
||||
OrderedMap<String, String> res = new OrderedMap<>();
|
||||
res.putAll(map);
|
||||
res.orderedKeys().sort();
|
||||
|
||||
//write assigned IDs
|
||||
PropertiesUtils.store(res, idProps.writer(false), "Maps entity names to IDs. Autogenerated.");
|
||||
|
||||
//build mapping class for sync IDs
|
||||
TypeSpec.Builder idBuilder = TypeSpec.classBuilder("EntityMapping").addModifiers(Modifier.PUBLIC)
|
||||
.addField(FieldSpec.builder(TypeName.get(Prov[].class), "idMap", Modifier.PRIVATE, Modifier.STATIC).initializer("new Prov[256]").build())
|
||||
.addField(FieldSpec.builder(ParameterizedTypeName.get(ClassName.get(ObjectMap.class),
|
||||
tname(String.class), tname(Prov.class)),
|
||||
"nameMap", Modifier.PRIVATE, Modifier.STATIC).initializer("new ObjectMap<>()").build())
|
||||
.addMethod(MethodSpec.methodBuilder("map").addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.returns(TypeName.get(Prov.class)).addParameter(int.class, "id").addStatement("return idMap[id]").build())
|
||||
.addMethod(MethodSpec.methodBuilder("map").addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.returns(TypeName.get(Prov.class)).addParameter(String.class, "name").addStatement("return nameMap.get(name)").build());
|
||||
|
||||
CodeBlock.Builder idStore = CodeBlock.builder();
|
||||
|
||||
//store the mappings
|
||||
for(EntityDefinition def : definitions){
|
||||
//store mapping
|
||||
idStore.addStatement("idMap[$L] = $L::new", def.classID, def.name);
|
||||
extraNames.get(def.naming).each(extra -> {
|
||||
idStore.addStatement("nameMap.put($S, $L::new)", extra, def.name);
|
||||
if(!Strings.camelToKebab(extra).equals(extra)){
|
||||
idStore.addStatement("nameMap.put($S, $L::new)", Strings.camelToKebab(extra), def.name);
|
||||
}
|
||||
});
|
||||
|
||||
//return mapping
|
||||
def.builder.addMethod(MethodSpec.methodBuilder("classId").addAnnotation(Override.class)
|
||||
.returns(int.class).addModifiers(Modifier.PUBLIC).addStatement("return " + def.classID).build());
|
||||
}
|
||||
|
||||
|
||||
idBuilder.addStaticBlock(idStore.build());
|
||||
|
||||
write(idBuilder);
|
||||
}else{
|
||||
//round 3: generate actual classes and implement interfaces
|
||||
|
||||
//write base classes
|
||||
for(TypeSpec.Builder b : baseClasses){
|
||||
write(b, imports.asArray());
|
||||
}
|
||||
|
||||
//implement each definition
|
||||
for(EntityDefinition def : definitions){
|
||||
|
||||
ObjectSet<String> methodNames = def.components.flatMap(type -> type.methods().map(Smethod::simpleString)).<String>as().asSet();
|
||||
|
||||
//add base class extension if it exists
|
||||
if(def.extend != null){
|
||||
def.builder.superclass(def.extend);
|
||||
}
|
||||
|
||||
//get interface for each component
|
||||
for(Stype comp : def.components){
|
||||
|
||||
//implement the interface
|
||||
Stype inter = allInterfaces.find(i -> i.name().equals(interfaceName(comp)));
|
||||
if(inter == null){
|
||||
err("Failed to generate interface for", comp);
|
||||
return;
|
||||
}
|
||||
|
||||
def.builder.addSuperinterface(inter.tname());
|
||||
|
||||
//generate getter/setter for each method
|
||||
for(Smethod method : inter.methods()){
|
||||
String var = method.name();
|
||||
FieldSpec field = Seq.with(def.fieldSpecs).find(f -> f.name.equals(var));
|
||||
//make sure it's a real variable AND that the component doesn't already implement it somewhere with custom logic
|
||||
if(field == null || methodNames.contains(method.simpleString())) continue;
|
||||
|
||||
//getter
|
||||
if(!method.isVoid()){
|
||||
def.builder.addMethod(MethodSpec.overriding(method.e).addStatement("return " + var).build());
|
||||
}
|
||||
|
||||
//setter
|
||||
if(method.isVoid() && !Seq.with(field.annotations).contains(f -> f.type.toString().equals("@mindustry.annotations.Annotations.ReadOnly"))){
|
||||
def.builder.addMethod(MethodSpec.overriding(method.e).addStatement("this." + var + " = " + var).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write(def.builder, imports.asArray());
|
||||
}
|
||||
|
||||
//store nulls
|
||||
TypeSpec.Builder nullsBuilder = TypeSpec.classBuilder("Nulls").addModifiers(Modifier.PUBLIC).addModifiers(Modifier.FINAL);
|
||||
|
||||
//create mock types of all components
|
||||
for(Stype interf : allInterfaces){
|
||||
//indirect interfaces to implement methods for
|
||||
Seq<Stype> dependencies = interf.allInterfaces().and(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);
|
||||
String className = "Null" + baseName;
|
||||
TypeSpec.Builder nullBuilder = TypeSpec.classBuilder(className)
|
||||
.addModifiers(Modifier.FINAL);
|
||||
|
||||
nullBuilder.addSuperinterface(interf.tname());
|
||||
if(superclass != null) nullBuilder.superclass(tname(baseName(superclass)));
|
||||
|
||||
for(Smethod method : methods){
|
||||
String signature = method.toString();
|
||||
if(signatures.contains(signature)) continue;
|
||||
|
||||
Stype compType = interfaceToComp(method.type());
|
||||
MethodSpec.Builder builder = MethodSpec.overriding(method.e).addModifiers(Modifier.PUBLIC, Modifier.FINAL);
|
||||
builder.addAnnotation(OverrideCallSuper.class); //just in case
|
||||
|
||||
if(!method.isVoid()){
|
||||
if(method.name().equals("isNull")){
|
||||
builder.addStatement("return true");
|
||||
}else if(method.name().equals("id")){
|
||||
builder.addStatement("return -1");
|
||||
}else{
|
||||
Svar variable = compType == null || method.params().size > 0 ? null : compType.fields().find(v -> v.name().equals(method.name()));
|
||||
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());
|
||||
|
||||
signatures.add(signature);
|
||||
}
|
||||
|
||||
nullsBuilder.addField(FieldSpec.builder(type, Strings.camelize(baseName)).initializer("new " + className + "()").addModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PUBLIC).build());
|
||||
|
||||
write(nullBuilder);
|
||||
}
|
||||
|
||||
write(nullsBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
Seq<String> getImports(Element elem){
|
||||
return Seq.with(trees.getPath(elem).getCompilationUnit().getImports()).map(Object::toString);
|
||||
}
|
||||
|
||||
/** @return interface for a component type */
|
||||
String interfaceName(Stype comp){
|
||||
String suffix = "Comp";
|
||||
if(!comp.name().endsWith(suffix)) err("All components must have names that end with 'Comp'", comp.e);
|
||||
|
||||
//example: BlockComp -> IBlock
|
||||
return comp.name().substring(0, comp.name().length() - suffix.length()) + "c";
|
||||
}
|
||||
|
||||
/** @return base class name for a component type */
|
||||
String baseName(Stype comp){
|
||||
String suffix = "Comp";
|
||||
if(!comp.name().endsWith(suffix)) err("All components must have names that end with 'Comp'", comp.e);
|
||||
|
||||
return comp.name().substring(0, comp.name().length() - suffix.length());
|
||||
}
|
||||
|
||||
@Nullable Stype interfaceToComp(Stype type){
|
||||
//example: IBlock -> BlockComp
|
||||
String name = type.name().substring(0, type.name().length() - 1) + "Comp";
|
||||
return componentNames.get(name);
|
||||
}
|
||||
|
||||
/** @return all components that a entity def has */
|
||||
Seq<Stype> allComponents(Selement<?> type){
|
||||
if(!defComponents.containsKey(type)){
|
||||
//get base defs
|
||||
Seq<Stype> interfaces = types(type.annotation(EntityDef.class), EntityDef::value);
|
||||
Seq<Stype> components = new Seq<>();
|
||||
for(Stype i : interfaces){
|
||||
Stype comp = interfaceToComp(i);
|
||||
if(comp != null){
|
||||
components.add(comp);
|
||||
}else{
|
||||
throw new IllegalArgumentException("Type '" + i + "' is not a component interface!");
|
||||
}
|
||||
}
|
||||
|
||||
ObjectSet<Stype> out = new ObjectSet<>();
|
||||
for(Stype comp : components){
|
||||
//get dependencies for each def, add them
|
||||
out.add(comp);
|
||||
out.addAll(getDependencies(comp));
|
||||
}
|
||||
|
||||
defComponents.put(type, out.asArray());
|
||||
}
|
||||
|
||||
return defComponents.get(type);
|
||||
}
|
||||
|
||||
Seq<Stype> getDependencies(Stype component){
|
||||
if(!componentDependencies.containsKey(component)){
|
||||
ObjectSet<Stype> out = new ObjectSet<>();
|
||||
//add base component interfaces
|
||||
out.addAll(component.interfaces().select(this::isCompInterface).map(this::interfaceToComp));
|
||||
//remove self interface
|
||||
out.remove(component);
|
||||
|
||||
//out now contains the base dependencies; finish constructing the tree
|
||||
ObjectSet<Stype> result = new ObjectSet<>();
|
||||
for(Stype type : out){
|
||||
result.add(type);
|
||||
result.addAll(getDependencies(type));
|
||||
}
|
||||
|
||||
if(component.annotation(BaseComponent.class) == null){
|
||||
result.addAll(baseComponents);
|
||||
}
|
||||
|
||||
//remove it again just in case
|
||||
out.remove(component);
|
||||
componentDependencies.put(component, result.asArray());
|
||||
}
|
||||
|
||||
return componentDependencies.get(component);
|
||||
}
|
||||
|
||||
boolean isCompInterface(Stype type){
|
||||
return interfaceToComp(type) != null;
|
||||
}
|
||||
|
||||
String createName(Selement<?> elem){
|
||||
Seq<Stype> comps = types(elem.annotation(EntityDef.class), EntityDef::value).map(this::interfaceToComp);;
|
||||
comps.sortComparing(Selement::name);
|
||||
return comps.toString("", s -> s.name().replace("Comp", ""));
|
||||
}
|
||||
|
||||
<T extends Annotation> Seq<Stype> types(T t, Cons<T> consumer){
|
||||
try{
|
||||
consumer.get(t);
|
||||
}catch(MirroredTypesException e){
|
||||
return Seq.with(e.getTypeMirrors()).map(Stype::of);
|
||||
}
|
||||
throw new IllegalArgumentException("Missing types.");
|
||||
}
|
||||
|
||||
class GroupDefinition{
|
||||
final String name;
|
||||
final ClassName baseType;
|
||||
final Seq<Stype> components;
|
||||
final boolean spatial, mapping, collides;
|
||||
final ObjectSet<Selement> manualInclusions = new ObjectSet<>();
|
||||
|
||||
public GroupDefinition(String name, ClassName bestType, Seq<Stype> components, boolean spatial, boolean mapping, boolean collides){
|
||||
this.baseType = bestType;
|
||||
this.components = components;
|
||||
this.name = name;
|
||||
this.spatial = spatial;
|
||||
this.mapping = mapping;
|
||||
this.collides = collides;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
class EntityDefinition{
|
||||
final Seq<GroupDefinition> groups;
|
||||
final Seq<Stype> components;
|
||||
final Seq<FieldSpec> fieldSpecs;
|
||||
final TypeSpec.Builder builder;
|
||||
final Selement naming;
|
||||
final String name;
|
||||
final @Nullable TypeName extend;
|
||||
int classID;
|
||||
|
||||
public EntityDefinition(String name, Builder builder, Selement naming, TypeName extend, Seq<Stype> components, Seq<GroupDefinition> groups, Seq<FieldSpec> fieldSpec){
|
||||
this.builder = builder;
|
||||
this.name = name;
|
||||
this.naming = naming;
|
||||
this.groups = groups;
|
||||
this.components = components;
|
||||
this.extend = extend;
|
||||
this.fieldSpecs = fieldSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "Definition{" +
|
||||
"groups=" + groups +
|
||||
"components=" + components +
|
||||
", base=" + naming +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package mindustry.annotations.impl;
|
||||
|
||||
import arc.files.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.serialization.*;
|
||||
import com.squareup.javapoet.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.annotations.*;
|
||||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.*;
|
||||
import javax.lang.model.element.*;
|
||||
import java.util.*;
|
||||
|
||||
@SupportedAnnotationTypes("mindustry.annotations.Annotations.StyleDefaults")
|
||||
public class AssetsProcess extends BaseProcessor{
|
||||
|
||||
@Override
|
||||
public void process(RoundEnvironment env) throws Exception{
|
||||
processSounds("Sounds", rootDirectory + "/core/assets/sounds", "arc.audio.Sound");
|
||||
processSounds("Musics", rootDirectory + "/core/assets/music", "arc.audio.Music");
|
||||
processUI(env.getElementsAnnotatedWith(StyleDefaults.class));
|
||||
}
|
||||
|
||||
void processUI(Set<? extends Element> elements) throws Exception{
|
||||
TypeSpec.Builder type = TypeSpec.classBuilder("Tex").addModifiers(Modifier.PUBLIC);
|
||||
TypeSpec.Builder ictype = TypeSpec.classBuilder("Icon").addModifiers(Modifier.PUBLIC);
|
||||
TypeSpec.Builder ichtype = TypeSpec.classBuilder("Iconc").addModifiers(Modifier.PUBLIC);
|
||||
MethodSpec.Builder load = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
MethodSpec.Builder loadStyles = MethodSpec.methodBuilder("loadStyles").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
MethodSpec.Builder icload = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
String resources = rootDirectory + "/core/assets-raw/sprites/ui";
|
||||
Jval icons = Jval.read(Fi.get(rootDirectory + "/core/assets-raw/fontgen/config.json").readString());
|
||||
|
||||
ictype.addField(FieldSpec.builder(ParameterizedTypeName.get(ObjectMap.class, String.class, TextureRegionDrawable.class),
|
||||
"icons", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("new ObjectMap<>()").build());
|
||||
|
||||
for(Jval val : icons.get("glyphs").asArray()){
|
||||
String name = capitalize(val.getString("css", ""));
|
||||
int code = val.getInt("code", 0);
|
||||
ichtype.addField(FieldSpec.builder(char.class, name, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("(char)" + code).build());
|
||||
|
||||
ictype.addField(TextureRegionDrawable.class, name + "Small", Modifier.PUBLIC, Modifier.STATIC);
|
||||
icload.addStatement(name + "Small = mindustry.ui.Fonts.getGlyph(mindustry.ui.Fonts.def, (char)" + code + ")");
|
||||
|
||||
ictype.addField(TextureRegionDrawable.class, name, Modifier.PUBLIC, Modifier.STATIC);
|
||||
icload.addStatement(name + " = mindustry.ui.Fonts.getGlyph(mindustry.ui.Fonts.icon, (char)" + code + ")");
|
||||
|
||||
icload.addStatement("icons.put($S, " + name + ")", name);
|
||||
icload.addStatement("icons.put($S, " + name + "Small)", name + "Small");
|
||||
}
|
||||
|
||||
Fi.get(resources).walk(p -> {
|
||||
if(!p.extEquals("png")) return;
|
||||
|
||||
String filename = p.name();
|
||||
filename = filename.substring(0, filename.indexOf("."));
|
||||
|
||||
String sfilen = filename;
|
||||
String dtype = p.name().endsWith(".9.png") ? "arc.scene.style.NinePatchDrawable" : "arc.scene.style.TextureRegionDrawable";
|
||||
|
||||
String varname = capitalize(sfilen);
|
||||
|
||||
if(SourceVersion.isKeyword(varname)) varname += "s";
|
||||
|
||||
type.addField(ClassName.bestGuess(dtype), varname, Modifier.STATIC, Modifier.PUBLIC);
|
||||
load.addStatement(varname + " = ("+dtype+")arc.Core.atlas.drawable($S)", sfilen);
|
||||
});
|
||||
|
||||
for(Element elem : elements){
|
||||
Seq.with(((TypeElement)elem).getEnclosedElements()).each(e -> e.getKind() == ElementKind.FIELD, field -> {
|
||||
String fname = field.getSimpleName().toString();
|
||||
if(fname.startsWith("default")){
|
||||
loadStyles.addStatement("arc.Core.scene.addStyle(" + field.asType().toString() + ".class, mindustry.ui.Styles." + fname + ")");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ictype.addMethod(icload.build());
|
||||
JavaFile.builder(packageName, ichtype.build()).build().writeTo(BaseProcessor.filer);
|
||||
JavaFile.builder(packageName, ictype.build()).build().writeTo(BaseProcessor.filer);
|
||||
|
||||
type.addMethod(load.build());
|
||||
type.addMethod(loadStyles.build());
|
||||
JavaFile.builder(packageName, type.build()).build().writeTo(BaseProcessor.filer);
|
||||
}
|
||||
|
||||
void processSounds(String classname, String path, String rtype) throws Exception{
|
||||
TypeSpec.Builder type = TypeSpec.classBuilder(classname).addModifiers(Modifier.PUBLIC);
|
||||
MethodSpec.Builder dispose = MethodSpec.methodBuilder("dispose").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
MethodSpec.Builder loadBegin = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
|
||||
HashSet<String> names = new HashSet<>();
|
||||
Fi.get(path).walk(p -> {
|
||||
String fname = p.name();
|
||||
String name = p.nameWithoutExtension();
|
||||
|
||||
if(names.contains(name)){
|
||||
BaseProcessor.err("Duplicate file name: " + p.toString() + "!");
|
||||
}else{
|
||||
names.add(name);
|
||||
}
|
||||
|
||||
if(SourceVersion.isKeyword(name)){
|
||||
name = name + "s";
|
||||
}
|
||||
|
||||
String filepath = path.substring(path.lastIndexOf("/") + 1) + "/" + fname;
|
||||
|
||||
String filename = "arc.Core.app.getType() != arc.Application.ApplicationType.iOS ? \"" + filepath + "\" : \"" + filepath.replace(".ogg", ".mp3")+"\"";
|
||||
|
||||
loadBegin.addStatement("arc.Core.assets.load("+filename +", "+rtype+".class).loaded = a -> " + name + " = ("+rtype+")a", filepath, filepath.replace(".ogg", ".mp3"));
|
||||
|
||||
dispose.addStatement("arc.Core.assets.unload(" + filename + ")");
|
||||
dispose.addStatement(name + " = null");
|
||||
type.addField(FieldSpec.builder(ClassName.bestGuess(rtype), name, Modifier.STATIC, Modifier.PUBLIC).initializer("new arc.mock.Mock" + rtype.substring(rtype.lastIndexOf(".") + 1)+ "()").build());
|
||||
});
|
||||
|
||||
if(classname.equals("Sounds")){
|
||||
type.addField(FieldSpec.builder(ClassName.bestGuess(rtype), "none", Modifier.STATIC, Modifier.PUBLIC).initializer("new arc.mock.Mock" + rtype.substring(rtype.lastIndexOf(".") + 1)+ "()").build());
|
||||
}
|
||||
|
||||
type.addMethod(loadBegin.build());
|
||||
type.addMethod(dispose.build());
|
||||
JavaFile.builder(packageName, type.build()).build().writeTo(BaseProcessor.filer);
|
||||
}
|
||||
|
||||
static String capitalize(String s){
|
||||
StringBuilder result = new StringBuilder(s.length());
|
||||
|
||||
for(int i = 0; i < s.length(); i++){
|
||||
char c = s.charAt(i);
|
||||
if(c != '_' && c != '-'){
|
||||
if(i > 0 && (s.charAt(i - 1) == '_' || s.charAt(i - 1) == '-')){
|
||||
result.append(Character.toUpperCase(c));
|
||||
}else{
|
||||
result.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package mindustry.annotations.impl;
|
||||
|
||||
import com.sun.source.tree.*;
|
||||
import com.sun.source.util.*;
|
||||
import com.sun.tools.javac.code.Scope;
|
||||
import com.sun.tools.javac.code.*;
|
||||
import com.sun.tools.javac.code.Symbol.*;
|
||||
import com.sun.tools.javac.code.Type.*;
|
||||
import com.sun.tools.javac.tree.*;
|
||||
import com.sun.tools.javac.tree.JCTree.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.*;
|
||||
import javax.lang.model.element.*;
|
||||
import javax.tools.Diagnostic.*;
|
||||
import java.lang.annotation.*;
|
||||
import java.util.*;
|
||||
|
||||
@SupportedAnnotationTypes({"java.lang.Override"})
|
||||
public class CallSuperProcess extends AbstractProcessor{
|
||||
private Trees trees;
|
||||
|
||||
@Override
|
||||
public void init(ProcessingEnvironment pe){
|
||||
super.init(pe);
|
||||
trees = Trees.instance(pe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
||||
for(Element e : roundEnv.getElementsAnnotatedWith(Override.class)){
|
||||
if(e.getAnnotation(OverrideCallSuper.class) != null) return false;
|
||||
|
||||
CodeAnalyzerTreeScanner codeScanner = new CodeAnalyzerTreeScanner();
|
||||
codeScanner.methodName = e.getSimpleName().toString();
|
||||
|
||||
TreePath tp = trees.getPath(e.getEnclosingElement());
|
||||
codeScanner.scan(tp, trees);
|
||||
|
||||
if(codeScanner.callSuperUsed){
|
||||
List list = codeScanner.method.getBody().getStatements();
|
||||
|
||||
if(!doesCallSuper(list, codeScanner.methodName)){
|
||||
processingEnv.getMessager().printMessage(Kind.ERROR, "Overriding method '" + codeScanner.methodName + "' must explicitly call super method from its parent class.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean doesCallSuper(List list, String methodName){
|
||||
for(Object object : list){
|
||||
if(object instanceof JCTree.JCExpressionStatement){
|
||||
JCTree.JCExpressionStatement expr = (JCExpressionStatement)object;
|
||||
String exprString = expr.toString();
|
||||
if(exprString.startsWith("super." + methodName) && exprString.endsWith(");")) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceVersion getSupportedSourceVersion(){
|
||||
return SourceVersion.RELEASE_8;
|
||||
}
|
||||
|
||||
static class CodeAnalyzerTreeScanner extends TreePathScanner<Object, Trees>{
|
||||
private String methodName;
|
||||
private MethodTree method;
|
||||
private boolean callSuperUsed;
|
||||
|
||||
@Override
|
||||
public Object visitClass(ClassTree classTree, Trees trees){
|
||||
Tree extendTree = classTree.getExtendsClause();
|
||||
|
||||
if(extendTree instanceof JCTypeApply){ //generic classes case
|
||||
JCTypeApply generic = (JCTypeApply)extendTree;
|
||||
extendTree = generic.clazz;
|
||||
}
|
||||
|
||||
if(extendTree instanceof JCIdent){
|
||||
JCIdent tree = (JCIdent)extendTree;
|
||||
com.sun.tools.javac.code.Scope members = tree.sym.members();
|
||||
|
||||
if(checkScope(members))
|
||||
return super.visitClass(classTree, trees);
|
||||
|
||||
if(checkSuperTypes((ClassType)tree.type))
|
||||
return super.visitClass(classTree, trees);
|
||||
|
||||
}
|
||||
callSuperUsed = false;
|
||||
|
||||
return super.visitClass(classTree, trees);
|
||||
}
|
||||
|
||||
public boolean checkSuperTypes(ClassType type){
|
||||
if(type.supertype_field != null && type.supertype_field.tsym != null){
|
||||
if(checkScope(type.supertype_field.tsym.members()))
|
||||
return true;
|
||||
else
|
||||
return checkSuperTypes((ClassType)type.supertype_field);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean checkScope(Scope members){
|
||||
Iterable<Symbol> it;
|
||||
try{
|
||||
it = (Iterable<Symbol>)members.getClass().getMethod("getElements").invoke(members);
|
||||
}catch(Throwable t){
|
||||
try{
|
||||
it = (Iterable<Symbol>)members.getClass().getMethod("getSymbols").invoke(members);
|
||||
}catch(Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
for(Symbol s : it){
|
||||
|
||||
if(s instanceof MethodSymbol){
|
||||
MethodSymbol ms = (MethodSymbol)s;
|
||||
|
||||
if(ms.getSimpleName().toString().equals(methodName)){
|
||||
Annotation annotation = ms.getAnnotation(CallSuper.class);
|
||||
if(annotation != null){
|
||||
callSuperUsed = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitMethod(MethodTree methodTree, Trees trees){
|
||||
if(methodTree.getName().toString().equals(methodName))
|
||||
method = methodTree;
|
||||
|
||||
return super.visitMethod(methodTree, trees);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package mindustry.annotations.impl;
|
||||
|
||||
import arc.struct.*;
|
||||
import com.squareup.javapoet.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.annotations.*;
|
||||
import mindustry.annotations.util.*;
|
||||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.element.*;
|
||||
import javax.lang.model.type.*;
|
||||
|
||||
/**
|
||||
* Generates ""value types"" classes that are packed into integer primitives of the most aproppriate size.
|
||||
* It would be nice if Java didn't make crazy hacks like this necessary.
|
||||
*/
|
||||
@SupportedAnnotationTypes({
|
||||
"mindustry.annotations.Annotations.Struct"
|
||||
})
|
||||
public class StructProcess extends BaseProcessor{
|
||||
|
||||
@Override
|
||||
public void process(RoundEnvironment env) throws Exception{
|
||||
Seq<Stype> elements = types(Struct.class);
|
||||
|
||||
for(Stype elem : elements){
|
||||
|
||||
if(!elem.name().endsWith("Struct")){
|
||||
err("All classes annotated with @Struct must have their class names end in 'Struct'.", elem);
|
||||
continue;
|
||||
}
|
||||
|
||||
String structName = elem.name().substring(0, elem.name().length() - "Struct".length());
|
||||
String structParam = structName.toLowerCase();
|
||||
|
||||
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(structName)
|
||||
.addModifiers(Modifier.FINAL, Modifier.PUBLIC);
|
||||
|
||||
try{
|
||||
Seq<Svar> variables = elem.fields();
|
||||
int structSize = variables.mapInt(StructProcess::varSize).sum();
|
||||
int structTotalSize = (structSize <= 8 ? 8 : structSize <= 16 ? 16 : structSize <= 32 ? 32 : 64);
|
||||
|
||||
if(variables.size == 0){
|
||||
err("making a struct with no fields is utterly pointles.", elem);
|
||||
continue;
|
||||
}
|
||||
|
||||
//obtain type which will be stored
|
||||
Class<?> structType = typeForSize(structSize);
|
||||
|
||||
//[constructor] get(fields...) : structType
|
||||
MethodSpec.Builder constructor = MethodSpec.methodBuilder("get")
|
||||
.addModifiers(Modifier.STATIC, Modifier.PUBLIC)
|
||||
.returns(structType);
|
||||
|
||||
StringBuilder cons = new StringBuilder();
|
||||
StringBuilder doc = new StringBuilder();
|
||||
doc.append("Bits used: ").append(structSize).append(" / ").append(structTotalSize).append("\n");
|
||||
|
||||
int offset = 0;
|
||||
for(Svar var : variables){
|
||||
int size = varSize(var);
|
||||
TypeName varType = var.tname();
|
||||
String varName = var.name();
|
||||
|
||||
//add val param to constructor
|
||||
constructor.addParameter(varType, varName);
|
||||
|
||||
//[get] field(structType) : fieldType
|
||||
MethodSpec.Builder getter = MethodSpec.methodBuilder(var.name().toString())
|
||||
.addModifiers(Modifier.STATIC, Modifier.PUBLIC)
|
||||
.returns(varType)
|
||||
.addParameter(structType, structParam);
|
||||
//[set] field(structType, fieldType) : structType
|
||||
MethodSpec.Builder setter = MethodSpec.methodBuilder(var.name().toString())
|
||||
.addModifiers(Modifier.STATIC, Modifier.PUBLIC)
|
||||
.returns(structType)
|
||||
.addParameter(structType, structParam).addParameter(varType, "value");
|
||||
|
||||
//[getter]
|
||||
if(varType == TypeName.BOOLEAN){
|
||||
//bools: single bit, is simplified
|
||||
getter.addStatement("return ($L & (1L << $L)) != 0", structParam, offset);
|
||||
}else if(varType == TypeName.FLOAT){
|
||||
//floats: need conversion
|
||||
getter.addStatement("return Float.intBitsToFloat((int)(($L >>> $L) & $L))", structParam, offset, bitString(size, structTotalSize));
|
||||
}else{
|
||||
//bytes, shorts, chars, ints
|
||||
getter.addStatement("return ($T)(($L >>> $L) & $L)", varType, structParam, offset, bitString(size, structTotalSize));
|
||||
}
|
||||
|
||||
//[setter] + [constructor building]
|
||||
if(varType == TypeName.BOOLEAN){
|
||||
cons.append(" | (").append(varName).append(" ? ").append("1L << ").append(offset).append("L : 0)");
|
||||
|
||||
//bools: single bit, needs special case to clear things
|
||||
setter.beginControlFlow("if(value)");
|
||||
setter.addStatement("return ($T)(($L & ~(1L << $LL)))", structType, structParam, offset);
|
||||
setter.nextControlFlow("else");
|
||||
setter.addStatement("return ($T)(($L & ~(1L << $LL)) | (1L << $LL))", structType, structParam, offset, offset);
|
||||
setter.endControlFlow();
|
||||
}else if(varType == TypeName.FLOAT){
|
||||
cons.append(" | (").append("(").append(structType).append(")").append("Float.floatToIntBits(").append(varName).append(") << ").append(offset).append("L)");
|
||||
|
||||
//floats: need conversion
|
||||
setter.addStatement("return ($T)(($L & $L) | (($T)Float.floatToIntBits(value) << $LL))", structType, structParam, bitString(offset, size, structTotalSize), structType, offset);
|
||||
}else{
|
||||
cons.append(" | (((").append(structType).append(")").append(varName).append(" << ").append(offset).append("L)").append(" & ").append(bitString(offset, size, structTotalSize)).append(")");
|
||||
|
||||
//bytes, shorts, chars, ints
|
||||
setter.addStatement("return ($T)(($L & $L) | (($T)value << $LL))", structType, structParam, bitString(offset, size, structTotalSize), structType, offset);
|
||||
}
|
||||
|
||||
doc.append("<br> ").append(varName).append(" [").append(offset).append("..").append(size + offset).append("]\n");
|
||||
|
||||
//add finished methods
|
||||
classBuilder.addMethod(getter.build());
|
||||
classBuilder.addMethod(setter.build());
|
||||
|
||||
offset += size;
|
||||
}
|
||||
|
||||
classBuilder.addJavadoc(doc.toString());
|
||||
|
||||
//add constructor final statement + add to class and build
|
||||
constructor.addStatement("return ($T)($L)", structType, cons.toString().substring(3));
|
||||
classBuilder.addMethod(constructor.build());
|
||||
|
||||
JavaFile.builder(packageName, classBuilder.build()).build().writeTo(BaseProcessor.filer);
|
||||
}catch(IllegalArgumentException e){
|
||||
e.printStackTrace();
|
||||
err(e.getMessage(), elem);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static String bitString(int offset, int size, int totalSize){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for(int i = 0; i < offset; i++) builder.append('0');
|
||||
for(int i = 0; i < size; i++) builder.append('1');
|
||||
for(int i = 0; i < totalSize - size - offset; i++) builder.append('0');
|
||||
return "0b" + builder.reverse().toString() + "L";
|
||||
}
|
||||
|
||||
static String bitString(int size, int totalSize){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for(int i = 0; i < size; i++) builder.append('1');
|
||||
for(int i = 0; i < totalSize - size; i++) builder.append('0');
|
||||
return "0b" + builder.reverse().toString() + "L";
|
||||
}
|
||||
|
||||
static int varSize(Svar var) throws IllegalArgumentException{
|
||||
if(!var.mirror().getKind().isPrimitive()){
|
||||
throw new IllegalArgumentException("All struct fields must be primitives: " + var);
|
||||
}
|
||||
|
||||
StructField an = var.annotation(StructField.class);
|
||||
if(var.mirror().getKind() == TypeKind.BOOLEAN && an != null && an.value() != 1){
|
||||
throw new IllegalArgumentException("Booleans can only be one bit long... why would you do this?");
|
||||
}
|
||||
|
||||
if(var.mirror().getKind() == TypeKind.FLOAT && an != null && an.value() != 32){
|
||||
throw new IllegalArgumentException("Float size can't be changed. Very sad.");
|
||||
}
|
||||
|
||||
return an == null ? typeSize(var.mirror().getKind()) : an.value();
|
||||
}
|
||||
|
||||
static Class<?> typeForSize(int size) throws IllegalArgumentException{
|
||||
if(size <= 8){
|
||||
return byte.class;
|
||||
}else if(size <= 16){
|
||||
return short.class;
|
||||
}else if(size <= 32){
|
||||
return int.class;
|
||||
}else if(size <= 64){
|
||||
return long.class;
|
||||
}
|
||||
throw new IllegalArgumentException("Too many fields, must fit in 64 bits. Curent size: " + size);
|
||||
}
|
||||
|
||||
/** returns a type's element size in bits. */
|
||||
static int typeSize(TypeKind kind) throws IllegalArgumentException{
|
||||
switch(kind){
|
||||
case BOOLEAN:
|
||||
return 1;
|
||||
case BYTE:
|
||||
return 8;
|
||||
case SHORT:
|
||||
return 16;
|
||||
case FLOAT:
|
||||
case CHAR:
|
||||
case INT:
|
||||
return 32;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid type kind: " + kind + ". Note that doubles and longs are not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package mindustry.annotations.misc;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.struct.*;
|
||||
import arc.struct.ObjectMap.*;
|
||||
import com.squareup.javapoet.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.annotations.*;
|
||||
import mindustry.annotations.util.*;
|
||||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.element.*;
|
||||
|
||||
@SupportedAnnotationTypes("mindustry.annotations.Annotations.Load")
|
||||
public class LoadRegionProcessor extends BaseProcessor{
|
||||
|
||||
@Override
|
||||
public void process(RoundEnvironment env) throws Exception{
|
||||
TypeSpec.Builder regionClass = TypeSpec.classBuilder("ContentRegions")
|
||||
.addModifiers(Modifier.PUBLIC);
|
||||
MethodSpec.Builder method = MethodSpec.methodBuilder("loadRegions")
|
||||
.addParameter(tname("mindustry.ctype.MappableContent"), "content")
|
||||
.addModifiers(Modifier.STATIC, Modifier.PUBLIC);
|
||||
|
||||
ObjectMap<Stype, Seq<Svar>> fieldMap = new ObjectMap<>();
|
||||
|
||||
for(Svar field : fields(Load.class)){
|
||||
if(!field.is(Modifier.PUBLIC)){
|
||||
err("@LoadRegion field must be public", field);
|
||||
}
|
||||
|
||||
fieldMap.get(field.enclosingType(), Seq::new).add(field);
|
||||
}
|
||||
|
||||
for(Entry<Stype, Seq<Svar>> entry : fieldMap){
|
||||
method.beginControlFlow("if(content instanceof $T)", entry.key.tname());
|
||||
|
||||
for(Svar field : entry.value){
|
||||
Load an = field.annotation(Load.class);
|
||||
//get # of array dimensions
|
||||
int dims = count(field.mirror().toString(), "[]");
|
||||
boolean doFallback = !an.fallback().equals("error");
|
||||
String fallbackString = doFallback ? ", " + parse(an.fallback()) : "";
|
||||
|
||||
//not an array
|
||||
if(dims == 0){
|
||||
method.addStatement("(($T)content).$L = $T.atlas.find($L$L)", entry.key.tname(), field.name(), Core.class, parse(an.value()), fallbackString);
|
||||
}else{
|
||||
//is an array, create length string
|
||||
int[] lengths = an.lengths();
|
||||
if(lengths.length == 0) lengths = new int[]{an.length()};
|
||||
|
||||
if(dims != lengths.length){
|
||||
err("Length dimensions must match array dimensions: " + dims + " != " + lengths.length, field);
|
||||
}
|
||||
|
||||
StringBuilder lengthString = new StringBuilder();
|
||||
for(int value : lengths) lengthString.append("[").append(value).append("]");
|
||||
|
||||
method.addStatement("(($T)content).$L = new $T$L", entry.key.tname(), field.name(), TextureRegion.class, lengthString.toString());
|
||||
|
||||
for(int i = 0; i < dims; i++){
|
||||
method.beginControlFlow("for(int INDEX$L = 0; INDEX$L < $L; INDEX$L ++)", i, i, lengths[i], i);
|
||||
}
|
||||
|
||||
StringBuilder indexString = new StringBuilder();
|
||||
for(int i = 0; i < dims; i++){
|
||||
indexString.append("[INDEX").append(i).append("]");
|
||||
}
|
||||
|
||||
method.addStatement("(($T)content).$L$L = $T.atlas.find($L$L)", entry.key.tname(), field.name(), indexString.toString(), Core.class, parse(an.value()), fallbackString);
|
||||
|
||||
for(int i = 0; i < dims; i++){
|
||||
method.endControlFlow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
method.endControlFlow();
|
||||
}
|
||||
|
||||
regionClass.addMethod(method.build());
|
||||
|
||||
write(regionClass);
|
||||
}
|
||||
|
||||
private static int count(String str, String substring){
|
||||
int lastIndex = 0;
|
||||
int count = 0;
|
||||
|
||||
while(lastIndex != -1){
|
||||
|
||||
lastIndex = str.indexOf(substring, lastIndex);
|
||||
|
||||
if(lastIndex != -1){
|
||||
count ++;
|
||||
lastIndex += substring.length();
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private String parse(String value){
|
||||
value = '"' + value + '"';
|
||||
value = value.replace("@size", "\" + ((mindustry.world.Block)content).size + \"");
|
||||
value = value.replace("@", "\" + content.name + \"");
|
||||
value = value.replace("#1", "\" + INDEX0 + \"");
|
||||
value = value.replace("#2", "\" + INDEX1 + \"");
|
||||
value = value.replace("#", "\" + INDEX0 + \"");
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package mindustry.annotations.misc;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.struct.*;
|
||||
import com.squareup.javapoet.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.annotations.*;
|
||||
import mindustry.annotations.util.*;
|
||||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.element.*;
|
||||
|
||||
@SupportedAnnotationTypes("mindustry.annotations.Annotations.RegisterStatement")
|
||||
public class LogicStatementProcessor extends BaseProcessor{
|
||||
|
||||
@Override
|
||||
public void process(RoundEnvironment env) throws Exception{
|
||||
TypeSpec.Builder type = TypeSpec.classBuilder("LogicIO")
|
||||
.addModifiers(Modifier.PUBLIC);
|
||||
|
||||
MethodSpec.Builder writer = MethodSpec.methodBuilder("write")
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.addParameter(Object.class, "obj")
|
||||
.addParameter(StringBuilder.class, "out");
|
||||
|
||||
MethodSpec.Builder reader = MethodSpec.methodBuilder("read")
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.returns(tname("mindustry.logic.LStatement"))
|
||||
.addParameter(String[].class, "tokens");
|
||||
|
||||
Seq<Stype> types = types(RegisterStatement.class);
|
||||
|
||||
type.addField(FieldSpec.builder(
|
||||
ParameterizedTypeName.get(
|
||||
ClassName.get(Seq.class),
|
||||
ParameterizedTypeName.get(ClassName.get(Prov.class),
|
||||
tname("mindustry.logic.LStatement"))), "allStatements", Modifier.PUBLIC, Modifier.STATIC)
|
||||
.initializer("Seq.with(" + types.toString(", ", t -> "" + t.toString() + "::new") + ")").build());
|
||||
|
||||
boolean beganWrite = false, beganRead = false;
|
||||
|
||||
for(Stype c : types){
|
||||
String name = c.annotation(RegisterStatement.class).value();
|
||||
|
||||
if(beganWrite){
|
||||
writer.nextControlFlow("else if(obj instanceof $T)", c.mirror());
|
||||
}else{
|
||||
writer.beginControlFlow("if(obj instanceof $T)", c.mirror());
|
||||
beganWrite = true;
|
||||
}
|
||||
|
||||
//write the name & individual fields
|
||||
writer.addStatement("out.append($S)", name);
|
||||
|
||||
Seq<Svar> fields = c.fields();
|
||||
|
||||
String readSt = "if(tokens[0].equals($S))";
|
||||
if(beganRead){
|
||||
reader.nextControlFlow("else " + readSt, name);
|
||||
}else{
|
||||
reader.beginControlFlow(readSt, name);
|
||||
beganRead = true;
|
||||
}
|
||||
|
||||
reader.addStatement("$T result = new $T()", c.mirror(), c.mirror());
|
||||
|
||||
int index = 0;
|
||||
|
||||
for(Svar field : fields){
|
||||
if(field.is(Modifier.TRANSIENT)) continue;
|
||||
|
||||
writer.addStatement("out.append(\" \")");
|
||||
writer.addStatement("out.append((($T)obj).$L$L)", c.mirror(), field.name(),
|
||||
field.mirror().toString().equals("java.lang.String") ?
|
||||
".replace(\"\\n\", \"\\\\n\")" :
|
||||
Seq.with(typeu.directSupertypes(field.mirror())).contains(t -> t.toString().contains("java.lang.Enum")) ? ".name()" :
|
||||
"");
|
||||
|
||||
//reading primitives, strings and enums is supported; nothing else is
|
||||
reader.addStatement("result.$L = $L(tokens[$L])$L",
|
||||
field.name(),
|
||||
field.mirror().toString().equals("java.lang.String") ?
|
||||
"" : (field.tname().isPrimitive() ? field.tname().box().toString() :
|
||||
field.mirror().toString()) + ".valueOf", //if it's not a string, it must have a valueOf method
|
||||
index + 1,
|
||||
field.mirror().toString().equals("java.lang.String") ?
|
||||
".replace(\"\\\\n\", \"\\n\")" :
|
||||
""
|
||||
);
|
||||
|
||||
index ++;
|
||||
}
|
||||
|
||||
reader.addStatement("return result");
|
||||
}
|
||||
|
||||
reader.endControlFlow();
|
||||
writer.endControlFlow();
|
||||
|
||||
reader.addStatement("return null");
|
||||
|
||||
type.addMethod(writer.build());
|
||||
type.addMethod(reader.build());
|
||||
|
||||
write(type);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.anuke.annotations;
|
||||
package mindustry.annotations.remote;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.anuke.annotations;
|
||||
package mindustry.annotations.remote;
|
||||
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
package mindustry.annotations.remote;
|
||||
|
||||
import arc.struct.*;
|
||||
import com.squareup.javapoet.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.annotations.*;
|
||||
import mindustry.annotations.util.*;
|
||||
import mindustry.annotations.util.TypeIOResolver.*;
|
||||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.element.*;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/** The annotation processor for generating remote method call code. */
|
||||
@SupportedAnnotationTypes({
|
||||
"mindustry.annotations.Annotations.Remote",
|
||||
"mindustry.annotations.Annotations.TypeIOHandler"
|
||||
})
|
||||
public class RemoteProcess extends BaseProcessor{
|
||||
/** Maximum size of each event packet. */
|
||||
public static final int maxPacketSize = 4096;
|
||||
/** Warning on top of each autogenerated file. */
|
||||
public static final String autogenWarning = "Autogenerated file. Do not modify!\n";
|
||||
|
||||
/** Name of class that handles reading and invoking packets on the server. */
|
||||
private static final String readServerName = "RemoteReadServer";
|
||||
/** Name of class that handles reading and invoking packets on the client. */
|
||||
private static final String readClientName = "RemoteReadClient";
|
||||
/** Simple class name of generated class name. */
|
||||
private static final String callLocation = "Call";
|
||||
|
||||
//class serializers
|
||||
private ClassSerializer serializer;
|
||||
//all elements with the Remote annotation
|
||||
private Seq<Smethod> elements;
|
||||
//map of all classes to generate by name
|
||||
private HashMap<String, ClassEntry> classMap;
|
||||
//list of all method entries
|
||||
private Seq<MethodEntry> methods;
|
||||
//list of all method entries
|
||||
private Seq<ClassEntry> classes;
|
||||
|
||||
{
|
||||
rounds = 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(RoundEnvironment roundEnv) throws Exception{
|
||||
//round 1: find all annotations, generate *writers*
|
||||
if(round == 1){
|
||||
//get serializers
|
||||
serializer = TypeIOResolver.resolve(this);
|
||||
//last method ID used
|
||||
int lastMethodID = 0;
|
||||
//find all elements with the Remote annotation
|
||||
elements = methods(Remote.class);
|
||||
//map of all classes to generate by name
|
||||
classMap = new HashMap<>();
|
||||
//list of all method entries
|
||||
methods = new Seq<>();
|
||||
//list of all method entries
|
||||
classes = new Seq<>();
|
||||
|
||||
Seq<Smethod> orderedElements = elements.copy();
|
||||
orderedElements.sortComparing(Object::toString);
|
||||
|
||||
//create methods
|
||||
for(Smethod element : orderedElements){
|
||||
Remote annotation = element.annotation(Remote.class);
|
||||
|
||||
//check for static
|
||||
if(!element.is(Modifier.STATIC) || !element.is(Modifier.PUBLIC)){
|
||||
err("All @Remote methods must be public and static: ", element);
|
||||
}
|
||||
|
||||
//can't generate none methods
|
||||
if(annotation.targets() == Loc.none){
|
||||
err("A @Remote method's targets() cannot be equal to 'none':", element);
|
||||
}
|
||||
|
||||
//get and create class entry if needed
|
||||
if(!classMap.containsKey(callLocation)){
|
||||
ClassEntry clas = new ClassEntry(callLocation);
|
||||
classMap.put(callLocation, clas);
|
||||
classes.add(clas);
|
||||
}
|
||||
|
||||
ClassEntry entry = classMap.get(callLocation);
|
||||
|
||||
//create and add entry
|
||||
MethodEntry method = new MethodEntry(entry.name, BaseProcessor.getMethodName(element.e), annotation.targets(), annotation.variants(),
|
||||
annotation.called(), annotation.unreliable(), annotation.forward(), lastMethodID++, element.e, annotation.priority());
|
||||
|
||||
entry.methods.add(method);
|
||||
methods.add(method);
|
||||
}
|
||||
|
||||
//create read/write generators
|
||||
RemoteWriteGenerator writegen = new RemoteWriteGenerator(serializer);
|
||||
|
||||
//generate the methods to invoke (write)
|
||||
writegen.generateFor(classes, packageName);
|
||||
}else if(round == 2){ //round 2: generate all *readers*
|
||||
RemoteReadGenerator readgen = new RemoteReadGenerator(serializer);
|
||||
|
||||
//generate server readers
|
||||
readgen.generateFor(methods.select(method -> method.where.isClient), readServerName, packageName, true);
|
||||
//generate client readers
|
||||
readgen.generateFor(methods.select(method -> method.where.isServer), readClientName, packageName, false);
|
||||
|
||||
//create class for storing unique method hash
|
||||
TypeSpec.Builder hashBuilder = TypeSpec.classBuilder("MethodHash").addModifiers(Modifier.PUBLIC);
|
||||
hashBuilder.addJavadoc(autogenWarning);
|
||||
hashBuilder.addField(FieldSpec.builder(int.class, "HASH", Modifier.STATIC, Modifier.PUBLIC, Modifier.FINAL)
|
||||
.initializer("$1L", Arrays.hashCode(methods.map(m -> m.element).toArray())).build());
|
||||
|
||||
//build and write resulting hash class
|
||||
TypeSpec spec = hashBuilder.build();
|
||||
JavaFile.builder(packageName, spec).build().writeTo(BaseProcessor.filer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,19 @@
|
||||
package io.anuke.annotations;
|
||||
package mindustry.annotations.remote;
|
||||
|
||||
import arc.struct.*;
|
||||
import arc.util.io.*;
|
||||
import com.squareup.javapoet.*;
|
||||
import io.anuke.annotations.IOFinder.ClassSerializer;
|
||||
import mindustry.annotations.*;
|
||||
import mindustry.annotations.util.TypeIOResolver.*;
|
||||
|
||||
import javax.lang.model.element.*;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/** Generates code for reading remote invoke packets on the client and server. */
|
||||
public class RemoteReadGenerator{
|
||||
private final HashMap<String, ClassSerializer> serializers;
|
||||
private final ClassSerializer serializers;
|
||||
|
||||
/** Creates a read generator that uses the supplied serializer setup. */
|
||||
public RemoteReadGenerator(HashMap<String, ClassSerializer> serializers){
|
||||
public RemoteReadGenerator(ClassSerializer serializers){
|
||||
this.serializers = serializers;
|
||||
}
|
||||
|
||||
@@ -28,28 +24,21 @@ public class RemoteReadGenerator{
|
||||
* @param packageName Full target package name.
|
||||
* @param needsPlayer Whether this read method requires a reference to the player sender.
|
||||
*/
|
||||
public void generateFor(List<MethodEntry> entries, String className, String packageName, boolean needsPlayer)
|
||||
throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException, IOException{
|
||||
public void generateFor(Seq<MethodEntry> entries, String className, String packageName, boolean needsPlayer) throws Exception{
|
||||
|
||||
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
|
||||
classBuilder.addJavadoc(RemoteMethodAnnotationProcessor.autogenWarning);
|
||||
classBuilder.addJavadoc(RemoteProcess.autogenWarning);
|
||||
|
||||
//create main method builder
|
||||
MethodSpec.Builder readMethod = MethodSpec.methodBuilder("readPacket")
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.addParameter(ByteBuffer.class, "buffer") //buffer to read form
|
||||
.addParameter(Reads.class, "read") //buffer to read form
|
||||
.addParameter(int.class, "id") //ID of method type to read
|
||||
.returns(void.class);
|
||||
|
||||
if(needsPlayer){
|
||||
//since the player type isn't loaded yet, creating a type def is necessary
|
||||
//this requires reflection since the TypeName constructor is private for some reason
|
||||
Constructor<TypeName> cons = TypeName.class.getDeclaredConstructor(String.class);
|
||||
cons.setAccessible(true);
|
||||
|
||||
TypeName playerType = cons.newInstance("io.anuke.mindustry.entities.type.Player");
|
||||
//add player parameter
|
||||
readMethod.addParameter(playerType, "player");
|
||||
readMethod.addParameter(ClassName.get(packageName, "Player"), "player");
|
||||
}
|
||||
|
||||
CodeBlock.Builder readBlock = CodeBlock.builder(); //start building block of code inside read method
|
||||
@@ -79,26 +68,22 @@ public class RemoteReadGenerator{
|
||||
//name of parameter
|
||||
String varName = var.getSimpleName().toString();
|
||||
//captialized version of type name for reading primitives
|
||||
String capName = typeName.equals("byte") ? "" : Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1);
|
||||
String pname = typeName.equals("boolean") ? "bool" : typeName.charAt(0) + "";
|
||||
|
||||
//write primitives automatically
|
||||
if(Utils.isPrimitive(typeName)){
|
||||
if(typeName.equals("boolean")){
|
||||
readBlock.addStatement("boolean " + varName + " = buffer.get() == 1");
|
||||
}else{
|
||||
readBlock.addStatement(typeName + " " + varName + " = buffer.get" + capName + "()");
|
||||
}
|
||||
if(BaseProcessor.isPrimitive(typeName)){
|
||||
readBlock.addStatement("$L $L = read.$L()", typeName, varName, pname);
|
||||
}else{
|
||||
//else, try and find a serializer
|
||||
ClassSerializer ser = serializers.get(typeName);
|
||||
String ser = serializers.readers.get(typeName.replace("mindustry.gen.", ""), SerializerResolver.locate(entry.element, var.asType(), false));
|
||||
|
||||
if(ser == null){ //make sure a serializer exists!
|
||||
Utils.messager.printMessage(Kind.ERROR, "No @ReadClass method to read class type: '" + typeName + "'", var);
|
||||
BaseProcessor.err("No read method to read class type '" + typeName + "' in method " + entry.targetMethod + "; " + serializers.readers, var);
|
||||
return;
|
||||
}
|
||||
|
||||
//add statement for reading it
|
||||
readBlock.addStatement(typeName + " " + varName + " = " + ser.readMethod + "(buffer)");
|
||||
readBlock.addStatement(typeName + " " + varName + " = " + ser + "(read)");
|
||||
}
|
||||
|
||||
//append variable name to string builder
|
||||
@@ -139,6 +124,6 @@ public class RemoteReadGenerator{
|
||||
|
||||
//build and write resulting class
|
||||
TypeSpec spec = classBuilder.build();
|
||||
JavaFile.builder(packageName, spec).build().writeTo(Utils.filer);
|
||||
JavaFile.builder(packageName, spec).build().writeTo(BaseProcessor.filer);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,39 @@
|
||||
package io.anuke.annotations;
|
||||
package mindustry.annotations.remote;
|
||||
|
||||
import arc.struct.*;
|
||||
import arc.util.io.*;
|
||||
import com.squareup.javapoet.*;
|
||||
import io.anuke.annotations.Annotations.Loc;
|
||||
import io.anuke.annotations.IOFinder.ClassSerializer;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.annotations.*;
|
||||
import mindustry.annotations.util.TypeIOResolver.*;
|
||||
|
||||
import javax.lang.model.element.*;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.io.*;
|
||||
|
||||
/** Generates code for writing remote invoke packets on the client and server. */
|
||||
public class RemoteWriteGenerator{
|
||||
private final HashMap<String, ClassSerializer> serializers;
|
||||
private final ClassSerializer serializers;
|
||||
|
||||
/** Creates a write generator that uses the supplied serializer setup. */
|
||||
public RemoteWriteGenerator(HashMap<String, ClassSerializer> serializers){
|
||||
public RemoteWriteGenerator(ClassSerializer serializers){
|
||||
this.serializers = serializers;
|
||||
}
|
||||
|
||||
/** Generates all classes in this list. */
|
||||
public void generateFor(List<ClassEntry> entries, String packageName) throws IOException{
|
||||
public void generateFor(Seq<ClassEntry> entries, String packageName) throws IOException{
|
||||
|
||||
for(ClassEntry entry : entries){
|
||||
//create builder
|
||||
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(entry.name).addModifiers(Modifier.PUBLIC);
|
||||
classBuilder.addJavadoc(RemoteMethodAnnotationProcessor.autogenWarning);
|
||||
classBuilder.addJavadoc(RemoteProcess.autogenWarning);
|
||||
|
||||
//add temporary write buffer
|
||||
classBuilder.addField(FieldSpec.builder(ByteBuffer.class, "TEMP_BUFFER", Modifier.STATIC, Modifier.PRIVATE, Modifier.FINAL)
|
||||
.initializer("ByteBuffer.allocate($1L)", RemoteMethodAnnotationProcessor.maxPacketSize).build());
|
||||
classBuilder.addField(FieldSpec.builder(ReusableByteOutStream.class, "OUT", Modifier.STATIC, Modifier.PRIVATE, Modifier.FINAL)
|
||||
.initializer("new ReusableByteOutStream($L)", RemoteProcess.maxPacketSize).build());
|
||||
|
||||
//add writer for that buffer
|
||||
classBuilder.addField(FieldSpec.builder(Writes.class, "WRITE", Modifier.STATIC, Modifier.PRIVATE, Modifier.FINAL)
|
||||
.initializer("new Writes(new $T(OUT))", DataOutputStream.class).build());
|
||||
|
||||
//go through each method entry in this class
|
||||
for(MethodEntry methodEntry : entry.methods){
|
||||
@@ -52,7 +55,7 @@ public class RemoteWriteGenerator{
|
||||
|
||||
//build and write resulting class
|
||||
TypeSpec spec = classBuilder.build();
|
||||
JavaFile.builder(packageName, spec).build().writeTo(Utils.filer);
|
||||
JavaFile.builder(packageName, spec).build().writeTo(BaseProcessor.filer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +65,7 @@ public class RemoteWriteGenerator{
|
||||
|
||||
//create builder
|
||||
MethodSpec.Builder method = MethodSpec.methodBuilder(elem.getSimpleName().toString() + (forwarded ? "__forward" : "")) //add except suffix when forwarding
|
||||
.addModifiers(Modifier.STATIC, Modifier.SYNCHRONIZED)
|
||||
.addModifiers(Modifier.STATIC)
|
||||
.returns(void.class);
|
||||
|
||||
//forwarded methods aren't intended for use, and are not public
|
||||
@@ -73,31 +76,31 @@ public class RemoteWriteGenerator{
|
||||
//validate client methods to make sure
|
||||
if(methodEntry.where.isClient){
|
||||
if(elem.getParameters().isEmpty()){
|
||||
Utils.messager.printMessage(Kind.ERROR, "Client invoke methods must have a first parameter of type Player.", elem);
|
||||
BaseProcessor.err("Client invoke methods must have a first parameter of type Player", elem);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!elem.getParameters().get(0).asType().toString().equals("io.anuke.mindustry.entities.type.Player")){
|
||||
Utils.messager.printMessage(Kind.ERROR, "Client invoke methods should have a first parameter of type Player.", elem);
|
||||
if(!elem.getParameters().get(0).asType().toString().contains("Player")){
|
||||
BaseProcessor.err("Client invoke methods should have a first parameter of type Player", elem);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//if toAll is false, it's a 'send to one player' variant, so add the player as a parameter
|
||||
if(!toAll){
|
||||
method.addParameter(ClassName.bestGuess("io.anuke.mindustry.net.NetConnection"), "playerConnection");
|
||||
method.addParameter(ClassName.bestGuess("mindustry.net.NetConnection"), "playerConnection");
|
||||
}
|
||||
|
||||
//add sender to ignore
|
||||
if(forwarded){
|
||||
method.addParameter(ClassName.bestGuess("io.anuke.mindustry.net.NetConnection"), "exceptConnection");
|
||||
method.addParameter(ClassName.bestGuess("mindustry.net.NetConnection"), "exceptConnection");
|
||||
}
|
||||
|
||||
//call local method if applicable, shouldn't happen when forwarding method as that already happens by default
|
||||
if(!forwarded && methodEntry.local != Loc.none){
|
||||
//add in local checks
|
||||
if(methodEntry.local != Loc.both){
|
||||
method.beginControlFlow("if(" + getCheckString(methodEntry.local) + " || !io.anuke.mindustry.Vars.net.active())");
|
||||
method.beginControlFlow("if(" + getCheckString(methodEntry.local) + " || !mindustry.Vars.net.active())");
|
||||
}
|
||||
|
||||
//concatenate parameters
|
||||
@@ -106,7 +109,7 @@ public class RemoteWriteGenerator{
|
||||
for(VariableElement var : elem.getParameters()){
|
||||
//special case: calling local-only methods uses the local player
|
||||
if(index == 0 && methodEntry.where == Loc.client){
|
||||
results.append("io.anuke.mindustry.Vars.player");
|
||||
results.append("mindustry.Vars.player");
|
||||
}else{
|
||||
results.append(var.getSimpleName());
|
||||
}
|
||||
@@ -127,15 +130,15 @@ public class RemoteWriteGenerator{
|
||||
method.beginControlFlow("if(" + getCheckString(methodEntry.where) + ")");
|
||||
|
||||
//add statement to create packet from pool
|
||||
method.addStatement("$1N packet = $2N.obtain($1N.class, $1N::new)", "io.anuke.mindustry.net.Packets.InvokePacket", "io.anuke.arc.util.pooling.Pools");
|
||||
//assign buffer
|
||||
method.addStatement("packet.writeBuffer = TEMP_BUFFER");
|
||||
method.addStatement("$1N packet = $2N.obtain($1N.class, $1N::new)", "mindustry.net.Packets.InvokePacket", "arc.util.pooling.Pools");
|
||||
//assign priority
|
||||
method.addStatement("packet.priority = (byte)" + methodEntry.priority.ordinal());
|
||||
//assign method ID
|
||||
method.addStatement("packet.type = (byte)" + methodEntry.id);
|
||||
//rewind buffer
|
||||
method.addStatement("TEMP_BUFFER.position(0)");
|
||||
//reset stream
|
||||
method.addStatement("OUT.reset()");
|
||||
|
||||
method.addTypeVariables(Seq.with(elem.getTypeParameters()).map(BaseProcessor::getTVN));
|
||||
|
||||
for(int i = 0; i < elem.getParameters().size(); i++){
|
||||
//first argument is skipped as it is always the player caller
|
||||
@@ -145,8 +148,12 @@ public class RemoteWriteGenerator{
|
||||
|
||||
VariableElement var = elem.getParameters().get(i);
|
||||
|
||||
//add parameter to method
|
||||
method.addParameter(TypeName.get(var.asType()), var.getSimpleName().toString());
|
||||
try{
|
||||
//add parameter to method
|
||||
method.addParameter(TypeName.get(var.asType()), var.getSimpleName().toString());
|
||||
}catch(Throwable t){
|
||||
throw new RuntimeException("Error parsing method " + methodEntry.targetMethod);
|
||||
}
|
||||
|
||||
//name of parameter
|
||||
String varName = var.getSimpleName().toString();
|
||||
@@ -159,27 +166,22 @@ public class RemoteWriteGenerator{
|
||||
boolean writePlayerSkipCheck = methodEntry.where == Loc.both && i == 0;
|
||||
|
||||
if(writePlayerSkipCheck){ //write begin check
|
||||
method.beginControlFlow("if(io.anuke.mindustry.Vars.net.server())");
|
||||
method.beginControlFlow("if(mindustry.Vars.net.server())");
|
||||
}
|
||||
|
||||
if(Utils.isPrimitive(typeName)){ //check if it's a primitive, and if so write it
|
||||
if(typeName.equals("boolean")){ //booleans are special
|
||||
method.addStatement("TEMP_BUFFER.put(" + varName + " ? (byte)1 : 0)");
|
||||
}else{
|
||||
method.addStatement("TEMP_BUFFER.put" +
|
||||
capName + "(" + varName + ")");
|
||||
}
|
||||
if(BaseProcessor.isPrimitive(typeName)){ //check if it's a primitive, and if so write it
|
||||
method.addStatement("WRITE.$L($L)", typeName.equals("boolean") ? "bool" : typeName.charAt(0) + "", varName);
|
||||
}else{
|
||||
//else, try and find a serializer
|
||||
ClassSerializer ser = serializers.get(typeName);
|
||||
String ser = serializers.writers.get(typeName.replace("mindustry.gen.", ""), SerializerResolver.locate(elem, var.asType(), true));
|
||||
|
||||
if(ser == null){ //make sure a serializer exists!
|
||||
Utils.messager.printMessage(Kind.ERROR, "No @WriteClass method to write class type: '" + typeName + "'", var);
|
||||
BaseProcessor.err("No @WriteClass method to write class type: '" + typeName + "'", var);
|
||||
return;
|
||||
}
|
||||
|
||||
//add statement for writing it
|
||||
method.addStatement(ser.writeMethod + "(TEMP_BUFFER, " + varName + ")");
|
||||
method.addStatement(ser + "(WRITE, " + varName + ")");
|
||||
}
|
||||
|
||||
if(writePlayerSkipCheck){ //write end check
|
||||
@@ -187,26 +189,28 @@ public class RemoteWriteGenerator{
|
||||
}
|
||||
}
|
||||
|
||||
//assign packet bytes
|
||||
method.addStatement("packet.bytes = OUT.getBytes()");
|
||||
//assign packet length
|
||||
method.addStatement("packet.writeLength = TEMP_BUFFER.position()");
|
||||
method.addStatement("packet.length = OUT.size()");
|
||||
|
||||
String sendString;
|
||||
|
||||
if(forwarded){ //forward packet
|
||||
if(!methodEntry.local.isClient){ //if the client doesn't get it called locally, forward it back after validation
|
||||
sendString = "io.anuke.mindustry.Vars.net.send(";
|
||||
sendString = "mindustry.Vars.net.send(";
|
||||
}else{
|
||||
sendString = "io.anuke.mindustry.Vars.net.sendExcept(exceptConnection, ";
|
||||
sendString = "mindustry.Vars.net.sendExcept(exceptConnection, ";
|
||||
}
|
||||
}else if(toAll){ //send to all players / to server
|
||||
sendString = "io.anuke.mindustry.Vars.net.send(";
|
||||
sendString = "mindustry.Vars.net.send(";
|
||||
}else{ //send to specific client from server
|
||||
sendString = "playerConnection.send(";
|
||||
}
|
||||
|
||||
//send the actual packet
|
||||
method.addStatement(sendString + "packet, " +
|
||||
(methodEntry.unreliable ? "io.anuke.mindustry.net.Net.SendMode.udp" : "io.anuke.mindustry.net.Net.SendMode.tcp") + ")");
|
||||
(methodEntry.unreliable ? "mindustry.net.Net.SendMode.udp" : "mindustry.net.Net.SendMode.tcp") + ")");
|
||||
|
||||
|
||||
//end check for server/client
|
||||
@@ -217,8 +221,8 @@ public class RemoteWriteGenerator{
|
||||
}
|
||||
|
||||
private String getCheckString(Loc loc){
|
||||
return loc.isClient && loc.isServer ? "io.anuke.mindustry.Vars.net.server() || io.anuke.mindustry.Vars.net.client()" :
|
||||
loc.isClient ? "io.anuke.mindustry.Vars.net.client()" :
|
||||
loc.isServer ? "io.anuke.mindustry.Vars.net.server()" : "false";
|
||||
return loc.isClient && loc.isServer ? "mindustry.Vars.net.server() || mindustry.Vars.net.client()" :
|
||||
loc.isClient ? "mindustry.Vars.net.client()" :
|
||||
loc.isServer ? "mindustry.Vars.net.server()" : "false";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package mindustry.annotations.remote;
|
||||
|
||||
import arc.struct.*;
|
||||
|
||||
import javax.lang.model.element.*;
|
||||
import javax.lang.model.type.*;
|
||||
|
||||
public class SerializerResolver{
|
||||
|
||||
public static String locate(ExecutableElement elem, TypeMirror mirror, boolean write){
|
||||
//generic type
|
||||
if((mirror.toString().equals("T") && Seq.with(elem.getTypeParameters().get(0).getBounds()).contains(SerializerResolver::isEntity)) ||
|
||||
isEntity(mirror)){
|
||||
return write ? "mindustry.io.TypeIO.writeEntity" : "mindustry.io.TypeIO.readEntity";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isEntity(TypeMirror mirror){
|
||||
return !mirror.toString().contains(".") || mirror.toString().startsWith("mindustry.gen.") && !mirror.toString().startsWith("byte");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
package mindustry.annotations.util;
|
||||
|
||||
import arc.func.*;
|
||||
import com.sun.tools.javac.code.*;
|
||||
import com.sun.tools.javac.code.Attribute.Array;
|
||||
import com.sun.tools.javac.code.Attribute.Enum;
|
||||
import com.sun.tools.javac.code.Attribute.Error;
|
||||
import com.sun.tools.javac.code.Attribute.Visitor;
|
||||
import com.sun.tools.javac.code.Attribute.*;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import com.sun.tools.javac.code.Symbol.*;
|
||||
import com.sun.tools.javac.code.Type.ArrayType;
|
||||
import com.sun.tools.javac.util.List;
|
||||
import com.sun.tools.javac.util.Name;
|
||||
import com.sun.tools.javac.util.*;
|
||||
import sun.reflect.annotation.*;
|
||||
|
||||
import javax.lang.model.element.*;
|
||||
import javax.lang.model.type.*;
|
||||
import java.lang.Class;
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import java.util.Map.*;
|
||||
|
||||
//replaces the standard Java AnnotationProxyMaker with one that doesn't crash
|
||||
//thanks, oracle.
|
||||
@SuppressWarnings({"sunapi", "unchecked"})
|
||||
public class AnnotationProxyMaker{
|
||||
private final Compound anno;
|
||||
private final Class<? extends Annotation> annoType;
|
||||
|
||||
private AnnotationProxyMaker(Compound var1, Class<? extends Annotation> var2){
|
||||
this.anno = var1;
|
||||
this.annoType = var2;
|
||||
}
|
||||
|
||||
public static <A extends Annotation> A generateAnnotation(Compound var0, Class<A> var1){
|
||||
AnnotationProxyMaker var2 = new AnnotationProxyMaker(var0, var1);
|
||||
return (A)var1.cast(var2.generateAnnotation());
|
||||
}
|
||||
|
||||
private Annotation generateAnnotation(){
|
||||
return AnnotationParser.annotationForMap(this.annoType, this.getAllReflectedValues());
|
||||
}
|
||||
|
||||
private Map<String, Object> getAllReflectedValues(){
|
||||
LinkedHashMap var1 = new LinkedHashMap();
|
||||
Iterator var2 = this.getAllValues().entrySet().iterator();
|
||||
|
||||
while(var2.hasNext()){
|
||||
Entry var3 = (Entry)var2.next();
|
||||
MethodSymbol var4 = (MethodSymbol)var3.getKey();
|
||||
Object var5 = this.generateValue(var4, (Attribute)var3.getValue());
|
||||
if(var5 != null){
|
||||
var1.put(var4.name.toString(), var5);
|
||||
}
|
||||
}
|
||||
|
||||
return var1;
|
||||
}
|
||||
|
||||
private Map<MethodSymbol, Attribute> getAllValues(){
|
||||
LinkedHashMap map = new LinkedHashMap();
|
||||
ClassSymbol cl = (ClassSymbol)this.anno.type.tsym;
|
||||
|
||||
//try to use Java 8 API for this if possible
|
||||
try{
|
||||
Class entryClass = Class.forName("com.sun.tools.javac.code.Scope$Entry");
|
||||
Object members = cl.members();
|
||||
Field field = members.getClass().getField("elems");
|
||||
Object elems = field.get(members);
|
||||
Field siblingField = entryClass.getField("sibling");
|
||||
Field symField = entryClass.getField("sym");
|
||||
for(Object currEntry = elems; currEntry != null; currEntry = siblingField.get(currEntry)){
|
||||
handleSymbol((Symbol)symField.get(currEntry), map);
|
||||
}
|
||||
|
||||
}catch(Throwable e){
|
||||
//otherwise try other API
|
||||
|
||||
try{
|
||||
Class lookupClass = Class.forName("com.sun.tools.javac.code.Scope$LookupKind");
|
||||
Field nonRecField = lookupClass.getField("NON_RECURSIVE");
|
||||
Object nonRec = nonRecField.get(null);
|
||||
Scope scope = cl.members();
|
||||
Method getSyms = scope.getClass().getMethod("getSymbols", lookupClass);
|
||||
Iterable<Symbol> it = (Iterable<Symbol>)getSyms.invoke(scope, nonRec);
|
||||
Iterator<Symbol> i = it.iterator();
|
||||
while(i.hasNext()){
|
||||
handleSymbol(i.next(), map);
|
||||
}
|
||||
|
||||
}catch(Throwable death){
|
||||
//I tried
|
||||
throw new RuntimeException(death);
|
||||
}
|
||||
}
|
||||
|
||||
for(Pair var7 : this.anno.values){
|
||||
map.put(var7.fst, var7.snd);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private void handleSymbol(Symbol sym, LinkedHashMap map){
|
||||
|
||||
if(sym.getKind() == ElementKind.METHOD){
|
||||
MethodSymbol var4 = (MethodSymbol)sym;
|
||||
Attribute var5 = var4.getDefaultValue();
|
||||
if(var5 != null){
|
||||
map.put(var4, var5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object generateValue(MethodSymbol var1, Attribute var2){
|
||||
AnnotationProxyMaker.ValueVisitor var3 = new AnnotationProxyMaker.ValueVisitor(var1);
|
||||
return var3.getValue(var2);
|
||||
}
|
||||
|
||||
private class ValueVisitor implements Visitor{
|
||||
private MethodSymbol meth;
|
||||
private Class<?> returnClass;
|
||||
private Object value;
|
||||
|
||||
ValueVisitor(MethodSymbol var2){
|
||||
this.meth = var2;
|
||||
}
|
||||
|
||||
Object getValue(Attribute var1){
|
||||
Method var2;
|
||||
try{
|
||||
var2 = AnnotationProxyMaker.this.annoType.getMethod(this.meth.name.toString());
|
||||
}catch(NoSuchMethodException var4){
|
||||
return null;
|
||||
}
|
||||
|
||||
this.returnClass = var2.getReturnType();
|
||||
var1.accept(this);
|
||||
if(!(this.value instanceof ExceptionProxy) && !AnnotationType.invocationHandlerReturnType(this.returnClass).isInstance(this.value)){
|
||||
this.typeMismatch(var2, var1);
|
||||
}
|
||||
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public void visitConstant(Constant var1){
|
||||
this.value = var1.getValue();
|
||||
}
|
||||
|
||||
public void visitClass(com.sun.tools.javac.code.Attribute.Class var1){
|
||||
this.value = mirrorProxy(var1.classType);
|
||||
}
|
||||
|
||||
public void visitArray(Array var1){
|
||||
Name var2 = ((ArrayType)var1.type).elemtype.tsym.getQualifiedName();
|
||||
int var6;
|
||||
if(var2.equals(var2.table.names.java_lang_Class)){
|
||||
ListBuffer var14 = new ListBuffer();
|
||||
Attribute[] var15 = var1.values;
|
||||
int var16 = var15.length;
|
||||
|
||||
for(var6 = 0; var6 < var16; ++var6){
|
||||
Attribute var7 = var15[var6];
|
||||
Type var8 = var7 instanceof UnresolvedClass ? ((UnresolvedClass)var7).classType : ((com.sun.tools.javac.code.Attribute.Class)var7).classType;
|
||||
var14.append(var8);
|
||||
}
|
||||
|
||||
this.value = mirrorProxy(var14.toList());
|
||||
}else{
|
||||
int var3 = var1.values.length;
|
||||
Class var4 = this.returnClass;
|
||||
this.returnClass = this.returnClass.getComponentType();
|
||||
|
||||
try{
|
||||
Object var5 = java.lang.reflect.Array.newInstance(this.returnClass, var3);
|
||||
|
||||
for(var6 = 0; var6 < var3; ++var6){
|
||||
var1.values[var6].accept(this);
|
||||
if(this.value == null || this.value instanceof ExceptionProxy){
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
java.lang.reflect.Array.set(var5, var6, this.value);
|
||||
}catch(IllegalArgumentException var12){
|
||||
this.value = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.value = var5;
|
||||
}finally{
|
||||
this.returnClass = var4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void visitEnum(Enum var1){
|
||||
if(this.returnClass.isEnum()){
|
||||
String var2 = var1.value.toString();
|
||||
|
||||
try{
|
||||
this.value = java.lang.Enum.valueOf((Class)this.returnClass, var2);
|
||||
}catch(IllegalArgumentException var4){
|
||||
this.value = proxify(() -> new EnumConstantNotPresentException((Class)this.returnClass, var2));
|
||||
}
|
||||
}else{
|
||||
this.value = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void visitCompound(Compound var1){
|
||||
try{
|
||||
Class var2 = this.returnClass.asSubclass(Annotation.class);
|
||||
this.value = AnnotationProxyMaker.generateAnnotation(var1, var2);
|
||||
}catch(ClassCastException var3){
|
||||
this.value = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void visitError(Error var1){
|
||||
if(var1 instanceof UnresolvedClass){
|
||||
this.value = mirrorProxy(((UnresolvedClass)var1).classType);
|
||||
}else{
|
||||
this.value = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void typeMismatch(Method var1, final Attribute var2){
|
||||
this.value = proxify(() -> new AnnotationTypeMismatchException(var1, var2.type.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
private static Object mirrorProxy(Type t){
|
||||
return proxify(() -> new MirroredTypeException(t));
|
||||
}
|
||||
|
||||
private static Object mirrorProxy(List<Type> t){
|
||||
return proxify(() -> new MirroredTypesException(t));
|
||||
}
|
||||
|
||||
private static <T extends Throwable> Object proxify(Prov<T> prov){
|
||||
try{
|
||||
|
||||
return new ExceptionProxy(){
|
||||
@Override
|
||||
protected RuntimeException generateException(){
|
||||
return (RuntimeException)prov.get();
|
||||
}
|
||||
};
|
||||
}catch(Throwable t){
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package mindustry.annotations.util;
|
||||
|
||||
import arc.struct.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import com.squareup.javapoet.*;
|
||||
import com.sun.tools.javac.code.Attribute.*;
|
||||
import mindustry.annotations.*;
|
||||
|
||||
import javax.lang.model.element.*;
|
||||
import javax.lang.model.type.*;
|
||||
import java.lang.Class;
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.*;
|
||||
|
||||
public class Selement<T extends Element>{
|
||||
public final T e;
|
||||
|
||||
public Selement(T e){
|
||||
this.e = e;
|
||||
}
|
||||
|
||||
public @Nullable String doc(){
|
||||
return BaseProcessor.elementu.getDocComment(e);
|
||||
}
|
||||
|
||||
public Seq<Selement<?>> enclosed(){
|
||||
return Seq.with(e.getEnclosedElements()).map(Selement::new);
|
||||
}
|
||||
|
||||
public String fullName(){
|
||||
return e.toString();
|
||||
}
|
||||
|
||||
public Stype asType(){
|
||||
return new Stype((TypeElement)e);
|
||||
}
|
||||
|
||||
public Svar asVar(){
|
||||
return new Svar((VariableElement)e);
|
||||
}
|
||||
|
||||
public Smethod asMethod(){
|
||||
return new Smethod((ExecutableElement)e);
|
||||
}
|
||||
|
||||
public boolean isVar(){
|
||||
return e instanceof VariableElement;
|
||||
}
|
||||
|
||||
public boolean isType(){
|
||||
return e instanceof TypeElement;
|
||||
}
|
||||
|
||||
public boolean isMethod(){
|
||||
return e instanceof ExecutableElement;
|
||||
}
|
||||
|
||||
public Seq<? extends AnnotationMirror> annotations(){
|
||||
return Seq.with(e.getAnnotationMirrors());
|
||||
}
|
||||
|
||||
public <A extends Annotation> A annotation(Class<A> annotation){
|
||||
try{
|
||||
Method m = com.sun.tools.javac.code.AnnoConstruct.class.getDeclaredMethod("getAttribute", Class.class);
|
||||
m.setAccessible(true);
|
||||
Compound compound = (Compound)m.invoke(e, annotation);
|
||||
return compound == null ? null : AnnotationProxyMaker.generateAnnotation(compound, annotation);
|
||||
}catch(Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public <A extends Annotation> boolean has(Class<A> annotation){
|
||||
return annotation(annotation) != null;
|
||||
}
|
||||
|
||||
public Element up(){
|
||||
return e.getEnclosingElement();
|
||||
}
|
||||
|
||||
public TypeMirror mirror(){
|
||||
return e.asType();
|
||||
}
|
||||
|
||||
public TypeName tname(){
|
||||
return TypeName.get(mirror());
|
||||
}
|
||||
|
||||
public ClassName cname(){
|
||||
return ClassName.get((TypeElement)BaseProcessor.typeu.asElement(mirror()));
|
||||
}
|
||||
|
||||
public String name(){
|
||||
return e.getSimpleName().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return e.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(){
|
||||
return e.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o){
|
||||
return o != null && o.getClass() == getClass() && e.equals(((Selement)o).e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package mindustry.annotations.util;
|
||||
|
||||
import arc.struct.*;
|
||||
import com.squareup.javapoet.*;
|
||||
import com.sun.source.tree.*;
|
||||
import mindustry.annotations.*;
|
||||
|
||||
import javax.lang.model.element.*;
|
||||
import javax.lang.model.type.*;
|
||||
|
||||
public class Smethod extends Selement<ExecutableElement>{
|
||||
|
||||
public Smethod(ExecutableElement executableElement){
|
||||
super(executableElement);
|
||||
}
|
||||
|
||||
public boolean isAny(Modifier... mod){
|
||||
for(Modifier m : mod){
|
||||
if(is(m)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String descString(){
|
||||
return up().asType().toString() + "#" + super.toString().replace("mindustry.gen.", "");
|
||||
}
|
||||
|
||||
public boolean is(Modifier mod){
|
||||
return e.getModifiers().contains(mod);
|
||||
}
|
||||
|
||||
public Stype type(){
|
||||
return new Stype((TypeElement)up());
|
||||
}
|
||||
|
||||
public Seq<TypeMirror> thrown(){
|
||||
return Seq.with(e.getThrownTypes()).as();
|
||||
}
|
||||
|
||||
public Seq<TypeName> thrownt(){
|
||||
return Seq.with(e.getThrownTypes()).map(TypeName::get);
|
||||
}
|
||||
|
||||
public Seq<TypeParameterElement> typeVariables(){
|
||||
return Seq.with(e.getTypeParameters()).as();
|
||||
}
|
||||
|
||||
public Seq<Svar> params(){
|
||||
return Seq.with(e.getParameters()).map(Svar::new);
|
||||
}
|
||||
|
||||
public boolean isVoid(){
|
||||
return ret().toString().equals("void");
|
||||
}
|
||||
|
||||
public TypeMirror ret(){
|
||||
return e.getReturnType();
|
||||
}
|
||||
|
||||
public TypeName retn(){
|
||||
return TypeName.get(ret());
|
||||
}
|
||||
|
||||
public MethodTree tree(){
|
||||
return BaseProcessor.trees.getTree(e);
|
||||
}
|
||||
|
||||
public String simpleString(){
|
||||
return name() + "(" + params().toString(", ", p -> BaseProcessor.simpleName(p.mirror().toString())) + ")";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package mindustry.annotations.util;
|
||||
|
||||
import arc.struct.*;
|
||||
import mindustry.annotations.*;
|
||||
|
||||
import javax.lang.model.element.*;
|
||||
import javax.lang.model.type.*;
|
||||
|
||||
public class Stype extends Selement<TypeElement>{
|
||||
|
||||
public Stype(TypeElement typeElement){
|
||||
super(typeElement);
|
||||
}
|
||||
|
||||
public static Stype of(TypeMirror mirror){
|
||||
return new Stype((TypeElement)BaseProcessor.typeu.asElement(mirror));
|
||||
}
|
||||
|
||||
public String fullName(){
|
||||
return mirror().toString();
|
||||
}
|
||||
|
||||
public Seq<Stype> interfaces(){
|
||||
return Seq.with(e.getInterfaces()).map(Stype::of);
|
||||
}
|
||||
|
||||
public Seq<Stype> allInterfaces(){
|
||||
return interfaces().flatMap(s -> s.allInterfaces().and(s)).distinct();
|
||||
}
|
||||
|
||||
public Seq<Stype> superclasses(){
|
||||
return Seq.with(BaseProcessor.typeu.directSupertypes(mirror())).map(Stype::of);
|
||||
}
|
||||
|
||||
public Seq<Stype> allSuperclasses(){
|
||||
return superclasses().flatMap(s -> s.allSuperclasses().and(s)).distinct();
|
||||
}
|
||||
|
||||
public Stype superclass(){
|
||||
return new Stype((TypeElement)BaseProcessor.typeu.asElement(BaseProcessor.typeu.directSupertypes(mirror()).get(0)));
|
||||
}
|
||||
|
||||
public Seq<Svar> fields(){
|
||||
return Seq.with(e.getEnclosedElements()).select(e -> e instanceof VariableElement).map(e -> new Svar((VariableElement)e));
|
||||
}
|
||||
|
||||
public Seq<Smethod> methods(){
|
||||
return Seq.with(e.getEnclosedElements()).select(e -> e instanceof ExecutableElement
|
||||
&& !e.getSimpleName().toString().contains("<")).map(e -> new Smethod((ExecutableElement)e));
|
||||
}
|
||||
|
||||
public Seq<Smethod> constructors(){
|
||||
return Seq.with(e.getEnclosedElements()).select(e -> e instanceof ExecutableElement
|
||||
&& e.getSimpleName().toString().contains("<")).map(e -> new Smethod((ExecutableElement)e));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeMirror mirror(){
|
||||
return e.asType();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package mindustry.annotations.util;
|
||||
|
||||
import com.sun.source.tree.*;
|
||||
import com.sun.tools.javac.tree.JCTree.*;
|
||||
import mindustry.annotations.*;
|
||||
|
||||
import javax.lang.model.element.*;
|
||||
|
||||
public class Svar extends Selement<VariableElement>{
|
||||
|
||||
public Svar(VariableElement e){
|
||||
super(e);
|
||||
}
|
||||
|
||||
public String descString(){
|
||||
return up().asType().toString() + "#" + super.toString().replace("mindustry.gen.", "");
|
||||
}
|
||||
|
||||
public JCVariableDecl jtree(){
|
||||
return (JCVariableDecl)BaseProcessor.elementu.getTree(e);
|
||||
}
|
||||
|
||||
public Stype enclosingType(){
|
||||
return new Stype((TypeElement)up());
|
||||
}
|
||||
|
||||
public boolean isAny(Modifier... mods){
|
||||
for(Modifier m : mods){
|
||||
if(is(m)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean is(Modifier mod){
|
||||
return e.getModifiers().contains(mod);
|
||||
}
|
||||
|
||||
public VariableTree tree(){
|
||||
return (VariableTree)BaseProcessor.trees.getTree(e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package mindustry.annotations.util;
|
||||
|
||||
import arc.struct.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.annotations.*;
|
||||
|
||||
import javax.lang.model.element.*;
|
||||
|
||||
/**
|
||||
* This class finds reader and writer methods.
|
||||
*/
|
||||
public class TypeIOResolver{
|
||||
|
||||
/**
|
||||
* Finds all class serializers for all types and returns them. Logs errors when necessary.
|
||||
* Maps fully qualified class names to their serializers.
|
||||
*/
|
||||
public static ClassSerializer resolve(BaseProcessor processor){
|
||||
ClassSerializer out = new ClassSerializer(new ObjectMap<>(), new ObjectMap<>(), new ObjectMap<>());
|
||||
for(Stype type : processor.types(TypeIOHandler.class)){
|
||||
//look at all TypeIOHandler methods
|
||||
Seq<Smethod> methods = type.methods();
|
||||
for(Smethod meth : methods){
|
||||
if(meth.is(Modifier.PUBLIC) && meth.is(Modifier.STATIC)){
|
||||
Seq<Svar> params = meth.params();
|
||||
//2 params, second one is type, first is writer
|
||||
if(params.size == 2 && params.first().tname().toString().equals("arc.util.io.Writes")){
|
||||
out.writers.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
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/** makes sure type names don't contain 'gen' */
|
||||
private static String fix(String str){
|
||||
return str.replace("mindustry.gen", "");
|
||||
}
|
||||
|
||||
/** Information about read/write methods for class types. */
|
||||
public static class ClassSerializer{
|
||||
public final ObjectMap<String, String> writers, readers, mutatorReaders;
|
||||
|
||||
public ClassSerializer(ObjectMap<String, String> writers, ObjectMap<String, String> readers, ObjectMap<String, String> mutatorReaders){
|
||||
this.writers = writers;
|
||||
this.readers = readers;
|
||||
this.mutatorReaders = mutatorReaders;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
io.anuke.annotations.RemoteMethodAnnotationProcessor
|
||||
io.anuke.annotations.SerializeAnnotationProcessor
|
||||
io.anuke.annotations.StructAnnotationProcessor
|
||||
io.anuke.annotations.CallSuperAnnotationProcessor
|
||||
io.anuke.annotations.AssetsAnnotationProcessor
|
||||
24
annotations/src/main/resources/classids.properties
Normal file
@@ -0,0 +1,24 @@
|
||||
#Maps entity names to IDs. Autogenerated.
|
||||
|
||||
alpha=0
|
||||
atrax=1
|
||||
block=2
|
||||
flare=3
|
||||
mace=4
|
||||
mega=5
|
||||
mindustry.entities.comp.BuildingComp=6
|
||||
mindustry.entities.comp.BulletComp=7
|
||||
mindustry.entities.comp.DecalComp=8
|
||||
mindustry.entities.comp.EffectStateComp=9
|
||||
mindustry.entities.comp.FireComp=10
|
||||
mindustry.entities.comp.LaunchCoreComp=11
|
||||
mindustry.entities.comp.PlayerComp=12
|
||||
mindustry.entities.comp.PuddleComp=13
|
||||
mindustry.type.Weather.WeatherStateComp=14
|
||||
mindustry.world.blocks.campaign.LaunchPad.LaunchPayloadComp=15
|
||||
mono=16
|
||||
nova=17
|
||||
poly=18
|
||||
pulsar=19
|
||||
risso=20
|
||||
spiroct=21
|
||||
@@ -0,0 +1 @@
|
||||
{fields:[{name:ammo,type:float,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,size:-1},{name:deactivated,type:boolean,size:1},{name:elevation,type:float,size:4},{name:health,type:float,size:4},{name:isShooting,type:boolean,size:1},{name:mounts,type:"mindustry.entities.units.WeaponMount[]",size:-1},{name:rotation,type:float,size:4},{name:shield,type:float,size:4},{name:spawnedByCore,type:boolean,size:1},{name:stack,type:mindustry.type.ItemStack,size:-1},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:type,type:mindustry.type.UnitType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{fields:[{name:ammo,type:float,size:4},{name:armor,type:float,size:4},{name:baseRotation,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,size:-1},{name:deactivated,type:boolean,size:1},{name:elevation,type:float,size:4},{name:health,type:float,size:4},{name:isShooting,type:boolean,size:1},{name:mineTile,type:mindustry.world.Tile,size:-1},{name:mounts,type:"mindustry.entities.units.WeaponMount[]",size:-1},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>,size:-1},{name:rotation,type:float,size:4},{name:shield,type:float,size:4},{name:spawnedByCore,type:boolean,size:1},{name:stack,type:mindustry.type.ItemStack,size:-1},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:type,type:mindustry.type.UnitType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{fields:[{name:ammo,type:float,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,size:-1},{name:deactivated,type:boolean,size:1},{name:elevation,type:float,size:4},{name:health,type:float,size:4},{name:isShooting,type:boolean,size:1},{name:mounts,type:"mindustry.entities.units.WeaponMount[]",size:-1},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>,size:-1},{name:rotation,type:float,size:4},{name:shield,type:float,size:4},{name:spawnedByCore,type:boolean,size:1},{name:stack,type:mindustry.type.ItemStack,size:-1},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:type,type:mindustry.type.UnitType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{fields:[{name:ammo,type:float,size:4},{name:armor,type:float,size:4},{name:baseRotation,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,size:-1},{name:deactivated,type:boolean,size:1},{name:elevation,type:float,size:4},{name:health,type:float,size:4},{name:isShooting,type:boolean,size:1},{name:mounts,type:"mindustry.entities.units.WeaponMount[]",size:-1},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>,size:-1},{name:rotation,type:float,size:4},{name:shield,type:float,size:4},{name:spawnedByCore,type:boolean,size:1},{name:stack,type:mindustry.type.ItemStack,size:-1},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:type,type:mindustry.type.UnitType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{fields:[{name:ammo,type:float,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,size:-1},{name:deactivated,type:boolean,size:1},{name:elevation,type:float,size:4},{name:health,type:float,size:4},{name:isShooting,type:boolean,size:1},{name:mineTile,type:mindustry.world.Tile,size:-1},{name:mounts,type:"mindustry.entities.units.WeaponMount[]",size:-1},{name:payloads,type:arc.struct.Seq<mindustry.world.blocks.payloads.Payload>,size:-1},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>,size:-1},{name:rotation,type:float,size:4},{name:shield,type:float,size:4},{name:spawnedByCore,type:boolean,size:1},{name:stack,type:mindustry.type.ItemStack,size:-1},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:type,type:mindustry.type.UnitType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{fields:[{name:ammo,type:float,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,size:-1},{name:deactivated,type:boolean,size:1},{name:elevation,type:float,size:4},{name:health,type:float,size:4},{name:isShooting,type:boolean,size:1},{name:mineTile,type:mindustry.world.Tile,size:-1},{name:mounts,type:"mindustry.entities.units.WeaponMount[]",size:-1},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>,size:-1},{name:rotation,type:float,size:4},{name:shield,type:float,size:4},{name:spawnedByCore,type:boolean,size:1},{name:stack,type:mindustry.type.ItemStack,size:-1},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:type,type:mindustry.type.UnitType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{fields:[{name:ammo,type:float,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,size:-1},{name:deactivated,type:boolean,size:1},{name:elevation,type:float,size:4},{name:health,type:float,size:4},{name:isShooting,type:boolean,size:1},{name:mineTile,type:mindustry.world.Tile,size:-1},{name:mounts,type:"mindustry.entities.units.WeaponMount[]",size:-1},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>,size:-1},{name:rotation,type:float,size:4},{name:shield,type:float,size:4},{name:spawnedByCore,type:boolean,size:1},{name:stack,type:mindustry.type.ItemStack,size:-1},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:type,type:mindustry.type.UnitType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
1
annotations/src/main/resources/revisions/Building/0.json
Normal file
@@ -0,0 +1 @@
|
||||
{fields:[{name:cons,type:mindustry.world.modules.ConsumeModule,size:-1},{name:health,type:float,size:4},{name:items,type:mindustry.world.modules.ItemModule,size:-1},{name:liquids,type:mindustry.world.modules.LiquidModule,size:-1},{name:power,type:mindustry.world.modules.PowerModule,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
1
annotations/src/main/resources/revisions/Bullet/0.json
Normal file
@@ -0,0 +1 @@
|
||||
{fields:[{name:collided,type:arc.struct.IntSeq,size:-1},{name:damage,type:float,size:4},{name:data,type:java.lang.Object,size:-1},{name:lifetime,type:float,size:4},{name:owner,type:mindustry.gen.Entityc,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:time,type:float,size:4},{name:type,type:mindustry.entities.bullet.BulletType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
1
annotations/src/main/resources/revisions/Bullet/1.json
Normal file
@@ -0,0 +1 @@
|
||||
{version:1,fields:[{name:collided,type:arc.struct.IntSeq,size:-1},{name:damage,type:float,size:4},{name:data,type:java.lang.Object,size:-1},{name:lifetime,type:float,size:4},{name:owner,type:Entityc,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:time,type:float,size:4},{name:type,type:mindustry.entities.bullet.BulletType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
1
annotations/src/main/resources/revisions/Bullet/2.json
Normal file
@@ -0,0 +1 @@
|
||||
{version:2,fields:[{name:collided,type:arc.struct.IntSeq,size:-1},{name:damage,type:float,size:4},{name:data,type:java.lang.Object,size:-1},{name:lifetime,type:float,size:4},{name:owner,type:mindustry.gen.Entityc,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:time,type:float,size:4},{name:type,type:mindustry.entities.bullet.BulletType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
1
annotations/src/main/resources/revisions/Bullet/3.json
Normal file
@@ -0,0 +1 @@
|
||||
{version:3,fields:[{name:collided,type:arc.struct.IntSeq,size:-1},{name:damage,type:float,size:4},{name:data,type:java.lang.Object,size:-1},{name:lifetime,type:float,size:4},{name:owner,type:Entityc,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:time,type:float,size:4},{name:type,type:mindustry.entities.bullet.BulletType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
1
annotations/src/main/resources/revisions/Bullet/4.json
Normal file
@@ -0,0 +1 @@
|
||||
{version:4,fields:[{name:collided,type:arc.struct.IntSeq,size:-1},{name:damage,type:float,size:4},{name:data,type:java.lang.Object,size:-1},{name:lifetime,type:float,size:4},{name:owner,type:Entityc,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:time,type:float,size:4},{name:type,type:mindustry.entities.bullet.BulletType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
1
annotations/src/main/resources/revisions/Bullet/5.json
Normal file
@@ -0,0 +1 @@
|
||||
{version:5,fields:[{name:collided,type:arc.struct.IntSeq,size:-1},{name:damage,type:float,size:4},{name:data,type:java.lang.Object,size:-1},{name:lifetime,type:float,size:4},{name:owner,type:Entityc,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:time,type:float,size:4},{name:type,type:mindustry.entities.bullet.BulletType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{fields:[{name:ammo,type:float,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,size:-1},{name:deactivated,type:boolean,size:1},{name:elevation,type:float,size:4},{name:health,type:float,size:4},{name:isShooting,type:boolean,size:1},{name:mounts,type:"mindustry.entities.units.WeaponMount[]",size:-1},{name:rotation,type:float,size:4},{name:shield,type:float,size:4},{name:spawnedByCore,type:boolean,size:1},{name:stack,type:mindustry.type.ItemStack,size:-1},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:type,type:mindustry.type.UnitType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
1
annotations/src/main/resources/revisions/Decal/0.json
Normal file
@@ -0,0 +1 @@
|
||||
{fields:[{name:color,type:arc.graphics.Color,size:-1},{name:lifetime,type:float,size:4},{name:region,type:arc.graphics.g2d.TextureRegion,size:-1},{name:rotation,type:float,size:4},{name:time,type:float,size:4},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{fields:[{name:color,type:arc.graphics.Color,size:-1},{name:data,type:java.lang.Object,size:-1},{name:effect,type:mindustry.entities.Effect,size:-1},{name:lifetime,type:float,size:4},{name:offsetX,type:float,size:4},{name:offsetY,type:float,size:4},{name:parent,type:mindustry.gen.Posc,size:-1},{name:rotation,type:float,size:4},{name:time,type:float,size:4},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{version:1,fields:[{name:color,type:arc.graphics.Color,size:-1},{name:data,type:java.lang.Object,size:-1},{name:effect,type:mindustry.entities.Effect,size:-1},{name:lifetime,type:float,size:4},{name:offsetX,type:float,size:4},{name:offsetY,type:float,size:4},{name:parent,type:Posc,size:-1},{name:rotation,type:float,size:4},{name:time,type:float,size:4},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{version:2,fields:[{name:color,type:arc.graphics.Color,size:-1},{name:data,type:java.lang.Object,size:-1},{name:effect,type:mindustry.entities.Effect,size:-1},{name:lifetime,type:float,size:4},{name:offsetX,type:float,size:4},{name:offsetY,type:float,size:4},{name:parent,type:mindustry.gen.Posc,size:-1},{name:rotation,type:float,size:4},{name:time,type:float,size:4},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{version:3,fields:[{name:color,type:arc.graphics.Color,size:-1},{name:data,type:java.lang.Object,size:-1},{name:effect,type:mindustry.entities.Effect,size:-1},{name:lifetime,type:float,size:4},{name:offsetX,type:float,size:4},{name:offsetY,type:float,size:4},{name:parent,type:Posc,size:-1},{name:rotation,type:float,size:4},{name:time,type:float,size:4},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{version:4,fields:[{name:color,type:arc.graphics.Color,size:-1},{name:data,type:java.lang.Object,size:-1},{name:effect,type:mindustry.entities.Effect,size:-1},{name:lifetime,type:float,size:4},{name:offsetX,type:float,size:4},{name:offsetY,type:float,size:4},{name:parent,type:Posc,size:-1},{name:rotation,type:float,size:4},{name:time,type:float,size:4},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{version:5,fields:[{name:color,type:arc.graphics.Color,size:-1},{name:data,type:java.lang.Object,size:-1},{name:effect,type:mindustry.entities.Effect,size:-1},{name:lifetime,type:float,size:4},{name:offsetX,type:float,size:4},{name:offsetY,type:float,size:4},{name:parent,type:Posc,size:-1},{name:rotation,type:float,size:4},{name:time,type:float,size:4},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
1
annotations/src/main/resources/revisions/Fire/0.json
Normal file
@@ -0,0 +1 @@
|
||||
{fields:[{name:baseFlammability,type:float,size:4},{name:block,type:mindustry.world.Block,size:-1},{name:lifetime,type:float,size:4},{name:puddleFlammability,type:float,size:4},{name:tile,type:mindustry.world.Tile,size:-1},{name:time,type:float,size:4},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{fields:[{name:block,type:mindustry.world.Block,size:-1},{name:lifetime,type:float,size:4},{name:time,type:float,size:4},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{fields:[{name:lifetime,type:float,size:4},{name:stacks,type:arc.struct.Seq<mindustry.type.ItemStack>,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:time,type:float,size:4},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
1
annotations/src/main/resources/revisions/LegsUnit/0.json
Normal file
@@ -0,0 +1 @@
|
||||
{fields:[{name:ammo,type:float,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,size:-1},{name:deactivated,type:boolean,size:1},{name:elevation,type:float,size:4},{name:health,type:float,size:4},{name:isShooting,type:boolean,size:1},{name:mounts,type:"mindustry.entities.units.WeaponMount[]",size:-1},{name:rotation,type:float,size:4},{name:shield,type:float,size:4},{name:spawnedByCore,type:boolean,size:1},{name:stack,type:mindustry.type.ItemStack,size:-1},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:type,type:mindustry.type.UnitType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
1
annotations/src/main/resources/revisions/MechUnit/0.json
Normal file
@@ -0,0 +1 @@
|
||||
{fields:[{name:ammo,type:float,size:4},{name:armor,type:float,size:4},{name:baseRotation,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,size:-1},{name:deactivated,type:boolean,size:1},{name:elevation,type:float,size:4},{name:health,type:float,size:4},{name:isShooting,type:boolean,size:1},{name:mounts,type:"mindustry.entities.units.WeaponMount[]",size:-1},{name:rotation,type:float,size:4},{name:shield,type:float,size:4},{name:spawnedByCore,type:boolean,size:1},{name:stack,type:mindustry.type.ItemStack,size:-1},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:type,type:mindustry.type.UnitType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{fields:[{name:ammo,type:float,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,size:-1},{name:deactivated,type:boolean,size:1},{name:elevation,type:float,size:4},{name:health,type:float,size:4},{name:isShooting,type:boolean,size:1},{name:mineTile,type:mindustry.world.Tile,size:-1},{name:mounts,type:"mindustry.entities.units.WeaponMount[]",size:-1},{name:rotation,type:float,size:4},{name:shield,type:float,size:4},{name:spawnedByCore,type:boolean,size:1},{name:stack,type:mindustry.type.ItemStack,size:-1},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:type,type:mindustry.type.UnitType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
1
annotations/src/main/resources/revisions/Player/0.json
Normal file
@@ -0,0 +1 @@
|
||||
{fields:[{name:admin,type:boolean,size:1},{name:boosting,type:boolean,size:1},{name:color,type:arc.graphics.Color,size:-1},{name:mouseX,type:float,size:4},{name:mouseY,type:float,size:4},{name:name,type:java.lang.String,size:-1},{name:shooting,type:boolean,size:1},{name:team,type:mindustry.game.Team,size:-1},{name:typing,type:boolean,size:1},{name:unit,type:Unit,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
1
annotations/src/main/resources/revisions/Puddle/0.json
Normal file
@@ -0,0 +1 @@
|
||||
{fields:[{name:amount,type:float,size:4},{name:generation,type:int,size:4},{name:liquid,type:mindustry.type.Liquid,size:-1},{name:tile,type:mindustry.world.Tile,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{fields:[{name:ammo,type:float,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,size:-1},{name:deactivated,type:boolean,size:1},{name:elevation,type:float,size:4},{name:health,type:float,size:4},{name:isShooting,type:boolean,size:1},{name:mounts,type:"mindustry.entities.units.WeaponMount[]",size:-1},{name:rotation,type:float,size:4},{name:shield,type:float,size:4},{name:spawnedByCore,type:boolean,size:1},{name:stack,type:mindustry.type.ItemStack,size:-1},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:type,type:mindustry.type.UnitType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{fields:[{name:intensity,type:float,size:4},{name:life,type:float,size:4},{name:opacity,type:float,size:4},{name:weather,type:mindustry.type.Weather,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
@@ -0,0 +1 @@
|
||||
{version:1,fields:[{name:effectTimer,type:float,size:4},{name:intensity,type:float,size:4},{name:life,type:float,size:4},{name:opacity,type:float,size:4},{name:weather,type:mindustry.type.Weather,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
|
||||
201
build.gradle
@@ -1,4 +1,12 @@
|
||||
buildscript{
|
||||
ext{
|
||||
getArcHash = {
|
||||
return new Properties().with{ p -> p.load(file('gradle.properties').newReader()); return p }["archash"]
|
||||
}
|
||||
|
||||
arcHash = getArcHash()
|
||||
}
|
||||
|
||||
repositories{
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
@@ -9,26 +17,30 @@ buildscript{
|
||||
}
|
||||
|
||||
dependencies{
|
||||
classpath 'com.mobidevelop.robovm:robovm-gradle-plugin:2.3.8-SNAPSHOT'
|
||||
classpath "com.badlogicgames.gdx:gdx-tools:1.9.10"
|
||||
classpath 'com.mobidevelop.robovm:robovm-gradle-plugin:2.3.8'
|
||||
classpath "com.github.anuken:packr:-SNAPSHOT"
|
||||
classpath "com.github.Anuken.Arc:packer:$arcHash"
|
||||
classpath "com.github.Anuken.Arc:arc-core:$arcHash"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects{
|
||||
version = 'release'
|
||||
apply plugin: 'maven'
|
||||
|
||||
version = 'release'
|
||||
group = 'com.github.Anuken'
|
||||
|
||||
ext{
|
||||
versionNumber = '5'
|
||||
if(!project.hasProperty("versionModifier")) versionModifier = 'release'
|
||||
versionNumber = '6'
|
||||
if(!project.hasProperty("versionModifier")) versionModifier = 'pre-alpha'
|
||||
if(!project.hasProperty("versionType")) versionType = 'official'
|
||||
appName = 'Mindustry'
|
||||
gdxVersion = '1.9.10'
|
||||
roboVMVersion = '2.3.8-SNAPSHOT'
|
||||
steamworksVersion = '1.8.0'
|
||||
arcHash = null
|
||||
steamworksVersion = '891ed912791e01fe9ee6237a6497e5212b85c256'
|
||||
rhinoVersion = 'eeb327d141146663ff3924bd20d2a5da8a6439cc'
|
||||
|
||||
loadVersionProps = {
|
||||
return new Properties().with{p -> p.load(file('../core/assets/version.properties').newReader()); return p }
|
||||
}
|
||||
|
||||
debugged = {
|
||||
return new File(projectDir.parent, '../Mindustry-Debug').exists() && !project.hasProperty("release") && project.hasProperty("args")
|
||||
@@ -38,10 +50,6 @@ allprojects{
|
||||
return !project.hasProperty("release") && new File(projectDir.parent, '../Arc').exists()
|
||||
}
|
||||
|
||||
getArcHash = {
|
||||
return new Properties().with{ p -> p.load(file('gradle.properties').newReader()); return p }["archash"]
|
||||
}
|
||||
|
||||
arcModule = { String name ->
|
||||
if(localArc()){
|
||||
return project(":Arc:$name")
|
||||
@@ -99,6 +107,7 @@ allprojects{
|
||||
output += other.name.substring("bundle".length() + 1, other.name.lastIndexOf('.')) + "\n"
|
||||
}
|
||||
new File(project(':core').projectDir, 'assets/locales').text = output
|
||||
new File(project(':core').projectDir, 'assets/basepartnames').text = new File(project(':core').projectDir, 'assets/baseparts/').list().join("\n")
|
||||
}
|
||||
|
||||
writeVersion = {
|
||||
@@ -124,6 +133,34 @@ allprojects{
|
||||
props.store(pfile.newWriter(), "Autogenerated file. Do not modify.")
|
||||
}
|
||||
}
|
||||
|
||||
writeProcessors = {
|
||||
new File(rootDir, "annotations/src/main/resources/META-INF/services/").mkdirs()
|
||||
def processorFile = new File(rootDir, "annotations/src/main/resources/META-INF/services/javax.annotation.processing.Processor")
|
||||
def text = new StringBuilder()
|
||||
def files = new File(rootDir, "annotations/src/main/java")
|
||||
files.eachFileRecurse(groovy.io.FileType.FILES){ file ->
|
||||
if(file.name.endsWith(".java") && (file.text.contains(" extends BaseProcessor") || (file.text.contains(" extends AbstractProcessor") && !file.text.contains("abstract class")))){
|
||||
text.append(file.path.substring(files.path.length() + 1)).append("\n")
|
||||
}
|
||||
}
|
||||
|
||||
processorFile.text = text.toString().replace(".java", "").replace("/", ".").replace("\\", ".")
|
||||
}
|
||||
|
||||
writePlugins = {
|
||||
new File(rootDir, "annotations/src/main/resources/META-INF/services/").mkdirs()
|
||||
def processorFile = new File(rootDir, "annotations/src/main/resources/META-INF/services/com.sun.source.util.Plugin")
|
||||
def text = new StringBuilder()
|
||||
def files = new File(rootDir, "annotations/src/main/java")
|
||||
files.eachFileRecurse(groovy.io.FileType.FILES){ file ->
|
||||
if(file.name.endsWith(".java") && (file.text.contains(" implements Plugin"))){
|
||||
text.append(file.path.substring(files.path.length() + 1)).append("\n")
|
||||
}
|
||||
}
|
||||
|
||||
processorFile.text = text.toString().replace(".java", "").replace("/", ".").replace("\\", ".")
|
||||
}
|
||||
}
|
||||
|
||||
repositories{
|
||||
@@ -135,25 +172,31 @@ allprojects{
|
||||
jcenter()
|
||||
}
|
||||
|
||||
tasks.withType(Javadoc).all{ enabled = false }
|
||||
tasks.withType(JavaCompile){
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
options.encoding = "UTF-8"
|
||||
options.compilerArgs += ["-Xlint:deprecation"]
|
||||
}
|
||||
}
|
||||
|
||||
project(":desktop"){
|
||||
apply plugin: "java"
|
||||
|
||||
compileJava.options.fork = true
|
||||
|
||||
dependencies{
|
||||
compile project(":core")
|
||||
implementation project(":core")
|
||||
implementation arcModule("natives:natives-box2d-desktop")
|
||||
implementation arcModule("natives:natives-desktop")
|
||||
implementation arcModule("natives:natives-freetype-desktop")
|
||||
implementation 'com.github.MinnDevelopment:java-discord-rpc:v2.0.1'
|
||||
|
||||
if(debugged()) compile project(":debug")
|
||||
if(debugged()) implementation project(":debug")
|
||||
|
||||
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
|
||||
compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop"
|
||||
implementation "com.github.Anuken:steamworks4j:$steamworksVersion"
|
||||
|
||||
compile "com.code-disaster.steamworks4j:steamworks4j:$steamworksVersion"
|
||||
compile "com.code-disaster.steamworks4j:steamworks4j-server:$steamworksVersion"
|
||||
|
||||
compile arcModule("backends:backend-sdl")
|
||||
compile 'com.github.MinnDevelopment:java-discord-rpc:v2.0.1'
|
||||
implementation arcModule("backends:backend-sdl")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,14 +206,13 @@ project(":ios"){
|
||||
|
||||
task incrementConfig{
|
||||
def vfile = file('robovm.properties')
|
||||
|
||||
def props = new Properties()
|
||||
if(vfile.exists()){
|
||||
props.load(new FileInputStream(vfile))
|
||||
}else{
|
||||
props['app.id'] = 'io.anuke.mindustry'
|
||||
props['app.version'] = '5.0'
|
||||
props['app.mainclass'] = 'io.anuke.mindustry.IOSLauncher'
|
||||
props['app.mainclass'] = 'mindustry.IOSLauncher'
|
||||
props['app.executable'] = 'IOSLauncher'
|
||||
props['app.name'] = 'Mindustry'
|
||||
}
|
||||
@@ -180,60 +222,61 @@ project(":ios"){
|
||||
}
|
||||
|
||||
dependencies{
|
||||
compile project(":core")
|
||||
implementation project(":core")
|
||||
|
||||
implementation arcModule("natives:natives-ios")
|
||||
implementation arcModule("natives:natives-freetype-ios")
|
||||
implementation arcModule("natives:natives-box2d-ios")
|
||||
implementation arcModule("backends:backend-robovm")
|
||||
|
||||
compileOnly project(":annotations")
|
||||
|
||||
compile arcModule("backends:backend-robovm")
|
||||
|
||||
compile "com.mobidevelop.robovm:robovm-rt:$roboVMVersion"
|
||||
compile "com.mobidevelop.robovm:robovm-cocoatouch:$roboVMVersion"
|
||||
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-ios"
|
||||
compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-ios"
|
||||
}
|
||||
}
|
||||
|
||||
project(":core"){
|
||||
apply plugin: "java"
|
||||
apply plugin: "java-library"
|
||||
|
||||
task preGen{
|
||||
outputs.upToDateWhen{ false }
|
||||
generateLocales()
|
||||
writeVersion()
|
||||
writeProcessors()
|
||||
writePlugins()
|
||||
}
|
||||
|
||||
task copyChangelog{
|
||||
doLast{
|
||||
def props = loadVersionProps()
|
||||
def androidVersion = props['androidBuildCode'].toInteger() - 2
|
||||
def buildVersion = props["build"]
|
||||
def loglines = file("../changelog").text.split("\n")
|
||||
def maxLength = 460
|
||||
|
||||
def androidLogList = loglines.findAll{ line -> !line.endsWith("]") || line.endsWith("[Mobile]") || line.endsWith("[Android]")}
|
||||
def result = ""
|
||||
androidLogList.forEach{line ->
|
||||
if(result.length() + line.length() + 1 < maxLength){
|
||||
result += line + "\n"
|
||||
}
|
||||
}
|
||||
def changelogs = file("../fastlane/metadata/android/en-US/changelogs/")
|
||||
new File(changelogs, buildVersion + ".txt").text = (result)
|
||||
new File(changelogs, androidVersion + ".txt").text = (result)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies{
|
||||
if(System.properties["user.name"] == "anuke"){
|
||||
task cleanGen{
|
||||
doFirst{
|
||||
delete{
|
||||
delete "../core/src/io/anuke/mindustry/gen/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task copyGen{
|
||||
doLast{
|
||||
copy{
|
||||
from("../core/build/generated/sources/annotationProcessor/java/main/io/anuke/mindustry/gen"){
|
||||
include "**/*.java"
|
||||
}
|
||||
|
||||
into "../core/src/io/anuke/mindustry/gen"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compileJava.dependsOn(cleanGen)
|
||||
compileJava.finalizedBy(copyGen)
|
||||
}
|
||||
|
||||
compileJava.dependsOn(preGen)
|
||||
|
||||
compile "org.lz4:lz4-java:1.4.1"
|
||||
compile arcModule("arc-core")
|
||||
compile arcModule("extensions:freetype")
|
||||
compile arcModule("extensions:arcnet")
|
||||
if(localArc() && debugged()) compile arcModule("extensions:recorder")
|
||||
api "org.lz4:lz4-java:1.4.1"
|
||||
api arcModule("arc-core")
|
||||
api arcModule("extensions:freetype")
|
||||
api arcModule("extensions:box2d")
|
||||
api arcModule("extensions:g3d")
|
||||
api arcModule("extensions:fx")
|
||||
api arcModule("extensions:arcnet")
|
||||
api "com.github.Anuken:rhino:$rhinoVersion"
|
||||
if(localArc() && debugged()) api arcModule("extensions:recorder")
|
||||
|
||||
compileOnly project(":annotations")
|
||||
annotationProcessor project(":annotations")
|
||||
@@ -244,8 +287,9 @@ project(":server"){
|
||||
apply plugin: "java"
|
||||
|
||||
dependencies{
|
||||
compile project(":core")
|
||||
compile arcModule("backends:backend-headless")
|
||||
implementation project(":core")
|
||||
implementation arcModule("natives:natives-box2d-desktop")
|
||||
implementation arcModule("backends:backend-headless")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,13 +300,17 @@ project(":tests"){
|
||||
testImplementation project(":core")
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params:5.3.1"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.1"
|
||||
testImplementation arcModule("backends:backend-headless")
|
||||
testImplementation arcModule("natives:natives-box2d-desktop")
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.3.1"
|
||||
compile arcModule("backends:backend-headless")
|
||||
}
|
||||
|
||||
test{
|
||||
useJUnitPlatform()
|
||||
workingDir = new File("../core/assets")
|
||||
testLogging {
|
||||
exceptionFormat = 'full'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,28 +318,29 @@ project(":tools"){
|
||||
apply plugin: "java"
|
||||
|
||||
dependencies{
|
||||
compile project(":core")
|
||||
implementation project(":core")
|
||||
|
||||
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
|
||||
compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop"
|
||||
|
||||
compile arcModule("backends:backend-sdl")
|
||||
implementation arcModule("natives:natives-desktop")
|
||||
implementation arcModule("natives:natives-freetype-desktop")
|
||||
implementation arcModule("natives:natives-box2d-desktop")
|
||||
implementation arcModule("backends:backend-headless")
|
||||
}
|
||||
}
|
||||
|
||||
project(":annotations"){
|
||||
apply plugin: "java"
|
||||
apply plugin: "java-library"
|
||||
|
||||
dependencies{
|
||||
compile 'com.squareup:javapoet:1.11.0'
|
||||
compile files("${System.getProperty('java.home')}/../lib/tools.jar")
|
||||
implementation 'com.squareup:javapoet:1.12.1'
|
||||
implementation "com.github.Anuken.Arc:arc-core:$arcHash"
|
||||
implementation files("${System.getProperty('java.home')}/../lib/tools.jar")
|
||||
}
|
||||
}
|
||||
|
||||
task deployAll{
|
||||
task cleanDeployOutput{
|
||||
doFirst{
|
||||
if("${getBuildVersion()}" == "custom build" || "${getBuildVersion()}" == "") throw new IllegalArgumentException("----\n\nSET A BUILD NUMBER FIRST!\n\n----")
|
||||
if(getBuildVersion() == "custom build" || getBuildVersion() == "") throw new IllegalArgumentException("----\n\nSET A BUILD NUMBER FIRST!\n\n----")
|
||||
if(!project.hasProperty("release")) throw new IllegalArgumentException("----\n\nSET THE RELEASE PROJECT PROPERTY FIRST!\n\n----")
|
||||
|
||||
delete{
|
||||
|
||||
924
core/assets-raw/fontgen/config.json
Normal file
@@ -0,0 +1,924 @@
|
||||
{
|
||||
"name": "",
|
||||
"css_prefix_text": "icon-",
|
||||
"css_use_suffix": false,
|
||||
"hinting": true,
|
||||
"units_per_em": 1000,
|
||||
"ascent": 850,
|
||||
"glyphs": [
|
||||
{
|
||||
"uid": "c08a1cde48d96cba21d8c05fa7d7feb1",
|
||||
"css": "file-text-fill",
|
||||
"code": 61788,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "178053298e3e5b03551d754d4b9acd8b",
|
||||
"css": "file",
|
||||
"code": 61787,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "5408be43f7c42bccee419c6be53fdef5",
|
||||
"css": "file-text",
|
||||
"code": 61686,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "555ef8c86832e686fef85f7af2eb7cde",
|
||||
"css": "left",
|
||||
"code": 59394,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "ad6b3fbb5324abe71a9c0b6609cbb9f1",
|
||||
"css": "right",
|
||||
"code": 59395,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "95376bf082bfec6ce06ea1cda7bd7ead",
|
||||
"css": "up",
|
||||
"code": 59396,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "1c4068ed75209e21af36017df8871802",
|
||||
"css": "down",
|
||||
"code": 59397,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "6f880cbd94039674f0de065ffb57c250",
|
||||
"css": "distribution",
|
||||
"code": 59398,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M0 0L0 50 0 100 0 150 0 200 0 250 0 300 0 350 0 400 0 450 0 500 0 550 0 600 0 650 0 700 0 750 0 800 0 850 0 900 0 950 0 1000 50 1000 100 1000 100 950 100 850 100 750 100 650 100 550 100 450 100 350 100 250 100 150 100 50 100 0 50 0 0 0ZM900 0L900 50 900 150 900 250 900 350 900 450 900 550 900 650 900 750 900 850 900 950 900 1000 950 1000 1000 1000 1000 950 1000 900 1000 850 1000 800 1000 750 1000 700 1000 650 1000 600 1000 550 1000 500 1000 450 1000 400 1000 350 1000 300 1000 250 1000 200 1000 150 1000 100 1000 50 1000 0 950 0 900 0ZM500 106.3C487.5 106.3 475 108.3 462.5 112.5 437.5 120.8 416.7 133.3 400 150L350 200C333.3 216.7 325 233.3 325 250 325 266.7 337.5 279.2 362.5 287.5 387.5 295.8 416.7 300 450 300L550 300C597.6 293.9 649.1 300.5 675 250 675 233.3 666.7 216.7 650 200L600 150C583.3 133.3 562.5 120.8 537.5 112.5 525 108.3 512.5 106.3 500 106.3ZM500 406.3C487.5 406.3 475 408.3 462.5 412.5 437.5 420.8 416.7 433.3 400 450L350 500C333.3 516.7 325 533.3 325 550 325 566.7 337.5 579.2 362.5 587.5 387.5 595.8 416.7 600 450 600L550 600C583.3 600 612.5 595.8 637.5 587.5 662.5 579.2 675 566.7 675 550 675 533.3 666.7 516.7 650 500L600 450C583.3 433.3 562.5 420.8 537.5 412.5 525 408.3 512.5 406.3 500 406.3ZM500 706.3C487.5 706.3 475 708.3 462.5 712.5 437.5 720.8 416.7 733.3 400 750L350 800C333.3 816.7 325 833.3 325 850 325 866.7 337.5 879.2 362.5 887.5 387.5 895.8 416.7 900 450 900L550 900C583.3 900 612.5 895.8 637.5 887.5 662.5 879.2 675 866.7 675 850 675 833.3 666.7 816.7 650 800L600 750C583.3 733.3 562.5 720.8 537.5 712.5 525 708.3 512.5 706.3 500 706.3Z",
|
||||
"width": 1000
|
||||
},
|
||||
"search": [
|
||||
"icon-distribution-transparent.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "w3nzesrlbezu6f30q7ytyq919p6gdlb6",
|
||||
"css": "home",
|
||||
"code": 59399,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "12f4ece88e46abd864e40b35e05b11cd",
|
||||
"css": "ok",
|
||||
"code": 59392,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "6a7ed3214c9049148cbdb01825e5efc2",
|
||||
"css": "image",
|
||||
"code": 59400,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "vyuzsm6wijlfwtjo4ifkoblfmsepk6g8",
|
||||
"css": "star",
|
||||
"code": 59401,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "dd4b00255957a608953c409346e7d7fb",
|
||||
"css": "warning",
|
||||
"code": 59402,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "cdfalpadi7huwv9ah4fef2gpfpb4c6qm",
|
||||
"css": "resize",
|
||||
"code": 59403,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "j6xzwioffey3gxe90yx3ie56j5dlt902",
|
||||
"css": "block_",
|
||||
"code": 59404,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "jh3jpcb1t1bcm80gidkadilh080aq79h",
|
||||
"css": "menu",
|
||||
"code": 59405,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "dpxunrtmigvk6jm0ieo7axfsgzx4awll",
|
||||
"css": "settings",
|
||||
"code": 59406,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "i7r8vgyqewsy2rlx0xhawybpw5uyss3z",
|
||||
"css": "wrench",
|
||||
"code": 59407,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "971842913945f59c7f3cf11e75e1e4f1",
|
||||
"css": "power_",
|
||||
"code": 59408,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "2b8ea04aa0e500ae056f9c7934af18e6",
|
||||
"css": "pine",
|
||||
"code": 59409,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "0ece9a12de796b8411f868d582bee678",
|
||||
"css": "github-square",
|
||||
"code": 62208,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "3c961c1a8d874815856fc6637dc5a13c",
|
||||
"css": "file-image",
|
||||
"code": 61893,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "1gf923f9wvaezxmfon515dglxa3drf0e",
|
||||
"css": "add",
|
||||
"code": 59411,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "k7sao112253txym1egnte7822irbyphe",
|
||||
"css": "eye",
|
||||
"code": 59412,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "jqzwo6i8oicjbn049sh2856d8anrqoli",
|
||||
"css": "edit",
|
||||
"code": 59414,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "890649841b2c37d56ff90065872fecf3",
|
||||
"css": "chart-bar",
|
||||
"code": 59417,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "db7651b88bb33a590710f551bfbbd371",
|
||||
"css": "plane-outline",
|
||||
"code": 59418,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "4109c474ff99cad28fd5a2c38af2ec6f",
|
||||
"css": "filter",
|
||||
"code": 61616,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "f8aa663c489bcbd6e68ec8147dca841e",
|
||||
"css": "folder",
|
||||
"code": 59421,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "ca1c07c0df52dbf2af6813c9e36c2611",
|
||||
"css": "copy",
|
||||
"code": 59424,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "785a9b232e86ae750516adc5228a5aa7",
|
||||
"css": "steam",
|
||||
"code": 59426,
|
||||
"src": "zocial"
|
||||
},
|
||||
{
|
||||
"uid": "348b04ea17f646fbc6a46e20ebe4fe12",
|
||||
"css": "download",
|
||||
"code": 61486,
|
||||
"src": "mfglabs"
|
||||
},
|
||||
{
|
||||
"uid": "4f3b31017c63ba04dc4f78ce7a8f02b1",
|
||||
"css": "upload",
|
||||
"code": 61487,
|
||||
"src": "mfglabs"
|
||||
},
|
||||
{
|
||||
"uid": "6020aff067fc3c119cdd75daa5249220",
|
||||
"css": "exchange",
|
||||
"code": 61676,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "ccddff8e8670dcd130e3cb55fdfc2fd0",
|
||||
"css": "down-open",
|
||||
"code": 59428,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "d870630ff8f81e6de3958ecaeac532f2",
|
||||
"css": "left-open",
|
||||
"code": 59429,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "fe6697b391355dec12f3d86d6d490397",
|
||||
"css": "up-open",
|
||||
"code": 59430,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "87d337fee4866c2c28f6082994ce0f41",
|
||||
"css": "map",
|
||||
"code": 59431,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "5qynhqqaenby227l5kogqj8sdk5s4e7h",
|
||||
"css": "rotate",
|
||||
"code": 59427,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "3e290a111c0f3ee3acbf7b5f17ccc04a",
|
||||
"css": "play",
|
||||
"code": 59433,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "e44ef09cb81413287d702eefa65dd790",
|
||||
"css": "pause",
|
||||
"code": 59434,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "lu2wqmd7v9zp21knhy4eifo41wjp4cnx",
|
||||
"css": "list",
|
||||
"code": 59435,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "5211af474d3a9848f67f945e2ccaf143",
|
||||
"css": "cancel",
|
||||
"code": 59436,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "fhkechw94uv9ydw6tukba3ka2hhoif4n",
|
||||
"css": "move",
|
||||
"code": 59438,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "287ee2cac4ded1712253c7b03ff4a5db",
|
||||
"css": "pipette",
|
||||
"code": 59439,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "0e26e70b4aa537cc206f41b21dcf2fcc",
|
||||
"css": "lock",
|
||||
"code": 59442,
|
||||
"src": "elusive"
|
||||
},
|
||||
{
|
||||
"uid": "8f562a6dca9c14c9c8268bdb64efa89f",
|
||||
"css": "lock-open",
|
||||
"code": 59443,
|
||||
"src": "elusive"
|
||||
},
|
||||
{
|
||||
"uid": "fa10777b2d88cc64cd6e4f26ef0e5264",
|
||||
"css": "terminal",
|
||||
"code": 61728,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "28r0xthd180mg0wnfh8ek3mtn761hs11",
|
||||
"css": "undo",
|
||||
"code": 59445,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "7p5gflyid3o4gtesy94a6erq94e4pz7u",
|
||||
"css": "redo",
|
||||
"code": 59446,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "3d4ea8a78dc34efe891f3a0f3d961274",
|
||||
"css": "info",
|
||||
"code": 61737,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "e82cedfa1d5f15b00c5a81c9bd731ea2",
|
||||
"css": "info-circle",
|
||||
"code": 59447,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "8a1d446e5555e76f82ddb1c8b526f579",
|
||||
"css": "tree",
|
||||
"code": 59448,
|
||||
"src": "entypo"
|
||||
},
|
||||
{
|
||||
"uid": "813cb2527308990d4870591d9d76f8cd",
|
||||
"css": "right-open-out",
|
||||
"code": 59449,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "6zhrgcf3co77hnljttd3b2mrc8z5fiq5",
|
||||
"css": "right-open",
|
||||
"code": 59450,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "fd8d9ae4422e55d3ca23f55d9cf4b20a",
|
||||
"css": "waves",
|
||||
"code": 59451,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "5c49851100aa021825b346d16d7f3abf",
|
||||
"css": "temperatire",
|
||||
"code": 59452,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "e3699d145bd628d2a35d4a508b616f0c",
|
||||
"css": "filters",
|
||||
"code": 59454,
|
||||
"src": "iconic"
|
||||
},
|
||||
{
|
||||
"uid": "d2c499942f8a7c037d5a94f123eeb478",
|
||||
"css": "layers",
|
||||
"code": 59455,
|
||||
"src": "iconic"
|
||||
},
|
||||
{
|
||||
"uid": "b9be509237c9abea9c258d677777b713",
|
||||
"css": "pick",
|
||||
"code": 59456,
|
||||
"src": "iconic"
|
||||
},
|
||||
{
|
||||
"uid": "ebc57fa8400e4ede049ac5dc665210e1",
|
||||
"css": "eraser",
|
||||
"code": 61741,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "51727ca007aa35ceabcaffc28934faee",
|
||||
"css": "book-open",
|
||||
"code": 59393,
|
||||
"src": "entypo"
|
||||
},
|
||||
{
|
||||
"uid": "da72ab64b02d997ff0521339f2997233",
|
||||
"css": "grid",
|
||||
"code": 61481,
|
||||
"src": "mfglabs"
|
||||
},
|
||||
{
|
||||
"uid": "f8d887df13c5f95ede5fb0e2cdb042b1",
|
||||
"css": "flip-x",
|
||||
"code": 59410,
|
||||
"src": "elusive"
|
||||
},
|
||||
{
|
||||
"uid": "f8b0d36761ba4c7e51d0af49d3386713",
|
||||
"css": "flip-y",
|
||||
"code": 59458,
|
||||
"src": "elusive"
|
||||
},
|
||||
{
|
||||
"uid": "9edf364d6c567aa474b0f9ec6c26c042",
|
||||
"css": "hammer",
|
||||
"code": 59459,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M470.3 0C458.1 0 446 6.1 433.8 18.3L397.3 54.8 360.7 91.3 324.2 127.9 287.7 164.4 251.1 200.9 214.6 237.4 178.1 274C165.9 286.1 159.8 298.3 159.8 310.5 159.8 322.7 165.9 334.9 178.1 347L214.6 383.6 251.1 420.1 287.7 456.6 324.2 493.2C336.4 505.3 342.5 517.5 342.5 529.7 342.5 541.9 336.4 554 324.2 566.2L287.7 602.7 251.1 639.3 214.6 675.8 178.1 712.3 141.6 748.9 105 785.4 68.5 821.9 32 858.4C19.8 870.6 10.7 885.8 4.6 904.1-1.5 922.4-1.5 940.6 4.6 958.9 10.7 977.2 22.8 989.3 41.1 995.4 59.4 1001.5 77.6 1001.5 95.9 995.4 114.2 989.3 129.4 980.2 141.6 968L178.1 931.5 214.6 895 251.1 858.4 287.7 821.9 324.2 785.4 360.7 748.9 397.3 712.3 433.8 675.8C446 663.6 458.1 657.5 470.3 657.5 482.5 657.5 494.7 663.6 506.8 675.8L543.4 712.3 579.9 748.9 616.4 785.4 653 821.9C665.1 834.1 677.3 840.2 689.5 840.2 701.7 840.2 713.9 834.1 726 821.9L762.6 785.4 799.1 748.9 835.6 712.3 872.1 675.8 908.7 639.3 945.2 602.7 981.7 566.2C993.9 554 1000 541.9 1000 529.7 1000 517.5 993.9 505.3 981.7 493.2L945.2 456.6 908.7 420.1 872.1 383.6 835.6 347 799.1 310.5 762.6 274 726 237.4 689.5 200.9 653 164.4 616.4 127.9 579.9 91.3 543.4 54.8 506.8 18.3C494.7 6.1 482.5 0 470.3 0Z",
|
||||
"width": 1000
|
||||
},
|
||||
"search": [
|
||||
"hammer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "e594fc6e5870b4ab7e49f52571d52577",
|
||||
"css": "diagonal",
|
||||
"code": 59460,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "wh8nbtrxjl2f67dpybsww6mri0xcaklm",
|
||||
"css": "export",
|
||||
"code": 59462,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "9e33d50c92e84bd26d83d1f37d6f0d57",
|
||||
"css": "admin",
|
||||
"code": 59432,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M427.1 385.4Q437.5 354.2 468.8 343.8 500 333.3 531.3 343.8 562.5 354.2 572.9 385.4 583.3 416.7 572.9 447.9 562.5 479.2 531.3 489.6 500 500 468.8 489.6 437.5 479.2 427.1 447.9 416.7 416.7 427.1 385.4M166.7 41.7Q187.5 20.8 218.8 10.4 250 0 291.7 0 333.3 0 375 0 416.7 0 458.3 0 500 0 541.7 0 583.3 0 625 0 666.7 0 708.3 0 750 0 781.3 10.4 812.5 20.8 833.3 41.7 854.2 62.5 875 83.3 895.8 104.2 916.7 125 937.5 145.8 958.3 166.7 979.2 187.5 989.6 218.8 1000 250 1000 291.7 1000 333.3 1000 375 1000 416.7 1000 458.3 1000 500 989.6 531.3 979.2 562.5 958.3 583.3 937.5 604.2 927.1 635.4 916.7 666.7 916.7 708.3 916.7 750 906.3 781.3 895.8 812.5 875 833.3 854.2 854.2 833.3 875 812.5 895.8 791.7 916.7 770.8 937.5 750 958.3 729.2 979.2 697.9 989.6 666.7 1000 625 1000 583.3 1000 541.7 1000 500 1000 458.3 1000 416.7 1000 375 1000 333.3 1000 302.1 989.6 270.8 979.2 250 958.3 229.2 937.5 208.3 916.7 187.5 895.8 166.7 875 145.8 854.2 125 833.3 104.2 812.5 93.8 781.3 83.3 750 83.3 708.3 83.3 666.7 72.9 635.4 62.5 604.2 41.7 583.3 20.8 562.5 10.4 531.3 0 500 0 458.3 0 416.7 0 375 0 333.3 0 291.7 0 250 10.4 218.8 20.8 187.5 41.7 166.7 62.5 145.8 83.3 125 104.2 104.2 125 83.3 145.8 62.5 166.7 41.7M541.7 750Q583.3 750 614.6 739.6 645.8 729.2 645.8 708.3 645.8 687.5 614.6 677.1 583.3 666.7 541.7 666.7 500 666.7 458.3 666.7 416.7 666.7 385.4 677.1 354.2 687.5 354.2 708.3 354.2 729.2 385.4 739.6 416.7 750 458.3 750 500 750 541.7 750M541.7 583.3Q583.3 583.3 614.6 572.9 645.8 562.5 666.7 541.7 687.5 520.8 708.3 500 729.2 479.2 739.6 447.9 750 416.7 739.6 385.4 729.2 354.2 708.3 333.3 687.5 312.5 666.7 291.7 645.8 270.8 614.6 260.4 583.3 250 541.7 250 500 250 458.3 250 416.7 250 385.4 260.4 354.2 270.8 333.3 291.7 312.5 312.5 291.7 333.3 270.8 354.2 260.4 385.4 250 416.7 260.4 447.9 270.8 479.2 291.7 500 312.5 520.8 333.3 541.7 354.2 562.5 385.4 572.9 416.7 583.3 458.3 583.3 500 583.3 541.7 583.3",
|
||||
"width": 1000
|
||||
},
|
||||
"search": [
|
||||
"admin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "4bad23bc52aaddaac37da019e46b6f6b",
|
||||
"css": "discord",
|
||||
"code": 59464,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M347 419.5C318.5 419.5 296 444.5 296 475 296 505.5 319 530.5 347 530.5 375.5 530.5 398 505.5 398 475 398.5 444.5 375.5 419.5 347 419.5ZM529.5 419.5C501 419.5 478.5 444.5 478.5 475 478.5 505.5 501.5 530.5 529.5 530.5 558 530.5 580.5 505.5 580.5 475 580.5 444.5 558 419.5 529.5 419.5ZM772.5 0H102.5C46 0 0 46 0 103V779C0 836 46 882 102.5 882H669.5L643 789.5 707 849 767.5 905 875 1000V103C875 46 829 0 772.5 0ZM579.5 653C579.5 653 561.5 631.5 546.5 612.5 612 594 637 553 637 553 616.5 566.5 597 576 579.5 582.5 554.5 593 530.5 600 507 604 459 613 415 610.5 377.5 603.5 349 598 324.5 590 304 582 292.5 577.5 280 572 267.5 565 266 564 264.5 563.5 263 562.5 262 562 261.5 561.5 261 561 252 556 247 552.5 247 552.5 247 552.5 271 592.5 334.5 611.5 319.5 630.5 301 653 301 653 190.5 649.5 148.5 577 148.5 577 148.5 416 220.5 285.5 220.5 285.5 292.5 231.5 361 233 361 233L366 239C276 265 234.5 304.5 234.5 304.5 234.5 304.5 245.5 298.5 264 290 317.5 266.5 360 260 377.5 258.5 380.5 258 383 257.5 386 257.5 416.5 253.5 451 252.5 487 256.5 534.5 262 585.5 276 637.5 304.5 637.5 304.5 598 267 513 241L520 233C520 233 588.5 231.5 660.5 285.5 660.5 285.5 732.5 416 732.5 577 732.5 577 690 649.5 579.5 653Z",
|
||||
"width": 875
|
||||
},
|
||||
"search": [
|
||||
"discord-logo-white"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "777bee87001d4219672e09caa12e1c78",
|
||||
"css": "crafting",
|
||||
"code": 59425,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M909.1 62.9Q923.1 21 965 7 1007-7 1049 7 1090.9 21 1104.9 62.9 1118.9 104.9 1118.9 160.8 1118.9 216.8 1118.9 272.7 1118.9 328.7 1118.9 384.6 1118.9 440.6 1118.9 496.5 1118.9 552.4 1118.9 608.4 1118.9 664.3 1118.9 720.3 1118.9 776.2 1118.9 832.2 1118.9 888.1 1104.9 930.1 1090.9 972 1049 986 1007 1000 951 1000 895.1 1000 839.2 1000 783.2 1000 727.3 1000 671.3 1000 615.4 1000 559.4 1000 503.5 1000 447.6 1000 391.6 1000 335.7 1000 279.7 1000 223.8 1000 167.8 1000 111.9 1000 69.9 986 28 972 14 930.1 0 888.1 0 832.2 0 776.2 0 720.3 0 664.3 0 608.4 0 552.4 0 496.5 0 440.6 0 384.6 0 328.7 0 272.7 0 216.8 14 174.8 28 132.9 55.9 132.9 83.9 132.9 111.9 160.8 139.9 188.8 167.8 216.8 195.8 244.8 223.8 272.7 251.7 300.7 279.7 328.7 307.7 356.6 335.7 384.6 363.6 412.6 391.6 412.6 419.6 412.6 433.6 370.6 447.6 328.7 447.6 272.7 447.6 216.8 461.5 174.8 475.5 132.9 503.5 132.9 531.5 132.9 559.4 160.8 587.4 188.8 615.4 216.8 643.4 244.8 671.3 272.7 699.3 300.7 727.3 328.7 755.2 356.6 783.2 384.6 811.2 412.6 839.2 412.6 867.1 412.6 881.1 370.6 895.1 328.7 895.1 272.7 895.1 216.8 895.1 160.8 895.1 104.9 909.1 62.9M839.2 888.1Q895.1 888.1 937.1 874.1 979 860.1 993 818.2 1007 776.2 1007 720.3 1007 664.3 993 622.4 979 580.4 937.1 566.4 895.1 552.4 853.1 538.5 811.2 524.5 783.2 496.5 755.2 468.5 727.3 440.6 699.3 412.6 671.3 384.6 643.4 356.6 615.4 356.6 587.4 356.6 573.4 398.6 559.4 440.6 545.5 482.5 531.5 524.5 489.5 538.5 447.6 552.4 405.6 538.5 363.6 524.5 335.7 496.5 307.7 468.5 279.7 440.6 251.7 412.6 223.8 384.6 195.8 356.6 167.8 356.6 139.9 356.6 125.9 398.6 111.9 440.6 111.9 496.5 111.9 552.4 111.9 608.4 111.9 664.3 111.9 720.3 111.9 776.2 125.9 818.2 139.9 860.1 181.8 874.1 223.8 888.1 279.7 888.1 335.7 888.1 391.6 888.1 447.6 888.1 503.5 888.1 559.4 888.1 615.4 888.1 671.3 888.1 727.3 888.1 783.2 888.1 839.2 888.1M251.7 720.3Q251.7 692.3 279.7 692.3 307.7 692.3 307.7 720.3 307.7 748.3 279.7 748.3 251.7 748.3 251.7 720.3M475.5 720.3Q475.5 692.3 503.5 692.3 531.5 692.3 531.5 720.3 531.5 748.3 503.5 748.3 475.5 748.3 475.5 720.3M699.3 720.3Q699.3 692.3 727.3 692.3 755.2 692.3 755.2 720.3 755.2 748.3 727.3 748.3 699.3 748.3 699.3 720.3",
|
||||
"width": 1119
|
||||
},
|
||||
"search": [
|
||||
"crafting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "39651356f0f411effa9ab4a83abd2d0d",
|
||||
"css": "defense",
|
||||
"code": 59465,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M101.3 0C88.6 0 75.9 2.1 63.3 6.3 38 14.8 21.1 31.6 12.7 57 4.2 82.3 0 111.8 0 145.6V246.8 348.1 449.4C0 483.1 4.2 512.7 12.7 538 21.1 563.3 33.8 584.4 50.6 601.3L101.3 651.9 151.9 702.5 202.5 753.2 253.2 803.8 303.8 854.4 354.4 905.1 405.1 955.7C421.9 972.6 443 985.2 468.4 993.7 493.7 1002.1 519 1002.1 544.3 993.7 569.6 985.2 590.7 972.6 607.6 955.7L658.2 905.1 708.9 854.4 759.5 803.8 810.1 753.2 860.8 702.5 911.4 651.9 962 601.3C978.9 584.4 991.6 563.3 1000 538 1008.4 512.7 1012.7 483.1 1012.7 449.4V348.1 246.8 145.6C1012.7 111.8 1008.4 82.3 1000 57 991.6 31.6 974.7 14.8 949.4 6.3 924.1-2.1 898.7-2.1 873.4 6.3 848.1 14.8 827 27.4 810.1 44.3L759.5 94.9 708.9 145.6C692 162.4 670.9 175.1 645.6 183.5 620.3 192 590.7 196.2 557 196.2H455.7C421.9 196.2 392.4 192 367.1 183.5 341.8 175.1 320.7 162.4 303.8 145.6L253.2 94.9 202.5 44.3C185.7 27.4 164.6 14.8 139.2 6.3 126.6 2.1 113.9 0 101.3 0ZM151.9 120.3C168.8 120.3 185.7 128.7 202.5 145.6L253.2 196.2 303.8 246.8C320.7 263.7 341.8 276.4 367.1 284.8 392.4 293.2 421.9 297.5 455.7 297.5H557C590.7 297.5 620.3 293.2 645.6 284.8 670.9 276.4 692 263.7 708.9 246.8L759.5 196.2 810.1 145.6C827 128.7 843.9 120.3 860.8 120.3 877.6 120.3 890.3 132.9 898.7 158.2 907.2 183.5 911.4 213.1 911.4 246.8V348.1 449.4C911.4 483.1 907.2 512.7 898.7 538 890.3 563.3 877.6 584.4 860.8 601.3L810.1 651.9 759.5 702.5 708.9 753.2 658.2 803.8 607.6 854.4C590.7 871.3 569.6 884 544.3 892.4 519 900.8 493.7 900.8 468.4 892.4 443 884 421.9 871.3 405.1 854.4L354.4 803.8 303.8 753.2 253.2 702.5 202.5 651.9 151.9 601.3C135 584.4 122.4 563.3 113.9 538 105.5 512.7 101.3 483.1 101.3 449.4V348.1 246.8C101.3 213.1 105.5 183.5 113.9 158.2 122.4 132.9 135 120.3 151.9 120.3Z",
|
||||
"width": 1013
|
||||
},
|
||||
"search": [
|
||||
"defense"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "10e2d46ee8b987f0a414451439b23036",
|
||||
"css": "effect",
|
||||
"code": 59466,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M250 0C216.7 0 187.5 4.2 162.5 12.5 137.5 20.8 116.7 33.3 100 50L50 100C33.3 116.7 20.8 137.5 12.5 162.5 4.2 187.5 0 216.7 0 250V350 450 550 650 750C0 783.3 4.2 812.5 12.5 837.5 20.8 862.5 33.3 883.3 50 900L100 950C116.7 966.7 137.5 979.2 162.5 987.5 187.5 995.8 216.7 1000 250 1000H350 450 550 650 750C783.3 1000 812.5 995.8 837.5 987.5 862.5 979.2 883.3 966.7 900 950L950 900C966.7 883.3 979.2 862.5 987.5 837.5 995.8 812.5 1000 783.3 1000 750V650 550 450 350 250C1000 216.7 995.8 187.5 987.5 162.5 979.2 137.5 966.7 116.7 950 100L900 50C883.3 33.3 862.5 20.8 837.5 12.5 812.5 4.2 783.3 0 750 0H650 550 450 350ZM350 100H450 550 650C683.3 100 712.5 104.2 737.5 112.5 762.5 120.8 783.3 133.3 800 150L850 200C866.7 216.7 879.2 237.5 887.5 262.5 895.8 287.5 900 316.7 900 350V450 550 650C900 683.3 895.8 712.5 887.5 737.5 879.2 762.5 866.7 783.3 850 800L800 850C783.3 866.7 762.5 879.2 737.5 887.5 712.5 895.8 683.3 900 650 900H550 450 350C316.7 900 287.5 895.8 262.5 887.5 237.5 879.2 216.7 866.7 200 850L150 800C133.3 783.3 120.8 762.5 112.5 737.5 104.2 712.5 100 683.3 100 650V550 450 350C100 316.7 104.2 287.5 112.5 262.5 120.8 237.5 133.3 216.7 150 200L200 150C216.7 133.3 237.5 120.8 262.5 112.5 287.5 104.2 316.7 100 350 100ZM500 306.3C487.5 306.3 475 308.3 462.5 312.5 437.5 320.8 416.7 333.3 400 350L350 400C333.3 416.7 320.8 437.5 312.5 462.5 304.2 487.5 304.2 512.5 312.5 537.5 320.8 562.5 333.3 583.3 350 600L400 650C416.7 666.7 437.5 679.2 462.5 687.5 487.5 695.8 512.5 695.8 537.5 687.5 562.5 679.2 583.3 666.7 600 650L650 600C666.7 583.3 679.2 562.5 687.5 537.5 695.8 512.5 695.8 487.5 687.5 462.5 679.2 437.5 666.7 416.7 650 400L600 350C583.3 333.3 562.5 320.8 537.5 312.5 525 308.3 512.5 306.3 500 306.3Z",
|
||||
"width": 1000
|
||||
},
|
||||
"search": [
|
||||
"effect"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "a4d157485929f47f7a7872bac82ffbb7",
|
||||
"css": "liquid",
|
||||
"code": 59467,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M0 57.1Q0 28.6 42.9 14.3 85.7 0 142.9 0 200 0 257.1 0 314.3 0 371.4 0 428.6 0 485.7 0 542.9 0 600 0 657.1 0 700 14.3 742.9 28.6 742.9 57.1 742.9 85.7 714.3 114.3 685.7 142.9 671.4 185.7 657.1 228.6 671.4 271.4 685.7 314.3 728.6 328.6 771.4 342.9 814.3 328.6 857.1 314.3 885.7 285.7 914.3 257.1 942.9 257.1 971.4 257.1 985.7 300 1000 342.9 1000 400 1000 457.1 1000 514.3 1000 571.4 1000 628.6 1000 685.7 1000 742.9 1000 800 1000 857.1 1000 914.3 985.7 957.1 971.4 1000 942.9 1000 914.3 1000 885.7 971.4 857.1 942.9 814.3 928.6 771.4 914.3 714.3 914.3 657.1 914.3 600 914.3 542.9 914.3 485.7 914.3 428.6 914.3 385.7 900 342.9 885.7 314.3 857.1 285.7 828.6 257.1 800 228.6 771.4 200 742.9 171.4 714.3 142.9 685.7 114.3 657.1 100 614.3 85.7 571.4 85.7 514.3 85.7 457.1 85.7 400 85.7 342.9 85.7 285.7 85.7 228.6 71.4 185.7 57.1 142.9 28.6 114.3 0 85.7 0 57.1M714.3 800Q771.4 800 814.3 785.7 857.1 771.4 871.4 728.6 885.7 685.7 885.7 628.6 885.7 571.4 871.4 528.6 857.1 485.7 814.3 471.4 771.4 457.1 728.6 442.9 685.7 428.6 657.1 400 628.6 371.4 600 342.9 571.4 314.3 557.1 271.4 542.9 228.6 528.6 185.7 514.3 142.9 471.4 128.6 428.6 114.3 371.4 114.3 314.3 114.3 271.4 128.6 228.6 142.9 214.3 185.7 200 228.6 200 285.7 200 342.9 200 400 200 457.1 200 514.3 200 571.4 214.3 614.3 228.6 657.1 257.1 685.7 285.7 714.3 314.3 742.9 342.9 771.4 385.7 785.7 428.6 800 485.7 800 542.9 800 600 800 657.1 800 714.3 800",
|
||||
"width": 1000
|
||||
},
|
||||
"search": [
|
||||
"liquid"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "757a660cd53e3349515bc329726c3dbd",
|
||||
"css": "production",
|
||||
"code": 59469,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M7 69.9Q21 28 62.9 14 104.9 0 160.8 0 216.8 0 272.7 0 328.7 0 384.6 0 440.6 0 496.5 0 552.4 0 608.4 0 664.3 0 720.3 0 776.2 0 818.2 14 860.1 28 874.1 69.9 888.1 111.9 874.1 153.8 860.1 195.8 832.2 223.8 804.2 251.7 776.2 279.7 748.3 307.7 720.3 335.7 692.3 363.6 678.3 405.6 664.3 447.6 664.3 503.5 664.3 559.4 664.3 615.4 664.3 671.3 664.3 727.3 664.3 783.2 650.3 825.2 636.4 867.1 608.4 895.1 580.4 923.1 552.4 951 524.5 979 482.5 993 440.6 1007 398.6 993 356.6 979 328.7 951 300.7 923.1 272.7 895.1 244.8 867.1 230.8 825.2 216.8 783.2 216.8 727.3 216.8 671.3 216.8 615.4 216.8 559.4 216.8 503.5 216.8 447.6 202.8 405.6 188.8 363.6 160.8 335.7 132.9 307.7 104.9 279.7 76.9 251.7 49 223.8 21 195.8 7 153.8-7 111.9 7 69.9M342.7 825.2Q356.6 867.1 384.6 867.1 412.6 867.1 440.6 839.2 468.5 811.2 496.5 783.2 524.5 755.2 524.5 727.3 524.5 699.3 496.5 671.3 468.5 643.4 468.5 615.4 468.5 587.4 496.5 559.4 524.5 531.5 524.5 503.5 524.5 475.5 496.5 447.6 468.5 419.6 440.6 391.6 412.6 363.6 384.6 363.6 356.6 363.6 342.7 405.6 328.7 447.6 328.7 503.5 328.7 559.4 328.7 615.4 328.7 671.3 328.7 727.3 328.7 783.2 342.7 825.2M608.4 223.8Q664.3 223.8 706.3 209.8 748.3 195.8 748.3 167.8 748.3 139.9 706.3 125.9 664.3 111.9 608.4 111.9 552.4 111.9 496.5 111.9 440.6 111.9 384.6 111.9 328.7 111.9 272.7 111.9 216.8 111.9 174.8 125.9 132.9 139.9 132.9 167.8 132.9 195.8 174.8 209.8 216.8 223.8 272.7 223.8 328.7 223.8 384.6 223.8 440.6 223.8 496.5 223.8 552.4 223.8 608.4 223.8",
|
||||
"width": 881
|
||||
},
|
||||
"search": [
|
||||
"production"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "fffdfd2df00136ae95e542b0af62d8fa",
|
||||
"css": "turret",
|
||||
"code": 59470,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M125.9 62.9Q139.9 21 181.8 7 223.8-7 265.7 7 307.7 21 321.7 62.9 335.7 104.9 349.7 146.9 363.6 188.8 405.6 202.8 447.6 216.8 489.5 202.8 531.5 188.8 545.5 146.9 559.4 104.9 573.4 62.9 587.4 21 629.4 7 671.3-7 713.3 7 755.2 21 769.2 62.9 783.2 104.9 783.2 160.8 783.2 216.8 783.2 272.7 783.2 328.7 797.2 370.6 811.2 412.6 839.2 440.6 867.1 468.5 881.1 510.5 895.1 552.4 895.1 608.4 895.1 664.3 895.1 720.3 895.1 776.2 895.1 832.2 895.1 888.1 881.1 930.1 867.1 972 825.2 986 783.2 1000 727.3 1000 671.3 1000 629.4 986 587.4 972 559.4 944.1 531.5 916.1 489.5 902.1 447.6 888.1 405.6 902.1 363.6 916.1 335.7 944.1 307.7 972 265.7 986 223.8 1000 167.8 1000 111.9 1000 69.9 986 28 972 14 930.1 0 888.1 0 832.2 0 776.2 0 720.3 0 664.3 0 608.4 0 552.4 14 510.5 28 468.5 55.9 440.6 83.9 412.6 97.9 370.6 111.9 328.7 111.9 272.7 111.9 216.8 111.9 160.8 111.9 104.9 125.9 62.9M629.4 874.1Q671.3 888.1 713.3 874.1 755.2 860.1 769.2 818.2 783.2 776.2 783.2 720.3 783.2 664.3 783.2 608.4 783.2 552.4 769.2 510.5 755.2 468.5 727.3 440.6 699.3 412.6 671.3 384.6 643.4 356.6 601.4 342.7 559.4 328.7 503.5 328.7 447.6 328.7 391.6 328.7 335.7 328.7 293.7 342.7 251.7 356.6 223.8 384.6 195.8 412.6 167.8 440.6 139.9 468.5 125.9 510.5 111.9 552.4 111.9 608.4 111.9 664.3 111.9 720.3 111.9 776.2 125.9 818.2 139.9 860.1 181.8 874.1 223.8 888.1 265.7 874.1 307.7 860.1 335.7 832.2 363.6 804.2 405.6 790.2 447.6 776.2 489.5 790.2 531.5 804.2 559.4 832.2 587.4 860.1 629.4 874.1",
|
||||
"width": 895
|
||||
},
|
||||
"search": [
|
||||
"turret"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "dbc386322a6eec797d94d5cf2af56dc3",
|
||||
"css": "units",
|
||||
"code": 59471,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M566.9 0C524.9 0 488.2 5.2 456.7 15.7 425.2 26.2 399 42 378 63L315 126 252 189 189 252 126 315 63 378C42 399 26.2 425.2 15.7 456.7 5.2 488.2 0 524.9 0 566.9 0 608.9 5.2 645.7 15.7 677.2 26.2 708.7 42 724.4 63 724.4 84 724.4 105 713.9 126 692.9 147 671.9 168 661.4 189 661.4 210 661.4 225.7 677.2 236.2 708.7 246.7 740.2 252 776.9 252 818.9 252 860.9 257.2 897.6 267.7 929.1 278.2 960.6 299.2 981.6 330.7 992.1 362.2 1002.6 393.7 1002.6 425.2 992.1 456.7 981.6 477.7 960.6 488.2 929.1L519.7 834.6C530.2 803.1 551.2 782.2 582.7 771.7 614.2 761.2 645.7 761.2 677.2 771.7 708.7 782.2 729.7 803.1 740.2 834.6L771.7 929.1C782.2 960.6 803.1 981.6 834.6 992.1 866.1 1002.6 897.6 1002.6 929.1 992.1 960.6 981.6 981.6 960.6 992.1 929.1 1002.6 897.6 1007.9 860.9 1007.9 818.9 1007.9 776.9 1013.1 740.2 1023.6 708.7 1034.1 677.2 1049.9 661.4 1070.9 661.4 1091.9 661.4 1112.9 671.9 1133.9 692.9 1154.9 713.9 1175.9 724.4 1196.9 724.4 1217.8 724.4 1233.6 708.7 1244.1 677.2 1254.6 645.7 1259.8 608.9 1259.8 566.9 1259.8 524.9 1254.6 488.2 1244.1 456.7 1233.6 425.2 1217.8 399 1196.9 378L1133.9 315 1070.9 252 1007.9 189 944.9 126 881.9 63C860.9 42 834.6 26.2 803.1 15.7 771.7 5.2 734.9 0 692.9 0ZM566.9 126H692.9C734.9 126 771.7 131.2 803.1 141.7 834.6 152.2 860.9 168 881.9 189L944.9 252 1007.9 315 1070.9 378C1091.9 399 1102.4 419.9 1102.4 440.9 1102.4 461.9 1086.6 477.7 1055.1 488.2L960.6 519.7C929.1 530.2 902.9 545.9 881.9 566.9 860.9 587.9 834.6 603.7 803.1 614.2 771.7 624.7 734.9 629.9 692.9 629.9H566.9C524.9 629.9 488.2 624.7 456.7 614.2 425.2 603.7 399 587.9 378 566.9 357 545.9 330.7 530.2 299.2 519.7L204.7 488.2C173.2 477.7 157.5 461.9 157.5 440.9 157.5 419.9 168 399 189 378L252 315 315 252 378 189C399 168 425.2 152.2 456.7 141.7 488.2 131.2 524.9 126 566.9 126ZM629.9 259.8C614.2 259.8 598.4 262.5 582.7 267.7 551.2 278.2 535.4 294 535.4 315 535.4 336 551.2 351.7 582.7 362.2 614.2 372.7 645.7 372.7 677.2 362.2 708.7 351.7 724.4 336 724.4 315 724.4 294 708.7 278.2 677.2 267.7 661.4 262.5 645.7 259.8 629.9 259.8Z",
|
||||
"width": 1260
|
||||
},
|
||||
"search": [
|
||||
"units"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "4fe8d91bf30522a9a76bab94836b2dd7",
|
||||
"css": "upgrade",
|
||||
"code": 59472,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M396.2 44Q421.4 18.9 459.1 6.3 496.9-6.3 534.6 6.3 572.3 18.9 597.5 44 622.6 69.2 647.8 94.3 673 119.5 698.1 144.7 723.3 169.8 748.4 195 773.6 220.1 798.7 245.3 823.9 270.4 849.1 295.6 874.2 320.8 899.4 345.9 924.5 371.1 949.7 396.2 974.8 421.4 987.4 459.1 1000 496.9 987.4 534.6 974.8 572.3 937.1 584.9 899.4 597.5 849.1 597.5 798.7 597.5 761 610.1 723.3 622.6 710.7 660.4 698.1 698.1 698.1 748.4 698.1 798.7 698.1 849.1 698.1 899.4 685.5 937.1 673 974.8 635.2 987.4 597.5 1000 547.2 1000 496.9 1000 446.5 1000 396.2 1000 358.5 987.4 320.8 974.8 308.2 937.1 295.6 899.4 295.6 849.1 295.6 798.7 295.6 748.4 295.6 698.1 283 660.4 270.4 622.6 232.7 610.1 195 597.5 144.7 597.5 94.3 597.5 56.6 584.9 18.9 572.3 6.3 534.6-6.3 496.9 6.3 459.1 18.9 421.4 44 396.2 69.2 371.1 94.3 345.9 119.5 320.8 144.7 295.6 169.8 270.4 195 245.3 220.1 220.1 245.3 195 270.4 169.8 295.6 144.7 320.8 119.5 345.9 94.3 371.1 69.2 396.2 44M459.1 886.8Q496.9 899.4 534.6 886.8 572.3 874.2 584.9 836.5 597.5 798.7 597.5 748.4 597.5 698.1 597.5 647.8 597.5 597.5 610.1 559.7 622.6 522 660.4 509.4 698.1 496.9 735.8 484.3 773.6 471.7 773.6 446.5 773.6 421.4 748.4 396.2 723.3 371.1 698.1 345.9 673 320.8 647.8 295.6 622.6 270.4 597.5 245.3 572.3 220.1 534.6 207.5 496.9 195 459.1 207.5 421.4 220.1 396.2 245.3 371.1 270.4 345.9 295.6 320.8 320.8 295.6 345.9 270.4 371.1 245.3 396.2 220.1 421.4 220.1 446.5 220.1 471.7 257.9 484.3 295.6 496.9 333.3 509.4 371.1 522 383.6 559.7 396.2 597.5 396.2 647.8 396.2 698.1 396.2 748.4 396.2 798.7 408.8 836.5 421.4 874.2 459.1 886.8",
|
||||
"width": 994
|
||||
},
|
||||
"search": [
|
||||
"upgrade"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "8cbea4558186fc7f4197ccba882dd534",
|
||||
"css": "save",
|
||||
"code": 59440,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M11.4 56.8Q22.7 22.7 56.8 11.4 90.9 0 136.4 0 181.8 0 227.3 0 272.7 0 318.2 0 363.6 0 409.1 0 454.5 0 500 0 545.5 0 590.9 0 636.4 0 681.8 0 727.3 0 761.4 11.4 795.5 22.7 818.2 45.5 840.9 68.2 863.6 90.9 886.4 113.6 897.7 147.7 909.1 181.8 909.1 227.3 909.1 272.7 909.1 318.2 909.1 363.6 909.1 409.1 909.1 454.5 909.1 500 909.1 545.5 909.1 590.9 909.1 636.4 909.1 681.8 909.1 727.3 909.1 772.7 909.1 818.2 909.1 863.6 909.1 909.1 897.7 943.2 886.4 977.3 852.3 988.6 818.2 1000 772.7 1000 727.3 1000 681.8 1000 636.4 1000 590.9 1000 545.5 1000 500 1000 454.5 1000 409.1 1000 363.6 1000 318.2 1000 272.7 1000 227.3 1000 181.8 1000 136.4 1000 90.9 1000 56.8 988.6 22.7 977.3 11.4 943.2 0 909.1 0 863.6 0 818.2 0 772.7 0 727.3 0 681.8 0 636.4 0 590.9 0 545.5 0 500 0 454.5 0 409.1 0 363.6 0 318.2 0 272.7 0 227.3 0 181.8 0 136.4 0 90.9 11.4 56.8M500 909.1Q545.5 909.1 579.5 897.7 613.6 886.4 625 852.3 636.4 818.2 636.4 772.7 636.4 727.3 625 693.2 613.6 659.1 579.5 647.7 545.5 636.4 500 636.4 454.5 636.4 409.1 636.4 363.6 636.4 329.5 647.7 295.5 659.1 284.1 693.2 272.7 727.3 272.7 772.7 272.7 818.2 284.1 852.3 295.5 886.4 329.5 897.7 363.6 909.1 409.1 909.1 454.5 909.1 500 909.1M738.6 852.3Q750 886.4 772.7 886.4 795.5 886.4 806.8 852.3 818.2 818.2 818.2 772.7 818.2 727.3 818.2 681.8 818.2 636.4 818.2 590.9 818.2 545.5 818.2 500 818.2 454.5 818.2 409.1 818.2 363.6 818.2 318.2 818.2 272.7 818.2 227.3 818.2 181.8 806.8 147.7 795.5 113.6 761.4 102.3 727.3 90.9 693.2 102.3 659.1 113.6 647.7 147.7 636.4 181.8 636.4 227.3 636.4 272.7 625 306.8 613.6 340.9 579.5 352.3 545.5 363.6 500 363.6 454.5 363.6 409.1 363.6 363.6 363.6 329.5 352.3 295.5 340.9 284.1 306.8 272.7 272.7 272.7 227.3 272.7 181.8 261.4 147.7 250 113.6 215.9 102.3 181.8 90.9 147.7 102.3 113.6 113.6 102.3 147.7 90.9 181.8 90.9 227.3 90.9 272.7 90.9 318.2 90.9 363.6 90.9 409.1 90.9 454.5 90.9 500 90.9 545.5 90.9 590.9 90.9 636.4 90.9 681.8 90.9 727.3 90.9 772.7 90.9 818.2 102.3 852.3 113.6 886.4 136.4 886.4 159.1 886.4 170.5 852.3 181.8 818.2 181.8 772.7 181.8 727.3 181.8 681.8 181.8 636.4 193.2 602.3 204.5 568.2 238.6 556.8 272.7 545.5 318.2 545.5 363.6 545.5 409.1 545.5 454.5 545.5 500 545.5 545.5 545.5 590.9 545.5 636.4 545.5 670.5 556.8 704.5 568.2 715.9 602.3 727.3 636.4 727.3 681.8 727.3 727.3 727.3 772.7 727.3 818.2 738.6 852.3M420.5 261.4Q454.5 272.7 488.6 261.4 522.7 250 534.1 215.9 545.5 181.8 534.1 147.7 522.7 113.6 488.6 102.3 454.5 90.9 420.5 102.3 386.4 113.6 375 147.7 363.6 181.8 375 215.9 386.4 250 420.5 261.4",
|
||||
"width": 909
|
||||
},
|
||||
"search": [
|
||||
"save"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "dd6df3b754e8d0dbf09d9b24088f0442",
|
||||
"css": "box",
|
||||
"code": 59422,
|
||||
"src": "iconic"
|
||||
},
|
||||
{
|
||||
"uid": "3b1a66b7efb81f4f8980f3edc369eeda",
|
||||
"css": "link",
|
||||
"code": 59444,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M208.3 0C180.6 0 156.2 3.5 135.4 10.4 114.6 17.4 97.2 27.8 83.3 41.7L41.7 83.3C27.8 97.2 17.4 114.6 10.4 135.4 3.5 156.3 0 180.6 0 208.3V291.7 375 458.3 541.7 625 708.3 791.7C0 819.4 3.5 843.8 10.4 864.6 17.4 885.4 27.8 902.8 41.7 916.7L83.3 958.3C97.2 972.2 114.6 982.6 135.4 989.6 156.2 996.5 180.6 1000 208.3 1000H291.7 375 458.3 541.7 625 708.3 791.7C819.4 1000 843.7 996.5 864.6 989.6 885.4 982.6 902.8 972.2 916.7 958.3L958.3 916.7C972.2 902.8 982.6 885.4 989.6 864.6 996.5 843.8 1000 819.4 1000 791.7 1000 763.9 996.5 739.6 989.6 718.8 982.6 697.9 972.2 680.6 958.3 666.7L916.7 625C902.8 611.1 888.9 604.2 875 604.2 861.1 604.2 850.7 614.6 843.7 635.4 836.8 656.3 833.3 680.6 833.3 708.3 833.3 736.1 829.9 760.4 822.9 781.3 816 802.1 802.1 816 781.2 822.9 760.4 829.9 736.1 833.3 708.3 833.3H625 541.7 458.3 375 291.7C263.9 833.3 239.6 829.9 218.7 822.9 197.9 816 184 802.1 177.1 781.3 170.1 760.4 166.7 736.1 166.7 708.3V625 541.7 458.3 375 291.7C166.7 263.9 170.1 239.6 177.1 218.8 184 197.9 197.9 184 218.7 177.1 239.6 170.1 263.9 166.7 291.7 166.7 319.4 166.7 343.7 163.2 364.6 156.3 385.4 149.3 395.8 138.9 395.8 125 395.8 111.1 388.9 97.2 375 83.3L333.3 41.7C319.4 27.8 302.1 17.4 281.2 10.4 260.4 3.5 236.1 0 208.3 0ZM625 0C597.2 0 572.9 3.5 552.1 10.4 531.2 17.4 520.8 27.8 520.8 41.7 520.8 55.6 527.8 69.4 541.7 83.3L583.3 125 625 166.7C638.9 180.6 645.8 194.4 645.8 208.3 645.8 222.2 638.9 236.1 625 250L583.3 291.7 541.7 333.3 500 375 458.3 416.7 416.7 458.3 375 500C361.1 513.9 350.7 531.3 343.7 552.1 336.8 572.9 336.8 593.8 343.7 614.6 350.7 635.4 364.6 649.3 385.4 656.3 406.2 663.2 427.1 663.2 447.9 656.3 468.7 649.3 486.1 638.9 500 625L541.7 583.3 583.3 541.7 625 500 666.7 458.3 708.3 416.7 750 375C763.9 361.1 777.8 354.2 791.7 354.2 805.6 354.2 819.4 361.1 833.3 375L875 416.7 916.7 458.3C930.6 472.2 944.4 479.2 958.3 479.2 972.2 479.2 982.6 468.8 989.6 447.9 996.5 427.1 1000 402.8 1000 375V291.7 208.3 125C1000 97.2 996.5 72.9 989.6 52.1 982.6 31.3 968.7 17.4 947.9 10.4 927.1 3.5 902.8 0 875 0H791.7 708.3Z",
|
||||
"width": 1000
|
||||
},
|
||||
"search": [
|
||||
"link"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "f08ba9f0e8f9b426a92efcaa59c1e932",
|
||||
"css": "reddit-alien",
|
||||
"code": 62081,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "4a0bb1ac004980bb3837560d4c9e8cee",
|
||||
"css": "github_",
|
||||
"code": 62216,
|
||||
"src": "brandico"
|
||||
},
|
||||
{
|
||||
"uid": "909f99f11c6b6551bf757b6e809f98f3",
|
||||
"css": "googleplay",
|
||||
"code": 59453,
|
||||
"src": "zocial"
|
||||
},
|
||||
{
|
||||
"uid": "aaf371ab44841e9aaffebd179d324ce4",
|
||||
"css": "android",
|
||||
"code": 59461,
|
||||
"src": "zocial"
|
||||
},
|
||||
{
|
||||
"uid": "b846892636bd74112998bb159bdddf27",
|
||||
"css": "trello",
|
||||
"code": 61825,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "ffe24157e3c63b2ad9526b4e779caefb",
|
||||
"css": "itchio",
|
||||
"code": 59468,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M303.8 44.3Q329.1 19 367.1 6.3 405.1-6.3 443 6.3 481 19 506.3 44.3 531.6 69.6 569.6 82.3 607.6 94.9 658.2 94.9 708.9 94.9 759.5 94.9 810.1 94.9 848.1 82.3 886.1 69.6 911.4 44.3 936.7 19 974.7 6.3 1012.7-6.3 1050.6 6.3 1088.6 19 1113.9 44.3 1139.2 69.6 1164.6 94.9 1189.9 120.3 1202.5 158.2 1215.2 196.2 1215.2 246.8 1215.2 297.5 1227.8 335.4 1240.5 373.4 1265.8 398.7 1291.1 424.1 1316.5 449.4 1341.8 474.7 1367.1 500 1392.4 525.3 1405.1 563.3 1417.7 601.3 1417.7 651.9 1417.7 702.5 1417.7 753.2 1417.7 803.8 1405.1 841.8 1392.4 879.7 1367.1 905.1 1341.8 930.4 1316.5 955.7 1291.1 981 1253.2 993.7 1215.2 1006.3 1177.2 993.7 1139.2 981 1113.9 955.7 1088.6 930.4 1063.3 905.1 1038 879.7 1012.7 854.4 987.3 829.1 949.4 816.5 911.4 803.8 860.8 803.8 810.1 803.8 759.5 803.8 708.9 803.8 658.2 803.8 607.6 803.8 557 803.8 506.3 803.8 468.4 816.5 430.4 829.1 405.1 854.4 379.7 879.7 354.4 905.1 329.1 930.4 303.8 955.7 278.5 981 240.5 993.7 202.5 1006.3 164.6 993.7 126.6 981 101.3 955.7 75.9 930.4 50.6 905.1 25.3 879.7 12.7 841.8 0 803.8 0 753.2 0 702.5 0 651.9 0 601.3 12.7 563.3 25.3 525.3 50.6 500 75.9 474.7 101.3 449.4 126.6 424.1 151.9 398.7 177.2 373.4 189.9 335.4 202.5 297.5 202.5 246.8 202.5 196.2 215.2 158.2 227.8 120.3 253.2 94.9 278.5 69.6 303.8 44.3M670.9 689.9Q708.9 702.5 746.8 689.9 784.8 677.2 797.5 639.2 810.1 601.3 822.8 563.3 835.4 525.3 873.4 512.7 911.4 500 949.4 487.3 987.3 474.7 987.3 449.4 987.3 424.1 962 398.7 936.7 373.4 911.4 348.1 886.1 322.8 860.8 297.5 835.4 272.2 810.1 246.8 784.8 221.5 746.8 208.9 708.9 196.2 670.9 208.9 632.9 221.5 607.6 246.8 582.3 272.2 557 297.5 531.6 322.8 506.3 348.1 481 373.4 455.7 398.7 430.4 424.1 430.4 449.4 430.4 474.7 468.4 487.3 506.3 500 544.3 512.7 582.3 525.3 594.9 563.3 607.6 601.3 620.3 639.2 632.9 677.2 670.9 689.9",
|
||||
"width": 1418
|
||||
},
|
||||
"search": [
|
||||
"itchio"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "4fa999c5ba68a4ec62d4abe5e8e51076",
|
||||
"css": "line",
|
||||
"code": 59473,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M836.8 36.8Q857.9 15.8 889.5 5.3 921.1-5.3 952.6 5.3 984.2 15.8 994.7 47.4 1005.3 78.9 994.7 110.5 984.2 142.1 963.2 163.2 942.1 184.2 921.1 205.3 900 226.3 878.9 247.4 857.9 268.4 836.8 289.5 815.8 310.5 794.7 331.6 773.7 352.6 752.6 373.7 731.6 394.7 710.5 415.8 689.5 436.8 668.4 457.9 647.4 478.9 626.3 500 605.3 521.1 584.2 542.1 563.2 563.2 542.1 584.2 521.1 605.3 500 626.3 478.9 647.4 457.9 668.4 436.8 689.5 415.8 710.5 394.7 731.6 373.7 752.6 352.6 773.7 331.6 794.7 310.5 815.8 289.5 836.8 268.4 857.9 247.4 878.9 226.3 900 205.3 921.1 184.2 942.1 163.2 963.2 142.1 984.2 110.5 994.7 78.9 1005.3 47.4 994.7 15.8 984.2 5.3 952.6-5.3 921.1 5.3 889.5 15.8 857.9 36.8 836.8 57.9 815.8 78.9 794.7 100 773.7 121.1 752.6 142.1 731.6 163.2 710.5 184.2 689.5 205.3 668.4 226.3 647.4 247.4 626.3 268.4 605.3 289.5 584.2 310.5 563.2 331.6 542.1 352.6 521.1 373.7 500 394.7 478.9 415.8 457.9 436.8 436.8 457.9 415.8 478.9 394.7 500 373.7 521.1 352.6 542.1 331.6 563.2 310.5 584.2 289.5 605.3 268.4 626.3 247.4 647.4 226.3 668.4 205.3 689.5 184.2 710.5 163.2 731.6 142.1 752.6 121.1 773.7 100 794.7 78.9 815.8 57.9 836.8 36.8",
|
||||
"width": 1000
|
||||
},
|
||||
"search": [
|
||||
"line"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "d4d06d27c6e566237097413b3695b4aa",
|
||||
"css": "spray",
|
||||
"code": 59474,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M18.2 0C6.1 0 0 6.1 0 18.2 0 30.3 6.1 36.4 18.2 36.4 30.3 36.4 36.4 30.3 36.4 18.2 36.4 6.1 30.3 0 18.2 0ZM454.5 54.5C430.3 54.5 409.1 57.6 390.9 63.6 372.7 69.7 363.6 78.8 363.6 90.9 363.6 103 369.7 115.2 381.8 127.3 393.9 139.4 400 151.5 400 163.6 400 175.8 393.9 187.9 381.8 200L345.5 236.4 309.1 272.7 272.7 309.1 236.4 345.5C224.2 357.6 218.2 369.7 218.2 381.8 218.2 393.9 227.3 403 245.5 409.1 263.6 415.2 284.8 418.2 309.1 418.2H381.8 454.5 527.3 600 672.7C697 418.2 718.2 415.2 736.4 409.1 754.5 403 763.6 393.9 763.6 381.8 763.6 369.7 757.6 357.6 745.5 345.5L709.1 309.1 672.7 272.7C660.6 260.6 651.5 245.5 645.5 227.3 639.4 209.1 636.4 187.9 636.4 163.6 636.4 139.4 633.3 118.2 627.3 100 621.2 81.8 609.1 69.7 590.9 63.6 572.7 57.6 551.5 54.5 527.3 54.5ZM163.6 72.7C151.5 72.7 145.5 78.8 145.5 90.9 145.5 103 151.5 115.2 163.6 127.3 175.8 139.4 181.8 151.5 181.8 163.6 181.8 175.8 175.8 187.9 163.6 200 151.5 212.1 145.5 224.2 145.5 236.4 145.5 248.5 151.5 254.5 163.6 254.5 175.8 254.5 187.9 248.5 200 236.4L236.4 200C248.5 187.9 254.5 175.8 254.5 163.6 254.5 151.5 248.5 139.4 236.4 127.3L200 90.9C187.9 78.8 175.8 72.7 163.6 72.7ZM18.2 145.5C6.1 145.5 0 151.5 0 163.6 0 175.8 6.1 181.8 18.2 181.8 30.3 181.8 36.4 175.8 36.4 163.6 36.4 151.5 30.3 145.5 18.2 145.5ZM18.2 290.9C6.1 290.9 0 297 0 309.1 0 321.2 6.1 327.3 18.2 327.3 30.3 327.3 36.4 321.2 36.4 309.1 36.4 297 30.3 290.9 18.2 290.9ZM309.1 490.9C284.8 490.9 263.6 493.9 245.5 500 227.3 506.1 215.2 518.2 209.1 536.4 203 554.5 200 575.8 200 600V672.7 745.5C200 769.7 203 790.9 209.1 809.1 215.2 827.3 227.3 839.4 245.5 845.5 263.6 851.5 284.8 854.5 309.1 854.5H381.8 454.5 527.3 600 672.7C697 854.5 718.2 851.5 736.4 845.5 754.5 839.4 766.7 827.3 772.7 809.1 778.8 790.9 781.8 769.7 781.8 745.5V672.7 600C781.8 575.8 778.8 554.5 772.7 536.4 766.7 518.2 754.5 506.1 736.4 500 718.2 493.9 697 490.9 672.7 490.9H600 527.3 454.5 381.8ZM309.1 927.3C284.8 927.3 263.6 930.3 245.5 936.4 227.3 942.4 218.2 951.5 218.2 963.6 218.2 975.8 227.3 984.8 245.5 990.9 263.6 997 284.8 1000 309.1 1000H381.8 454.5 527.3 600 672.7C697 1000 718.2 997 736.4 990.9 754.5 984.8 763.6 975.8 763.6 963.6 763.6 951.5 754.5 942.4 736.4 936.4 718.2 930.3 697 927.3 672.7 927.3H600 527.3 454.5 381.8Z",
|
||||
"width": 782
|
||||
},
|
||||
"search": [
|
||||
"spray"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "eab433e0de7fbc7ce341bca83f184abb",
|
||||
"css": "fill",
|
||||
"code": 59457,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M426 35.9Q443.9 17.9 470.9 9 497.8 0 533.6 0 569.5 0 596.4 9 623.3 17.9 641.3 35.9 659.2 53.8 677.1 71.7 695.1 89.7 704 116.6 713 143.5 713 179.4 713 215.2 704 242.2 695.1 269.1 677.1 287 659.2 304.9 659.2 322.9 659.2 340.8 686.1 349.8 713 358.7 748.9 358.7 784.8 358.7 811.7 367.7 838.6 376.7 856.5 394.6 874.4 412.6 892.4 430.5 910.3 448.4 928.3 466.4 946.2 484.3 964.1 502.2 982.1 520.2 991 547.1 1000 574 1000 609.9 1000 645.7 1000 681.6 1000 717.5 1000 753.4 1000 789.2 1000 825.1 1000 861 991 887.9 982.1 914.8 964.1 914.8 946.2 914.8 937.2 887.9 928.3 861 928.3 825.1 928.3 789.2 919.3 762.3 910.3 735.4 892.4 717.5 874.4 699.6 865.5 672.6 856.5 645.7 847.5 618.8 838.6 591.9 820.6 591.9 802.7 591.9 784.8 609.9 766.8 627.8 748.9 645.7 730.9 663.7 713 681.6 695.1 699.6 677.1 717.5 659.2 735.4 641.3 753.4 623.3 771.3 605.4 789.2 587.4 807.2 569.5 825.1 551.6 843 533.6 861 515.7 878.9 497.8 896.9 479.8 914.8 461.9 932.7 443.9 950.7 426 968.6 408.1 986.5 381.2 995.5 354.3 1004.5 327.4 995.5 300.4 986.5 282.5 968.6 264.6 950.7 246.6 932.7 228.7 914.8 210.8 896.9 192.8 878.9 174.9 861 157 843 139 825.1 121.1 807.2 103.1 789.2 85.2 771.3 67.3 753.4 49.3 735.4 31.4 717.5 13.5 699.6 4.5 672.6-4.5 645.7 4.5 618.8 13.5 591.9 31.4 574 49.3 556.1 67.3 538.1 85.2 520.2 103.1 502.2 121.1 484.3 139 466.4 157 448.4 174.9 430.5 192.8 412.6 210.8 394.6 228.7 376.7 246.6 376.7 264.6 376.7 273.5 403.6 282.5 430.5 273.5 457.4 264.6 484.3 246.6 502.2 228.7 520.2 210.8 538.1 192.8 556.1 174.9 574 157 591.9 148 618.8 139 645.7 148 672.6 157 699.6 174.9 717.5 192.8 735.4 210.8 753.4 228.7 771.3 246.6 789.2 264.6 807.2 282.5 825.1 300.4 843 327.4 852 354.3 861 381.2 852 408.1 843 426 825.1 443.9 807.2 461.9 789.2 479.8 771.3 497.8 753.4 515.7 735.4 533.6 717.5 551.6 699.6 569.5 681.6 587.4 663.7 605.4 645.7 623.3 627.8 641.3 609.9 659.2 591.9 677.1 574 695.1 556.1 695.1 538.1 695.1 520.2 677.1 502.2 659.2 484.3 641.3 466.4 623.3 448.4 605.4 430.5 587.4 412.6 569.5 394.6 551.6 376.7 533.6 358.7 515.7 340.8 506.7 313.9 497.8 287 506.7 260.1 515.7 233.2 533.6 233.2 551.6 233.2 569.5 251.1 587.4 269.1 605.4 269.1 623.3 269.1 632.3 242.2 641.3 215.2 641.3 179.4 641.3 143.5 632.3 116.6 623.3 89.7 596.4 80.7 569.5 71.7 533.6 71.7 497.8 71.7 470.9 80.7 443.9 89.7 435 116.6 426 143.5 426 179.4 426 215.2 426 251.1 426 287 426 322.9 426 358.7 426 394.6 426 430.5 417 457.4 408.1 484.3 390.1 484.3 372.2 484.3 363.2 457.4 354.3 430.5 354.3 394.6 354.3 358.7 354.3 322.9 354.3 287 354.3 251.1 354.3 215.2 354.3 179.4 354.3 143.5 363.2 116.6 372.2 89.7 390.1 71.7 408.1 53.8 426 35.9",
|
||||
"width": 1000
|
||||
},
|
||||
"search": [
|
||||
"fill"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "d9d4c18716c0d8b23485258e35d9e3a9",
|
||||
"css": "paste",
|
||||
"code": 59419,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M392.9 0C369 0 348.2 3 330.4 8.9 312.5 14.9 297.6 23.8 285.7 35.7 273.8 47.6 258.9 56.5 241.1 62.5 223.2 68.5 202.4 71.4 178.6 71.4 154.8 71.4 133.9 74.4 116.1 80.4 98.2 86.3 83.3 95.2 71.4 107.1L35.7 142.9C23.8 154.8 14.9 169.6 8.9 187.5 3 205.4 0 226.2 0 250V321.4 392.9 464.3 535.7 607.1 678.6 750 821.4C0 845.2 3 866.1 8.9 883.9 14.9 901.8 23.8 916.7 35.7 928.6L71.4 964.3C83.3 976.2 98.2 985.1 116.1 991.1 133.9 997 154.8 1000 178.6 1000H250 321.4 392.9 464.3 535.7 607.1 678.6C702.4 1000 723.2 997 741.1 991.1 758.9 985.1 773.8 976.2 785.7 964.3L821.4 928.6C833.3 916.7 842.3 901.8 848.2 883.9 854.2 866.1 857.1 845.2 857.1 821.4V750 678.6 607.1 535.7 464.3 392.9 321.4 250C857.1 226.2 854.2 205.4 848.2 187.5 842.3 169.6 833.3 154.8 821.4 142.9L785.7 107.1C773.8 95.2 758.9 86.3 741.1 80.4 723.2 74.4 702.4 71.4 678.6 71.4 654.8 71.4 633.9 68.5 616.1 62.5 598.2 56.5 583.3 47.6 571.4 35.7 559.5 23.8 544.6 14.9 526.8 8.9 508.9 3 488.1 0 464.3 0ZM428.6 75.9C437.5 75.9 446.4 77.4 455.4 80.4 473.2 86.3 482.1 95.2 482.1 107.1 482.1 119 473.2 128 455.4 133.9 437.5 139.9 419.6 139.9 401.8 133.9 383.9 128 375 119 375 107.1 375 95.2 383.9 86.3 401.8 80.4 410.7 77.4 419.6 75.9 428.6 75.9ZM214.3 218.8C223.2 218.8 232.1 220.2 241.1 223.2 258.9 229.2 273.8 238.1 285.7 250 297.6 261.9 312.5 270.8 330.4 276.8 348.2 282.7 369 285.7 392.9 285.7H464.3C488.1 285.7 508.9 282.7 526.8 276.8 544.6 270.8 559.5 261.9 571.4 250 583.3 238.1 598.2 229.2 616.1 223.2 633.9 217.3 651.8 217.3 669.6 223.2 687.5 229.2 699.4 241.1 705.4 258.9 711.3 276.8 714.3 297.6 714.3 321.4V392.9 464.3 535.7 607.1 678.6 750C714.3 773.8 711.3 794.6 705.4 812.5 699.4 830.4 687.5 842.3 669.6 848.2 651.8 854.2 631 857.1 607.1 857.1H535.7 464.3 392.9 321.4 250C226.2 857.1 205.4 854.2 187.5 848.2 169.6 842.3 157.7 830.4 151.8 812.5 145.8 794.6 142.9 773.8 142.9 750V678.6 607.1 535.7 464.3 392.9 321.4C142.9 297.6 145.8 276.8 151.8 258.9 157.7 241.1 169.6 229.2 187.5 223.2 196.4 220.2 205.4 218.8 214.3 218.8ZM321.4 428.6C297.6 428.6 276.8 431.5 258.9 437.5 241.1 443.5 232.1 452.4 232.1 464.3 232.1 476.2 241.1 485.1 258.9 491.1 276.8 497 297.6 500 321.4 500H392.9 464.3 535.7C559.5 500 580.4 497 598.2 491.1 616.1 485.1 625 476.2 625 464.3 625 452.4 616.1 443.5 598.2 437.5 580.4 431.5 559.5 428.6 535.7 428.6H464.3 392.9ZM321.4 571.4C297.6 571.4 276.8 574.4 258.9 580.4 241.1 586.3 232.1 595.2 232.1 607.1 232.1 619 241.1 628 258.9 633.9 276.8 639.9 297.6 642.9 321.4 642.9H392.9 464.3 535.7C559.5 642.9 580.4 639.9 598.2 633.9 616.1 628 625 619 625 607.1 625 595.2 616.1 586.3 598.2 580.4 580.4 574.4 559.5 571.4 535.7 571.4H464.3 392.9ZM321.4 714.3C297.6 714.3 276.8 717.3 258.9 723.2 241.1 729.2 232.1 738.1 232.1 750 232.1 761.9 241.1 770.8 258.9 776.8 276.8 782.7 297.6 785.7 321.4 785.7H392.9 464.3 535.7C559.5 785.7 580.4 782.7 598.2 776.8 616.1 770.8 625 761.9 625 750 625 738.1 616.1 729.2 598.2 723.2 580.4 717.3 559.5 714.3 535.7 714.3H464.3 392.9Z",
|
||||
"width": 857
|
||||
},
|
||||
"search": [
|
||||
"paste"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "a69daaa147a4c2bee4b985727c89e1ff",
|
||||
"css": "book",
|
||||
"code": 59423,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M227.3 0C197 0 170.5 3.8 147.7 11.4 125 18.9 106.1 30.3 90.9 45.5L45.5 90.9C30.3 106.1 18.9 125 11.4 147.7 3.8 170.5 0 197 0 227.3V318.2 409.1 500 590.9 681.8 772.7 863.6C0 893.9 3.8 920.5 11.4 943.2 18.9 965.9 34.1 981.1 56.8 988.6 79.5 996.2 106.1 1000 136.4 1000H227.3 318.2 409.1 500 590.9 681.8C712.1 1000 738.6 996.2 761.4 988.6 784.1 981.1 803 969.7 818.2 954.5L863.6 909.1C878.8 893.9 890.2 875 897.7 852.3 905.3 829.5 909.1 803 909.1 772.7V681.8 590.9 500 409.1 318.2 227.3 136.4C909.1 106.1 905.3 79.5 897.7 56.8 890.2 34.1 875 18.9 852.3 11.4 829.5 3.8 803 0 772.7 0H681.8 590.9 500 409.1 318.2ZM318.2 90.9H409.1 500 590.9 681.8C712.1 90.9 738.6 94.7 761.4 102.3 784.1 109.8 799.2 125 806.8 147.7 814.4 170.5 818.2 197 818.2 227.3V318.2 409.1 500 590.9 681.8C818.2 712.1 814.4 738.6 806.8 761.4 799.2 784.1 784.1 799.2 761.4 806.8 738.6 814.4 712.1 818.2 681.8 818.2H590.9 500 409.1 318.2C287.9 818.2 261.4 814.4 238.6 806.8 215.9 799.2 200.8 784.1 193.2 761.4 185.6 738.6 181.8 712.1 181.8 681.8V590.9 500 409.1 318.2 227.3C181.8 197 185.6 170.5 193.2 147.7 200.8 125 215.9 109.8 238.6 102.3 261.4 94.7 287.9 90.9 318.2 90.9ZM409.1 181.8C378.8 181.8 352.3 185.6 329.5 193.2 306.8 200.8 291.7 215.9 284.1 238.6 276.5 261.4 276.5 284.1 284.1 306.8 291.7 329.5 306.8 344.7 329.5 352.3 352.3 359.8 378.8 363.6 409.1 363.6H500 590.9C621.2 363.6 647.7 359.8 670.5 352.3 693.2 344.7 708.3 329.5 715.9 306.8 723.5 284.1 723.5 261.4 715.9 238.6 708.3 215.9 693.2 200.8 670.5 193.2 647.7 185.6 621.2 181.8 590.9 181.8H500ZM409.1 454.5C378.8 454.5 352.3 458.3 329.5 465.9 306.8 473.5 295.5 484.8 295.5 500 295.5 515.2 306.8 526.5 329.5 534.1 352.3 541.7 378.8 545.5 409.1 545.5H500 590.9C621.2 545.5 647.7 541.7 670.5 534.1 693.2 526.5 704.5 515.2 704.5 500 704.5 484.8 693.2 473.5 670.5 465.9 647.7 458.3 621.2 454.5 590.9 454.5H500ZM409.1 636.4C378.8 636.4 352.3 640.2 329.5 647.7 306.8 655.3 295.5 666.7 295.5 681.8 295.5 697 306.8 708.3 329.5 715.9 352.3 723.5 378.8 727.3 409.1 727.3H500 590.9C621.2 727.3 647.7 723.5 670.5 715.9 693.2 708.3 704.5 697 704.5 681.8 704.5 666.7 693.2 655.3 670.5 647.7 647.7 640.2 621.2 636.4 590.9 636.4H500Z",
|
||||
"width": 909
|
||||
},
|
||||
"search": [
|
||||
"book"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "ceb9cde822ac23f35c3bd67a8d0c4fac",
|
||||
"css": "host",
|
||||
"code": 59437,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M11.4 56.8Q22.7 22.7 56.8 11.4 90.9 0 136.4 0 181.8 0 227.3 0 272.7 0 318.2 0 363.6 0 409.1 0 454.5 0 500 0 545.5 0 590.9 0 636.4 0 681.8 0 727.3 0 772.7 0 818.2 0 863.6 0 909.1 0 954.5 0 1000 0 1034.1 11.4 1068.2 22.7 1079.5 56.8 1090.9 90.9 1090.9 136.4 1090.9 181.8 1079.5 215.9 1068.2 250 1034.1 261.4 1000 272.7 954.5 272.7 909.1 272.7 863.6 272.7 818.2 272.7 772.7 272.7 727.3 272.7 681.8 272.7 636.4 272.7 590.9 272.7 545.5 272.7 500 272.7 454.5 272.7 409.1 272.7 363.6 272.7 318.2 272.7 272.7 272.7 227.3 272.7 181.8 272.7 136.4 272.7 90.9 272.7 56.8 261.4 22.7 250 11.4 215.9 0 181.8 0 136.4 0 90.9 11.4 56.8M409.1 181.8Q454.5 181.8 488.6 170.5 522.7 159.1 522.7 136.4 522.7 113.6 488.6 102.3 454.5 90.9 409.1 90.9 363.6 90.9 318.2 90.9 272.7 90.9 227.3 90.9 181.8 90.9 147.7 102.3 113.6 113.6 113.6 136.4 113.6 159.1 147.7 170.5 181.8 181.8 227.3 181.8 272.7 181.8 318.2 181.8 363.6 181.8 409.1 181.8M840.9 136.4Q840.9 159.1 863.6 159.1 886.4 159.1 886.4 136.4 886.4 113.6 863.6 113.6 840.9 113.6 840.9 136.4M11.4 420.5Q22.7 386.4 56.8 375 90.9 363.6 136.4 363.6 181.8 363.6 227.3 363.6 272.7 363.6 318.2 363.6 363.6 363.6 409.1 363.6 454.5 363.6 500 363.6 545.5 363.6 590.9 363.6 636.4 363.6 681.8 363.6 727.3 363.6 772.7 363.6 818.2 363.6 863.6 363.6 909.1 363.6 954.5 363.6 1000 363.6 1034.1 375 1068.2 386.4 1079.5 420.5 1090.9 454.5 1090.9 500 1090.9 545.5 1079.5 579.5 1068.2 613.6 1034.1 625 1000 636.4 954.5 636.4 909.1 636.4 863.6 636.4 818.2 636.4 772.7 636.4 727.3 636.4 681.8 636.4 636.4 636.4 590.9 636.4 545.5 636.4 500 636.4 454.5 636.4 409.1 636.4 363.6 636.4 318.2 636.4 272.7 636.4 227.3 636.4 181.8 636.4 136.4 636.4 90.9 636.4 56.8 625 22.7 613.6 11.4 579.5 0 545.5 0 500 0 454.5 11.4 420.5M409.1 545.5Q454.5 545.5 488.6 534.1 522.7 522.7 522.7 500 522.7 477.3 488.6 465.9 454.5 454.5 409.1 454.5 363.6 454.5 318.2 454.5 272.7 454.5 227.3 454.5 181.8 454.5 147.7 465.9 113.6 477.3 113.6 500 113.6 522.7 147.7 534.1 181.8 545.5 227.3 545.5 272.7 545.5 318.2 545.5 363.6 545.5 409.1 545.5M840.9 500Q840.9 522.7 863.6 522.7 886.4 522.7 886.4 500 886.4 477.3 863.6 477.3 840.9 477.3 840.9 500M11.4 784.1Q22.7 750 56.8 738.6 90.9 727.3 136.4 727.3 181.8 727.3 227.3 727.3 272.7 727.3 318.2 727.3 363.6 727.3 409.1 727.3 454.5 727.3 500 727.3 545.5 727.3 590.9 727.3 636.4 727.3 681.8 727.3 727.3 727.3 772.7 727.3 818.2 727.3 863.6 727.3 909.1 727.3 954.5 727.3 1000 727.3 1034.1 738.6 1068.2 750 1079.5 784.1 1090.9 818.2 1090.9 863.6 1090.9 909.1 1079.5 943.2 1068.2 977.3 1034.1 988.6 1000 1000 954.5 1000 909.1 1000 863.6 1000 818.2 1000 772.7 1000 727.3 1000 681.8 1000 636.4 1000 590.9 1000 545.5 1000 500 1000 454.5 1000 409.1 1000 363.6 1000 318.2 1000 272.7 1000 227.3 1000 181.8 1000 136.4 1000 90.9 1000 56.8 988.6 22.7 977.3 11.4 943.2 0 909.1 0 863.6 0 818.2 11.4 784.1M409.1 909.1Q454.5 909.1 488.6 897.7 522.7 886.4 522.7 863.6 522.7 840.9 488.6 829.5 454.5 818.2 409.1 818.2 363.6 818.2 318.2 818.2 272.7 818.2 227.3 818.2 181.8 818.2 147.7 829.5 113.6 840.9 113.6 863.6 113.6 886.4 147.7 897.7 181.8 909.1 227.3 909.1 272.7 909.1 318.2 909.1 363.6 909.1 409.1 909.1M840.9 863.6Q840.9 886.4 863.6 886.4 886.4 886.4 886.4 863.6 886.4 840.9 863.6 840.9 840.9 840.9 840.9 863.6",
|
||||
"width": 1091
|
||||
},
|
||||
"search": [
|
||||
"host"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "496a07ca2bfa65f351c24b819fae0362",
|
||||
"css": "exit",
|
||||
"code": 59416,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M208.3 0C180.6 0 156.2 3.5 135.4 10.4 114.6 17.4 97.2 27.8 83.3 41.7L41.7 83.3C27.8 97.2 17.4 114.6 10.4 135.4 3.5 156.2 0 180.6 0 208.3V291.7 375 458.3 541.7 625 708.3 791.7C0 819.4 3.5 843.7 10.4 864.6 17.4 885.4 27.8 902.8 41.7 916.7L83.3 958.3C97.2 972.2 114.6 982.6 135.4 989.6 156.2 996.5 180.6 1000 208.3 1000H291.7 375 458.3 541.7 625 708.3C736.1 1000 760.4 996.5 781.3 989.6 802.1 982.6 819.4 972.2 833.3 958.3L875 916.7C888.9 902.8 895.8 888.9 895.8 875 895.8 861.1 885.4 850.7 864.6 843.7 843.8 836.8 819.4 833.3 791.7 833.3H708.3 625 541.7 458.3 375 291.7C263.9 833.3 239.6 829.9 218.7 822.9 197.9 816 184 802.1 177.1 781.2 170.1 760.4 166.7 736.1 166.7 708.3V625 541.7 458.3 375 291.7C166.7 263.9 170.1 239.6 177.1 218.7 184 197.9 197.9 184 218.7 177.1 239.6 170.1 263.9 166.7 291.7 166.7H375 458.3 541.7 625 708.3 791.7C819.4 166.7 843.8 163.2 864.6 156.2 885.4 149.3 895.8 138.9 895.8 125 895.8 111.1 888.9 97.2 875 83.3L833.3 41.7C819.4 27.8 802.1 17.4 781.3 10.4 760.4 3.5 736.1 0 708.3 0H625 541.7 458.3 375 291.7ZM791.7 270.8C777.8 270.8 767.4 281.2 760.4 302.1L739.6 364.6C732.6 385.4 718.8 399.3 697.9 406.2 677.1 413.2 652.8 416.7 625 416.7H541.7 458.3C430.6 416.7 406.2 420.1 385.4 427.1 364.6 434 350.7 447.9 343.7 468.7 336.8 489.6 336.8 510.4 343.7 531.2 350.7 552.1 364.6 566 385.4 572.9 406.2 579.9 430.6 583.3 458.3 583.3H541.7 625C652.8 583.3 677.1 586.8 697.9 593.7 718.8 600.7 732.6 614.6 739.6 635.4L760.4 697.9C767.4 718.7 777.8 729.2 791.7 729.2 805.6 729.2 819.4 722.2 833.3 708.3L875 666.7 916.7 625 958.3 583.3C972.2 569.4 982.6 552.1 989.6 531.2 996.5 510.4 996.5 489.6 989.6 468.7 982.6 447.9 972.2 430.6 958.3 416.7L916.7 375 875 333.3 833.3 291.7C819.4 277.8 805.6 270.8 791.7 270.8Z",
|
||||
"width": 995
|
||||
},
|
||||
"search": [
|
||||
"exit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "06a8fb7f89285292492baf4e4f3657be",
|
||||
"css": "terrain",
|
||||
"code": 59475,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M340.4 21.3Q361.7 0 383 0 404.3 0 425.5 21.3 446.8 42.6 468.1 63.8 489.4 85.1 510.6 106.4 531.9 127.7 553.2 148.9 574.5 170.2 595.7 191.5 617 212.8 638.3 212.8 659.6 212.8 680.9 191.5 702.1 170.2 723.4 170.2 744.7 170.2 766 191.5 787.2 212.8 808.5 234 829.8 255.3 851.1 276.6 872.3 297.9 893.6 319.1 914.9 340.4 936.2 361.7 957.4 383 978.7 404.3 1000 425.5 1010.6 457.4 1021.3 489.4 1021.3 531.9 1021.3 574.5 1021.3 617 1021.3 659.6 1021.3 702.1 1021.3 744.7 1021.3 787.2 1021.3 829.8 1021.3 872.3 1021.3 914.9 1010.6 946.8 1000 978.7 968.1 989.4 936.2 1000 893.6 1000 851.1 1000 808.5 1000 766 1000 723.4 1000 680.9 1000 638.3 1000 595.7 1000 553.2 1000 510.6 1000 468.1 1000 425.5 1000 383 1000 340.4 1000 297.9 1000 255.3 1000 212.8 1000 170.2 1000 127.7 1000 85.1 1000 53.2 989.4 21.3 978.7 10.6 946.8 0 914.9 0 872.3 0 829.8 0 787.2 0 744.7 0 702.1 0 659.6 0 617 0 574.5 0 531.9 0 489.4 0 446.8 0 404.3 10.6 372.3 21.3 340.4 42.6 319.1 63.8 297.9 85.1 276.6 106.4 255.3 127.7 234 148.9 212.8 170.2 191.5 191.5 170.2 212.8 148.9 234 127.7 255.3 106.4 276.6 85.1 297.9 63.8 319.1 42.6 340.4 21.3M808.5 914.9Q851.1 914.9 883 904.3 914.9 893.6 925.5 861.7 936.2 829.8 936.2 787.2 936.2 744.7 936.2 702.1 936.2 659.6 936.2 617 936.2 574.5 925.5 542.6 914.9 510.6 893.6 489.4 872.3 468.1 851.1 446.8 829.8 425.5 808.5 404.3 787.2 383 766 361.7 744.7 340.4 723.4 340.4 702.1 340.4 680.9 361.7 659.6 383 638.3 383 617 383 595.7 361.7 574.5 340.4 553.2 319.1 531.9 297.9 510.6 276.6 489.4 255.3 468.1 234 446.8 212.8 425.5 191.5 404.3 170.2 383 170.2 361.7 170.2 340.4 191.5 319.1 212.8 297.9 234 276.6 255.3 255.3 276.6 234 297.9 212.8 319.1 191.5 340.4 170.2 361.7 148.9 383 127.7 404.3 106.4 425.5 95.7 457.4 85.1 489.4 85.1 531.9 85.1 574.5 85.1 617 85.1 659.6 85.1 702.1 85.1 744.7 85.1 787.2 85.1 829.8 95.7 861.7 106.4 893.6 138.3 904.3 170.2 914.9 212.8 914.9 255.3 914.9 297.9 914.9 340.4 914.9 383 914.9 425.5 914.9 468.1 914.9 510.6 914.9 553.2 914.9 595.7 914.9 638.3 914.9 680.9 914.9 723.4 914.9 766 914.9 808.5 914.9",
|
||||
"width": 1021
|
||||
},
|
||||
"search": [
|
||||
"terrain"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "c97c45befeea2eaee193c77fc6c6fd64",
|
||||
"css": "trash",
|
||||
"code": 59413,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M260.4 52.1Q270.8 20.8 302.1 10.4 333.3 0 375 0 416.7 0 458.3 0 500 0 531.3 10.4 562.5 20.8 572.9 52.1 583.3 83.3 593.8 114.6 604.2 145.8 635.4 156.3 666.7 166.7 708.3 166.7 750 166.7 781.3 177.1 812.5 187.5 822.9 218.8 833.3 250 833.3 291.7 833.3 333.3 833.3 375 833.3 416.7 822.9 447.9 812.5 479.2 791.7 500 770.8 520.8 760.4 552.1 750 583.3 750 625 750 666.7 750 708.3 750 750 750 791.7 750 833.3 750 875 750 916.7 739.6 947.9 729.2 979.2 697.9 989.6 666.7 1000 625 1000 583.3 1000 541.7 1000 500 1000 458.3 1000 416.7 1000 375 1000 333.3 1000 291.7 1000 250 1000 208.3 1000 166.7 1000 135.4 989.6 104.2 979.2 93.8 947.9 83.3 916.7 83.3 875 83.3 833.3 83.3 791.7 83.3 750 83.3 708.3 83.3 666.7 83.3 625 83.3 583.3 72.9 552.1 62.5 520.8 41.7 500 20.8 479.2 10.4 447.9 0 416.7 0 375 0 333.3 0 291.7 0 250 10.4 218.8 20.8 187.5 52.1 177.1 83.3 166.7 125 166.7 166.7 166.7 197.9 156.3 229.2 145.8 239.6 114.6 250 83.3 260.4 52.1M177.1 864.6Q187.5 895.8 208.3 895.8 229.2 895.8 239.6 864.6 250 833.3 250 791.7 250 750 250 708.3 250 666.7 250 625 250 583.3 239.6 552.1 229.2 520.8 208.3 520.8 187.5 520.8 177.1 552.1 166.7 583.3 166.7 625 166.7 666.7 166.7 708.3 166.7 750 166.7 791.7 166.7 833.3 177.1 864.6M385.4 906.3Q416.7 916.7 447.9 906.3 479.2 895.8 489.6 864.6 500 833.3 500 791.7 500 750 500 708.3 500 666.7 500 625 500 583.3 489.6 552.1 479.2 520.8 447.9 510.4 416.7 500 385.4 510.4 354.2 520.8 343.8 552.1 333.3 583.3 333.3 625 333.3 666.7 333.3 708.3 333.3 750 333.3 791.7 333.3 833.3 343.8 864.6 354.2 895.8 385.4 906.3M593.8 864.6Q604.2 895.8 625 895.8 645.8 895.8 656.3 864.6 666.7 833.3 666.7 791.7 666.7 750 666.7 708.3 666.7 666.7 666.7 625 666.7 583.3 656.3 552.1 645.8 520.8 625 520.8 604.2 520.8 593.8 552.1 583.3 583.3 583.3 625 583.3 666.7 583.3 708.3 583.3 750 583.3 791.7 583.3 833.3 593.8 864.6M625 416.7Q666.7 416.7 697.9 406.3 729.2 395.8 739.6 364.6 750 333.3 739.6 302.1 729.2 270.8 697.9 260.4 666.7 250 625 250 583.3 250 541.7 250 500 250 458.3 250 416.7 250 375 250 333.3 250 291.7 250 250 250 208.3 250 166.7 250 135.4 260.4 104.2 270.8 93.8 302.1 83.3 333.3 93.8 364.6 104.2 395.8 135.4 406.3 166.7 416.7 208.3 416.7 250 416.7 291.7 416.7 333.3 416.7 375 416.7 416.7 416.7 458.3 416.7 500 416.7 541.7 416.7 583.3 416.7 625 416.7M385.4 156.3Q416.7 166.7 447.9 156.3 479.2 145.8 479.2 125 479.2 104.2 447.9 93.8 416.7 83.3 385.4 93.8 354.2 104.2 354.2 125 354.2 145.8 385.4 156.3",
|
||||
"width": 833
|
||||
},
|
||||
"search": [
|
||||
"trash"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "7e5b51e48fad210964d9b2ef40e120ca",
|
||||
"css": "refresh",
|
||||
"code": 59463,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M178.6 0C154.8 0 133.9 3 116.1 8.9 98.2 14.9 83.3 23.8 71.4 35.7L35.7 71.4C23.8 83.3 14.9 98.2 8.9 116.1 3 133.9 0 154.8 0 178.6V250 321.4 392.9 464.3C0 488.1 3 508.9 8.9 526.8 14.9 544.6 26.8 556.5 44.6 562.5 62.5 568.5 80.4 568.5 98.2 562.5 116.1 556.5 128 544.6 133.9 526.8 139.9 508.9 142.9 488.1 142.9 464.3V392.9 321.4 250C142.9 226.2 145.8 205.4 151.8 187.5 157.7 169.6 169.6 157.7 187.5 151.8 205.4 145.8 226.2 142.9 250 142.9H321.4 392.9 464.3 535.7 607.1C631 142.9 651.8 145.8 669.6 151.8 687.5 157.7 696.4 166.7 696.4 178.6 696.4 190.5 690.5 202.4 678.6 214.3 666.7 226.2 660.7 238.1 660.7 250 660.7 261.9 669.6 270.8 687.5 276.8 705.4 282.7 726.2 285.7 750 285.7H821.4C845.2 285.7 866.1 282.7 883.9 276.8 901.8 270.8 913.7 258.9 919.6 241.1 925.6 223.2 928.6 202.4 928.6 178.6V107.1C928.6 83.3 925.6 62.5 919.6 44.6 913.7 26.8 904.8 17.9 892.9 17.9 881 17.9 869 23.8 857.1 35.7 845.2 47.6 833.3 53.6 821.4 53.6 809.5 53.6 797.6 47.6 785.7 35.7 773.8 23.8 758.9 14.9 741.1 8.9 723.2 3 702.4 0 678.6 0H607.1 535.7 464.3 392.9 321.4 250ZM928.6 433C919.6 433 910.7 434.5 901.8 437.5 883.9 443.5 872 455.4 866.1 473.2 860.1 491.1 857.1 511.9 857.1 535.7V607.1 678.6 750C857.1 773.8 854.2 794.6 848.2 812.5 842.3 830.4 830.4 842.3 812.5 848.2 794.6 854.2 773.8 857.1 750 857.1H678.6 607.1 535.7 464.3 392.9C369 857.1 348.2 854.2 330.4 848.2 312.5 842.3 303.6 833.3 303.6 821.4 303.6 809.5 309.5 797.6 321.4 785.7 333.3 773.8 339.3 761.9 339.3 750 339.3 738.1 330.4 729.2 312.5 723.2 294.6 717.3 273.8 714.3 250 714.3H178.6C154.8 714.3 133.9 717.3 116.1 723.2 98.2 729.2 86.3 741.1 80.4 758.9 74.4 776.8 71.4 797.6 71.4 821.4V892.9C71.4 916.7 74.4 937.5 80.4 955.4 86.3 973.2 95.2 982.1 107.1 982.1 119 982.1 131 976.2 142.9 964.3 154.8 952.4 166.7 946.4 178.6 946.4 190.5 946.4 202.4 952.4 214.3 964.3 226.2 976.2 241.1 985.1 258.9 991.1 276.8 997 297.6 1000 321.4 1000H392.9 464.3 535.7 607.1 678.6 750 821.4C845.2 1000 866.1 997 883.9 991.1 901.8 985.1 916.7 976.2 928.6 964.3L964.3 928.6C976.2 916.7 985.1 901.8 991.1 883.9 997 866.1 1000 845.2 1000 821.4V750 678.6 607.1 535.7C1000 511.9 997 491.1 991.1 473.2 985.1 455.4 973.2 443.5 955.4 437.5 946.4 434.5 937.5 433 928.6 433Z",
|
||||
"width": 1000
|
||||
},
|
||||
"search": [
|
||||
"refresh"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "59925475e7cc98ccd821771cff586bb6",
|
||||
"css": "pencil_",
|
||||
"code": 59476,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M836.4 0C824.2 0 812.1 6.1 800 18.2L763.6 54.5 727.3 90.9 690.9 127.3C678.8 139.4 672.7 151.5 672.7 163.6 672.7 175.8 678.8 187.9 690.9 200L727.3 236.4 763.6 272.7 800 309.1C812.1 321.2 824.2 327.3 836.4 327.3 848.5 327.3 860.6 321.2 872.7 309.1L909.1 272.7 945.5 236.4 981.8 200C993.9 187.9 1000 175.8 1000 163.6 1000 151.5 993.9 139.4 981.8 127.3L945.5 90.9 909.1 54.5 872.7 18.2C860.6 6.1 848.5 0 836.4 0ZM545.5 290.9C533.3 290.9 521.2 297 509.1 309.1L472.7 345.5 436.4 381.8 400 418.2 363.6 454.5 327.3 490.9 290.9 527.3 254.5 563.6 218.2 600 181.8 636.4 145.5 672.7 109.1 709.1 72.7 745.5 36.4 781.8C24.2 793.9 15.2 809.1 9.1 827.3 3 845.5 0 866.7 0 890.9 0 915.2 3 936.4 9.1 954.5 15.2 972.7 27.3 984.8 45.5 990.9 63.6 997 84.8 1000 109.1 1000 133.3 1000 154.5 997 172.7 990.9 190.9 984.8 206.1 975.8 218.2 963.6L254.5 927.3 290.9 890.9 327.3 854.5 363.6 818.2 400 781.8 436.4 745.5 472.7 709.1 509.1 672.7 545.5 636.4 581.8 600 618.2 563.6 654.5 527.3 690.9 490.9C703 478.8 709.1 466.7 709.1 454.5 709.1 442.4 703 430.3 690.9 418.2L654.5 381.8 618.2 345.5 581.8 309.1C569.7 297 557.6 290.9 545.5 290.9ZM254.5 654.5C266.7 654.5 278.8 660.6 290.9 672.7L327.3 709.1C339.4 721.2 345.5 733.3 345.5 745.5 345.5 757.6 339.4 769.7 327.3 781.8L290.9 818.2 254.5 854.5 218.2 890.9C206.1 903 190.9 912.1 172.7 918.2 154.5 924.2 136.4 924.2 118.2 918.2 100 912.1 87.9 900 81.8 881.8 75.8 863.6 75.8 845.5 81.8 827.3 87.9 809.1 97 793.9 109.1 781.8L145.5 745.5 181.8 709.1 218.2 672.7C230.3 660.6 242.4 654.5 254.5 654.5Z",
|
||||
"width": 1000
|
||||
},
|
||||
"search": [
|
||||
"pencil"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "346f9aef245e0c2ded44e31ad7c66acb",
|
||||
"css": "mode-pvp",
|
||||
"code": 59477,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M305.8 0C296.1 0 286.4 1.6 276.7 4.9 257.3 11.3 244.3 24.3 237.9 43.7 231.4 63.1 231.4 82.5 237.9 101.9 244.3 121.4 254 137.5 267 150.5L305.8 189.3 344.7 228.2 383.5 267C396.4 279.9 409.4 286.4 422.3 286.4 435.3 286.4 448.2 279.9 461.2 267L500 228.2C512.9 215.2 519.4 202.3 519.4 189.3 519.4 176.4 512.9 163.4 500 150.5L461.2 111.7 422.3 72.8 383.5 34C370.6 21 354.4 11.3 335 4.9 325.2 1.6 315.5 0 305.8 0ZM927.2 0C917.5 0 907.8 1.6 898.1 4.9 878.6 11.3 862.5 21 849.5 34L810.7 72.8 771.8 111.7 733 150.5 694.2 189.3 655.3 228.2 616.5 267 577.7 305.8 538.8 344.7 500 383.5 461.2 422.3 422.3 461.2 383.5 500C370.6 512.9 354.4 522.7 335 529.1 315.5 535.6 296.1 535.6 276.7 529.1 257.3 522.7 241.1 512.9 228.2 500 215.2 487.1 199 477.3 179.6 470.9 160.2 464.4 140.8 464.4 121.4 470.9 101.9 477.3 89 490.3 82.5 509.7 76.1 529.1 76.1 548.5 82.5 568 89 587.4 98.7 603.6 111.7 616.5 124.6 629.5 134.3 645.6 140.8 665 147.2 684.5 147.2 703.9 140.8 723.3 134.3 742.7 124.6 758.9 111.7 771.8L72.8 810.7 34 849.5C21 862.5 11.3 878.6 4.9 898.1-1.6 917.5-1.6 936.9 4.9 956.3 11.3 975.7 24.3 988.7 43.7 995.1 63.1 1001.6 82.5 1001.6 101.9 995.1 121.4 988.7 137.5 979 150.5 966L189.3 927.2 228.2 888.3C241.1 875.4 257.3 865.7 276.7 859.2 296.1 852.8 315.5 852.8 335 859.2 354.4 865.7 370.6 875.4 383.5 888.3 396.4 901.3 412.6 911 432 917.5 451.5 923.9 470.9 923.9 490.3 917.5 509.7 911 522.7 898.1 529.1 878.6 535.6 859.2 535.6 839.8 529.1 820.4 522.7 801 512.9 784.8 500 771.8 487.1 758.9 477.3 742.7 470.9 723.3 464.4 703.9 464.4 684.5 470.9 665 477.3 645.6 487.1 629.5 500 616.5L538.8 577.7 577.7 538.8 616.5 500 655.3 461.2 694.2 422.3 733 383.5 771.8 344.7 810.7 305.8 849.5 267 888.3 228.2 927.2 189.3 966 150.5C979 137.5 988.7 121.4 995.1 101.9 1001.6 82.5 1001.6 63.1 995.1 43.7 988.7 24.3 975.7 11.3 956.3 4.9 946.6 1.6 936.9 0 927.2 0ZM1082.5 466C1072.8 466 1063.1 467.6 1053.4 470.9 1034 477.3 1017.8 487.1 1004.9 500 991.9 512.9 975.7 522.7 956.3 529.1 936.9 535.6 917.5 535.6 898.1 529.1 878.6 522.7 862.5 512.9 849.5 500 836.6 487.1 823.6 480.6 810.7 480.6 797.7 480.6 784.8 487.1 771.8 500L733 538.8C720.1 551.8 713.6 564.7 713.6 577.7 713.6 590.6 720.1 603.6 733 616.5 746 629.5 755.7 645.6 762.1 665 768.6 684.5 768.6 703.9 762.1 723.3 755.7 742.7 746 758.9 733 771.8 720.1 784.8 710.4 801 703.9 820.4 697.4 839.8 697.4 859.2 703.9 878.6 710.4 898.1 723.3 911 742.7 917.5 762.1 923.9 781.6 923.9 801 917.5 820.4 911 836.6 901.3 849.5 888.3 862.5 875.4 878.6 865.7 898.1 859.2 917.5 852.8 936.9 852.8 956.3 859.2 975.7 865.7 991.9 875.4 1004.9 888.3L1043.7 927.2 1082.5 966C1095.5 979 1111.7 988.7 1131.1 995.1 1150.5 1001.6 1169.9 1001.6 1189.3 995.1 1208.7 988.7 1221.7 975.7 1228.2 956.3 1234.6 936.9 1234.6 917.5 1228.2 898.1 1221.7 878.6 1212 862.5 1199 849.5L1160.2 810.7 1121.4 771.8C1108.4 758.9 1098.7 742.7 1092.2 723.3 1085.8 703.9 1085.8 684.5 1092.2 665 1098.7 645.6 1108.4 629.5 1121.4 616.5 1134.3 603.6 1144 587.4 1150.5 568 1157 548.5 1157 529.1 1150.5 509.7 1144 490.3 1131.1 477.3 1111.7 470.9 1101.9 467.6 1092.2 466 1082.5 466Z",
|
||||
"width": 1233
|
||||
},
|
||||
"search": [
|
||||
"mode-pvp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "8d74cd519427de451b48df6554aaf593",
|
||||
"css": "mode-attack",
|
||||
"code": 59478,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M849.5 34Q868.9 14.6 898.1 4.9 927.2-4.9 956.3 4.9 985.4 14.6 995.1 43.7 1004.9 72.8 995.1 101.9 985.4 131.1 966 150.5 946.6 169.9 927.2 189.3 907.8 208.7 888.3 228.2 868.9 247.6 849.5 267 830.1 286.4 810.7 305.8 791.3 325.2 771.8 344.7 752.4 364.1 733 383.5 713.6 402.9 694.2 422.3 674.8 441.7 655.3 461.2 635.9 480.6 616.5 500 597.1 519.4 577.7 538.8 558.3 558.3 538.8 577.7 519.4 597.1 500 616.5 480.6 635.9 470.9 665 461.2 694.2 470.9 723.3 480.6 752.4 500 771.8 519.4 791.3 529.1 820.4 538.8 849.5 529.1 878.6 519.4 907.8 490.3 917.5 461.2 927.2 432 917.5 402.9 907.8 383.5 888.3 364.1 868.9 335 859.2 305.8 849.5 276.7 859.2 247.6 868.9 228.2 888.3 208.7 907.8 189.3 927.2 169.9 946.6 150.5 966 131.1 985.4 101.9 995.1 72.8 1004.9 43.7 995.1 14.6 985.4 4.9 956.3-4.9 927.2 4.9 898.1 14.6 868.9 34 849.5 53.4 830.1 72.8 810.7 92.2 791.3 111.7 771.8 131.1 752.4 140.8 723.3 150.5 694.2 140.8 665 131.1 635.9 111.7 616.5 92.2 597.1 82.5 568 72.8 538.8 82.5 509.7 92.2 480.6 121.4 470.9 150.5 461.2 179.6 470.9 208.7 480.6 228.2 500 247.6 519.4 276.7 529.1 305.8 538.8 335 529.1 364.1 519.4 383.5 500 402.9 480.6 422.3 461.2 441.7 441.7 461.2 422.3 480.6 402.9 500 383.5 519.4 364.1 538.8 344.7 558.3 325.2 577.7 305.8 597.1 286.4 616.5 267 635.9 247.6 655.3 228.2 674.8 208.7 694.2 189.3 713.6 169.9 733 150.5 752.4 131.1 771.8 111.7 791.3 92.2 810.7 72.8 830.1 53.4 849.5 34",
|
||||
"width": 1000
|
||||
},
|
||||
"search": [
|
||||
"mode-attack"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "09e5948ca30589e5baa8d27e1c509588",
|
||||
"css": "mode-survival",
|
||||
"code": 59479,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M9.7 43.5Q19.3 14.5 48.3 4.8 77.3-4.8 106.3 4.8 135.3 14.5 154.6 33.8 173.9 53.1 193.2 72.5 212.6 91.8 231.9 111.1 251.2 130.4 280.2 140.1 309.2 149.8 347.8 149.8 386.5 149.8 425.1 149.8 463.8 149.8 502.4 149.8 541.1 149.8 579.7 149.8 618.4 149.8 647.3 140.1 676.3 130.4 695.7 111.1 715 91.8 734.3 72.5 753.6 53.1 772.9 33.8 792.3 14.5 821.3 4.8 850.2-4.8 879.2 4.8 908.2 14.5 917.9 43.5 927.5 72.5 927.5 111.1 927.5 149.8 927.5 188.4 927.5 227.1 927.5 265.7 927.5 304.3 927.5 343 927.5 381.6 927.5 420.3 927.5 458.9 927.5 497.6 927.5 536.2 927.5 574.9 927.5 613.5 917.9 642.5 908.2 671.5 888.9 690.8 869.6 710.1 850.2 729.5 830.9 748.8 811.6 768.1 792.3 787.4 772.9 806.8 753.6 826.1 734.3 845.4 715 864.7 695.7 884.1 676.3 903.4 657 922.7 637.7 942 618.4 961.4 599 980.7 570 990.3 541.1 1000 502.4 1000 463.8 1000 425.1 1000 386.5 1000 357.5 990.3 328.5 980.7 309.2 961.4 289.9 942 270.5 922.7 251.2 903.4 231.9 884.1 212.6 864.7 193.2 845.4 173.9 826.1 154.6 806.8 135.3 787.4 115.9 768.1 96.6 748.8 77.3 729.5 58 710.1 38.6 690.8 19.3 671.5 9.7 642.5 0 613.5 0 574.9 0 536.2 0 497.6 0 458.9 0 420.3 0 381.6 0 343 0 304.3 0 265.7 0 227.1 0 188.4 0 149.8 0 111.1 0 72.5 9.7 43.5",
|
||||
"width": 928
|
||||
},
|
||||
"search": [
|
||||
"mode-survival"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "3a617b3ed2fe766baec5b723b1d9502f",
|
||||
"css": "command-rally",
|
||||
"code": 59480,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M166.7 83.3Q208.3 41.7 270.8 20.8 333.3 0 416.7 0 500 0 583.3 0 666.7 0 729.2 20.8 791.7 41.7 833.3 83.3 875 125 916.7 166.7 958.3 208.3 979.2 270.8 1000 333.3 1000 416.7 1000 500 1000 583.3 1000 666.7 979.2 729.2 958.3 791.7 916.7 833.3 875 875 833.3 916.7 791.7 958.3 729.2 979.2 666.7 1000 583.3 1000 500 1000 416.7 1000 333.3 1000 270.8 979.2 208.3 958.3 166.7 916.7 125 875 83.3 833.3 41.7 791.7 20.8 729.2 0 666.7 0 583.3 0 500 0 416.7 0 333.3 20.8 270.8 41.7 208.3 83.3 166.7 125 125 166.7 83.3M437.5 812.5Q500 833.3 562.5 812.5 625 791.7 666.7 750 708.3 708.3 750 666.7 791.7 625 812.5 562.5 833.3 500 812.5 437.5 791.7 375 750 333.3 708.3 291.7 666.7 250 625 208.3 562.5 187.5 500 166.7 437.5 187.5 375 208.3 333.3 250 291.7 291.7 250 333.3 208.3 375 187.5 437.5 166.7 500 187.5 562.5 208.3 625 250 666.7 291.7 708.3 333.3 750 375 791.7 437.5 812.5",
|
||||
"width": 1000
|
||||
},
|
||||
"search": [
|
||||
"command-rally"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "90fb5a431ca95c46a446c8f4a481d5ce",
|
||||
"css": "command-attack",
|
||||
"code": 59481,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M670.2 74.5Q712.8 31.9 776.6 10.6 840.4-10.6 904.3 10.6 968.1 31.9 989.4 95.7 1010.6 159.6 989.4 223.4 968.1 287.2 925.5 329.8 883 372.3 840.4 414.9 797.9 457.4 755.3 500 712.8 542.6 670.2 585.1 627.7 627.7 585.1 670.2 542.6 712.8 542.6 755.3 542.6 797.9 585.1 840.4 627.7 883 627.7 925.5 627.7 968.1 585.1 968.1 542.6 968.1 500 925.5 457.4 883 414.9 883 372.3 883 329.8 925.5 287.2 968.1 223.4 989.4 159.6 1010.6 95.7 989.4 31.9 968.1 10.6 904.3-10.6 840.4 10.6 776.6 31.9 712.8 74.5 670.2 117 627.7 117 585.1 117 542.6 74.5 500 31.9 457.4 31.9 414.9 31.9 372.3 74.5 372.3 117 372.3 159.6 414.9 202.1 457.4 244.7 457.4 287.2 457.4 329.8 414.9 372.3 372.3 414.9 329.8 457.4 287.2 500 244.7 542.6 202.1 585.1 159.6 627.7 117 670.2 74.5",
|
||||
"width": 1000
|
||||
},
|
||||
"search": [
|
||||
"command-attack"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "17ef812a059c83b5ea3612f860f9569a",
|
||||
"css": "command-retreat",
|
||||
"code": 59482,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M352.3 45.5Q397.7 0 443.2 0 488.6 0 511.4 68.2 534.1 136.4 556.8 204.5 579.5 272.7 647.7 295.5 715.9 318.2 806.8 318.2 897.7 318.2 965.9 340.9 1034.1 363.6 1056.8 431.8 1079.5 500 1056.8 568.2 1034.1 636.4 965.9 659.1 897.7 681.8 806.8 681.8 715.9 681.8 647.7 704.5 579.5 727.3 556.8 795.5 534.1 863.6 511.4 931.8 488.6 1000 443.2 1000 397.7 1000 352.3 954.5 306.8 909.1 261.4 863.6 215.9 818.2 170.5 772.7 125 727.3 79.5 681.8 34.1 636.4 11.4 568.2-11.4 500 11.4 431.8 34.1 363.6 79.5 318.2 125 272.7 170.5 227.3 215.9 181.8 261.4 136.4 306.8 90.9 352.3 45.5",
|
||||
"width": 1068
|
||||
},
|
||||
"search": [
|
||||
"command-retreat"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "1bc31b80669cb5edc2ee5d1370554bc9",
|
||||
"css": "players",
|
||||
"code": 59483,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M370.1 55.1Q401.6 23.6 448.8 7.9 496.1-7.9 543.3 7.9 590.6 23.6 622 55.1 653.5 86.6 685 118.1 716.5 149.6 732.3 196.9 748 244.1 732.3 291.3 716.5 338.6 685 370.1 653.5 401.6 653.5 433.1 653.5 464.6 685 496.1 716.5 527.6 748 559.1 779.5 590.6 811 622 842.5 653.5 874 685 905.5 716.5 937 748 968.5 779.5 984.3 826.8 1000 874 984.3 921.3 968.5 968.5 921.3 984.3 874 1000 811 1000 748 1000 685 1000 622 1000 559.1 1000 496.1 1000 433.1 1000 370.1 1000 307.1 1000 244.1 1000 181.1 1000 118.1 1000 70.9 984.3 23.6 968.5 7.9 921.3-7.9 874 7.9 826.8 23.6 779.5 55.1 748 86.6 716.5 118.1 685 149.6 653.5 181.1 622 212.6 590.6 244.1 559.1 275.6 527.6 307.1 496.1 338.6 464.6 338.6 433.1 338.6 401.6 307.1 370.1 275.6 338.6 259.8 291.3 244.1 244.1 259.8 196.9 275.6 149.6 307.1 118.1 338.6 86.6 370.1 55.1",
|
||||
"width": 992
|
||||
},
|
||||
"search": [
|
||||
"players"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "2073dbd997e5d8e1ffc1322d13ba5585",
|
||||
"css": "chat",
|
||||
"code": 59484,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M129 64.5Q161.3 32.3 209.7 16.1 258.1 0 322.6 0 387.1 0 451.6 0 516.1 0 580.6 0 645.2 0 709.7 0 774.2 0 822.6 16.1 871 32.3 903.2 64.5 935.5 96.8 967.7 129 1000 161.3 1016.1 209.7 1032.3 258.1 1032.3 322.6 1032.3 387.1 1032.3 451.6 1032.3 516.1 1016.1 564.5 1000 612.9 967.7 645.2 935.5 677.4 903.2 709.7 871 741.9 822.6 758.1 774.2 774.2 709.7 774.2 645.2 774.2 580.6 774.2 516.1 774.2 451.6 774.2 387.1 774.2 338.7 790.3 290.3 806.5 274.2 854.8 258.1 903.2 241.9 951.6 225.8 1000 193.5 1000 161.3 1000 129 967.7 96.8 935.5 64.5 903.2 32.3 871 16.1 822.6 0 774.2 0 709.7 0 645.2 0 580.6 0 516.1 0 451.6 0 387.1 0 322.6 0 258.1 16.1 209.7 32.3 161.3 64.5 129 96.8 96.8 129 64.5",
|
||||
"width": 1032
|
||||
},
|
||||
"search": [
|
||||
"chat"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "9dd9e835aebe1060ba7190ad2b2ed951",
|
||||
"css": "zoom",
|
||||
"code": 59415,
|
||||
"src": "fontawesome"
|
||||
}
|
||||
]
|
||||
}
|
||||
62
core/assets-raw/fontgen/icons/admin.svg
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
sodipodi:docname="admin.svg"
|
||||
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||
<metadata
|
||||
id="metadata8">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs6" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1318"
|
||||
id="namedview4"
|
||||
showgrid="false"
|
||||
inkscape:zoom="52.678571"
|
||||
inkscape:cx="7.760678"
|
||||
inkscape:cy="6.4128813"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g827" />
|
||||
<g
|
||||
id="g827"
|
||||
transform="translate(-1,-1)">
|
||||
<path
|
||||
id="path823"
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
d="M 6.125,5.625 Q 6.25,5.25 6.625,5.125 7,5 7.375,5.125 7.75,5.25 7.875,5.625 8,6 7.875,6.375 7.75,6.75 7.375,6.875 7,7 6.625,6.875 6.25,6.75 6.125,6.375 6,6 6.125,5.625 M 3,1.5 Q 3.25,1.25 3.625,1.125 4,1 4.5,1 5,1 5.5,1 6,1 6.5,1 7,1 7.5,1 8,1 8.5,1 9,1 9.5,1 10,1 10.375,1.125 10.75,1.25 11,1.5 11.25,1.75 11.5,2 11.75,2.25 12,2.5 12.25,2.75 12.5,3 12.75,3.25 12.875,3.625 13,4 13,4.5 13,5 13,5.5 13,6 13,6.5 13,7 12.875,7.375 12.75,7.75 12.5,8 12.25,8.25 12.125,8.625 12,9 12,9.5 12,10 11.875,10.375 11.75,10.75 11.5,11 11.25,11.25 11,11.5 10.75,11.75 10.5,12 10.25,12.25 10,12.5 9.75,12.75 9.375,12.875 9,13 8.5,13 8,13 7.5,13 7,13 6.5,13 6,13 5.5,13 5,13 4.625,12.875 4.25,12.75 4,12.5 3.75,12.25 3.5,12 3.25,11.75 3,11.5 2.75,11.25 2.5,11 2.25,10.75 2.125,10.375 2,10 2,9.5 2,9 1.875,8.625 1.75,8.25 1.5,8 1.25,7.75 1.125,7.375 1,7 1,6.5 1,6 1,5.5 1,5 1,4.5 1,4 1.125,3.625 1.25,3.25 1.5,3 1.75,2.75 2,2.5 2.25,2.25 2.5,2 2.75,1.75 3,1.5 M 7.5,10 Q 8,10 8.375,9.875 8.75,9.75 8.75,9.5 8.75,9.25 8.375,9.125 8,9 7.5,9 7,9 6.5,9 6,9 5.625,9.125 5.25,9.25 5.25,9.5 5.25,9.75 5.625,9.875 6,10 6.5,10 q 0.5,0 1,0 m 0,-2 Q 8,8 8.375,7.875 8.75,7.75 9,7.5 9.25,7.25 9.5,7 9.75,6.75 9.875,6.375 10,6 9.875,5.625 9.75,5.25 9.5,5 9.25,4.75 9,4.5 8.75,4.25 8.375,4.125 8,4 7.5,4 7,4 6.5,4 6,4 5.625,4.125 5.25,4.25 5,4.5 4.75,4.75 4.5,5 4.25,5.25 4.125,5.625 4,6 4.125,6.375 4.25,6.75 4.5,7 4.75,7.25 5,7.5 5.25,7.75 5.625,7.875 6,8 6.5,8 7,8 7.5,8"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
62
core/assets-raw/fontgen/icons/book.svg
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
width="10"
|
||||
height="11"
|
||||
viewBox="0 0 10 11"
|
||||
sodipodi:docname="book.svg"
|
||||
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||
<metadata
|
||||
id="metadata8">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs6" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1318"
|
||||
id="namedview4"
|
||||
showgrid="false"
|
||||
inkscape:zoom="18.624687"
|
||||
inkscape:cx="-15.26171"
|
||||
inkscape:cy="11.56781"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g24" />
|
||||
<g
|
||||
id="g24"
|
||||
transform="translate(-2,-2)">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
d="M 4.5,2 C 4.1666667,2 3.875,2.0416667 3.625,2.125 3.375,2.2083333 3.1666667,2.3333333 3,2.5 L 2.5,3 C 2.3333333,3.1666667 2.2083333,3.375 2.125,3.625 2.0416667,3.875 2,4.1666667 2,4.5 v 1 1 1 1 1 1 1 c 0,0.333333 0.041667,0.625 0.125,0.875 0.083333,0.25 0.25,0.416667 0.5,0.5 C 2.875,12.958333 3.1666667,13 3.5,13 h 1 1 1 1 1 1 c 0.3333333,0 0.625,-0.04167 0.875,-0.125 C 10.625,12.79167 10.833333,12.666667 11,12.5 L 11.5,12 c 0.166667,-0.166667 0.291667,-0.375 0.375,-0.625 C 11.958333,11.125 12,10.833333 12,10.5 v -1 -1 -1 -1 -1 -1 -1 C 12,3.1666667 11.95833,2.875 11.875,2.625 11.79167,2.375 11.625,2.2083333 11.375,2.125 11.125,2.0416667 10.833333,2 10.5,2 h -1 -1 -1 -1 -1 z m 1,1 h 1 1 1 1 c 0.3333333,0 0.625,0.041667 0.875,0.125 0.25,0.083333 0.416667,0.25 0.5,0.5 C 10.958333,3.875 11,4.1666667 11,4.5 v 1 1 1 1 1 c 0,0.3333333 -0.04167,0.625 -0.125,0.875 -0.08333,0.25 -0.25,0.416667 -0.5,0.5 C 10.125,10.958333 9.8333333,11 9.5,11 h -1 -1 -1 -1 C 5.1666667,11 4.875,10.95833 4.625,10.875 4.375,10.79167 4.2083333,10.625 4.125,10.375 4.0416667,10.125 4,9.8333333 4,9.5 v -1 -1 -1 -1 -1 C 4,4.1666667 4.0416667,3.875 4.125,3.625 4.2083333,3.375 4.375,3.2083333 4.625,3.125 4.875,3.0416667 5.1666667,3 5.5,3 Z m 1,1 C 6.1666667,4 5.875,4.041667 5.625,4.125 c -0.25,0.083333 -0.4166667,0.25 -0.5,0.5 -0.083333,0.25 -0.083333,0.5 0,0.75 0.083333,0.25 0.25,0.4166667 0.5,0.5 C 5.875,5.9583333 6.1666667,6 6.5,6 h 1 1 c 0.3333333,0 0.625,-0.041667 0.875,-0.125 0.25,-0.083333 0.4166667,-0.25 0.5,-0.5 0.083333,-0.25 0.083333,-0.5 0,-0.75 C 9.791667,4.375 9.625,4.2083333 9.375,4.125 9.125,4.0416667 8.8333333,4 8.5,4 h -1 z m 0,3 C 6.1666667,7 5.875,7.0416667 5.625,7.125 5.375,7.2083333 5.25,7.3333333 5.25,7.5 5.25,7.6666667 5.375,7.7916667 5.625,7.875 5.875,7.9583333 6.1666667,8 6.5,8 h 1 1 C 8.8333333,8 9.125,7.9583333 9.375,7.875 9.625,7.7916667 9.75,7.6666667 9.75,7.5 9.75,7.3333333 9.625,7.2083333 9.375,7.125 9.125,7.0416667 8.8333333,7 8.5,7 h -1 z m 0,2 C 6.1666667,9 5.875,9.0416667 5.625,9.125 5.375,9.2083333 5.25,9.3333333 5.25,9.5 5.25,9.6666667 5.375,9.7916667 5.625,9.875 5.875,9.9583333 6.1666667,10 6.5,10 h 1 1 C 8.8333333,10 9.125,9.9583333 9.375,9.875 9.625,9.7916667 9.75,9.6666667 9.75,9.5 9.75,9.3333333 9.625,9.2083333 9.375,9.125 9.125,9.0416667 8.8333333,9 8.5,9 h -1 z"
|
||||
id="path14"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
62
core/assets-raw/fontgen/icons/chat.svg
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
width="8"
|
||||
height="7.75"
|
||||
viewBox="0 0 8 7.75"
|
||||
sodipodi:docname="chat.svg"
|
||||
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||
<metadata
|
||||
id="metadata8">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs6" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1318"
|
||||
id="namedview4"
|
||||
showgrid="false"
|
||||
inkscape:zoom="23.6"
|
||||
inkscape:cx="-8.033898"
|
||||
inkscape:cy="4.0042373"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g16" />
|
||||
<g
|
||||
id="g16"
|
||||
transform="translate(-1,-1)">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
d="M 2,1.5 Q 2.25,1.25 2.625,1.125 3,1 3.5,1 4,1 4.5,1 5,1 5.5,1 6,1 6.5,1 7,1 7.375,1.125 7.75,1.25 8,1.5 8.25,1.75 8.5,2 8.75,2.25 8.875,2.625 9,3 9,3.5 9,4 9,4.5 9,5 8.875,5.375 8.75,5.75 8.5,6 8.25,6.25 8,6.5 7.75,6.75 7.375,6.875 7,7 6.5,7 6,7 5.5,7 5,7 4.5,7 4,7 3.625,7.125 3.25,7.25 3.125,7.625 3,8 2.875,8.375 2.75,8.75 2.5,8.75 2.25,8.75 2,8.5 1.75,8.25 1.5,8 1.25,7.75 1.125,7.375 1,7 1,6.5 1,6 1,5.5 1,5 1,4.5 1,4 1,3.5 1,3 1.125,2.625 1.25,2.25 1.5,2 1.75,1.75 2,1.5"
|
||||
id="path14"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
62
core/assets-raw/fontgen/icons/command-attack.svg
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg12"
|
||||
width="5.875"
|
||||
height="5.875"
|
||||
viewBox="0 0 5.875 5.875"
|
||||
sodipodi:docname="command-attack.svg"
|
||||
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||
<metadata
|
||||
id="metadata18">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs16" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1318"
|
||||
id="namedview14"
|
||||
showgrid="false"
|
||||
inkscape:zoom="29.5"
|
||||
inkscape:cx="-7.706568"
|
||||
inkscape:cy="1.5137712"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g61" />
|
||||
<g
|
||||
id="g61"
|
||||
transform="translate(-1.0625,-1.0625)">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
d="M 5,1.5 Q 5.25,1.25 5.625,1.125 6,1 6.375,1.125 6.75,1.25 6.875,1.625 7,2 6.875,2.375 6.75,2.75 6.5,3 6.25,3.25 6,3.5 5.75,3.75 5.5,4 5.25,4.25 5,4.5 4.75,4.75 4.5,5 4.25,5.25 4.25,5.5 4.25,5.75 4.5,6 4.75,6.25 4.75,6.5 4.75,6.75 4.5,6.75 4.25,6.75 4,6.5 3.75,6.25 3.5,6.25 3.25,6.25 3,6.5 2.75,6.75 2.375,6.875 2,7 1.625,6.875 1.25,6.75 1.125,6.375 1,6 1.125,5.625 1.25,5.25 1.5,5 1.75,4.75 1.75,4.5 1.75,4.25 1.5,4 1.25,3.75 1.25,3.5 1.25,3.25 1.5,3.25 1.75,3.25 2,3.5 2.25,3.75 2.5,3.75 2.75,3.75 3,3.5 3.25,3.25 3.5,3 3.75,2.75 4,2.5 4.25,2.25 4.5,2 4.75,1.75 5,1.5"
|
||||
id="path59"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
62
core/assets-raw/fontgen/icons/command-rally.svg
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
width="6"
|
||||
height="6"
|
||||
viewBox="0 0 6 6"
|
||||
sodipodi:docname="command-rally.svg"
|
||||
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||
<metadata
|
||||
id="metadata8">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs6" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1318"
|
||||
id="namedview4"
|
||||
showgrid="false"
|
||||
inkscape:zoom="29.5"
|
||||
inkscape:cx="-12.118644"
|
||||
inkscape:cy="2.7966102"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g88" />
|
||||
<g
|
||||
id="g88"
|
||||
transform="translate(-1,-1)">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
d="M 2,1.5 Q 2.25,1.25 2.625,1.125 3,1 3.5,1 4,1 4.5,1 5,1 5.375,1.125 5.75,1.25 6,1.5 6.25,1.75 6.5,2 6.75,2.25 6.875,2.625 7,3 7,3.5 7,4 7,4.5 7,5 6.875,5.375 6.75,5.75 6.5,6 6.25,6.25 6,6.5 5.75,6.75 5.375,6.875 5,7 4.5,7 4,7 3.5,7 3,7 2.625,6.875 2.25,6.75 2,6.5 1.75,6.25 1.5,6 1.25,5.75 1.125,5.375 1,5 1,4.5 1,4 1,3.5 1,3 1.125,2.625 1.25,2.25 1.5,2 1.75,1.75 2,1.5 M 3.625,5.875 Q 4,6 4.375,5.875 4.75,5.75 5,5.5 5.25,5.25 5.5,5 5.75,4.75 5.875,4.375 6,4 5.875,3.625 5.75,3.25 5.5,3 5.25,2.75 5,2.5 4.75,2.25 4.375,2.125 4,2 3.625,2.125 3.25,2.25 3,2.5 2.75,2.75 2.5,3 2.25,3.25 2.125,3.625 2,4 2.125,4.375 2.25,4.75 2.5,5 2.75,5.25 3,5.5 3.25,5.75 3.625,5.875"
|
||||
id="path84"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
62
core/assets-raw/fontgen/icons/command-retreat.svg
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg22"
|
||||
width="5.875"
|
||||
height="5.5"
|
||||
viewBox="0 0 5.875 5.5"
|
||||
sodipodi:docname="command-retreat.svg"
|
||||
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||
<metadata
|
||||
id="metadata28">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs26" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1318"
|
||||
id="namedview24"
|
||||
showgrid="false"
|
||||
inkscape:zoom="29.5"
|
||||
inkscape:cx="-14.520127"
|
||||
inkscape:cy="1.4618644"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g36" />
|
||||
<g
|
||||
id="g36"
|
||||
transform="translate(-1.0625,-1.25)">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
d="M 3,1.5 Q 3.25,1.25 3.5,1.25 3.75,1.25 3.875,1.625 4,2 4.125,2.375 4.25,2.75 4.625,2.875 5,3 5.5,3 6,3 6.375,3.125 6.75,3.25 6.875,3.625 7,4 6.875,4.375 6.75,4.75 6.375,4.875 6,5 5.5,5 5,5 4.625,5.125 4.25,5.25 4.125,5.625 4,6 3.875,6.375 3.75,6.75 3.5,6.75 3.25,6.75 3,6.5 2.75,6.25 2.5,6 2.25,5.75 2,5.5 1.75,5.25 1.5,5 1.25,4.75 1.125,4.375 1,4 1.125,3.625 1.25,3.25 1.5,3 1.75,2.75 2,2.5 2.25,2.25 2.5,2 2.75,1.75 3,1.5"
|
||||
id="path34"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
77
core/assets-raw/fontgen/icons/crafting.svg
Normal file
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
width="10"
|
||||
height="8.9375"
|
||||
viewBox="0 0 10 8.9375"
|
||||
sodipodi:docname="crafting.svg"
|
||||
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||
<metadata
|
||||
id="metadata8">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs6" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1318"
|
||||
id="namedview4"
|
||||
showgrid="false"
|
||||
inkscape:zoom="61.458335"
|
||||
inkscape:cx="3.677966"
|
||||
inkscape:cy="5.0935595"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
<g
|
||||
id="g5253"
|
||||
transform="translate(-1,-2.0625)">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
d="M 9.125,2.625 Q 9.25,2.25 9.625,2.125 10,2 10.375,2.125 10.75,2.25 10.875,2.625 11,3 11,3.5 q 0,0.5 0,1 0,0.5 0,1 0,0.5 0,1 0,0.5 0,1 0,0.5 0,1 0,0.5 0,1 0,0.5 -0.125,0.875 -0.125,0.375 -0.5,0.5 Q 10,11 9.5,11 9,11 8.5,11 8,11 7.5,11 7,11 6.5,11 6,11 5.5,11 5,11 4.5,11 4,11 3.5,11 3,11 2.5,11 2,11 1.625,10.875 1.25,10.75 1.125,10.375 1,10 1,9.5 1,9 1,8.5 1,8 1,7.5 1,7 1,6.5 1,6 1,5.5 1,5 1,4.5 1,4 1.125,3.625 1.25,3.25 1.5,3.25 1.75,3.25 2,3.5 2.25,3.75 2.5,4 2.75,4.25 3,4.5 3.25,4.75 3.5,5 3.75,5.25 4,5.5 4.25,5.75 4.5,5.75 4.75,5.75 4.875,5.375 5,5 5,4.5 5,4 5.125,3.625 5.25,3.25 5.5,3.25 5.75,3.25 6,3.5 6.25,3.75 6.5,4 6.75,4.25 7,4.5 7.25,4.75 7.5,5 7.75,5.25 8,5.5 8.25,5.75 8.5,5.75 8.75,5.75 8.875,5.375 9,5 9,4.5 9,4 9,3.5 9,3 9.125,2.625 M 8.5,10 Q 9,10 9.375,9.875 9.75,9.75 9.875,9.375 10,9 10,8.5 10,8 9.875,7.625 9.75,7.25 9.375,7.125 9,7 8.625,6.875 8.25,6.75 8,6.5 7.75,6.25 7.5,6 7.25,5.75 7,5.5 6.75,5.25 6.5,5.25 6.25,5.25 6.125,5.625 6,6 5.875,6.375 5.75,6.75 5.375,6.875 5,7 4.625,6.875 4.25,6.75 4,6.5 3.75,6.25 3.5,6 3.25,5.75 3,5.5 2.75,5.25 2.5,5.25 2.25,5.25 2.125,5.625 2,6 2,6.5 2,7 2,7.5 2,8 2,8.5 2,9 2.125,9.375 2.25,9.75 2.625,9.875 3,10 3.5,10 q 0.5,0 1,0 0.5,0 1,0 0.5,0 1,0 0.5,0 1,0 0.5,0 1,0"
|
||||
id="path5243"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
d="m 3.25,8.5 q 0,-0.25 0.25,-0.25 0.25,0 0.25,0.25 0,0.25 -0.25,0.25 -0.25,0 -0.25,-0.25"
|
||||
id="path5247"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
d="m 5.25,8.5 q 0,-0.25 0.25,-0.25 0.25,0 0.25,0.25 0,0.25 -0.25,0.25 -0.25,0 -0.25,-0.25"
|
||||
id="path5249"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
d="m 7.25,8.5 q 0,-0.25 0.25,-0.25 0.25,0 0.25,0.25 0,0.25 -0.25,0.25 -0.25,0 -0.25,-0.25"
|
||||
id="path5251"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
63
core/assets-raw/fontgen/icons/defense.svg
Normal file
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg817"
|
||||
width="10"
|
||||
height="9.875"
|
||||
viewBox="0 0 10 9.875"
|
||||
sodipodi:docname="defense.svg"
|
||||
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||
<metadata
|
||||
id="metadata823">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs821" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1318"
|
||||
id="namedview819"
|
||||
showgrid="false"
|
||||
inkscape:zoom="43.457605"
|
||||
inkscape:cx="10.243339"
|
||||
inkscape:cy="4.6479408"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g5280" />
|
||||
<g
|
||||
transform="translate(-0.999269,-1.0631022)"
|
||||
id="g5280">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
d="m -10.472656,-2.8027344 c -0.125,0 -0.25,0.020833 -0.375,0.0625 -0.25,0.083333 -0.416667,0.25 -0.5,0.5 -0.08333,0.25 -0.125,0.5416667 -0.125,0.875 v 1.00000002 1 0.99999998 c 0,0.3333334 0.04167,0.625 0.125,0.875 0.08333,0.25 0.208333,0.4583334 0.375,0.625 l 0.5,0.5 0.4999998,0.5 0.5,0.5 0.5,0.5 0.5,0.5 0.5,0.5 0.5,0.5 c 0.1666666,0.1666667 0.3749999,0.2916667 0.625,0.375 0.25,0.083333 0.4999999,0.083333 0.75,0 0.2499999,-0.083333 0.4583333,-0.2083333 0.625,-0.375 l 0.5,-0.5 0.5,-0.5 0.5,-0.5 0.5,-0.5 0.5,-0.5 0.5,-0.5 0.5,-0.5 c 0.1666666,-0.1666666 0.2916666,-0.375 0.375,-0.625 0.083333,-0.25 0.125,-0.5416666 0.125,-0.875 v -0.99999998 -1 -1.00000002 c 0,-0.3333333 -0.041667,-0.625 -0.125,-0.875 -0.083333,-0.25 -0.25,-0.4166666 -0.5,-0.5 -0.25,-0.083333 -0.5,-0.083333 -0.75,0 -0.2500001,0.083333 -0.4583334,0.2083334 -0.625,0.375 l -0.5,0.5 -0.5,0.5 c -0.1666667,0.1666667 -0.375,0.2916667 -0.625,0.37500002 -0.2500001,0.0833333 -0.5416667,0.125 -0.875,0.125 h -1 c -0.3333334,10e-9 -0.6250001,-0.0416667 -0.875,-0.125 -0.25,-0.0833333 -0.4583334,-0.20833332 -0.625,-0.37500002 l -0.5,-0.5 -0.5,-0.5 c -0.1666667,-0.1666666 -0.375,-0.2916666 -0.6249998,-0.375 -0.125,-0.041667 -0.25,-0.0625 -0.375,-0.0625 z m 0.4999998,1.1875 c 0.1666666,0 0.3333333,0.083333 0.5,0.25 l 0.5,0.50000002 0.5,0.5 c 0.1666666,0.16666667 0.375,0.29166667 0.625,0.37500001 0.2499999,0.08333333 0.5416666,0.12499999 0.875,0.12499999 h 1 c 0.3333333,0 0.6249999,-0.04166666 0.875,-0.12499999 0.25,-0.08333334 0.4583333,-0.20833334 0.625,-0.37500001 l 0.5,-0.5 0.5,-0.50000002 c 0.1666666,-0.1666666 0.3333333,-0.25 0.5,-0.25 0.1666666,0 0.2916666,0.125 0.375,0.375 0.083333,0.25000002 0.125,0.54166669 0.125,0.87500002 v 1 0.99999998 c 0,0.3333334 -0.041667,0.625 -0.125,0.875 -0.083333,0.25 -0.2083334,0.4583334 -0.375,0.625 l -0.5,0.5 -0.5,0.5 -0.5,0.5 -0.5,0.5 -0.5,0.5 c -0.1666667,0.1666667 -0.3750001,0.2916667 -0.625,0.375 -0.25,0.083333 -0.5000001,0.083333 -0.75,0 -0.2500001,-0.083333 -0.4583334,-0.2083333 -0.625,-0.375 l -0.5,-0.5 -0.5,-0.5 -0.5,-0.5 -0.5,-0.5 -0.5,-0.5 c -0.1666668,-0.1666666 -0.2916668,-0.375 -0.3749998,-0.625 -0.08333,-0.25 -0.125,-0.5416666 -0.125,-0.875 v -0.99999998 -1 c 0,-0.33333333 0.04167,-0.625 0.125,-0.87500002 0.08333,-0.25 0.208333,-0.375 0.3749998,-0.375 z"
|
||||
transform="translate(12.471925,3.8658366)"
|
||||
id="path5276"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
60
core/assets-raw/fontgen/icons/distribution.svg
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg3799"
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 10 10"
|
||||
sodipodi:docname="icon-distribution-transparent.png.svg"
|
||||
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||
<metadata
|
||||
id="metadata3805">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs3803" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="1318"
|
||||
id="namedview3801"
|
||||
showgrid="false"
|
||||
inkscape:zoom="45.254834"
|
||||
inkscape:cx="6.2899407"
|
||||
inkscape:cy="4.0348889"
|
||||
inkscape:window-x="1280"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="g3829" />
|
||||
<g
|
||||
id="g3829">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
d="M 0 0 L 0 0.5 L 0 1 L 0 1.5 L 0 2 L 0 2.5 L 0 3 L 0 3.5 L 0 4 L 0 4.5 L 0 5 L 0 5.5 L 0 6 L 0 6.5 L 0 7 L 0 7.5 L 0 8 L 0 8.5 L 0 9 L 0 9.5 L 0 10 L 0.5 10 L 1 10 L 1 9.5 L 1 8.5 L 1 7.5 L 1 6.5 L 1 5.5 L 1 4.5 L 1 3.5 L 1 2.5 L 1 1.5 L 1 0.5 L 1 0 L 0.5 0 L 0 0 z M 9 0 L 9 0.5 L 9 1.5 L 9 2.5 L 9 3.5 L 9 4.5 L 9 5.5 L 9 6.5 L 9 7.5 L 9 8.5 L 9 9.5 L 9 10 L 9.5 10 L 10 10 L 10 9.5 L 10 9 L 10 8.5 L 10 8 L 10 7.5 L 10 7 L 10 6.5 L 10 6 L 10 5.5 L 10 5 L 10 4.5 L 10 4 L 10 3.5 L 10 3 L 10 2.5 L 10 2 L 10 1.5 L 10 1 L 10 0.5 L 10 0 L 9.5 0 L 9 0 z M 5 1.0625 C 4.875 1.0625 4.75 1.0833335 4.625 1.125 C 4.375 1.2083333 4.1666667 1.3333333 4 1.5 L 3.5 2 C 3.3333333 2.1666667 3.25 2.3333333 3.25 2.5 C 3.25 2.6666667 3.375 2.7916667 3.625 2.875 C 3.875 2.9583333 4.1666667 3 4.5 3 L 5.5 3 C 5.9760929 2.9389882 6.4908205 3.0048105 6.75 2.5 C 6.75 2.3333333 6.6666667 2.1666667 6.5 2 L 6 1.5 C 5.8333333 1.3333333 5.625 1.2083333 5.375 1.125 C 5.25 1.0833335 5.125 1.0625 5 1.0625 z M 5 4.0625 C 4.875 4.0625 4.75 4.0833333 4.625 4.125 C 4.375 4.2083333 4.1666667 4.3333333 4 4.5 L 3.5 5 C 3.3333333 5.1666667 3.25 5.3333333 3.25 5.5 C 3.25 5.6666667 3.375 5.7916667 3.625 5.875 C 3.875 5.9583333 4.1666667 6 4.5 6 L 5.5 6 C 5.8333333 6 6.125 5.9583333 6.375 5.875 C 6.625 5.7916667 6.75 5.6666667 6.75 5.5 C 6.75 5.3333333 6.6666667 5.1666667 6.5 5 L 6 4.5 C 5.8333333 4.3333333 5.625 4.2083333 5.375 4.125 C 5.25 4.0833333 5.125 4.0625 5 4.0625 z M 5 7.0625 C 4.875 7.0625 4.75 7.0833333 4.625 7.125 C 4.375 7.2083333 4.1666667 7.3333333 4 7.5 L 3.5 8 C 3.3333333 8.1666667 3.25 8.3333333 3.25 8.5 C 3.25 8.6666667 3.375 8.7916667 3.625 8.875 C 3.875 8.9583333 4.1666667 9 4.5 9 L 5.5 9 C 5.8333333 9 6.125 8.9583333 6.375 8.875 C 6.625 8.7916667 6.75 8.6666667 6.75 8.5 C 6.75 8.3333333 6.6666667 8.1666667 6.5 8 L 6 7.5 C 5.8333333 7.3333333 5.625 7.2083333 5.375 7.125 C 5.25 7.0833333 5.125 7.0625 5 7.0625 z "
|
||||
id="path3817" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
62
core/assets-raw/fontgen/icons/effect.svg
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg1370"
|
||||
width="10"
|
||||
height="10"
|
||||
viewBox="0 0 10 10"
|
||||
sodipodi:docname="effect.svg"
|
||||
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||
<metadata
|
||||
id="metadata1376">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs1374" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1318"
|
||||
id="namedview1372"
|
||||
showgrid="false"
|
||||
inkscape:zoom="61.458335"
|
||||
inkscape:cx="3.4444574"
|
||||
inkscape:cy="5.2674622"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g5329" />
|
||||
<g
|
||||
id="g5329"
|
||||
transform="translate(-1,-1)">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
d="M 3.5,1 C 3.1666667,1 2.875,1.0416667 2.625,1.125 2.375,1.2083333 2.1666667,1.3333333 2,1.5 L 1.5,2 C 1.3333333,2.1666667 1.2083333,2.375 1.125,2.625 1.0416667,2.875 1,3.1666667 1,3.5 v 1 1 1 1 1 C 1,8.8333333 1.0416667,9.125 1.125,9.375 1.2083333,9.625 1.3333333,9.8333333 1.5,10 L 2,10.5 c 0.1666667,0.166667 0.375,0.291667 0.625,0.375 C 2.875,10.958333 3.1666667,11 3.5,11 h 1 1 1 1 1 C 8.8333333,11 9.125,10.958333 9.375,10.875 9.625,10.791667 9.8333333,10.666667 10,10.5 L 10.5,10 C 10.666667,9.8333333 10.791667,9.625 10.875,9.375 10.958333,9.125 11,8.8333333 11,8.5 v -1 -1 -1 -1 -1 C 11,3.1666667 10.958333,2.875 10.875,2.625 10.791667,2.375 10.666667,2.1666667 10.5,2 L 10,1.5 C 9.8333333,1.3333333 9.625,1.2083333 9.375,1.125 9.125,1.0416667 8.8333333,1 8.5,1 h -1 -1 -1 -1 z m 1,1 h 1 1 1 C 7.8333333,2 8.125,2.0416667 8.375,2.125 8.625,2.2083333 8.8333333,2.3333333 9,2.5 L 9.5,3 C 9.6666667,3.1666667 9.7916667,3.375 9.875,3.625 9.9583333,3.875 10,4.1666667 10,4.5 v 1 1 1 C 10,7.8333333 9.9583333,8.125 9.875,8.375 9.7916667,8.625 9.6666667,8.8333333 9.5,9 L 9,9.5 C 8.8333333,9.6666667 8.625,9.7916667 8.375,9.875 8.125,9.9583333 7.8333333,10 7.5,10 h -1 -1 -1 C 4.1666667,10 3.875,9.9583333 3.625,9.875 3.375,9.7916667 3.1666667,9.6666667 3,9.5 L 2.5,9 C 2.3333333,8.8333333 2.2083333,8.625 2.125,8.375 2.0416667,8.125 2,7.8333333 2,7.5 v -1 -1 -1 C 2,4.1666667 2.0416667,3.875 2.125,3.625 2.2083333,3.375 2.3333333,3.1666667 2.5,3 L 3,2.5 C 3.1666667,2.3333333 3.375,2.2083333 3.625,2.125 3.875,2.0416667 4.1666667,2 4.5,2 Z M 6,4.0625 C 5.875,4.0625 5.75,4.0833333 5.625,4.125 5.375,4.2083333 5.1666667,4.3333333 5,4.5 L 4.5,5 C 4.3333333,5.1666667 4.2083333,5.375 4.125,5.625 c -0.083333,0.25 -0.083333,0.5 0,0.75 C 4.2083333,6.625 4.3333333,6.8333333 4.5,7 L 5,7.5 c 0.1666667,0.1666667 0.375,0.2916667 0.625,0.375 0.25,0.083333 0.5,0.083333 0.75,0 C 6.625,7.7916667 6.8333333,7.6666667 7,7.5 L 7.5,7 C 7.6666667,6.8333333 7.7916667,6.625 7.875,6.375 c 0.083333,-0.25 0.083333,-0.5 0,-0.75 C 7.7916667,5.375 7.6666667,5.1666667 7.5,5 L 7,4.5 C 6.8333333,4.3333333 6.625,4.2083333 6.375,4.125 6.25,4.0833333 6.125,4.0625 6,4.0625 Z"
|
||||
id="path5323"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |