Partial 7.0 merge - API preview

This commit is contained in:
Anuken
2021-06-02 11:08:08 -04:00
parent ea75a357ca
commit 28b235ef07
531 changed files with 12356 additions and 6286 deletions

View File

@@ -17,7 +17,8 @@
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/ArcTheme" 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"

View File

@@ -20,22 +20,7 @@ configurations{ natives }
repositories{ repositories{
mavenCentral() mavenCentral()
maven{ url "https://maven.google.com" } maven{ url "https://maven.google.com" }
jcenter() //remove later once google fixes the dependency jcenter() //remove later once google/JetBrains fixes the dependency
}
dependencies{
implementation project(":core")
implementation arcModule("backends:backend-android")
implementation 'com.jakewharton.android.repackaged:dalvik-dx:9.0.0_r3'
natives "com.github.Anuken.Arc:natives-android:${getArcHash()}"
natives "com.github.Anuken.Arc:natives-freetype-android:${getArcHash()}"
natives "com.github.Anuken.Arc:natives-box2d-android:${getArcHash()}"
//android dependencies magically disappear during compilation, thanks gradle!
def sdkFile = new File((String)findSdkDir(), "/platforms/android-29/android.jar")
if(sdkFile.exists()) compileOnly files(sdkFile.absolutePath)
} }
task deploy(type: Copy){ task deploy(type: Copy){
@@ -47,8 +32,8 @@ task deploy(type: Copy){
} }
android{ android{
buildToolsVersion '29.0.3' buildToolsVersion '30.0.2'
compileSdkVersion 29 compileSdkVersion 30
sourceSets{ sourceSets{
main{ main{
manifest.srcFile 'AndroidManifest.xml' manifest.srcFile 'AndroidManifest.xml'
@@ -59,9 +44,13 @@ android{
assets.srcDirs = ['assets', 'src/main/assets', '../core/assets/'] assets.srcDirs = ['assets', 'src/main/assets', '../core/assets/']
jniLibs.srcDirs = ['libs'] jniLibs.srcDirs = ['libs']
} }
gp{
java.srcDirs = ['srcgp']
}
androidTest.setRoot('tests') androidTest.setRoot('tests')
} }
packagingOptions{ packagingOptions{
exclude 'META-INF/robovm/ios/robovm.xml' exclude 'META-INF/robovm/ios/robovm.xml'
} }
@@ -73,7 +62,7 @@ android{
applicationId "io.anuke.mindustry" applicationId "io.anuke.mindustry"
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 29 targetSdkVersion 30
versionName versionNameResult versionName versionNameResult
versionCode = (System.getenv("TRAVIS_BUILD_ID") != null ? System.getenv("TRAVIS_BUILD_ID").toInteger() : vcode) versionCode = (System.getenv("TRAVIS_BUILD_ID") != null ? System.getenv("TRAVIS_BUILD_ID").toInteger() : vcode)
@@ -109,6 +98,14 @@ android{
} }
} }
buildTypes{
all{
minifyEnabled = true
shrinkResources = true
proguardFiles("proguard-rules.pro")
}
}
if(project.hasProperty("RELEASE_STORE_FILE") || System.getenv("CI") == "true"){ if(project.hasProperty("RELEASE_STORE_FILE") || System.getenv("CI") == "true"){
buildTypes{ buildTypes{
release{ release{
@@ -116,10 +113,37 @@ android{
} }
} }
} }
// Specifies one flavor dimension.
flavorDimensions "version"
productFlavors{
standard{
}
gp{
applicationIdSuffix ".gp"
versionNameSuffix "-gp"
}
}
} }
// called every time gradle gets executed, takes the native dependencies of
// the natives configuration, and extracts them to the proper libs/ folders dependencies{
// so they get packed with the APK. implementation project(":core")
implementation arcModule("backends:backend-android")
implementation 'com.jakewharton.android.repackaged:dalvik-dx:9.0.0_r3'
natives "com.github.Anuken.Arc:natives-android:${getArcHash()}"
natives "com.github.Anuken.Arc:natives-freetype-android:${getArcHash()}"
gpImplementation "com.google.android.gms:play-services-games:21.0.0"
gpImplementation "com.google.android.gms:play-services-auth:19.0.0"
//android dependencies magically disappear during compilation, thanks gradle!
def sdkFile = new File((String)findSdkDir(), "/platforms/android-29/android.jar")
if(sdkFile.exists()) compileOnly files(sdkFile.absolutePath)
}
task copyAndroidNatives(){ task copyAndroidNatives(){
configurations.natives.files.each{ jar -> configurations.natives.files.each{ jar ->
copy{ copy{
@@ -131,25 +155,7 @@ task copyAndroidNatives(){
} }
task run(type: Exec){ task run(type: Exec){
def path commandLine "${findSdkDir()}/platform-tools/adb", 'shell', 'am', 'start', '-n', 'io.anuke.mindustry/mindustry.android.AndroidLauncher'
def localProperties = project.file("../local.properties")
if(localProperties.exists()){
Properties properties = new Properties()
localProperties.withInputStream{ instr ->
properties.load(instr)
}
def sdkDir = properties.getProperty('sdk.dir')
if(sdkDir){
path = sdkDir
}else{
path = "$System.env.ANDROID_HOME"
}
}else{
path = "$System.env.ANDROID_HOME"
}
def adb = path + "/platform-tools/adb"
commandLine "$adb", 'shell', 'am', 'start', '-n', 'io.anuke.mindustry/mindustry.android.AndroidLauncher'
} }
if(!project.ext.hasSprites()){ if(!project.ext.hasSprites()){

8
android/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,8 @@
-dontobfuscate
#these are essential packages that should not be "optimized" in any way
#the main purpose of d8 here is to shrink the absurdly-large google play games libraries
-keep class mindustry.** { *; }
-keep class arc.** { *; }
-keep class net.jpountz.** { *; }
-keep class rhino.** { *; }

View File

@@ -1,6 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">Mindustry</string> <string name="app_name">Mindustry</string>
</resources> </resources>

View File

@@ -1,5 +1,4 @@
<resources> <resources>
<style name="ArcTheme" 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>
@@ -8,5 +7,4 @@
<item name="android:windowContentOverlay">@null</item> <item name="android:windowContentOverlay">@null</item>
<item name="android:windowFullscreen">true</item> <item name="android:windowFullscreen">true</item>
</style> </style>
</resources> </resources>

View File

@@ -15,9 +15,11 @@ import arc.func.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import arc.util.*; import arc.util.*;
import dalvik.system.*; import dalvik.system.*;
import io.anuke.mindustry.*;
import mindustry.*; import mindustry.*;
import mindustry.game.Saves.*; import mindustry.game.Saves.*;
import mindustry.io.*; import mindustry.io.*;
import mindustry.mod.*;
import mindustry.net.*; import mindustry.net.*;
import mindustry.ui.dialogs.*; import mindustry.ui.dialogs.*;
@@ -33,6 +35,9 @@ public class AndroidLauncher extends AndroidApplication{
FileChooser chooser; FileChooser chooser;
Runnable permCallback; Runnable permCallback;
Object gpService;
Class<?> serviceClass;
@Override @Override
protected void onCreate(Bundle savedInstanceState){ protected void onCreate(Bundle savedInstanceState){
UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler(); UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
@@ -50,7 +55,7 @@ public class AndroidLauncher extends AndroidApplication{
}); });
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if(doubleScaleTablets && isTablet(this.getContext())){ if(doubleScaleTablets && isTablet(this)){
Scl.setAddition(0.5f); Scl.setAddition(0.5f);
} }
@@ -63,7 +68,9 @@ public class AndroidLauncher extends AndroidApplication{
@Override @Override
public rhino.Context getScriptContext(){ public rhino.Context getScriptContext(){
return AndroidRhinoContext.enter(getContext().getCacheDir()); rhino.Context result = AndroidRhinoContext.enter(((Context)AndroidLauncher.this).getCacheDir());
result.setClassShutter(Scripts::allowClass);
return result;
} }
@Override @Override
@@ -71,8 +78,8 @@ public class AndroidLauncher extends AndroidApplication{
} }
@Override @Override
public ClassLoader loadJar(Fi jar, String mainClass) throws Exception{ public ClassLoader loadJar(Fi jar, ClassLoader parent) throws Exception{
return new DexClassLoader(jar.file().getPath(), getFilesDir().getPath(), null, getClassLoader()); return new DexClassLoader(jar.file().getPath(), getFilesDir().getPath(), null, parent);
} }
@Override @Override
@@ -165,9 +172,20 @@ public class AndroidLauncher extends AndroidApplication{
try{ try{
//new external folder //new external folder
Fi data = Core.files.absolute(getContext().getExternalFilesDir(null).getAbsolutePath()); Fi data = Core.files.absolute(((Context)this).getExternalFilesDir(null).getAbsolutePath());
Core.settings.setDataDirectory(data); Core.settings.setDataDirectory(data);
//delete unused cache folder to free up space
try{
Fi cache = Core.settings.getDataDirectory().child("cache");
if(cache.exists()){
cache.deleteDirectory();
}
}catch(Throwable t){
Log.err("Failed to delete cached folder", t);
}
//move to internal storage if there's no file indicating that it moved //move to internal storage if there's no file indicating that it moved
if(!Core.files.local("files_moved").exists()){ if(!Core.files.local("files_moved").exists()){
Log.info("Moving files to external storage..."); Log.info("Moving files to external storage...");
@@ -209,6 +227,24 @@ public class AndroidLauncher extends AndroidApplication{
} }
} }
@Override
public void onResume(){
super.onResume();
//TODO enable once GPGS is set up on the GP console
if(false && BuildConfig.FLAVOR.equals("gp")){
try{
if(gpService == null){
serviceClass = Class.forName("mindustry.android.GPGameService");
gpService = serviceClass.getConstructor().newInstance();
}
serviceClass.getMethod("onResume", Context.class).invoke(gpService, this);
}catch(Exception e){
Log.err("Failed to update Google Play Services", e);
}
}
}
private void checkFiles(Intent intent){ private void checkFiles(Intent intent){
try{ try{
Uri uri = intent.getData(); Uri uri = intent.getData();

View File

@@ -175,7 +175,7 @@ public class AndroidRhinoContext{
}catch(IOException e){ }catch(IOException e){
e.printStackTrace(); e.printStackTrace();
} }
android.content.Context context = ((AndroidApplication) Core.app).getContext(); android.content.Context context = (android.content.Context)((AndroidApplication)Core.app);
return new DexClassLoader(dexFile.getPath(), VERSION.SDK_INT >= 21 ? context.getCodeCacheDir().getPath() : context.getCacheDir().getAbsolutePath(), null, getParent()).loadClass(name); return new DexClassLoader(dexFile.getPath(), VERSION.SDK_INT >= 21 ? context.getCodeCacheDir().getPath() : context.getCacheDir().getAbsolutePath(), null, getParent()).loadClass(name);
} }

View File

@@ -0,0 +1,40 @@
package mindustry.android;
import android.content.*;
import arc.util.*;
import com.google.android.gms.auth.api.signin.*;
import com.google.android.gms.games.*;
import mindustry.service.*;
public class GPGameService extends GameService{
private GoogleSignInAccount account;
public void onResume(Context context){
Log.info("[GooglePlayService] Resuming.");
GoogleSignInAccount current = GoogleSignIn.getLastSignedInAccount(context);
GoogleSignInOptions options =
new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN)
.requestScopes(Games.SCOPE_GAMES_SNAPSHOTS)
.build();
if(GoogleSignIn.hasPermissions(current, options.getScopeArray())){
this.account = current;
Log.info("Already signed in to Google Play Games.");
}else{
GoogleSignIn.getClient(context, options).silentSignIn().addOnCompleteListener(complete -> {
if(!complete.isSuccessful()){
if(complete.getException() != null){
Log.err("Failed to sign in to Google Play Games.", complete.getException());
}else{
Log.warn("Failed to sign in to Google Play Games.");
}
}else{
this.account = complete.getResult();
Log.info("Signed in to Google Play Games.");
}
});
}
}
}

View File

@@ -118,7 +118,7 @@ public class Annotations{
/** /**
* The region name to load. Variables can be used: * The region name to load. Variables can be used:
* "@" -> block name * "@" -> block name
* "$size" -> block size * "@size" -> block size
* "#" "#1" "#2" -> index number, for arrays * "#" "#1" "#2" -> index number, for arrays
* */ * */
String value(); String value();
@@ -177,12 +177,12 @@ public class Annotations{
//region remote //region remote
public enum PacketPriority{ public enum PacketPriority{
/** Does not get handled unless client is connected. */
low,
/** Gets put in a queue and processed if not connected. */ /** Gets put in a queue and processed if not connected. */
normal, normal,
/** Gets handled immediately, regardless of connection status. */ /** Gets handled immediately, regardless of connection status. */
high, high,
/** Does not get handled unless client is connected. */
low
} }
/** A set of two booleans, one specifying server and one specifying client. */ /** A set of two booleans, one specifying server and one specifying client. */

View File

@@ -2,15 +2,10 @@ package mindustry.annotations;
import arc.files.*; import arc.files.*;
import arc.struct.*; import arc.struct.*;
import arc.util.Log;
import arc.util.Log.*;
import arc.util.*; import arc.util.*;
import arc.util.Log.*;
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.*;
@@ -22,7 +17,6 @@ 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)
@@ -31,19 +25,16 @@ 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 JavacElements elementu; public static Elements 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 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();
} }
@@ -186,7 +177,7 @@ public abstract class BaseProcessor extends AbstractProcessor{
Log.err("[CODEGEN ERROR] " + message + ": " + elem); Log.err("[CODEGEN ERROR] " + message + ": " + elem);
} }
public void err(String message, Selement elem){ public static void err(String message, Selement elem){
err(message, elem.e); err(message, elem.e);
} }
@@ -194,15 +185,11 @@ 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 = javacProcessingEnv.getElementUtils(); elementu = env.getElementUtils();
filer = env.getFiler(); filer = env.getFiler();
messager = env.getMessager(); messager = env.getMessager();
context = ((JavacProcessingEnvironment)env).getContext();
maker = TreeMaker.instance(javacProcessingEnv.getContext());
Log.level = LogLevel.info; Log.level = LogLevel.info;

View File

@@ -132,6 +132,7 @@ public class EntityProcess extends BaseProcessor{
.build())).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT).build()); .build())).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT).build());
} }
//generate interface getters and setters for all "standard" fields
for(Svar field : component.fields().select(e -> !e.is(Modifier.STATIC) && !e.is(Modifier.PRIVATE) && !e.has(Import.class))){ for(Svar field : component.fields().select(e -> !e.is(Modifier.STATIC) && !e.is(Modifier.PRIVATE) && !e.has(Import.class))){
String cname = field.name(); String cname = field.name();
@@ -674,11 +675,28 @@ public class EntityProcess extends BaseProcessor{
//build mapping class for sync IDs //build mapping class for sync IDs
TypeSpec.Builder idBuilder = TypeSpec.classBuilder("EntityMapping").addModifiers(Modifier.PUBLIC) TypeSpec.Builder idBuilder = TypeSpec.classBuilder("EntityMapping").addModifiers(Modifier.PUBLIC)
.addField(FieldSpec.builder(TypeName.get(Prov[].class), "idMap", Modifier.PUBLIC, Modifier.STATIC).initializer("new Prov[256]").build()) .addField(FieldSpec.builder(TypeName.get(Prov[].class), "idMap", Modifier.PUBLIC, Modifier.STATIC).initializer("new Prov[256]").build())
.addField(FieldSpec.builder(ParameterizedTypeName.get(ClassName.get(ObjectMap.class), .addField(FieldSpec.builder(ParameterizedTypeName.get(ClassName.get(ObjectMap.class),
tname(String.class), tname(Prov.class)), tname(String.class), tname(Prov.class)),
"nameMap", Modifier.PUBLIC, Modifier.STATIC).initializer("new ObjectMap<>()").build()) "nameMap", Modifier.PUBLIC, Modifier.STATIC).initializer("new ObjectMap<>()").build())
.addField(FieldSpec.builder(ParameterizedTypeName.get(ClassName.get(IntMap.class), tname(String.class)),
"customIdMap", Modifier.PUBLIC, Modifier.STATIC).initializer("new IntMap<>()").build())
.addMethod(MethodSpec.methodBuilder("register").addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(TypeName.get(int.class))
.addParameter(String.class, "name").addParameter(Prov.class, "constructor")
.addStatement("int next = arc.util.Structs.indexOf(idMap, v -> v == null)")
.addStatement("idMap[next] = constructor")
.addStatement("nameMap.put(name, constructor)")
.addStatement("customIdMap.put(next, name)")
.addStatement("return next")
.addJavadoc("Use this method for obtaining a classId for custom modded unit types. Only call this once for each type. Modded types should return this id in their overridden classId method.")
.build())
.addMethod(MethodSpec.methodBuilder("map").addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addMethod(MethodSpec.methodBuilder("map").addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(TypeName.get(Prov.class)).addParameter(int.class, "id").addStatement("return idMap[id]").build()) .returns(TypeName.get(Prov.class)).addParameter(int.class, "id").addStatement("return idMap[id]").build())
.addMethod(MethodSpec.methodBuilder("map").addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addMethod(MethodSpec.methodBuilder("map").addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(TypeName.get(Prov.class)).addParameter(String.class, "name").addStatement("return nameMap.get(name)").build()); .returns(TypeName.get(Prov.class)).addParameter(String.class, "name").addStatement("return nameMap.get(name)").build());
@@ -707,11 +725,6 @@ public class EntityProcess extends BaseProcessor{
}else{ }else{
//round 3: generate actual classes and implement interfaces //round 3: generate actual classes and implement interfaces
//write base classes
for(TypeSpec.Builder b : baseClasses){
write(b, imports.asArray());
}
//implement each definition //implement each definition
for(EntityDefinition def : definitions){ for(EntityDefinition def : definitions){
@@ -736,6 +749,12 @@ public class EntityProcess extends BaseProcessor{
if(def.legacy) continue; if(def.legacy) continue;
@Nullable TypeSpec.Builder superclass = null;
if(def.extend != null){
superclass = baseClasses.find(b -> (packageName + "." + Reflect.get(b, "name")).equals(def.extend.toString()));
}
//generate getter/setter for each method //generate getter/setter for each method
for(Smethod method : inter.methods()){ for(Smethod method : inter.methods()){
String var = method.name(); String var = method.name();
@@ -743,14 +762,36 @@ public class EntityProcess extends BaseProcessor{
//make sure it's a real variable AND that the component doesn't already implement it somewhere with custom logic //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; if(field == null || methodNames.contains(method.simpleString())) continue;
MethodSpec result = null;
//getter //getter
if(!method.isVoid()){ if(!method.isVoid()){
def.builder.addMethod(MethodSpec.overriding(method.e).addStatement("return " + var).build()); result = MethodSpec.overriding(method.e).addStatement("return " + var).build();
} }
//setter //setter
if(method.isVoid() && !Seq.with(field.annotations).contains(f -> f.type.toString().equals("@mindustry.annotations.Annotations.ReadOnly"))){ 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()); result = MethodSpec.overriding(method.e).addStatement("this." + var + " = " + var).build();
}
//add getter/setter to parent class, if possible. when this happens, skip adding getters setters *here* because they are defined in the superclass.
if(result != null && superclass != null){
FieldSpec superField = Seq.with(superclass.fieldSpecs).find(f -> f.name.equals(var));
//found the right field, try to check for the method already existing now
if(superField != null){
MethodSpec fr = result;
MethodSpec targetMethod = Seq.with(superclass.methodSpecs).find(m -> m.name.equals(var) && m.returnType.equals(fr.returnType));
//if the method isn't added yet, add it. in any case, skip.
if(targetMethod == null){
superclass.addMethod(result);
}
continue;
}
}
if(result != null){
def.builder.addMethod(result);
} }
} }
} }
@@ -758,9 +799,16 @@ public class EntityProcess extends BaseProcessor{
write(def.builder, imports.asArray()); write(def.builder, imports.asArray());
} }
//write base classes last
for(TypeSpec.Builder b : baseClasses){
write(b, imports.asArray());
}
//TODO nulls were an awful idea
//store nulls //store nulls
TypeSpec.Builder nullsBuilder = TypeSpec.classBuilder("Nulls").addModifiers(Modifier.PUBLIC).addModifiers(Modifier.FINAL); TypeSpec.Builder nullsBuilder = TypeSpec.classBuilder("Nulls").addModifiers(Modifier.PUBLIC).addModifiers(Modifier.FINAL);
ObjectSet<String> nullList = ObjectSet.with("unit", "blockUnit"); //TODO should be dynamic
ObjectSet<String> nullList = ObjectSet.with("unit");
//create mock types of all components //create mock types of all components
for(Stype interf : allInterfaces){ for(Stype interf : allInterfaces){
@@ -918,7 +966,7 @@ public class EntityProcess extends BaseProcessor{
} }
String createName(Selement<?> elem){ String createName(Selement<?> elem){
Seq<Stype> comps = types(elem.annotation(EntityDef.class), EntityDef::value).map(this::interfaceToComp);; Seq<Stype> comps = types(elem.annotation(EntityDef.class), EntityDef::value).map(this::interfaceToComp);
comps.sortComparing(Selement::name); comps.sortComparing(Selement::name);
return comps.toString("", s -> s.name().replace("Comp", "")); return comps.toString("", s -> s.name().replace("Comp", ""));
} }

View File

@@ -43,7 +43,7 @@ public class AssetsProcess extends BaseProcessor{
texIcons.each((key, val) -> { texIcons.each((key, val) -> {
String[] split = val.split("\\|"); String[] split = val.split("\\|");
String name = Strings.kebabToCamel(split[1]).replace("Medium", "").replace("Icon", ""); String name = Strings.kebabToCamel(split[1]).replace("Medium", "").replace("Icon", "").replace("Ui", "");
if(SourceVersion.isKeyword(name) || name.equals("char")) name += "i"; if(SourceVersion.isKeyword(name) || name.equals("char")) name += "i";
ichtype.addField(FieldSpec.builder(char.class, name, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).addJavadoc(String.format("\\u%04x", Integer.parseInt(key))).initializer("'" + ((char)Integer.parseInt(key)) + "'").build()); ichtype.addField(FieldSpec.builder(char.class, name, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).addJavadoc(String.format("\\u%04x", Integer.parseInt(key))).initializer("'" + ((char)Integer.parseInt(key)) + "'").build());

View File

@@ -1,154 +0,0 @@
package mindustry.annotations.impl;
import com.sun.source.tree.*;
import com.sun.source.util.*;
import com.sun.tools.javac.code.Scope;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.code.Type.*;
import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.tree.JCTree.*;
import mindustry.annotations.Annotations.*;
import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.lang.model.element.*;
import javax.tools.Diagnostic.*;
import java.lang.annotation.*;
import java.util.*;
@SupportedAnnotationTypes({"java.lang.Override"})
public class CallSuperProcess extends AbstractProcessor{
private Trees trees;
@Override
public void init(ProcessingEnvironment pe){
super.init(pe);
trees = Trees.instance(pe);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
for(Element e : roundEnv.getElementsAnnotatedWith(Override.class)){
if(e.getAnnotation(OverrideCallSuper.class) != null) return false;
CodeAnalyzerTreeScanner codeScanner = new CodeAnalyzerTreeScanner();
codeScanner.methodName = e.getSimpleName().toString();
TreePath tp = trees.getPath(e.getEnclosingElement());
codeScanner.scan(tp, trees);
if(codeScanner.callSuperUsed){
List list = codeScanner.method.getBody().getStatements();
if(!doesCallSuper(list, codeScanner.methodName)){
processingEnv.getMessager().printMessage(Kind.ERROR, "Overriding method '" + codeScanner.methodName + "' must explicitly call super method from its parent class.", e);
}
}
}
return false;
}
private boolean doesCallSuper(List list, String methodName){
for(Object object : list){
if(object instanceof JCTree.JCExpressionStatement){
JCTree.JCExpressionStatement expr = (JCExpressionStatement)object;
String exprString = expr.toString();
if(exprString.startsWith("super." + methodName) && exprString.endsWith(");")) return true;
}
}
return false;
}
@Override
public SourceVersion getSupportedSourceVersion(){
return SourceVersion.RELEASE_8;
}
static class CodeAnalyzerTreeScanner extends TreePathScanner<Object, Trees>{
String methodName;
MethodTree method;
boolean callSuperUsed;
@Override
public Object visitClass(ClassTree classTree, Trees trees){
Tree extendTree = classTree.getExtendsClause();
if(extendTree instanceof JCTypeApply){ //generic classes case
JCTypeApply generic = (JCTypeApply)extendTree;
extendTree = generic.clazz;
}
if(extendTree instanceof JCIdent){
JCIdent tree = (JCIdent)extendTree;
if(tree == null || tree.sym == null) return super.visitClass(classTree, trees);
com.sun.tools.javac.code.Scope members = tree.sym.members();
if(checkScope(members))
return super.visitClass(classTree, trees);
if(checkSuperTypes((ClassType)tree.type))
return super.visitClass(classTree, trees);
}
callSuperUsed = false;
return super.visitClass(classTree, trees);
}
public boolean checkSuperTypes(ClassType type){
if(type.supertype_field != null && type.supertype_field.tsym != null){
if(checkScope(type.supertype_field.tsym.members()))
return true;
else
return checkSuperTypes((ClassType)type.supertype_field);
}
return false;
}
@SuppressWarnings("unchecked")
public boolean checkScope(Scope members){
Iterable<Symbol> it;
try{
it = (Iterable<Symbol>)members.getClass().getMethod("getElements").invoke(members);
}catch(Throwable t){
try{
it = (Iterable<Symbol>)members.getClass().getMethod("getSymbols").invoke(members);
}catch(Exception e){
throw new RuntimeException(e);
}
}
for(Symbol s : it){
if(s instanceof MethodSymbol){
MethodSymbol ms = (MethodSymbol)s;
if(ms.getSimpleName().toString().equals(methodName)){
Annotation annotation = ms.getAnnotation(CallSuper.class);
if(annotation != null){
callSuperUsed = true;
return true;
}
}
}
}
return false;
}
@Override
public Object visitMethod(MethodTree methodTree, Trees trees){
if(methodTree.getName().toString().equals(methodName))
method = methodTree;
return super.visitMethod(methodTree, trees);
}
}
}

View File

@@ -18,6 +18,7 @@ public class LoadRegionProcessor extends BaseProcessor{
@Override @Override
public void process(RoundEnvironment env) throws Exception{ public void process(RoundEnvironment env) throws Exception{
TypeSpec.Builder regionClass = TypeSpec.classBuilder("ContentRegions") TypeSpec.Builder regionClass = TypeSpec.classBuilder("ContentRegions")
.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "\"deprecation\"").build())
.addModifiers(Modifier.PUBLIC); .addModifiers(Modifier.PUBLIC);
MethodSpec.Builder method = MethodSpec.methodBuilder("loadRegions") MethodSpec.Builder method = MethodSpec.methodBuilder("loadRegions")
.addParameter(tname("mindustry.ctype.MappableContent"), "content") .addParameter(tname("mindustry.ctype.MappableContent"), "content")
@@ -34,7 +35,7 @@ public class LoadRegionProcessor extends BaseProcessor{
} }
for(Entry<Stype, Seq<Svar>> entry : fieldMap){ for(Entry<Stype, Seq<Svar>> entry : fieldMap){
method.beginControlFlow("if(content instanceof $T)", entry.key.tname()); method.beginControlFlow("if(content instanceof $L)", entry.key.fullName());
for(Svar field : entry.value){ for(Svar field : entry.value){
Load an = field.annotation(Load.class); Load an = field.annotation(Load.class);
@@ -45,7 +46,7 @@ public class LoadRegionProcessor extends BaseProcessor{
//not an array //not an array
if(dims == 0){ if(dims == 0){
method.addStatement("(($T)content).$L = $T.atlas.find($L$L)", entry.key.tname(), field.name(), Core.class, parse(an.value()), fallbackString); method.addStatement("(($L)content).$L = $T.atlas.find($L$L)", entry.key.fullName(), field.name(), Core.class, parse(an.value()), fallbackString);
}else{ }else{
//is an array, create length string //is an array, create length string
int[] lengths = an.lengths(); int[] lengths = an.lengths();

View File

@@ -0,0 +1,363 @@
package mindustry.annotations.remote;
import arc.struct.*;
import arc.util.io.*;
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 java.io.*;
import static mindustry.annotations.BaseProcessor.*;
/** Generates code for writing remote invoke packets on the client and server. */
public class CallGenerator{
/** Generates all classes in this list. */
public static void generate(ClassSerializer serializer, Seq<MethodEntry> methods) throws IOException{
//create builder
TypeSpec.Builder callBuilder = TypeSpec.classBuilder(RemoteProcess.callLocation).addModifiers(Modifier.PUBLIC);
MethodSpec.Builder register = MethodSpec.methodBuilder("registerPackets")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
//go through each method entry in this class
for(MethodEntry ent : methods){
//builder for the packet type
TypeSpec.Builder packet = TypeSpec.classBuilder(ent.packetClassName)
.addModifiers(Modifier.PUBLIC);
packet.superclass(tname("mindustry.net.Packet"));
//return the correct priority
if(ent.priority != PacketPriority.normal){
packet.addMethod(MethodSpec.methodBuilder("getPriority")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class).returns(int.class).addStatement("return $L", ent.priority.ordinal())
.build());
}
//implement read & write methods
packet.addMethod(makeWriter(ent, serializer));
packet.addMethod(makeReader(ent, serializer));
//generate handlers
if(ent.where.isClient){
packet.addMethod(writeHandleMethod(ent, false));
}
if(ent.where.isServer){
packet.addMethod(writeHandleMethod(ent, true));
}
//register packet
register.addStatement("mindustry.net.Net.registerPacket($L.$L::new)", packageName, ent.packetClassName);
//add fields to the type
for(Svar param : ent.element.params()){
packet.addField(param.tname(), param.name(), Modifier.PUBLIC);
}
//write the 'send event to all players' variant: always happens for clients, but only happens if 'all' is enabled on the server method
if(ent.where.isClient || ent.target.isAll){
writeCallMethod(callBuilder, ent, true, false);
}
//write the 'send event to one player' variant, which is only applicable on the server
if(ent.where.isServer && ent.target.isOne){
writeCallMethod(callBuilder, ent, false, false);
}
//write the forwarded method version
if(ent.where.isServer && ent.forward){
writeCallMethod(callBuilder, ent, true, true);
}
//write the completed packet class
JavaFile.builder(packageName, packet.build()).build().writeTo(BaseProcessor.filer);
}
callBuilder.addMethod(register.build());
//build and write resulting class
TypeSpec spec = callBuilder.build();
JavaFile.builder(packageName, spec).build().writeTo(BaseProcessor.filer);
}
private static MethodSpec makeWriter(MethodEntry ent, ClassSerializer serializer){
MethodSpec.Builder builder = MethodSpec.methodBuilder("write")
.addParameter(Writes.class, "WRITE")
.addModifiers(Modifier.PUBLIC).addAnnotation(Override.class);
Seq<Svar> params = ent.element.params();
for(int i = 0; i < params.size; i++){
//first argument is skipped as it is always the player caller
if(!ent.where.isServer && i == 0){
continue;
}
Svar var = params.get(i);
//name of parameter
String varName = var.name();
//name of parameter type
String typeName = var.mirror().toString();
//special case: method can be called from anywhere to anywhere
//thus, only write the player when the SERVER is writing data, since the client is the only one who reads the player anyway
boolean writePlayerSkipCheck = ent.where == Loc.both && i == 0;
if(writePlayerSkipCheck){ //write begin check
builder.beginControlFlow("if(mindustry.Vars.net.server())");
}
if(BaseProcessor.isPrimitive(typeName)){ //check if it's a primitive, and if so write it
builder.addStatement("WRITE.$L($L)", typeName.equals("boolean") ? "bool" : typeName.charAt(0) + "", varName);
}else{
//else, try and find a serializer
String ser = serializer.writers.get(typeName.replace("mindustry.gen.", ""), SerializerResolver.locate(ent.element.e, var.mirror(), true));
if(ser == null){ //make sure a serializer exists!
BaseProcessor.err("No method to write class type: '" + typeName + "'", var);
}
//add statement for writing it
builder.addStatement(ser + "(WRITE, " + varName + ")");
}
if(writePlayerSkipCheck){ //write end check
builder.endControlFlow();
}
}
return builder.build();
}
private static MethodSpec makeReader(MethodEntry ent, ClassSerializer serializer){
MethodSpec.Builder builder = MethodSpec.methodBuilder("read")
.addParameter(Reads.class, "READ")
.addModifiers(Modifier.PUBLIC).addAnnotation(Override.class);
Seq<Svar> params = ent.element.params();
//go through each parameter
for(int i = 0; i < params.size; i++){
Svar var = params.get(i);
//first argument is skipped as it is always the player caller
if(!ent.where.isServer && i == 0){
continue;
}
//special case: method can be called from anywhere to anywhere
//thus, only read the player when the CLIENT is receiving data, since the client is the only one who cares about the player anyway
boolean writePlayerSkipCheck = ent.where == Loc.both && i == 0;
if(writePlayerSkipCheck){ //write begin check
builder.beginControlFlow("if(mindustry.Vars.net.client())");
}
//full type name of parameter
String typeName = var.mirror().toString();
//name of parameter
String varName = var.name();
//capitalized version of type name for reading primitives
String pname = typeName.equals("boolean") ? "bool" : typeName.charAt(0) + "";
//write primitives automatically
if(BaseProcessor.isPrimitive(typeName)){
builder.addStatement("$L = READ.$L()", varName, pname);
}else{
//else, try and find a serializer
String ser = serializer.readers.get(typeName.replace("mindustry.gen.", ""), SerializerResolver.locate(ent.element.e, var.mirror(), false));
if(ser == null){ //make sure a serializer exists!
BaseProcessor.err("No read method to read class type '" + typeName + "' in method " + ent.targetMethod + "; " + serializer.readers, var);
}
//add statement for reading it
builder.addStatement("$L = $L(READ)", varName, ser);
}
if(writePlayerSkipCheck){ //write end check
builder.endControlFlow();
}
}
return builder.build();
}
/** Creates a specific variant for a method entry. */
private static void writeCallMethod(TypeSpec.Builder classBuilder, MethodEntry ent, boolean toAll, boolean forwarded){
Smethod elem = ent.element;
Seq<Svar> params = elem.params();
//create builder
MethodSpec.Builder method = MethodSpec.methodBuilder(elem.name() + (forwarded ? "__forward" : "")) //add except suffix when forwarding
.addModifiers(Modifier.STATIC)
.returns(void.class);
//forwarded methods aren't intended for use, and are not public
if(!forwarded){
method.addModifiers(Modifier.PUBLIC);
}
//validate client methods to make sure
if(ent.where.isClient){
if(params.isEmpty()){
BaseProcessor.err("Client invoke methods must have a first parameter of type Player", elem);
return;
}
if(!params.get(0).mirror().toString().contains("Player")){
BaseProcessor.err("Client invoke methods should have a first parameter of type Player", elem);
return;
}
}
//if toAll is false, it's a 'send to one player' variant, so add the player as a parameter
if(!toAll){
method.addParameter(ClassName.bestGuess("mindustry.net.NetConnection"), "playerConnection");
}
//add sender to ignore
if(forwarded){
method.addParameter(ClassName.bestGuess("mindustry.net.NetConnection"), "exceptConnection");
}
//call local method if applicable, shouldn't happen when forwarding method as that already happens by default
if(!forwarded && ent.local != Loc.none){
//add in local checks
if(ent.local != Loc.both){
method.beginControlFlow("if(" + getCheckString(ent.local) + " || !mindustry.Vars.net.active())");
}
//concatenate parameters
int index = 0;
StringBuilder results = new StringBuilder();
for(Svar var : params){
//special case: calling local-only methods uses the local player
if(index == 0 && ent.where == Loc.client){
results.append("mindustry.Vars.player");
}else{
results.append(var.name());
}
if(index != params.size - 1) results.append(", ");
index++;
}
//add the statement to call it
method.addStatement("$N." + elem.name() + "(" + results + ")",
((TypeElement)elem.up()).getQualifiedName().toString());
if(ent.local != Loc.both){
method.endControlFlow();
}
}
//start control flow to check if it's actually client/server so no netcode is called
method.beginControlFlow("if(" + getCheckString(ent.where) + ")");
//add statement to create packet from pool
method.addStatement("$1T packet = new $1T()", tname("mindustry.gen." + ent.packetClassName));
method.addTypeVariables(Seq.with(elem.e.getTypeParameters()).map(BaseProcessor::getTVN));
for(int i = 0; i < params.size; i++){
//first argument is skipped as it is always the player caller
if((!ent.where.isServer) && i == 0){
continue;
}
Svar var = params.get(i);
method.addParameter(var.tname(), var.name());
//name of parameter
String varName = var.name();
//special case: method can be called from anywhere to anywhere
//thus, only write the player when the SERVER is writing data, since the client is the only one who reads it
boolean writePlayerSkipCheck = ent.where == Loc.both && i == 0;
if(writePlayerSkipCheck){ //write begin check
method.beginControlFlow("if(mindustry.Vars.net.server())");
}
method.addStatement("packet.$L = $L", varName, varName);
if(writePlayerSkipCheck){ //write end check
method.endControlFlow();
}
}
String sendString;
if(forwarded){ //forward packet
if(!ent.local.isClient){ //if the client doesn't get it called locally, forward it back after validation
sendString = "mindustry.Vars.net.send(";
}else{
sendString = "mindustry.Vars.net.sendExcept(exceptConnection, ";
}
}else if(toAll){ //send to all players / to server
sendString = "mindustry.Vars.net.send(";
}else{ //send to specific client from server
sendString = "playerConnection.send(";
}
//send the actual packet
method.addStatement(sendString + "packet, " + (!ent.unreliable) + ")");
//end check for server/client
method.endControlFlow();
//add method to class, finally
classBuilder.addMethod(method.build());
}
private static String getCheckString(Loc loc){
return
loc.isClient && loc.isServer ? "mindustry.Vars.net.server() || mindustry.Vars.net.client()" :
loc.isClient ? "mindustry.Vars.net.client()" :
loc.isServer ? "mindustry.Vars.net.server()" : "false";
}
/** Generates handleServer / handleClient methods. */
public static MethodSpec writeHandleMethod(MethodEntry ent, boolean isClient){
//create main method builder
MethodSpec.Builder builder = MethodSpec.methodBuilder(isClient ? "handleClient" : "handleServer")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.returns(void.class);
Smethod elem = ent.element;
Seq<Svar> params = elem.params();
if(!isClient){
//add player parameter
builder.addParameter(ClassName.get("mindustry.net", "NetConnection"), "con");
//skip if player is invalid
builder.beginControlFlow("if(con.player == null || con.kicked)");
builder.addStatement("return");
builder.endControlFlow();
//make sure to use the actual player who sent the packet
builder.addStatement("mindustry.gen.Player player = con.player");
}
//execute the relevant method before the forward
//if it throws a ValidateException, the method won't be forwarded
builder.addStatement("$N." + elem.name() + "(" + params.toString(", ", s -> s.name()) + ")", ((TypeElement)elem.up()).getQualifiedName().toString());
//call forwarded method, don't forward on the client reader
if(ent.forward && ent.where.isServer && !isClient){
//call forwarded method
builder.addStatement("$L.$L.$L__forward(con, $L)", packageName, ent.className, elem.name(), params.toString(", ", s -> s.name()));
}
return builder.build();
}
}

View File

@@ -1,15 +0,0 @@
package mindustry.annotations.remote;
import java.util.ArrayList;
/** Represents a class witha list method entries to include in it. */
public class ClassEntry{
/** All methods in this generated class. */
public final ArrayList<MethodEntry> methods = new ArrayList<>();
/** Simple class name. */
public final String name;
public ClassEntry(String name){
this.name = name;
}
}

View File

@@ -1,8 +1,7 @@
package mindustry.annotations.remote; package mindustry.annotations.remote;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
import mindustry.annotations.util.*;
import javax.lang.model.element.ExecutableElement;
/** Class that repesents a remote method to be constructed and put into a class. */ /** Class that repesents a remote method to be constructed and put into a class. */
public class MethodEntry{ public class MethodEntry{
@@ -10,6 +9,8 @@ public class MethodEntry{
public final String className; public final String className;
/** Fully qualified target method to call. */ /** Fully qualified target method to call. */
public final String targetMethod; public final String targetMethod;
/** Simple name of the generated packet class. */
public final String packetClassName;
/** Whether this method can be called on a client/server. */ /** Whether this method can be called on a client/server. */
public final Loc where; public final Loc where;
/** /**
@@ -26,12 +27,13 @@ public class MethodEntry{
/** Unique method ID. */ /** Unique method ID. */
public final int id; public final int id;
/** The element method associated with this entry. */ /** The element method associated with this entry. */
public final ExecutableElement element; public final Smethod element;
/** The assigned packet priority. Only used in clients. */ /** The assigned packet priority. Only used in clients. */
public final PacketPriority priority; public final PacketPriority priority;
public MethodEntry(String className, String targetMethod, Loc where, Variant target, public MethodEntry(String className, String targetMethod, String packetClassName, Loc where, Variant target,
Loc local, boolean unreliable, boolean forward, int id, ExecutableElement element, PacketPriority priority){ Loc local, boolean unreliable, boolean forward, int id, Smethod element, PacketPriority priority){
this.packetClassName = packetClassName;
this.className = className; this.className = className;
this.forward = forward; this.forward = forward;
this.targetMethod = targetMethod; this.targetMethod = targetMethod;

View File

@@ -1,7 +1,7 @@
package mindustry.annotations.remote; package mindustry.annotations.remote;
import arc.struct.*; import arc.struct.*;
import com.squareup.javapoet.*; import arc.util.*;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
import mindustry.annotations.*; import mindustry.annotations.*;
import mindustry.annotations.util.*; import mindustry.annotations.util.*;
@@ -9,7 +9,6 @@ import mindustry.annotations.util.TypeIOResolver.*;
import javax.annotation.processing.*; import javax.annotation.processing.*;
import javax.lang.model.element.*; import javax.lang.model.element.*;
import java.util.*;
/** The annotation processor for generating remote method call code. */ /** The annotation processor for generating remote method call code. */
@@ -18,106 +17,58 @@ import java.util.*;
"mindustry.annotations.Annotations.TypeIOHandler" "mindustry.annotations.Annotations.TypeIOHandler"
}) })
public class RemoteProcess extends BaseProcessor{ public class RemoteProcess extends BaseProcessor{
/** Maximum size of each event packet. */
public static final int maxPacketSize = 8192;
/** Warning on top of each autogenerated file. */
public static final String autogenWarning = "Autogenerated file. Do not modify!\n";
/** Name of class that handles reading and invoking packets on the server. */
private static final String readServerName = "RemoteReadServer";
/** Name of class that handles reading and invoking packets on the client. */
private static final String readClientName = "RemoteReadClient";
/** Simple class name of generated class name. */ /** Simple class name of generated class name. */
private static final String callLocation = "Call"; public static final String callLocation = "Call";
//class serializers
private ClassSerializer serializer;
//all elements with the Remote annotation
private Seq<Smethod> elements;
//map of all classes to generate by name
private HashMap<String, ClassEntry> classMap;
//list of all method entries
private Seq<MethodEntry> methods;
//list of all method entries
private Seq<ClassEntry> classes;
{
rounds = 2;
}
@Override @Override
public void process(RoundEnvironment roundEnv) throws Exception{ public void process(RoundEnvironment roundEnv) throws Exception{
//round 1: find all annotations, generate *writers* //get serializers
if(round == 1){ //class serializers
//get serializers ClassSerializer serializer = TypeIOResolver.resolve(this);
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 //all elements with the Remote annotation
elements = methods(Remote.class); Seq<Smethod> elements = methods(Remote.class);
//map of all classes to generate by name //list of all method entries
classMap = new HashMap<>(); Seq<MethodEntry> methods = new Seq<>();
//list of all method entries
methods = new Seq<>();
//list of all method entries
classes = new Seq<>();
Seq<Smethod> orderedElements = elements.copy(); Seq<Smethod> orderedElements = elements.copy();
orderedElements.sort((a, b) -> -a.toString().compareTo(b.toString())); orderedElements.sortComparing(Selement::toString);
//create methods //create methods
for(Smethod element : orderedElements){ for(Smethod element : orderedElements){
Remote annotation = element.annotation(Remote.class); Remote annotation = element.annotation(Remote.class);
//check for static //check for static
if(!element.is(Modifier.STATIC) || !element.is(Modifier.PUBLIC)){ if(!element.is(Modifier.STATIC) || !element.is(Modifier.PUBLIC)){
err("All @Remote methods must be public and static", element); err("All @Remote methods must be public and static", element);
}
//can't generate none methods
if(annotation.targets() == Loc.none){
err("A @Remote method's targets() cannot be equal to 'none'", element);
}
//get and create class entry if needed
if(!classMap.containsKey(callLocation)){
ClassEntry clas = new ClassEntry(callLocation);
classMap.put(callLocation, clas);
classes.add(clas);
}
ClassEntry entry = classMap.get(callLocation);
//create and add entry
MethodEntry method = new MethodEntry(entry.name, BaseProcessor.getMethodName(element.e), annotation.targets(), annotation.variants(),
annotation.called(), annotation.unreliable(), annotation.forward(), lastMethodID++, element.e, annotation.priority());
entry.methods.add(method);
methods.add(method);
} }
//create read/write generators //can't generate none methods
RemoteWriteGenerator writegen = new RemoteWriteGenerator(serializer); if(annotation.targets() == Loc.none){
err("A @Remote method's targets() cannot be equal to 'none'", element);
}
//generate the methods to invoke (write) String packetName = Strings.capitalize(element.name()) + "CallPacket";
writegen.generateFor(classes, packageName); int[] index = {1};
}else if(round == 2){ //round 2: generate all *readers*
RemoteReadGenerator readgen = new RemoteReadGenerator(serializer);
//generate server readers while(methods.contains(m -> m.packetClassName.equals(packetName + (index[0] == 1 ? "" : index[0])))){
readgen.generateFor(methods.select(method -> method.where.isClient), readServerName, packageName, true); index[0] ++;
//generate client readers }
readgen.generateFor(methods.select(method -> method.where.isServer), readClientName, packageName, false);
//create class for storing unique method hash //create and add entry
TypeSpec.Builder hashBuilder = TypeSpec.classBuilder("MethodHash").addModifiers(Modifier.PUBLIC); MethodEntry method = new MethodEntry(
hashBuilder.addJavadoc(autogenWarning); callLocation, BaseProcessor.getMethodName(element.e), packetName + (index[0] == 1 ? "" : index[0]),
hashBuilder.addField(FieldSpec.builder(int.class, "HASH", Modifier.STATIC, Modifier.PUBLIC, Modifier.FINAL) annotation.targets(), annotation.variants(),
.initializer("$1L", Arrays.hashCode(methods.map(m -> m.element).toArray())).build()); annotation.called(), annotation.unreliable(), annotation.forward(), lastMethodID++,
element, annotation.priority()
);
//build and write resulting hash class methods.add(method);
TypeSpec spec = hashBuilder.build();
JavaFile.builder(packageName, spec).build().writeTo(BaseProcessor.filer);
} }
//generate the methods to invoke, as well as the packet classes
CallGenerator.generate(serializer, methods);
} }
} }

View File

@@ -1,129 +0,0 @@
package mindustry.annotations.remote;
import arc.struct.*;
import arc.util.io.*;
import com.squareup.javapoet.*;
import mindustry.annotations.*;
import mindustry.annotations.util.TypeIOResolver.*;
import javax.lang.model.element.*;
/** Generates code for reading remote invoke packets on the client and server. */
public class RemoteReadGenerator{
private final ClassSerializer serializers;
/** Creates a read generator that uses the supplied serializer setup. */
public RemoteReadGenerator(ClassSerializer serializers){
this.serializers = serializers;
}
/**
* Generates a class for reading remote invoke packets.
* @param entries List of methods to use.
* @param className Simple target class name.
* @param packageName Full target package name.
* @param needsPlayer Whether this read method requires a reference to the player sender.
*/
public void generateFor(Seq<MethodEntry> entries, String className, String packageName, boolean needsPlayer) throws Exception{
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
classBuilder.addJavadoc(RemoteProcess.autogenWarning);
//create main method builder
MethodSpec.Builder readMethod = MethodSpec.methodBuilder("readPacket")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(Reads.class, "read") //buffer to read form
.addParameter(int.class, "id") //ID of method type to read
.returns(void.class);
if(needsPlayer){
//add player parameter
readMethod.addParameter(ClassName.get(packageName, "Player"), "player");
}
CodeBlock.Builder readBlock = CodeBlock.builder(); //start building block of code inside read method
boolean started = false; //whether an if() statement has been written yet
for(MethodEntry entry : entries){
//write if check for this entry ID
if(!started){
started = true;
readBlock.beginControlFlow("if(id == " + entry.id + ")");
}else{
readBlock.nextControlFlow("else if(id == " + entry.id + ")");
}
readBlock.beginControlFlow("try");
//concatenated list of variable names for method invocation
StringBuilder varResult = new StringBuilder();
//go through each parameter
for(int i = 0; i < entry.element.getParameters().size(); i++){
VariableElement var = entry.element.getParameters().get(i);
if(!needsPlayer || i != 0){ //if client, skip first parameter since it's always of type player and doesn't need to be read
//full type name of parameter
String typeName = var.asType().toString();
//name of parameter
String varName = var.getSimpleName().toString();
//captialized version of type name for reading primitives
String pname = typeName.equals("boolean") ? "bool" : typeName.charAt(0) + "";
//write primitives automatically
if(BaseProcessor.isPrimitive(typeName)){
readBlock.addStatement("$L $L = read.$L()", typeName, varName, pname);
}else{
//else, try and find a serializer
String ser = serializers.readers.get(typeName.replace("mindustry.gen.", ""), SerializerResolver.locate(entry.element, var.asType(), false));
if(ser == null){ //make sure a serializer exists!
BaseProcessor.err("No read method to read class type '" + typeName + "' in method " + entry.targetMethod + "; " + serializers.readers, var);
return;
}
//add statement for reading it
readBlock.addStatement(typeName + " " + varName + " = " + ser + "(read)");
}
//append variable name to string builder
varResult.append(var.getSimpleName());
if(i != entry.element.getParameters().size() - 1) varResult.append(", ");
}else{
varResult.append("player");
if(i != entry.element.getParameters().size() - 1) varResult.append(", ");
}
}
//execute the relevant method before the forward
//if it throws a ValidateException, the method won't be forwarded
readBlock.addStatement("$N." + entry.element.getSimpleName() + "(" + varResult.toString() + ")", ((TypeElement)entry.element.getEnclosingElement()).getQualifiedName().toString());
//call forwarded method, don't forward on the client reader
if(entry.forward && entry.where.isServer && needsPlayer){
//call forwarded method
readBlock.addStatement(packageName + "." + entry.className + "." + entry.element.getSimpleName() +
"__forward(player.con" + (varResult.length() == 0 ? "" : ", ") + varResult.toString() + ")");
}
readBlock.nextControlFlow("catch (java.lang.Exception e)");
readBlock.addStatement("throw new java.lang.RuntimeException(\"Failed to read remote method '" + entry.element.getSimpleName() + "'!\", e)");
readBlock.endControlFlow();
}
//end control flow if necessary
if(started){
readBlock.nextControlFlow("else");
readBlock.addStatement("throw new $1N(\"Invalid read method ID: \" + id + \"\")", RuntimeException.class.getName()); //handle invalid method IDs
readBlock.endControlFlow();
}
//add block and method to class
readMethod.addCode(readBlock.build());
classBuilder.addMethod(readMethod.build());
//build and write resulting class
TypeSpec spec = classBuilder.build();
JavaFile.builder(packageName, spec).build().writeTo(BaseProcessor.filer);
}
}

View File

@@ -1,228 +0,0 @@
package mindustry.annotations.remote;
import arc.struct.*;
import arc.util.io.*;
import com.squareup.javapoet.*;
import mindustry.annotations.Annotations.*;
import mindustry.annotations.*;
import mindustry.annotations.util.TypeIOResolver.*;
import javax.lang.model.element.*;
import java.io.*;
/** Generates code for writing remote invoke packets on the client and server. */
public class RemoteWriteGenerator{
private final ClassSerializer serializers;
/** Creates a write generator that uses the supplied serializer setup. */
public RemoteWriteGenerator(ClassSerializer serializers){
this.serializers = serializers;
}
/** Generates all classes in this list. */
public void generateFor(Seq<ClassEntry> entries, String packageName) throws IOException{
for(ClassEntry entry : entries){
//create builder
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(entry.name).addModifiers(Modifier.PUBLIC);
classBuilder.addJavadoc(RemoteProcess.autogenWarning);
//add temporary write buffer
classBuilder.addField(FieldSpec.builder(ReusableByteOutStream.class, "OUT", Modifier.STATIC, Modifier.PRIVATE, Modifier.FINAL)
.initializer("new ReusableByteOutStream($L)", RemoteProcess.maxPacketSize).build());
//add writer for that buffer
classBuilder.addField(FieldSpec.builder(Writes.class, "WRITE", Modifier.STATIC, Modifier.PRIVATE, Modifier.FINAL)
.initializer("new Writes(new $T(OUT))", DataOutputStream.class).build());
//go through each method entry in this class
for(MethodEntry methodEntry : entry.methods){
//write the 'send event to all players' variant: always happens for clients, but only happens if 'all' is enabled on the server method
if(methodEntry.where.isClient || methodEntry.target.isAll){
writeMethodVariant(classBuilder, methodEntry, true, false);
}
//write the 'send event to one player' variant, which is only applicable on the server
if(methodEntry.where.isServer && methodEntry.target.isOne){
writeMethodVariant(classBuilder, methodEntry, false, false);
}
//write the forwarded method version
if(methodEntry.where.isServer && methodEntry.forward){
writeMethodVariant(classBuilder, methodEntry, true, true);
}
}
//build and write resulting class
TypeSpec spec = classBuilder.build();
JavaFile.builder(packageName, spec).build().writeTo(BaseProcessor.filer);
}
}
/** Creates a specific variant for a method entry. */
private void writeMethodVariant(TypeSpec.Builder classBuilder, MethodEntry methodEntry, boolean toAll, boolean forwarded){
ExecutableElement elem = methodEntry.element;
//create builder
MethodSpec.Builder method = MethodSpec.methodBuilder(elem.getSimpleName().toString() + (forwarded ? "__forward" : "")) //add except suffix when forwarding
.addModifiers(Modifier.STATIC)
.returns(void.class);
//forwarded methods aren't intended for use, and are not public
if(!forwarded){
method.addModifiers(Modifier.PUBLIC);
}
//validate client methods to make sure
if(methodEntry.where.isClient){
if(elem.getParameters().isEmpty()){
BaseProcessor.err("Client invoke methods must have a first parameter of type Player", elem);
return;
}
if(!elem.getParameters().get(0).asType().toString().contains("Player")){
BaseProcessor.err("Client invoke methods should have a first parameter of type Player", elem);
return;
}
}
//if toAll is false, it's a 'send to one player' variant, so add the player as a parameter
if(!toAll){
method.addParameter(ClassName.bestGuess("mindustry.net.NetConnection"), "playerConnection");
}
//add sender to ignore
if(forwarded){
method.addParameter(ClassName.bestGuess("mindustry.net.NetConnection"), "exceptConnection");
}
//call local method if applicable, shouldn't happen when forwarding method as that already happens by default
if(!forwarded && methodEntry.local != Loc.none){
//add in local checks
if(methodEntry.local != Loc.both){
method.beginControlFlow("if(" + getCheckString(methodEntry.local) + " || !mindustry.Vars.net.active())");
}
//concatenate parameters
int index = 0;
StringBuilder results = new StringBuilder();
for(VariableElement var : elem.getParameters()){
//special case: calling local-only methods uses the local player
if(index == 0 && methodEntry.where == Loc.client){
results.append("mindustry.Vars.player");
}else{
results.append(var.getSimpleName());
}
if(index != elem.getParameters().size() - 1) results.append(", ");
index++;
}
//add the statement to call it
method.addStatement("$N." + elem.getSimpleName() + "(" + results.toString() + ")",
((TypeElement)elem.getEnclosingElement()).getQualifiedName().toString());
if(methodEntry.local != Loc.both){
method.endControlFlow();
}
}
//start control flow to check if it's actually client/server so no netcode is called
method.beginControlFlow("if(" + getCheckString(methodEntry.where) + ")");
//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");
//assign priority
method.addStatement("packet.priority = (byte)" + methodEntry.priority.ordinal());
//assign method ID
method.addStatement("packet.type = (byte)" + methodEntry.id);
//reset stream
method.addStatement("OUT.reset()");
method.addTypeVariables(Seq.with(elem.getTypeParameters()).map(BaseProcessor::getTVN));
for(int i = 0; i < elem.getParameters().size(); i++){
//first argument is skipped as it is always the player caller
if((!methodEntry.where.isServer/* || methodEntry.mode == Loc.both*/) && i == 0){
continue;
}
VariableElement var = elem.getParameters().get(i);
try{
//add parameter to method
method.addParameter(TypeName.get(var.asType()), var.getSimpleName().toString());
}catch(Throwable t){
throw new RuntimeException("Error parsing method " + methodEntry.targetMethod);
}
//name of parameter
String varName = var.getSimpleName().toString();
//name of parameter type
String typeName = var.asType().toString();
//captialized version of type name for writing primitives
String capName = typeName.equals("byte") ? "" : Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1);
//special case: method can be called from anywhere to anywhere
//thus, only write the player when the SERVER is writing data, since the client is the only one who reads it
boolean writePlayerSkipCheck = methodEntry.where == Loc.both && i == 0;
if(writePlayerSkipCheck){ //write begin check
method.beginControlFlow("if(mindustry.Vars.net.server())");
}
if(BaseProcessor.isPrimitive(typeName)){ //check if it's a primitive, and if so write it
method.addStatement("WRITE.$L($L)", typeName.equals("boolean") ? "bool" : typeName.charAt(0) + "", varName);
}else{
//else, try and find a serializer
String ser = serializers.writers.get(typeName.replace("mindustry.gen.", ""), SerializerResolver.locate(elem, var.asType(), true));
if(ser == null){ //make sure a serializer exists!
BaseProcessor.err("No @WriteClass method to write class type: '" + typeName + "'", var);
return;
}
//add statement for writing it
method.addStatement(ser + "(WRITE, " + varName + ")");
}
if(writePlayerSkipCheck){ //write end check
method.endControlFlow();
}
}
//assign packet bytes
method.addStatement("packet.bytes = OUT.getBytes()");
//assign packet length
method.addStatement("packet.length = OUT.size()");
String sendString;
if(forwarded){ //forward packet
if(!methodEntry.local.isClient){ //if the client doesn't get it called locally, forward it back after validation
sendString = "mindustry.Vars.net.send(";
}else{
sendString = "mindustry.Vars.net.sendExcept(exceptConnection, ";
}
}else if(toAll){ //send to all players / to server
sendString = "mindustry.Vars.net.send(";
}else{ //send to specific client from server
sendString = "playerConnection.send(";
}
//send the actual packet
method.addStatement(sendString + "packet, " +
(methodEntry.unreliable ? "mindustry.net.Net.SendMode.udp" : "mindustry.net.Net.SendMode.tcp") + ")");
//end check for server/client
method.endControlFlow();
//add method to class, finally
classBuilder.addMethod(method.build());
}
private String getCheckString(Loc loc){
return loc.isClient && loc.isServer ? "mindustry.Vars.net.server() || mindustry.Vars.net.client()" :
loc.isClient ? "mindustry.Vars.net.client()" :
loc.isServer ? "mindustry.Vars.net.server()" : "false";
}
}

View File

@@ -1,7 +1,6 @@
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.*;
@@ -16,10 +15,6 @@ public class Svar extends Selement<VariableElement>{
return up().asType().toString() + "#" + super.toString().replace("mindustry.gen.", ""); return up().asType().toString() + "#" + super.toString().replace("mindustry.gen.", "");
} }
public JCVariableDecl jtree(){
return (JCVariableDecl)BaseProcessor.elementu.getTree(e);
}
public Stype enclosingType(){ public Stype enclosingType(){
return new Stype((TypeElement)up()); return new Stype((TypeElement)up());
} }

View File

@@ -22,6 +22,7 @@ mindustry.entities.comp.PosTeamDef=28
mindustry.entities.comp.PuddleComp=13 mindustry.entities.comp.PuddleComp=13
mindustry.type.Weather.WeatherStateComp=14 mindustry.type.Weather.WeatherStateComp=14
mindustry.world.blocks.campaign.LaunchPad.LaunchPayloadComp=15 mindustry.world.blocks.campaign.LaunchPad.LaunchPayloadComp=15
mindustry.world.blocks.campaign.PayloadLaunchPad.LargeLaunchPayloadComp=34
mindustry.world.blocks.defense.ForceProjector.ForceDrawComp=22 mindustry.world.blocks.defense.ForceProjector.ForceDrawComp=22
mono=16 mono=16
nova=17 nova=17

View File

@@ -0,0 +1 @@
{fields:[{name:lifetime,type:float},{name:payload,type:mindustry.world.blocks.payloads.Payload},{name:team,type:mindustry.game.Team},{name:time,type:float},{name:x,type:float},{name:y,type:float}]}

View File

@@ -29,18 +29,18 @@ plugins{
} }
allprojects{ allprojects{
apply plugin: 'maven' apply plugin: 'maven-publish'
version = 'release' version = 'release'
group = 'com.github.Anuken' group = 'com.github.Anuken'
ext{ ext{
versionNumber = '6' versionNumber = '7'
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'
steamworksVersion = '891ed912791e01fe9ee6237a6497e5212b85c256' steamworksVersion = '0b86023401880bb5e586bc404bedbaae9b1f1c94'
rhinoVersion = '378626d8abc552bba57864358358045d2f2dbe9b' rhinoVersion = '099aed6c82f8094b3ba39a273b8d2ba7bdcc6443'
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 }
@@ -88,13 +88,11 @@ allprojects{
} }
hasSprites = { hasSprites = {
return new File(rootDir, "core/assets/sprites/sprites.atlas").exists() return new File(rootDir, "core/assets/sprites/sprites.aatls").exists()
} }
getModifierString = { getModifierString = {
if(versionModifier != "release"){ if(versionModifier != "release") return "[${versionModifier.toUpperCase()}]"
return "[${versionModifier.toUpperCase()}]"
}
return "" return ""
} }
@@ -112,8 +110,7 @@ allprojects{
def v = System.getenv("ANDROID_HOME") def v = System.getenv("ANDROID_HOME")
if(v != null) return v if(v != null) return v
//rootDir is null here, amazing. brilliant. //rootDir is null here, amazing. brilliant.
def file = new File("local.properties") def file = new File(rootDir, "local.properties")
if(!file.exists()) file = new File("../local.properties")
def props = new Properties().with{p -> p.load(file.newReader()); return p } def props = new Properties().with{p -> p.load(file.newReader()); return p }
return props.get("sdk.dir") return props.get("sdk.dir")
} }
@@ -200,10 +197,20 @@ allprojects{
tasks.withType(JavaCompile){ tasks.withType(JavaCompile){
targetCompatibility = 8 targetCompatibility = 8
sourceCompatibility = 14 //TODO fix dynamically, this is a hack
if(System.getProperty("user.name") == "anuke"){
sourceCompatibility = JavaVersion.VERSION_15
}else{
sourceCompatibility = JavaVersion.VERSION_14
}
options.encoding = "UTF-8" options.encoding = "UTF-8"
options.compilerArgs += ["-Xlint:deprecation"] options.compilerArgs += ["-Xlint:deprecation"]
dependsOn clearCache dependsOn clearCache
options.forkOptions.jvmArgs.addAll([
'--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED',
'--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED'
])
} }
} }
@@ -211,15 +218,14 @@ configure(project(":annotations")){
tasks.withType(JavaCompile){ tasks.withType(JavaCompile){
targetCompatibility = 8 targetCompatibility = 8
sourceCompatibility = 8 sourceCompatibility = 8
options.fork = true
} }
} }
//compile with java 8 compatibility for everything except the annotation project //compile with java 8 compatibility for everything except the annotation project
configure(subprojects - project(":annotations")){ configure(subprojects - project(":annotations")){
tasks.withType(JavaCompile){ tasks.withType(JavaCompile){
if(JavaVersion.current() != JavaVersion.VERSION_1_8){ options.compilerArgs.addAll(['--release', '8', '--enable-preview'])
options.compilerArgs.addAll(['--release', '8', '--enable-preview'])
}
doFirst{ doFirst{
options.compilerArgs = options.compilerArgs.findAll{it != '--enable-preview' } options.compilerArgs = options.compilerArgs.findAll{it != '--enable-preview' }
@@ -242,9 +248,9 @@ project(":desktop"){
dependencies{ dependencies{
implementation project(":core") implementation project(":core")
implementation arcModule("extensions:discord")
implementation arcModule("natives:natives-desktop") implementation arcModule("natives:natives-desktop")
implementation arcModule("natives:natives-freetype-desktop") implementation arcModule("natives:natives-freetype-desktop")
implementation 'com.github.MinnDevelopment:java-discord-rpc:v2.0.1'
if(debugged()) implementation project(":debug") if(debugged()) implementation project(":debug")
@@ -360,14 +366,16 @@ project(":core"){
dependencies{ dependencies{
compileJava.dependsOn(preGen) compileJava.dependsOn(preGen)
api "org.lz4:lz4-java:1.4.1" api "org.lz4:lz4-java:1.7.1"
api arcModule("arc-core") api arcModule("arc-core")
api arcModule("extensions:flabel")
api arcModule("extensions:freetype") api arcModule("extensions:freetype")
api arcModule("extensions:g3d") api arcModule("extensions:g3d")
api arcModule("extensions:fx") api arcModule("extensions:fx")
api arcModule("extensions:arcnet") api arcModule("extensions:arcnet")
api "com.github.Anuken:rhino:$rhinoVersion" api "com.github.Anuken:rhino:$rhinoVersion"
if(localArc() && debugged()) api arcModule("extensions:recorder") if(localArc() && debugged()) api arcModule("extensions:recorder")
if(localArc()) api arcModule(":extensions:packer")
annotationProcessor 'com.github.Anuken:jabel:34e4c172e65b3928cd9eabe1993654ea79c409cd' annotationProcessor 'com.github.Anuken:jabel:34e4c172e65b3928cd9eabe1993654ea79c409cd'
compileOnly project(":annotations") compileOnly project(":annotations")
@@ -425,7 +433,7 @@ project(":tests"){
test{ test{
useJUnitPlatform() useJUnitPlatform()
workingDir = new File("../core/assets") workingDir = new File("../core/assets")
testLogging { testLogging{
exceptionFormat = 'full' exceptionFormat = 'full'
showStandardStreams = true showStandardStreams = true
} }
@@ -453,6 +461,21 @@ project(":annotations"){
} }
} }
configure([":core", ":desktop", ":server", ":tools"].collect{project(it)}){
java{
withJavadocJar()
withSourcesJar()
}
publishing{
publications{
maven(MavenPublication){
from components.java
}
}
}
}
task deployAll{ task deployAll{
task cleanDeployOutput{ task cleanDeployOutput{
doFirst{ doFirst{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 329 B

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 B

After

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 B

After

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 243 B

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 631 B

After

Width:  |  Height:  |  Size: 1001 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 389 B

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 B

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 B

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 B

After

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 B

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 279 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

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