Merge branch 'master' into master
6
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -2,7 +2,7 @@
|
|||||||
name: Bug report
|
name: Bug report
|
||||||
about: Create a report to help fix an issue.
|
about: Create a report to help fix an issue.
|
||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
labels: bug
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -15,6 +15,10 @@ assignees: ''
|
|||||||
|
|
||||||
**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.*
|
*Place an X (no spaces) between the brackets to confirm that you have read the line below.*
|
||||||
|
|||||||
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.
|
||||||
11
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Do not make a new issue for feature requests! Instead, post in the suggestions
|
|
||||||
repository. See the README.
|
|
||||||
title: ''
|
|
||||||
labels: invalid
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Do not make a new issue for feature requests!** Instead, post it in suggestions repository: https://github.com/Anuken/Mindustry-Suggestions/issues/new/choose
|
|
||||||
23
.github/workflows/gradle.yml
vendored
@@ -3,15 +3,26 @@ name: Java CI
|
|||||||
on: [push]
|
on: [push]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
buildJava8:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Set up JDK 1.8
|
- name: Set up JDK 8
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 1.8
|
java-version: 8
|
||||||
#- name: Run unit tests with gradle
|
- name: Run unit tests with gradle and Java 8
|
||||||
# run: ./gradlew test
|
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
|
||||||
|
|||||||
7
.gitignore
vendored
@@ -10,6 +10,7 @@ logs/
|
|||||||
/desktop/mindustry-saves/
|
/desktop/mindustry-saves/
|
||||||
/desktop/mindustry-maps/
|
/desktop/mindustry-maps/
|
||||||
/desktop/gifexport/
|
/desktop/gifexport/
|
||||||
|
/gifs/
|
||||||
/core/lib/
|
/core/lib/
|
||||||
/ios/assets/
|
/ios/assets/
|
||||||
/core/assets-raw/sprites/generated/
|
/core/assets-raw/sprites/generated/
|
||||||
@@ -18,6 +19,7 @@ logs/
|
|||||||
/annotations/out/
|
/annotations/out/
|
||||||
/net/build/
|
/net/build/
|
||||||
/tools/build/
|
/tools/build/
|
||||||
|
/core/build/
|
||||||
/tests/build/
|
/tests/build/
|
||||||
/server/build/
|
/server/build/
|
||||||
changelog
|
changelog
|
||||||
@@ -27,7 +29,6 @@ core/assets/saves/
|
|||||||
/core/assets/saves/
|
/core/assets/saves/
|
||||||
steam_appid.txt
|
steam_appid.txt
|
||||||
/test_files/
|
/test_files/
|
||||||
/annotations/build/
|
|
||||||
/android/assets/mindustry-maps/
|
/android/assets/mindustry-maps/
|
||||||
/android/assets/mindustry-saves/
|
/android/assets/mindustry-saves/
|
||||||
/core/assets/gifexport/
|
/core/assets/gifexport/
|
||||||
@@ -39,11 +40,9 @@ steam_appid.txt
|
|||||||
ios/robovm.properties
|
ios/robovm.properties
|
||||||
packr-out/
|
packr-out/
|
||||||
config/
|
config/
|
||||||
changelog
|
|
||||||
*.gif
|
*.gif
|
||||||
/core/assets/saves/
|
|
||||||
/out/
|
|
||||||
|
|
||||||
|
/core/assets/basepartnames
|
||||||
version.properties
|
version.properties
|
||||||
|
|
||||||
.attach_*
|
.attach_*
|
||||||
|
|||||||
10
.travis.yml
@@ -1,10 +1,10 @@
|
|||||||
jdk:
|
jdk:
|
||||||
- openjdk8
|
- openjdk8
|
||||||
dist: trusty
|
dist: xenial
|
||||||
android:
|
android:
|
||||||
components:
|
components:
|
||||||
- android-29
|
- android-29
|
||||||
- build-tools-29.0.2
|
- build-tools-29.0.3
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- git clone --depth=1 --branch=master https://github.com/Anuken/MindustryBuilds ../MindustryBuilds
|
- git clone --depth=1 --branch=master https://github.com/Anuken/MindustryBuilds ../MindustryBuilds
|
||||||
@@ -20,6 +20,12 @@ script:
|
|||||||
- "./gradlew test"
|
- "./gradlew test"
|
||||||
- "./gradlew desktop:dist -Pbuildversion=${TRAVIS_TAG:1}"
|
- "./gradlew desktop:dist -Pbuildversion=${TRAVIS_TAG:1}"
|
||||||
- "./gradlew server: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:
|
deploy:
|
||||||
- provider: releases
|
- provider: releases
|
||||||
skip_cleanup: true
|
skip_cleanup: true
|
||||||
|
|||||||
@@ -26,11 +26,12 @@ This means:
|
|||||||
- 4 spaces indentation
|
- 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.
|
- `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)
|
- 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.
|
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).
|
#### Do not use incompatible Java features (java.util.function, java.awt).
|
||||||
Android [does not support](https://developer.android.com/studio/write/java8-support#supported_features) many of Java 8's features, such as the packages `java.util.function`, `java.util.stream` or `forEach` in collections. Do not use these in your code.
|
Android and RoboVM (iOS) do not support many of Java 8's features, such as the packages `java.util.function`, `java.util.stream` or `forEach` in collections. Do not use these in your code.
|
||||||
If you need to use functional interfaces, use the ones in `arc.func`, which are more or less the same with different naming schemes.
|
If you need to use functional interfaces, use the ones in `arc.func`, which are more or less the same with different naming schemes.
|
||||||
|
|
||||||
The same applies to any class *outside* of the standard `java.[n]io` / `java.net` / `java.util` packages: Most of them are not supported.
|
The same applies to any class *outside* of the standard `java.[n]io` / `java.net` / `java.util` packages: Most of them are not supported.
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
A sandbox tower defense game written in Java.
|
A sandbox tower defense game written in Java.
|
||||||
|
|
||||||
_[Trello Board](https://trello.com/b/aE2tcUwF/mindustry-40-plans)_
|
_[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
|
### Contributing
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
android:appCategory="game"
|
android:appCategory="game"
|
||||||
android:label="@string/app_name"
|
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"/>
|
<meta-data android:name="android.max_aspect" android:value="2.1"/>
|
||||||
<activity
|
<activity
|
||||||
android:name="mindustry.android.AndroidLauncher"
|
android:name="mindustry.android.AndroidLauncher"
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ buildscript{
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies{
|
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'
|
classpath 'com.android.tools.build:gradle:3.4.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,9 +21,7 @@ configurations{ natives }
|
|||||||
repositories{
|
repositories{
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
jcenter()
|
jcenter()
|
||||||
maven{
|
maven{ url "https://maven.google.com" }
|
||||||
url "https://maven.google.com"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
@@ -29,16 +29,10 @@ dependencies{
|
|||||||
|
|
||||||
implementation arcModule("backends:backend-android")
|
implementation arcModule("backends:backend-android")
|
||||||
implementation 'com.jakewharton.android.repackaged:dalvik-dx:9.0.0_r3'
|
implementation 'com.jakewharton.android.repackaged:dalvik-dx:9.0.0_r3'
|
||||||
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi"
|
|
||||||
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a"
|
natives "com.github.Anuken.Arc:natives-android:${getArcHash()}"
|
||||||
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-arm64-v8a"
|
natives "com.github.Anuken.Arc:natives-freetype-android:${getArcHash()}"
|
||||||
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86"
|
natives "com.github.Anuken.Arc:natives-box2d-android:${getArcHash()}"
|
||||||
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"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
task deploy(type: Copy){
|
task deploy(type: Copy){
|
||||||
@@ -50,7 +44,7 @@ task deploy(type: Copy){
|
|||||||
}
|
}
|
||||||
|
|
||||||
android{
|
android{
|
||||||
buildToolsVersion '29.0.2'
|
buildToolsVersion '29.0.3'
|
||||||
compileSdkVersion 29
|
compileSdkVersion 29
|
||||||
sourceSets{
|
sourceSets{
|
||||||
main{
|
main{
|
||||||
@@ -119,25 +113,11 @@ android{
|
|||||||
// the natives configuration, and extracts them to the proper libs/ folders
|
// the natives configuration, and extracts them to the proper libs/ folders
|
||||||
// so they get packed with the APK.
|
// so they get packed with the APK.
|
||||||
task copyAndroidNatives(){
|
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 ->
|
configurations.natives.files.each{ jar ->
|
||||||
def outputDir = null
|
copy{
|
||||||
if(jar.name.endsWith("natives-arm64-v8a.jar")) outputDir = file("libs/arm64-v8a")
|
from zipTree(jar)
|
||||||
if(jar.name.endsWith("natives-armeabi-v7a.jar")) outputDir = file("libs/armeabi-v7a")
|
into file("libs/")
|
||||||
if(jar.name.endsWith("natives-armeabi.jar")) outputDir = file("libs/armeabi")
|
include "**"
|
||||||
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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<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:windowBackground">@android:color/transparent</item>
|
||||||
<item name="android:colorBackgroundCacheHint">@null</item>
|
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||||
<item name="android:windowAnimationStyle">@android:style/Animation</item>
|
<item name="android:windowAnimationStyle">@android:style/Animation</item>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<style name="GdxTheme" parent="android:Theme">
|
<style name="ArcTheme" parent="android:Theme">
|
||||||
<item name="android:windowBackground">@android:color/transparent</item>
|
<item name="android:windowBackground">@android:color/transparent</item>
|
||||||
<item name="android:colorBackgroundCacheHint">@null</item>
|
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||||
<item name="android:windowAnimationStyle">@android:style/Animation</item>
|
<item name="android:windowAnimationStyle">@android:style/Animation</item>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package mindustry.android;
|
package mindustry.android;
|
||||||
|
|
||||||
import android.*;
|
import android.*;
|
||||||
|
import android.annotation.*;
|
||||||
import android.app.*;
|
import android.app.*;
|
||||||
import android.content.*;
|
import android.content.*;
|
||||||
import android.content.pm.*;
|
import android.content.pm.*;
|
||||||
@@ -68,7 +69,7 @@ public class AndroidLauncher extends AndroidApplication{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public org.mozilla.javascript.Context getScriptContext(){
|
public rhino.Context getScriptContext(){
|
||||||
return AndroidRhinoContext.enter(getContext().getCacheDir());
|
return AndroidRhinoContext.enter(getContext().getCacheDir());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,9 +145,9 @@ public class AndroidLauncher extends AndroidApplication{
|
|||||||
|
|
||||||
}, new AndroidApplicationConfiguration(){{
|
}, new AndroidApplicationConfiguration(){{
|
||||||
useImmersiveMode = true;
|
useImmersiveMode = true;
|
||||||
depth = 0;
|
|
||||||
hideStatusBar = true;
|
hideStatusBar = true;
|
||||||
errorHandler = CrashSender::log;
|
errorHandler = CrashSender::log;
|
||||||
|
stencil = 8;
|
||||||
}});
|
}});
|
||||||
checkFiles(getIntent());
|
checkFiles(getIntent());
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import com.android.dx.dex.cf.*;
|
|||||||
import com.android.dx.dex.file.DexFile;
|
import com.android.dx.dex.file.DexFile;
|
||||||
import com.android.dx.merge.*;
|
import com.android.dx.merge.*;
|
||||||
import dalvik.system.*;
|
import dalvik.system.*;
|
||||||
import org.mozilla.javascript.*;
|
import rhino.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.*;
|
import java.nio.*;
|
||||||
|
|||||||
@@ -1,6 +1,2 @@
|
|||||||
apply plugin: "java"
|
|
||||||
|
|
||||||
sourceCompatibility = 1.8
|
|
||||||
sourceSets.main.java.srcDirs = ["src/main/java/"]
|
sourceSets.main.java.srcDirs = ["src/main/java/"]
|
||||||
sourceSets.main.resources.srcDirs = ["src/main/resources/"]
|
sourceSets.main.resources.srcDirs = ["src/main/resources/"]
|
||||||
|
|
||||||
@@ -5,11 +5,67 @@ import java.lang.annotation.*;
|
|||||||
public class Annotations{
|
public class Annotations{
|
||||||
//region entity interfaces
|
//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. */
|
/** Indicates multiple inheritance on a component type. */
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface Depends{
|
public @interface Component{
|
||||||
Class[] value();
|
/** 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. */
|
/** Indicates that a component def is present on all entities. */
|
||||||
@@ -18,11 +74,30 @@ public class Annotations{
|
|||||||
public @interface BaseComponent{
|
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. */
|
/** Indicates an entity definition. */
|
||||||
@Target(ElementType.TYPE)
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface EntityDef{
|
public @interface EntityDef{
|
||||||
|
/** List of component interfaces */
|
||||||
Class[] value();
|
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. */
|
/** Indicates an internal interface for entity components. */
|
||||||
@@ -34,6 +109,25 @@ public class Annotations{
|
|||||||
//endregion
|
//endregion
|
||||||
//region misc. utility
|
//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";
|
||||||
|
}
|
||||||
|
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface StyleDefaults{
|
public @interface StyleDefaults{
|
||||||
@@ -52,13 +146,6 @@ public class Annotations{
|
|||||||
public @interface OverrideCallSuper{
|
public @interface OverrideCallSuper{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Marks a class as serializable. */
|
|
||||||
@Target(ElementType.TYPE)
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
|
||||||
public @interface Serialize{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
//region struct
|
//region struct
|
||||||
|
|
||||||
@@ -153,26 +240,9 @@ public class Annotations{
|
|||||||
PacketPriority priority() default PacketPriority.normal;
|
PacketPriority priority() default PacketPriority.normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Target(ElementType.TYPE)
|
||||||
* 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)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface WriteClass{
|
public @interface TypeIOHandler{
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|||||||
@@ -1,19 +1,28 @@
|
|||||||
package mindustry.annotations;
|
package mindustry.annotations;
|
||||||
|
|
||||||
|
import arc.files.*;
|
||||||
import arc.struct.*;
|
import arc.struct.*;
|
||||||
|
import arc.util.Log;
|
||||||
|
import arc.util.Log.*;
|
||||||
import arc.util.*;
|
import arc.util.*;
|
||||||
import com.squareup.javapoet.*;
|
import com.squareup.javapoet.*;
|
||||||
import com.sun.source.util.*;
|
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 mindustry.annotations.util.*;
|
||||||
|
|
||||||
import javax.annotation.processing.*;
|
import javax.annotation.processing.*;
|
||||||
import javax.lang.model.*;
|
import javax.lang.model.*;
|
||||||
import javax.lang.model.element.*;
|
import javax.lang.model.element.*;
|
||||||
|
import javax.lang.model.type.*;
|
||||||
import javax.lang.model.util.*;
|
import javax.lang.model.util.*;
|
||||||
import javax.tools.Diagnostic.*;
|
import javax.tools.Diagnostic.*;
|
||||||
import javax.tools.*;
|
import javax.tools.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
import java.util.List;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||||
@@ -22,14 +31,18 @@ public abstract class BaseProcessor extends AbstractProcessor{
|
|||||||
public static final String packageName = "mindustry.gen";
|
public static final String packageName = "mindustry.gen";
|
||||||
|
|
||||||
public static Types typeu;
|
public static Types typeu;
|
||||||
public static Elements elementu;
|
public static JavacElements elementu;
|
||||||
public static Filer filer;
|
public static Filer filer;
|
||||||
public static Messager messager;
|
public static Messager messager;
|
||||||
public static Trees trees;
|
public static Trees trees;
|
||||||
|
public static TreeMaker maker;
|
||||||
|
|
||||||
protected int round;
|
protected int round;
|
||||||
protected int rounds = 1;
|
protected int rounds = 1;
|
||||||
protected RoundEnvironment env;
|
protected RoundEnvironment env;
|
||||||
|
protected Fi rootDirectory;
|
||||||
|
|
||||||
|
protected Context context;
|
||||||
|
|
||||||
public static String getMethodName(Element element){
|
public static String getMethodName(Element element){
|
||||||
return ((TypeElement)element.getEnclosingElement()).getQualifiedName().toString() + "." + element.getSimpleName();
|
return ((TypeElement)element.getEnclosingElement()).getQualifiedName().toString() + "." + element.getSimpleName();
|
||||||
@@ -40,16 +53,90 @@ public abstract class BaseProcessor extends AbstractProcessor{
|
|||||||
|| type.equals("long") || type.equals("float") || type.equals("double") || type.equals("char");
|
|| 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{
|
public static void write(TypeSpec.Builder builder) throws Exception{
|
||||||
write(builder, null);
|
write(builder, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void write(TypeSpec.Builder builder, Array<String> imports) throws Exception{
|
public static void write(TypeSpec.Builder builder, Seq<String> imports) throws Exception{
|
||||||
JavaFile file = JavaFile.builder(packageName, builder.build()).skipJavaLangImports(true).build();
|
JavaFile file = JavaFile.builder(packageName, builder.build()).skipJavaLangImports(true).build();
|
||||||
|
|
||||||
if(imports != null){
|
if(imports != null){
|
||||||
String rawSource = file.toString();
|
String rawSource = file.toString();
|
||||||
Array<String> result = new Array<>();
|
Seq<String> result = new Seq<>();
|
||||||
for (String s : rawSource.split("\n", -1)) {
|
for (String s : rawSource.split("\n", -1)) {
|
||||||
result.add(s);
|
result.add(s);
|
||||||
if (s.startsWith("package ")) {
|
if (s.startsWith("package ")) {
|
||||||
@@ -70,27 +157,31 @@ public abstract class BaseProcessor extends AbstractProcessor{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Array<Stype> types(Class<? extends Annotation> type){
|
public Seq<Selement> elements(Class<? extends Annotation> type){
|
||||||
return Array.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof TypeElement)
|
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));
|
.map(e -> new Stype((TypeElement)e));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Array<Svar> fields(Class<? extends Annotation> type){
|
public Seq<Svar> fields(Class<? extends Annotation> type){
|
||||||
return Array.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof VariableElement)
|
return Seq.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof VariableElement)
|
||||||
.map(e -> new Svar((VariableElement)e));
|
.map(e -> new Svar((VariableElement)e));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Array<Smethod> methods(Class<? extends Annotation> type){
|
public Seq<Smethod> methods(Class<? extends Annotation> type){
|
||||||
return Array.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof ExecutableElement)
|
return Seq.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof ExecutableElement)
|
||||||
.map(e -> new Smethod((ExecutableElement)e));
|
.map(e -> new Smethod((ExecutableElement)e));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void err(String message){
|
public static void err(String message){
|
||||||
messager.printMessage(Kind.ERROR, message);
|
messager.printMessage(Kind.ERROR, message);
|
||||||
Log.err("[CODEGEN ERROR] " +message);
|
Log.err("[CODEGEN ERROR] " +message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void err(String message, Element elem){
|
public static void err(String message, Element elem){
|
||||||
messager.printMessage(Kind.ERROR, message, elem);
|
messager.printMessage(Kind.ERROR, message, elem);
|
||||||
Log.err("[CODEGEN ERROR] " + message + ": " + elem);
|
Log.err("[CODEGEN ERROR] " + message + ": " + elem);
|
||||||
}
|
}
|
||||||
@@ -103,20 +194,41 @@ public abstract class BaseProcessor extends AbstractProcessor{
|
|||||||
public synchronized void init(ProcessingEnvironment env){
|
public synchronized void init(ProcessingEnvironment env){
|
||||||
super.init(env);
|
super.init(env);
|
||||||
|
|
||||||
|
JavacProcessingEnvironment javacProcessingEnv = (JavacProcessingEnvironment)env;
|
||||||
|
|
||||||
trees = Trees.instance(env);
|
trees = Trees.instance(env);
|
||||||
typeu = env.getTypeUtils();
|
typeu = env.getTypeUtils();
|
||||||
elementu = env.getElementUtils();
|
elementu = javacProcessingEnv.getElementUtils();
|
||||||
filer = env.getFiler();
|
filer = env.getFiler();
|
||||||
messager = env.getMessager();
|
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
|
@Override
|
||||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
||||||
if(round++ >= rounds) return false; //only process 1 round
|
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;
|
this.env = roundEnv;
|
||||||
try{
|
try{
|
||||||
process(roundEnv);
|
process(roundEnv);
|
||||||
}catch(Exception e){
|
}catch(Throwable e){
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"); //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,929 @@
|
|||||||
|
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());
|
||||||
|
|
||||||
|
//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 +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,29 +5,21 @@ import arc.scene.style.*;
|
|||||||
import arc.struct.*;
|
import arc.struct.*;
|
||||||
import arc.util.serialization.*;
|
import arc.util.serialization.*;
|
||||||
import com.squareup.javapoet.*;
|
import com.squareup.javapoet.*;
|
||||||
import mindustry.annotations.*;
|
|
||||||
import mindustry.annotations.Annotations.*;
|
import mindustry.annotations.Annotations.*;
|
||||||
|
import mindustry.annotations.*;
|
||||||
|
|
||||||
import javax.annotation.processing.*;
|
import javax.annotation.processing.*;
|
||||||
import javax.lang.model.*;
|
import javax.lang.model.*;
|
||||||
import javax.lang.model.element.*;
|
import javax.lang.model.element.*;
|
||||||
import javax.tools.Diagnostic.*;
|
|
||||||
import javax.tools.*;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@SupportedAnnotationTypes("mindustry.annotations.Annotations.StyleDefaults")
|
@SupportedAnnotationTypes("mindustry.annotations.Annotations.StyleDefaults")
|
||||||
public class AssetsProcess extends BaseProcessor{
|
public class AssetsProcess extends BaseProcessor{
|
||||||
private String path;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process(RoundEnvironment env) throws Exception{
|
public void process(RoundEnvironment env) throws Exception{
|
||||||
path = Fi.get(BaseProcessor.filer.createResource(StandardLocation.CLASS_OUTPUT, "no", "no")
|
processSounds("Sounds", rootDirectory + "/core/assets/sounds", "arc.audio.Sound");
|
||||||
.toUri().toURL().toString().substring(System.getProperty("os.name").contains("Windows") ? 6 : "file:".length()))
|
processSounds("Musics", rootDirectory + "/core/assets/music", "arc.audio.Music");
|
||||||
.parent().parent().parent().parent().parent().parent().toString();
|
|
||||||
path = path.replace("%20", " ");
|
|
||||||
|
|
||||||
processSounds("Sounds", path + "/assets/sounds", "arc.audio.Sound");
|
|
||||||
processSounds("Musics", path + "/assets/music", "arc.audio.Music");
|
|
||||||
processUI(env.getElementsAnnotatedWith(StyleDefaults.class));
|
processUI(env.getElementsAnnotatedWith(StyleDefaults.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,8 +30,8 @@ public class AssetsProcess extends BaseProcessor{
|
|||||||
MethodSpec.Builder load = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
MethodSpec.Builder load = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||||
MethodSpec.Builder loadStyles = MethodSpec.methodBuilder("loadStyles").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);
|
MethodSpec.Builder icload = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||||
String resources = path + "/assets-raw/sprites/ui";
|
String resources = rootDirectory + "/core/assets-raw/sprites/ui";
|
||||||
Jval icons = Jval.read(Fi.get(path + "/assets-raw/fontgen/config.json").readString());
|
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),
|
ictype.addField(FieldSpec.builder(ParameterizedTypeName.get(ObjectMap.class, String.class, TextureRegionDrawable.class),
|
||||||
"icons", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("new ObjectMap<>()").build());
|
"icons", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("new ObjectMap<>()").build());
|
||||||
@@ -77,7 +69,7 @@ public class AssetsProcess extends BaseProcessor{
|
|||||||
});
|
});
|
||||||
|
|
||||||
for(Element elem : elements){
|
for(Element elem : elements){
|
||||||
Array.with(((TypeElement)elem).getEnclosedElements()).each(e -> e.getKind() == ElementKind.FIELD, field -> {
|
Seq.with(((TypeElement)elem).getEnclosedElements()).each(e -> e.getKind() == ElementKind.FIELD, field -> {
|
||||||
String fname = field.getSimpleName().toString();
|
String fname = field.getSimpleName().toString();
|
||||||
if(fname.startsWith("default")){
|
if(fname.startsWith("default")){
|
||||||
loadStyles.addStatement("arc.Core.scene.addStyle(" + field.asType().toString() + ".class, mindustry.ui.Styles." + fname + ")");
|
loadStyles.addStatement("arc.Core.scene.addStyle(" + field.asType().toString() + ".class, mindustry.ui.Styles." + fname + ")");
|
||||||
@@ -105,7 +97,7 @@ public class AssetsProcess extends BaseProcessor{
|
|||||||
String name = p.nameWithoutExtension();
|
String name = p.nameWithoutExtension();
|
||||||
|
|
||||||
if(names.contains(name)){
|
if(names.contains(name)){
|
||||||
BaseProcessor.messager.printMessage(Kind.ERROR, "Duplicate file name: " + p.toString() + "!");
|
BaseProcessor.err("Duplicate file name: " + p.toString() + "!");
|
||||||
}else{
|
}else{
|
||||||
names.add(name);
|
names.add(name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,115 +0,0 @@
|
|||||||
package mindustry.annotations.impl;
|
|
||||||
|
|
||||||
import arc.util.serialization.*;
|
|
||||||
import com.squareup.javapoet.*;
|
|
||||||
import mindustry.annotations.Annotations.*;
|
|
||||||
import mindustry.annotations.*;
|
|
||||||
import mindustry.annotations.remote.*;
|
|
||||||
|
|
||||||
import javax.annotation.processing.*;
|
|
||||||
import javax.lang.model.element.Modifier;
|
|
||||||
import javax.lang.model.element.*;
|
|
||||||
import javax.lang.model.util.*;
|
|
||||||
import javax.tools.*;
|
|
||||||
import java.io.*;
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.zip.*;
|
|
||||||
|
|
||||||
@SupportedAnnotationTypes("mindustry.annotations.Annotations.Serialize")
|
|
||||||
public class SerializeProcess extends BaseProcessor{
|
|
||||||
/** Target class name. */
|
|
||||||
private static final String className = "Serialization";
|
|
||||||
/** Name of the base package to put all the generated classes. */
|
|
||||||
private static final String data = "eJy1WHtzEzkS/xb3t3bqrmpm8c464RFggK28uHMdBC5mSV2tKUrWyLbIvE6jcRK8/ib7YffXkmyPExvM1ZIC29Nq9fPXrdb87Y+Ki0s+lixXRdrURt/EqRw240TlVakN41rEP7YfRiqT9S1SU9xiyrmZrFMguRFmndYYlW2g1FIrnqnP3KiyWK2vDBSllpvoqtxELaSJD7U4k+atLqcqlXob11uEQpqWc5/4lK9JtYSMF+NYy1Em2w7ZpeIOs/Xos6pAFhmva9YrPmFfqWdVM8yUYLWBn4JNS5UypcJodu4l19KEzqK+DwgstzI6LLjSykgddFh4XBb1/rNNjnTY0Y2RR81oJPWLKKw6bBixn16wmRqFFVMFNBdCliMGEQU0ul3RbO2RCfbcKmlxVMkQgn/7wLSsm8z0UrAU8opZavdDAjNIx5t+rOrDItVwDVKt92IPvPZnPCr1Gc9lGHDHEpd1fNSoLA2ipG+0Ksb2SWo29N9Oy9paGCUvlcxSGDOi7xpMYi8eS2PJNdahJ7QPjoM99ZwR2Wh/Evu7m0qGEXv+3Mt3kY5m5IvXH/OqkkW62hMWTZZFUTIX3IhJ+G6iyys+zCRDEOfzuQ8SfdXtCD3+kNgIPPvlBavF9nhUC7j2pTEwqf57X4pGS8RnuV/o7ftFWRhZmPjYfZ/LusymAE1rO+cbtqMOh5ToIo0XonwWD6sKkLV1uWbEF5xYM+LaYNtraSZlysa0iXOK44lEsLVM3UoYgLRifzOkemHCEbBnLGJVTMtLCVSiDyApHgM+v88R1CUAwuDw7OT8Te/kY+9kiSvGVVoQrN1jdCuhSwvr0VJYyzS3CaUndGcdLCvXtMXh7Z13E+Gd055ivdP1wj/v89LsWqUtq2HegpHM7iyldKx/UXILttgdJQTCc+QmnPB6Ei44TOlkhlEUoYFcG2ocdWiRGyWtKreUZC6zWrJFhV+g9ZRXKBXaVAHwaEPcyLGyqJ+Frud4FzJVSFCxUV5LEQZXOXqfqAH1FMcDQ5DYr78iVXENpJkwYAEswtY8jOK6GdbOzMDxZLIYmwmMRjeuMi4Au0GBxATBYk8CIzNUj9R1SKoj9ow9ZL//bu1Y7gfx/v6SaspX5ZXUx7xGQ7D45eiVYSC1LpGzJZ/8X8OzujcugELLHHTvd7vd/W73p+4D+nhIH93uI/o4wP/H+P+kJWAlmrWo29Tj3M1kugMjiiYy1Ipsx+lh15hnh3rc5MDe6bWQFdVvGAw5dVotTaMLRnBAilyQknlnh6yd9Pr/PjnvvT+1WXNn1FmTDwHsr2RvnfcvzOLeg2/O4s7h/rZsURLYX5WFlxi5GM1dyAH9joWWqLB3Mq/oMQwwKowvKWTxdFgH1A4zeWEnBDa68kfPihaSKJyLV7GdIigfhpXDTxeve8jMVAnS809pXHMKB8GVKvJxbuqnA/zFg4EuSzMYCJVP9wdBhGTdY1aGKLOekTmV/Zq4+BSo+U8j9Q37yBw7YwyC+5ImHfYjG+kyZ2gk9/c/HiGURyXXKUR75pelZqdcTEgqKcDsstLlWC5qoVVl4lMxKRdscRtnnk9eK8NwSPnHMzpSBnSYIRoiKymLyRbQC6eCwvzzz2fI+7jEb4oltfnDIZpvY+Rb7rDsUb8V0nZfCv+N9AC/heXH3wTlrdXxvfBd7gpw2KUVhqKtKJ8vAl5gcEDA+zc1JW9s5/VKanMTBg3uBDGtQ8qwLDPJC8aLG3CP0IOlnfFUYZgCpZvg61nrFPJRBfnePTfQtU60FdtvoWL3rBExWXhcpnA5Yv+4K+mDnRWixFmAi41MhqjIyy2DIJL7A1gj1DFLlR3YaHBxFynucRMGxygveDzWPD/hhg8GAczpYQIY2xP6X/LaH9KvAbFRrHGKl3lfyjT8SsRa7nRYt8Oe+D8c9gS8H2ATIK5qg2k5mtFTfolPmp39CMu1vjXA0gzByARPtzNFtCkPe10feOJuDReQGSWkTEwUzWkX/f/2d/E49LaDNYhPDt/BP9vGWlLnc0TajGlY3FHBwcEBVnaPo48cdLQi94V5fxWiu+MVCWl54KnzFkaJA/hKHQP5RwPYbFV9501hVC5bVUeFMt8ER4K/w6G9hd64DkcNrKoIdieLhfA7Qo/it7RghTbY5U+51aJL3iZbvjlbtuXeSdf3xPZXfbgLbIrNXUD/n9CyHn8ZW5ZlDVx3QLMfLebAprE3ENu+an8pjZdXIoQf6+6Yo4DTE+4Hp3mF1KwC7pTv4oXjjBKvdnX5D2lIePSAsovWVQjKsmfGtXTNuqoxS7vo6/Z6zad0ALci4n66S077pGjrTKXVuUMgqAznQzID+Q/f45Cml1r24hUl9NKh98ZlyG8d4l4ZTx0bLW9nIphvX23sdc8qDkWcl0MqrF+YhUC0x56yrl8lswRO9qzUC/6F04jV+TFuRkILH377jHAJeldXpTgWW8x286sSFtAyovGeZw2VnxfrdJMxKeKuPsuNNdVa98W1zUXLRyexihY9kVV+ph2Cgn/+5ZpH3mLwaA14u3Vte1m27Buvx3aq8Vdjr5GcWgxli9t7NEOsaU4GYrwmR9jcXOrlNOjjgG0xT3GRR0Ph+tD77dXToo3ZwoIMibAeL/W71wYztwA9e3v7Dx+j43QfPX6w//DJwavEWV24sYjeaywt2GAf1rxhJOz+Hsb4CQ5Rv3Np4tLCSTKHBKlHmIftm8lqEXs7TblhsmbLxoOU/gkCfcbF";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void process(RoundEnvironment env) throws Exception{
|
|
||||||
Set<TypeElement> elements = ElementFilter.typesIn(env.getElementsAnnotatedWith(Serialize.class));
|
|
||||||
|
|
||||||
JavaFileObject obj = filer.createSourceFile(packageName + ".Injector");
|
|
||||||
OutputStream stream = obj.openOutputStream();
|
|
||||||
stream.write(new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(Base64Coder.decode(data)))).readUTF().replace("debug", "gen").getBytes());
|
|
||||||
stream.close();
|
|
||||||
|
|
||||||
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
|
|
||||||
classBuilder.addStaticBlock(CodeBlock.of("Injector.ii();"));
|
|
||||||
classBuilder.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "\"unchecked\"").build());
|
|
||||||
classBuilder.addJavadoc(RemoteProcess.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("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(BaseProcessor.elementu.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 = BaseProcessor.typeu.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("arc.Core.settings.getSerializer(" + typeName + ".class).write(stream, object." + name + ")");
|
|
||||||
readMethod.addStatement("object." + name + " = (" + typeName + ")arc.Core.settings.getSerializer(" + typeName + ".class).read(stream)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readMethod.addStatement("return object");
|
|
||||||
|
|
||||||
serializer.addMethod(writeMethod.build());
|
|
||||||
serializer.addMethod(readMethod.build());
|
|
||||||
|
|
||||||
method.addStatement("arc.Core.settings.setSerializer($N, $L)", BaseProcessor.elementu.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(BaseProcessor.filer);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,7 +29,7 @@ public class StructProcess extends BaseProcessor{
|
|||||||
for(TypeElement elem : elements){
|
for(TypeElement elem : elements){
|
||||||
|
|
||||||
if(!elem.getSimpleName().toString().endsWith("Struct")){
|
if(!elem.getSimpleName().toString().endsWith("Struct")){
|
||||||
BaseProcessor.messager.printMessage(Kind.ERROR, "All classes annotated with @Struct must have their class names end in 'Struct'.", elem);
|
BaseProcessor.err("All classes annotated with @Struct must have their class names end in 'Struct'.", elem);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ public class StructProcess extends BaseProcessor{
|
|||||||
int structTotalSize = (structSize <= 8 ? 8 : structSize <= 16 ? 16 : structSize <= 32 ? 32 : 64);
|
int structTotalSize = (structSize <= 8 ? 8 : structSize <= 16 ? 16 : structSize <= 32 ? 32 : 64);
|
||||||
|
|
||||||
if(variables.size() == 0){
|
if(variables.size() == 0){
|
||||||
BaseProcessor.messager.printMessage(Kind.ERROR, "making a struct with no fields is utterly pointles.", elem);
|
BaseProcessor.err("making a struct with no fields is utterly pointles.", elem);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ public class StructProcess extends BaseProcessor{
|
|||||||
JavaFile.builder(packageName, classBuilder.build()).build().writeTo(BaseProcessor.filer);
|
JavaFile.builder(packageName, classBuilder.build()).build().writeTo(BaseProcessor.filer);
|
||||||
}catch(IllegalArgumentException e){
|
}catch(IllegalArgumentException e){
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
BaseProcessor.messager.printMessage(Kind.ERROR, e.getMessage(), elem);
|
BaseProcessor.err(e.getMessage(), elem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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("@", "\" + content.name + \"");
|
||||||
|
value = value.replace("#1", "\" + INDEX0 + \"");
|
||||||
|
value = value.replace("#2", "\" + INDEX1 + \"");
|
||||||
|
value = value.replace("#", "\" + INDEX0 + \"");
|
||||||
|
value = value.replace("$size", "\" + ((mindustry.world.Block)content).size + \"");
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
package mindustry.annotations.remote;
|
|
||||||
|
|
||||||
import mindustry.annotations.*;
|
|
||||||
import mindustry.annotations.Annotations.ReadClass;
|
|
||||||
import mindustry.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 WriteClass}
|
|
||||||
* and {@link 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){
|
|
||||||
BaseProcessor.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){
|
|
||||||
BaseProcessor.messager.printMessage(Kind.ERROR, "Writer method does not have an accompanying reader: ", writer);
|
|
||||||
}else if(count > 1){
|
|
||||||
BaseProcessor.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(BaseProcessor.getMethodName(reader), BaseProcessor.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,11 +3,11 @@ package mindustry.annotations.remote;
|
|||||||
import com.squareup.javapoet.*;
|
import com.squareup.javapoet.*;
|
||||||
import mindustry.annotations.*;
|
import mindustry.annotations.*;
|
||||||
import mindustry.annotations.Annotations.*;
|
import mindustry.annotations.Annotations.*;
|
||||||
import mindustry.annotations.remote.IOFinder.*;
|
import mindustry.annotations.util.*;
|
||||||
|
import mindustry.annotations.util.TypeIOResolver.*;
|
||||||
|
|
||||||
import javax.annotation.processing.*;
|
import javax.annotation.processing.*;
|
||||||
import javax.lang.model.element.*;
|
import javax.lang.model.element.*;
|
||||||
import javax.tools.Diagnostic.*;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.*;
|
import java.util.stream.*;
|
||||||
|
|
||||||
@@ -15,8 +15,7 @@ import java.util.stream.*;
|
|||||||
/** The annotation processor for generating remote method call code. */
|
/** The annotation processor for generating remote method call code. */
|
||||||
@SupportedAnnotationTypes({
|
@SupportedAnnotationTypes({
|
||||||
"mindustry.annotations.Annotations.Remote",
|
"mindustry.annotations.Annotations.Remote",
|
||||||
"mindustry.annotations.Annotations.WriteClass",
|
"mindustry.annotations.Annotations.TypeIOHandler"
|
||||||
"mindustry.annotations.Annotations.ReadClass",
|
|
||||||
})
|
})
|
||||||
public class RemoteProcess extends BaseProcessor{
|
public class RemoteProcess extends BaseProcessor{
|
||||||
/** Maximum size of each event packet. */
|
/** Maximum size of each event packet. */
|
||||||
@@ -32,7 +31,7 @@ public class RemoteProcess extends BaseProcessor{
|
|||||||
private static final String callLocation = "Call";
|
private static final String callLocation = "Call";
|
||||||
|
|
||||||
//class serializers
|
//class serializers
|
||||||
private HashMap<String, ClassSerializer> serializers;
|
private ClassSerializer serializer;
|
||||||
//all elements with the Remote annotation
|
//all elements with the Remote annotation
|
||||||
private Set<? extends Element> elements;
|
private Set<? extends Element> elements;
|
||||||
//map of all classes to generate by name
|
//map of all classes to generate by name
|
||||||
@@ -51,7 +50,7 @@ public class RemoteProcess extends BaseProcessor{
|
|||||||
//round 1: find all annotations, generate *writers*
|
//round 1: find all annotations, generate *writers*
|
||||||
if(round == 1){
|
if(round == 1){
|
||||||
//get serializers
|
//get serializers
|
||||||
serializers = new IOFinder().findSerializers(roundEnv);
|
serializer = TypeIOResolver.resolve(this);
|
||||||
//last method ID used
|
//last method ID used
|
||||||
int lastMethodID = 0;
|
int lastMethodID = 0;
|
||||||
//find all elements with the Remote annotation
|
//find all elements with the Remote annotation
|
||||||
@@ -72,12 +71,12 @@ public class RemoteProcess extends BaseProcessor{
|
|||||||
|
|
||||||
//check for static
|
//check for static
|
||||||
if(!element.getModifiers().contains(Modifier.STATIC) || !element.getModifiers().contains(Modifier.PUBLIC)){
|
if(!element.getModifiers().contains(Modifier.STATIC) || !element.getModifiers().contains(Modifier.PUBLIC)){
|
||||||
BaseProcessor.messager.printMessage(Kind.ERROR, "All @Remote methods must be public and static: ", element);
|
err("All @Remote methods must be public and static: ", element);
|
||||||
}
|
}
|
||||||
|
|
||||||
//can't generate none methods
|
//can't generate none methods
|
||||||
if(annotation.targets() == Loc.none){
|
if(annotation.targets() == Loc.none){
|
||||||
BaseProcessor.messager.printMessage(Kind.ERROR, "A @Remote method's targets() cannot be equal to 'none':", element);
|
err("A @Remote method's targets() cannot be equal to 'none':", element);
|
||||||
}
|
}
|
||||||
|
|
||||||
//get and create class entry if needed
|
//get and create class entry if needed
|
||||||
@@ -98,12 +97,12 @@ public class RemoteProcess extends BaseProcessor{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//create read/write generators
|
//create read/write generators
|
||||||
RemoteWriteGenerator writegen = new RemoteWriteGenerator(serializers);
|
RemoteWriteGenerator writegen = new RemoteWriteGenerator(serializer);
|
||||||
|
|
||||||
//generate the methods to invoke (write)
|
//generate the methods to invoke (write)
|
||||||
writegen.generateFor(classes, packageName);
|
writegen.generateFor(classes, packageName);
|
||||||
}else if(round == 2){ //round 2: generate all *readers*
|
}else if(round == 2){ //round 2: generate all *readers*
|
||||||
RemoteReadGenerator readgen = new RemoteReadGenerator(serializers);
|
RemoteReadGenerator readgen = new RemoteReadGenerator(serializer);
|
||||||
|
|
||||||
//generate server readers
|
//generate server readers
|
||||||
readgen.generateFor(methods.stream().filter(method -> method.where.isClient).collect(Collectors.toList()), readServerName, packageName, true);
|
readgen.generateFor(methods.stream().filter(method -> method.where.isClient).collect(Collectors.toList()), readServerName, packageName, true);
|
||||||
|
|||||||
@@ -1,24 +1,19 @@
|
|||||||
package mindustry.annotations.remote;
|
package mindustry.annotations.remote;
|
||||||
|
|
||||||
|
import arc.util.io.*;
|
||||||
import com.squareup.javapoet.*;
|
import com.squareup.javapoet.*;
|
||||||
import mindustry.annotations.*;
|
import mindustry.annotations.*;
|
||||||
import mindustry.annotations.remote.IOFinder.ClassSerializer;
|
import mindustry.annotations.util.TypeIOResolver.*;
|
||||||
|
|
||||||
import javax.lang.model.element.*;
|
import javax.lang.model.element.*;
|
||||||
import javax.tools.Diagnostic.Kind;
|
import java.util.*;
|
||||||
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. */
|
/** Generates code for reading remote invoke packets on the client and server. */
|
||||||
public class RemoteReadGenerator{
|
public class RemoteReadGenerator{
|
||||||
private final HashMap<String, ClassSerializer> serializers;
|
private final ClassSerializer serializers;
|
||||||
|
|
||||||
/** Creates a read generator that uses the supplied serializer setup. */
|
/** Creates a read generator that uses the supplied serializer setup. */
|
||||||
public RemoteReadGenerator(HashMap<String, ClassSerializer> serializers){
|
public RemoteReadGenerator(ClassSerializer serializers){
|
||||||
this.serializers = serializers;
|
this.serializers = serializers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,8 +24,7 @@ public class RemoteReadGenerator{
|
|||||||
* @param packageName Full target package name.
|
* @param packageName Full target package name.
|
||||||
* @param needsPlayer Whether this read method requires a reference to the player sender.
|
* @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)
|
public void generateFor(List<MethodEntry> entries, String className, String packageName, boolean needsPlayer) throws Exception{
|
||||||
throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException, IOException{
|
|
||||||
|
|
||||||
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
|
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
|
||||||
classBuilder.addJavadoc(RemoteProcess.autogenWarning);
|
classBuilder.addJavadoc(RemoteProcess.autogenWarning);
|
||||||
@@ -38,19 +32,13 @@ public class RemoteReadGenerator{
|
|||||||
//create main method builder
|
//create main method builder
|
||||||
MethodSpec.Builder readMethod = MethodSpec.methodBuilder("readPacket")
|
MethodSpec.Builder readMethod = MethodSpec.methodBuilder("readPacket")
|
||||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
.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
|
.addParameter(int.class, "id") //ID of method type to read
|
||||||
.returns(void.class);
|
.returns(void.class);
|
||||||
|
|
||||||
if(needsPlayer){
|
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("mindustry.entities.type.Player");
|
|
||||||
//add player parameter
|
//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
|
CodeBlock.Builder readBlock = CodeBlock.builder(); //start building block of code inside read method
|
||||||
@@ -80,26 +68,22 @@ public class RemoteReadGenerator{
|
|||||||
//name of parameter
|
//name of parameter
|
||||||
String varName = var.getSimpleName().toString();
|
String varName = var.getSimpleName().toString();
|
||||||
//captialized version of type name for reading primitives
|
//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
|
//write primitives automatically
|
||||||
if(BaseProcessor.isPrimitive(typeName)){
|
if(BaseProcessor.isPrimitive(typeName)){
|
||||||
if(typeName.equals("boolean")){
|
readBlock.addStatement("$L $L = read.$L()", typeName, varName, pname);
|
||||||
readBlock.addStatement("boolean " + varName + " = buffer.get() == 1");
|
|
||||||
}else{
|
|
||||||
readBlock.addStatement(typeName + " " + varName + " = buffer.get" + capName + "()");
|
|
||||||
}
|
|
||||||
}else{
|
}else{
|
||||||
//else, try and find a serializer
|
//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!
|
if(ser == null){ //make sure a serializer exists!
|
||||||
BaseProcessor.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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//add statement for reading it
|
//add statement for reading it
|
||||||
readBlock.addStatement(typeName + " " + varName + " = " + ser.readMethod + "(buffer)");
|
readBlock.addStatement(typeName + " " + varName + " = " + ser + "(read)");
|
||||||
}
|
}
|
||||||
|
|
||||||
//append variable name to string builder
|
//append variable name to string builder
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
package mindustry.annotations.remote;
|
package mindustry.annotations.remote;
|
||||||
|
|
||||||
|
import arc.struct.*;
|
||||||
|
import arc.util.io.*;
|
||||||
import com.squareup.javapoet.*;
|
import com.squareup.javapoet.*;
|
||||||
|
import mindustry.annotations.Annotations.*;
|
||||||
import mindustry.annotations.*;
|
import mindustry.annotations.*;
|
||||||
import mindustry.annotations.Annotations.Loc;
|
import mindustry.annotations.util.TypeIOResolver.*;
|
||||||
import mindustry.annotations.remote.IOFinder.ClassSerializer;
|
|
||||||
|
|
||||||
import javax.lang.model.element.*;
|
import javax.lang.model.element.*;
|
||||||
import javax.tools.Diagnostic.Kind;
|
import java.io.*;
|
||||||
import java.io.IOException;
|
import java.util.*;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/** Generates code for writing remote invoke packets on the client and server. */
|
/** Generates code for writing remote invoke packets on the client and server. */
|
||||||
public class RemoteWriteGenerator{
|
public class RemoteWriteGenerator{
|
||||||
private final HashMap<String, ClassSerializer> serializers;
|
private final ClassSerializer serializers;
|
||||||
|
|
||||||
/** Creates a write generator that uses the supplied serializer setup. */
|
/** Creates a write generator that uses the supplied serializer setup. */
|
||||||
public RemoteWriteGenerator(HashMap<String, ClassSerializer> serializers){
|
public RemoteWriteGenerator(ClassSerializer serializers){
|
||||||
this.serializers = serializers;
|
this.serializers = serializers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,8 +29,12 @@ public class RemoteWriteGenerator{
|
|||||||
classBuilder.addJavadoc(RemoteProcess.autogenWarning);
|
classBuilder.addJavadoc(RemoteProcess.autogenWarning);
|
||||||
|
|
||||||
//add temporary write buffer
|
//add temporary write buffer
|
||||||
classBuilder.addField(FieldSpec.builder(ByteBuffer.class, "TEMP_BUFFER", Modifier.STATIC, Modifier.PRIVATE, Modifier.FINAL)
|
classBuilder.addField(FieldSpec.builder(ReusableByteOutStream.class, "OUT", Modifier.STATIC, Modifier.PRIVATE, Modifier.FINAL)
|
||||||
.initializer("ByteBuffer.allocate($1L)", RemoteProcess.maxPacketSize).build());
|
.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
|
//go through each method entry in this class
|
||||||
for(MethodEntry methodEntry : entry.methods){
|
for(MethodEntry methodEntry : entry.methods){
|
||||||
@@ -74,12 +77,12 @@ public class RemoteWriteGenerator{
|
|||||||
//validate client methods to make sure
|
//validate client methods to make sure
|
||||||
if(methodEntry.where.isClient){
|
if(methodEntry.where.isClient){
|
||||||
if(elem.getParameters().isEmpty()){
|
if(elem.getParameters().isEmpty()){
|
||||||
BaseProcessor.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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!elem.getParameters().get(0).asType().toString().equals("mindustry.entities.type.Player")){
|
if(!elem.getParameters().get(0).asType().toString().contains("Player")){
|
||||||
BaseProcessor.messager.printMessage(Kind.ERROR, "Client invoke methods should have a first parameter of type Player.", elem);
|
BaseProcessor.err("Client invoke methods should have a first parameter of type Player", elem);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,14 +132,14 @@ public class RemoteWriteGenerator{
|
|||||||
|
|
||||||
//add statement to create packet from pool
|
//add statement to create packet from pool
|
||||||
method.addStatement("$1N packet = $2N.obtain($1N.class, $1N::new)", "mindustry.net.Packets.InvokePacket", "arc.util.pooling.Pools");
|
method.addStatement("$1N packet = $2N.obtain($1N.class, $1N::new)", "mindustry.net.Packets.InvokePacket", "arc.util.pooling.Pools");
|
||||||
//assign buffer
|
|
||||||
method.addStatement("packet.writeBuffer = TEMP_BUFFER");
|
|
||||||
//assign priority
|
//assign priority
|
||||||
method.addStatement("packet.priority = (byte)" + methodEntry.priority.ordinal());
|
method.addStatement("packet.priority = (byte)" + methodEntry.priority.ordinal());
|
||||||
//assign method ID
|
//assign method ID
|
||||||
method.addStatement("packet.type = (byte)" + methodEntry.id);
|
method.addStatement("packet.type = (byte)" + methodEntry.id);
|
||||||
//rewind buffer
|
//reset stream
|
||||||
method.addStatement("TEMP_BUFFER.position(0)");
|
method.addStatement("OUT.reset()");
|
||||||
|
|
||||||
|
method.addTypeVariables(Seq.with(elem.getTypeParameters()).map(BaseProcessor::getTVN));
|
||||||
|
|
||||||
for(int i = 0; i < elem.getParameters().size(); i++){
|
for(int i = 0; i < elem.getParameters().size(); i++){
|
||||||
//first argument is skipped as it is always the player caller
|
//first argument is skipped as it is always the player caller
|
||||||
@@ -146,8 +149,12 @@ public class RemoteWriteGenerator{
|
|||||||
|
|
||||||
VariableElement var = elem.getParameters().get(i);
|
VariableElement var = elem.getParameters().get(i);
|
||||||
|
|
||||||
//add parameter to method
|
try{
|
||||||
method.addParameter(TypeName.get(var.asType()), var.getSimpleName().toString());
|
//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
|
//name of parameter
|
||||||
String varName = var.getSimpleName().toString();
|
String varName = var.getSimpleName().toString();
|
||||||
@@ -164,23 +171,18 @@ public class RemoteWriteGenerator{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(BaseProcessor.isPrimitive(typeName)){ //check if it's a primitive, and if so write it
|
if(BaseProcessor.isPrimitive(typeName)){ //check if it's a primitive, and if so write it
|
||||||
if(typeName.equals("boolean")){ //booleans are special
|
method.addStatement("WRITE.$L($L)", typeName.equals("boolean") ? "bool" : typeName.charAt(0) + "", varName);
|
||||||
method.addStatement("TEMP_BUFFER.put(" + varName + " ? (byte)1 : 0)");
|
|
||||||
}else{
|
|
||||||
method.addStatement("TEMP_BUFFER.put" +
|
|
||||||
capName + "(" + varName + ")");
|
|
||||||
}
|
|
||||||
}else{
|
}else{
|
||||||
//else, try and find a serializer
|
//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!
|
if(ser == null){ //make sure a serializer exists!
|
||||||
BaseProcessor.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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//add statement for writing it
|
//add statement for writing it
|
||||||
method.addStatement(ser.writeMethod + "(TEMP_BUFFER, " + varName + ")");
|
method.addStatement(ser + "(WRITE, " + varName + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(writePlayerSkipCheck){ //write end check
|
if(writePlayerSkipCheck){ //write end check
|
||||||
@@ -188,8 +190,10 @@ public class RemoteWriteGenerator{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//assign packet bytes
|
||||||
|
method.addStatement("packet.bytes = OUT.getBytes()");
|
||||||
//assign packet length
|
//assign packet length
|
||||||
method.addStatement("packet.writeLength = TEMP_BUFFER.position()");
|
method.addStatement("packet.length = OUT.size()");
|
||||||
|
|
||||||
String sendString;
|
String sendString;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,16 @@
|
|||||||
package mindustry.annotations.util;
|
package mindustry.annotations.util;
|
||||||
|
|
||||||
|
import arc.struct.*;
|
||||||
|
import arc.util.ArcAnnotate.*;
|
||||||
import com.squareup.javapoet.*;
|
import com.squareup.javapoet.*;
|
||||||
|
import com.sun.tools.javac.code.Attribute.*;
|
||||||
import mindustry.annotations.*;
|
import mindustry.annotations.*;
|
||||||
|
|
||||||
import javax.lang.model.element.*;
|
import javax.lang.model.element.*;
|
||||||
import javax.lang.model.type.*;
|
import javax.lang.model.type.*;
|
||||||
|
import java.lang.Class;
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
|
||||||
public class Selement<T extends Element>{
|
public class Selement<T extends Element>{
|
||||||
public final T e;
|
public final T e;
|
||||||
@@ -13,6 +19,61 @@ public class Selement<T extends Element>{
|
|||||||
this.e = 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(){
|
public Element up(){
|
||||||
return e.getEnclosingElement();
|
return e.getEnclosingElement();
|
||||||
}
|
}
|
||||||
@@ -45,6 +106,6 @@ public class Selement<T extends Element>{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o){
|
public boolean equals(Object o){
|
||||||
return o != null && o.getClass() == getClass() && ((Selement)o).e.equals(e);
|
return o != null && o.getClass() == getClass() && e.equals(((Selement)o).e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,24 +14,43 @@ public class Smethod extends Selement<ExecutableElement>{
|
|||||||
super(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){
|
public boolean is(Modifier mod){
|
||||||
return e.getModifiers().contains(mod);
|
return e.getModifiers().contains(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Array<TypeMirror> thrown(){
|
public Stype type(){
|
||||||
return Array.with(e.getThrownTypes()).as(TypeMirror.class);
|
return new Stype((TypeElement)up());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Array<TypeName> thrownt(){
|
public Seq<TypeMirror> thrown(){
|
||||||
return Array.with(e.getThrownTypes()).map(TypeName::get);
|
return Seq.with(e.getThrownTypes()).as();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Array<TypeParameterElement> typeVariables(){
|
public Seq<TypeName> thrownt(){
|
||||||
return Array.with(e.getTypeParameters()).as(TypeParameterElement.class);
|
return Seq.with(e.getThrownTypes()).map(TypeName::get);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Array<Svar> params(){
|
public Seq<TypeParameterElement> typeVariables(){
|
||||||
return Array.with(e.getParameters()).map(Svar::new);
|
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(){
|
public TypeMirror ret(){
|
||||||
@@ -45,4 +64,8 @@ public class Smethod extends Selement<ExecutableElement>{
|
|||||||
public MethodTree tree(){
|
public MethodTree tree(){
|
||||||
return BaseProcessor.trees.getTree(e);
|
return BaseProcessor.trees.getTree(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String simpleString(){
|
||||||
|
return name() + "(" + params().toString(", ", p -> BaseProcessor.simpleName(p.mirror().toString())) + ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import mindustry.annotations.*;
|
|||||||
|
|
||||||
import javax.lang.model.element.*;
|
import javax.lang.model.element.*;
|
||||||
import javax.lang.model.type.*;
|
import javax.lang.model.type.*;
|
||||||
import java.lang.annotation.*;
|
|
||||||
|
|
||||||
public class Stype extends Selement<TypeElement>{
|
public class Stype extends Selement<TypeElement>{
|
||||||
|
|
||||||
@@ -17,39 +16,41 @@ public class Stype extends Selement<TypeElement>{
|
|||||||
return new Stype((TypeElement)BaseProcessor.typeu.asElement(mirror));
|
return new Stype((TypeElement)BaseProcessor.typeu.asElement(mirror));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Array<Stype> interfaces(){
|
public String fullName(){
|
||||||
return Array.with(e.getInterfaces()).map(Stype::of);
|
return mirror().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Array<Stype> superclasses(){
|
public Seq<Stype> interfaces(){
|
||||||
Array<Stype> out = new Array<>();
|
return Seq.with(e.getInterfaces()).map(Stype::of);
|
||||||
Stype sup = superclass();
|
}
|
||||||
while(!sup.name().equals("Object")){
|
|
||||||
out.add(sup);
|
public Seq<Stype> allInterfaces(){
|
||||||
sup = sup.superclass();
|
return interfaces().flatMap(s -> s.allInterfaces().and(s)).distinct();
|
||||||
}
|
}
|
||||||
return out;
|
|
||||||
|
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(){
|
public Stype superclass(){
|
||||||
return new Stype((TypeElement)BaseProcessor.typeu.asElement(BaseProcessor.typeu.directSupertypes(mirror()).get(0)));
|
return new Stype((TypeElement)BaseProcessor.typeu.asElement(BaseProcessor.typeu.directSupertypes(mirror()).get(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public <A extends Annotation> A annotation(Class<A> annotation){
|
public Seq<Svar> fields(){
|
||||||
return e.getAnnotation(annotation);
|
return Seq.with(e.getEnclosedElements()).select(e -> e instanceof VariableElement).map(e -> new Svar((VariableElement)e));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Array<Svar> fields(){
|
public Seq<Smethod> methods(){
|
||||||
return Array.with(e.getEnclosedElements()).select(e -> e instanceof VariableElement).map(e -> new Svar((VariableElement)e));
|
return Seq.with(e.getEnclosedElements()).select(e -> e instanceof ExecutableElement
|
||||||
}
|
|
||||||
|
|
||||||
public Array<Smethod> methods(){
|
|
||||||
return Array.with(e.getEnclosedElements()).select(e -> e instanceof ExecutableElement
|
|
||||||
&& !e.getSimpleName().toString().contains("<")).map(e -> new Smethod((ExecutableElement)e));
|
&& !e.getSimpleName().toString().contains("<")).map(e -> new Smethod((ExecutableElement)e));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Array<Smethod> constructors(){
|
public Seq<Smethod> constructors(){
|
||||||
return Array.with(e.getEnclosedElements()).select(e -> e instanceof ExecutableElement
|
return Seq.with(e.getEnclosedElements()).select(e -> e instanceof ExecutableElement
|
||||||
&& e.getSimpleName().toString().contains("<")).map(e -> new Smethod((ExecutableElement)e));
|
&& e.getSimpleName().toString().contains("<")).map(e -> new Smethod((ExecutableElement)e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package mindustry.annotations.util;
|
package mindustry.annotations.util;
|
||||||
|
|
||||||
import com.sun.source.tree.*;
|
import com.sun.source.tree.*;
|
||||||
|
import com.sun.tools.javac.tree.JCTree.*;
|
||||||
import mindustry.annotations.*;
|
import mindustry.annotations.*;
|
||||||
|
|
||||||
import javax.lang.model.element.*;
|
import javax.lang.model.element.*;
|
||||||
@@ -11,6 +12,25 @@ public class Svar extends Selement<VariableElement>{
|
|||||||
super(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){
|
public boolean is(Modifier mod){
|
||||||
return e.getModifiers().contains(mod);
|
return e.getModifiers().contains(mod);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
annotations/src/main/resources/classids.properties
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#Maps entity names to IDs. Autogenerated.
|
||||||
|
|
||||||
|
alpha=0
|
||||||
|
block=1
|
||||||
|
cix=2
|
||||||
|
draug=3
|
||||||
|
mace=4
|
||||||
|
mindustry.entities.comp.BuildingComp=22
|
||||||
|
mindustry.entities.comp.Buildingomp=11
|
||||||
|
mindustry.entities.comp.BulletComp=24
|
||||||
|
mindustry.entities.comp.Bulletomp=5
|
||||||
|
mindustry.entities.comp.DecalComp=6
|
||||||
|
mindustry.entities.comp.EffectComp=7
|
||||||
|
mindustry.entities.comp.EffectInstanceComp=23
|
||||||
|
mindustry.entities.comp.EffectStateComp=25
|
||||||
|
mindustry.entities.comp.FireComp=8
|
||||||
|
mindustry.entities.comp.LaunchCoreComp=21
|
||||||
|
mindustry.entities.comp.PlayerComp=9
|
||||||
|
mindustry.entities.comp.PuddleComp=10
|
||||||
|
mindustry.type.Weather.WeatherComp=12
|
||||||
|
mindustry.type.Weather.WeatherStateComp=26
|
||||||
|
mindustry.world.blocks.campaign.CoreLauncher.LaunchCoreComp=13
|
||||||
|
mindustry.world.blocks.campaign.LaunchPad.LaunchPayloadComp=14
|
||||||
|
oculon=15
|
||||||
|
phantom=16
|
||||||
|
tau=17
|
||||||
|
trident=18
|
||||||
|
vanguard=19
|
||||||
|
wraith=20
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{fields:[{name:ammo,type:int,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,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:int,size:4},{name:armor,type:float,size:4},{name:baseRotation,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,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:int,size:4},{name:armor,type:float,size:4},{name:baseRotation,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,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:int,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,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:int,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,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:int,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,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:int,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,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}]}
|
||||||
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: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:int,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,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: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:int,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,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:int,size:4},{name:armor,type:float,size:4},{name:baseRotation,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,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:int,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,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:int,size:4},{name:armor,type:float,size:4},{name:controller,type:mindustry.entities.units.UnitController,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}]}
|
||||||
119
build.gradle
@@ -25,18 +25,18 @@ buildscript{
|
|||||||
}
|
}
|
||||||
|
|
||||||
allprojects{
|
allprojects{
|
||||||
version = 'release'
|
|
||||||
apply plugin: 'maven'
|
apply plugin: 'maven'
|
||||||
|
|
||||||
|
version = 'release'
|
||||||
group = 'com.github.Anuken'
|
group = 'com.github.Anuken'
|
||||||
|
|
||||||
ext{
|
ext{
|
||||||
versionNumber = '5'
|
versionNumber = '6'
|
||||||
if(!project.hasProperty("versionModifier")) versionModifier = 'release'
|
if(!project.hasProperty("versionModifier")) versionModifier = 'pre-alpha'
|
||||||
if(!project.hasProperty("versionType")) versionType = 'official'
|
if(!project.hasProperty("versionType")) versionType = 'official'
|
||||||
appName = 'Mindustry'
|
appName = 'Mindustry'
|
||||||
gdxVersion = '1.9.10'
|
|
||||||
roboVMVersion = '2.3.8'
|
|
||||||
steamworksVersion = '891ed912791e01fe9ee6237a6497e5212b85c256'
|
steamworksVersion = '891ed912791e01fe9ee6237a6497e5212b85c256'
|
||||||
|
rhinoVersion = 'eeb327d141146663ff3924bd20d2a5da8a6439cc'
|
||||||
|
|
||||||
loadVersionProps = {
|
loadVersionProps = {
|
||||||
return new Properties().with{p -> p.load(file('../core/assets/version.properties').newReader()); return p }
|
return new Properties().with{p -> p.load(file('../core/assets/version.properties').newReader()); return p }
|
||||||
@@ -107,6 +107,7 @@ allprojects{
|
|||||||
output += other.name.substring("bundle".length() + 1, other.name.lastIndexOf('.')) + "\n"
|
output += other.name.substring("bundle".length() + 1, other.name.lastIndexOf('.')) + "\n"
|
||||||
}
|
}
|
||||||
new File(project(':core').projectDir, 'assets/locales').text = output
|
new File(project(':core').projectDir, 'assets/locales').text = output
|
||||||
|
new File(project(':core').projectDir, 'assets/basepartnames').text = new File(project(':core').projectDir, 'assets/baseparts/').list().join("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
writeVersion = {
|
writeVersion = {
|
||||||
@@ -146,6 +147,20 @@ allprojects{
|
|||||||
|
|
||||||
processorFile.text = text.toString().replace(".java", "").replace("/", ".").replace("\\", ".")
|
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{
|
repositories{
|
||||||
@@ -157,24 +172,31 @@ allprojects{
|
|||||||
jcenter()
|
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"){
|
project(":desktop"){
|
||||||
apply plugin: "java"
|
apply plugin: "java"
|
||||||
|
|
||||||
|
compileJava.options.fork = true
|
||||||
|
|
||||||
dependencies{
|
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"
|
implementation "com.github.Anuken:steamworks4j:$steamworksVersion"
|
||||||
compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop"
|
|
||||||
|
|
||||||
compile "com.github.Anuken:steamworks4j:$steamworksVersion"
|
implementation arcModule("backends:backend-sdl")
|
||||||
|
|
||||||
compile arcModule("backends:backend-sdl")
|
|
||||||
compile 'com.github.MinnDevelopment:java-discord-rpc:v2.0.1'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,26 +222,26 @@ project(":ios"){
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies{
|
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")
|
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"){
|
project(":core"){
|
||||||
apply plugin: "java"
|
apply plugin: "java-library"
|
||||||
|
|
||||||
task preGen{
|
task preGen{
|
||||||
outputs.upToDateWhen{ false }
|
outputs.upToDateWhen{ false }
|
||||||
generateLocales()
|
generateLocales()
|
||||||
writeVersion()
|
writeVersion()
|
||||||
writeProcessors()
|
writeProcessors()
|
||||||
|
writePlugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
task copyChangelog{
|
task copyChangelog{
|
||||||
@@ -232,11 +254,11 @@ project(":core"){
|
|||||||
|
|
||||||
def androidLogList = loglines.findAll{ line -> !line.endsWith("]") || line.endsWith("[Mobile]") || line.endsWith("[Android]")}
|
def androidLogList = loglines.findAll{ line -> !line.endsWith("]") || line.endsWith("[Mobile]") || line.endsWith("[Android]")}
|
||||||
def result = ""
|
def result = ""
|
||||||
androidLogList.forEach({line ->
|
androidLogList.forEach{line ->
|
||||||
if(result.length() + line.length() + 1 < maxLength){
|
if(result.length() + line.length() + 1 < maxLength){
|
||||||
result += line + "\n"
|
result += line + "\n"
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
def changelogs = file("../fastlane/metadata/android/en-US/changelogs/")
|
def changelogs = file("../fastlane/metadata/android/en-US/changelogs/")
|
||||||
new File(changelogs, buildVersion + ".txt").text = (result)
|
new File(changelogs, buildVersion + ".txt").text = (result)
|
||||||
new File(changelogs, androidVersion + ".txt").text = (result)
|
new File(changelogs, androidVersion + ".txt").text = (result)
|
||||||
@@ -246,12 +268,15 @@ project(":core"){
|
|||||||
dependencies{
|
dependencies{
|
||||||
compileJava.dependsOn(preGen)
|
compileJava.dependsOn(preGen)
|
||||||
|
|
||||||
compile "org.lz4:lz4-java:1.4.1"
|
api "org.lz4:lz4-java:1.4.1"
|
||||||
compile arcModule("arc-core")
|
api arcModule("arc-core")
|
||||||
compile arcModule("extensions:freetype")
|
api arcModule("extensions:freetype")
|
||||||
compile arcModule("extensions:arcnet")
|
api arcModule("extensions:box2d")
|
||||||
compile "org.mozilla:rhino:1.7.11"
|
api arcModule("extensions:g3d")
|
||||||
if(localArc() && debugged()) compile arcModule("extensions:recorder")
|
api arcModule("extensions:fx")
|
||||||
|
api arcModule("extensions:arcnet")
|
||||||
|
api "com.github.Anuken:rhino:$rhinoVersion"
|
||||||
|
if(localArc() && debugged()) api arcModule("extensions:recorder")
|
||||||
|
|
||||||
compileOnly project(":annotations")
|
compileOnly project(":annotations")
|
||||||
annotationProcessor project(":annotations")
|
annotationProcessor project(":annotations")
|
||||||
@@ -262,8 +287,9 @@ project(":server"){
|
|||||||
apply plugin: "java"
|
apply plugin: "java"
|
||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
compile project(":core")
|
implementation project(":core")
|
||||||
compile arcModule("backends:backend-headless")
|
implementation arcModule("natives:natives-box2d-desktop")
|
||||||
|
implementation arcModule("backends:backend-headless")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,13 +300,17 @@ project(":tests"){
|
|||||||
testImplementation project(":core")
|
testImplementation project(":core")
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-params:5.3.1"
|
testImplementation "org.junit.jupiter:junit-jupiter-params:5.3.1"
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api: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"
|
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.3.1"
|
||||||
compile arcModule("backends:backend-headless")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test{
|
test{
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
workingDir = new File("../core/assets")
|
workingDir = new File("../core/assets")
|
||||||
|
testLogging {
|
||||||
|
exceptionFormat = 'full'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,30 +318,31 @@ project(":tools"){
|
|||||||
apply plugin: "java"
|
apply plugin: "java"
|
||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
compile project(":core")
|
implementation project(":core")
|
||||||
|
|
||||||
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
|
implementation arcModule("natives:natives-desktop")
|
||||||
compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop"
|
implementation arcModule("natives:natives-freetype-desktop")
|
||||||
compile "org.reflections:reflections:0.9.12"
|
implementation arcModule("natives:natives-box2d-desktop")
|
||||||
|
implementation arcModule("backends:backend-headless")
|
||||||
|
|
||||||
compile arcModule("backends:backend-sdl")
|
implementation "org.reflections:reflections:0.9.12"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
project(":annotations"){
|
project(":annotations"){
|
||||||
apply plugin: "java"
|
apply plugin: "java-library"
|
||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
compile 'com.squareup:javapoet:1.12.1'
|
implementation 'com.squareup:javapoet:1.12.1'
|
||||||
compile "com.github.Anuken.Arc:arc-core:$arcHash"
|
implementation "com.github.Anuken.Arc:arc-core:$arcHash"
|
||||||
compile files("${System.getProperty('java.home')}/../lib/tools.jar")
|
implementation files("${System.getProperty('java.home')}/../lib/tools.jar")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task deployAll{
|
task deployAll{
|
||||||
task cleanDeployOutput{
|
task cleanDeployOutput{
|
||||||
doFirst{
|
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----")
|
if(!project.hasProperty("release")) throw new IllegalArgumentException("----\n\nSET THE RELEASE PROJECT PROPERTY FIRST!\n\n----")
|
||||||
|
|
||||||
delete{
|
delete{
|
||||||
|
|||||||
BIN
core/assets-raw/sprites/blocks/campaign/core-silo.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
core/assets-raw/sprites/blocks/campaign/data-processor-2.png
Normal file
|
After Width: | Height: | Size: 836 B |
BIN
core/assets-raw/sprites/blocks/campaign/data-processor-top.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
core/assets-raw/sprites/blocks/campaign/data-processor.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
BIN
core/assets-raw/sprites/blocks/campaign/launch-pad-light.png
Normal file
|
After Width: | Height: | Size: 186 B |
BIN
core/assets-raw/sprites/blocks/campaign/launch-pad.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
core/assets-raw/sprites/blocks/campaign/launchpod.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 516 B |
|
After Width: | Height: | Size: 1.3 KiB |
BIN
core/assets-raw/sprites/blocks/defense/segment.png
Normal file
|
After Width: | Height: | Size: 794 B |
BIN
core/assets-raw/sprites/blocks/distribution/block-loader.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
core/assets-raw/sprites/blocks/distribution/block-unloader.png
Normal file
|
After Width: | Height: | Size: 895 B |
|
After Width: | Height: | Size: 178 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
BIN
core/assets-raw/sprites/blocks/distribution/cross.png
Normal file
|
After Width: | Height: | Size: 170 B |
|
After Width: | Height: | Size: 503 B |
|
After Width: | Height: | Size: 926 B |
|
After Width: | Height: | Size: 513 B |
BIN
core/assets-raw/sprites/blocks/distribution/mass-conveyor.png
Normal file
|
After Width: | Height: | Size: 412 B |
|
After Width: | Height: | Size: 129 B |
|
After Width: | Height: | Size: 1000 B |
|
After Width: | Height: | Size: 720 B |
|
After Width: | Height: | Size: 513 B |
BIN
core/assets-raw/sprites/blocks/distribution/payload-router.png
Normal file
|
After Width: | Height: | Size: 307 B |
BIN
core/assets-raw/sprites/blocks/environment/cliff.png
Normal file
|
After Width: | Height: | Size: 258 B |
|
Before Width: | Height: | Size: 239 B After Width: | Height: | Size: 291 B |
@@ -2,7 +2,7 @@
|
|||||||
duplicatePadding: true,
|
duplicatePadding: true,
|
||||||
combineSubdirectories: true,
|
combineSubdirectories: true,
|
||||||
flattenPaths: true,
|
flattenPaths: true,
|
||||||
maxWidth: 2048,
|
maxWidth: 4096,
|
||||||
maxHeight: 2048,
|
maxHeight: 4096,
|
||||||
fast: true
|
fast: true
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 177 B After Width: | Height: | Size: 237 B |
BIN
core/assets-raw/sprites/blocks/environment/slag.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 239 B After Width: | Height: | Size: 290 B |
|
Before Width: | Height: | Size: 240 B After Width: | Height: | Size: 291 B |
|
Before Width: | Height: | Size: 520 B |
|
Before Width: | Height: | Size: 870 B |
|
Before Width: | Height: | Size: 991 B |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |