Merge branches 'master' and 'splinterface' of https://github.com/Anuken/Mindustry
This commit is contained in:
@@ -3,10 +3,40 @@ package mindustry.annotations;
|
|||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
public class Annotations{
|
public class Annotations{
|
||||||
|
//region entity interfaces
|
||||||
|
|
||||||
|
/** Indicates multiple inheritance on a component type. */
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface Depends{
|
||||||
|
Class[] value();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates that a component def is present on all entities. */
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface BaseComponent{
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates an entity definition. */
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface EntityDef{
|
||||||
|
Class[] value();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates an internal interface for entity components. */
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface EntityInterface{
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
//region misc. utility
|
||||||
|
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface StyleDefaults {
|
public @interface StyleDefaults{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Indicates that a method should always call its super version. */
|
/** Indicates that a method should always call its super version. */
|
||||||
@@ -16,10 +46,10 @@ public class Annotations{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Annotation that allows overriding CallSuper annotation. To be used on method that overrides method with CallSuper annotation from parent class.*/
|
/** Annotation that allows overriding CallSuper annotation. To be used on method that overrides method with CallSuper annotation from parent class. */
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface OverrideCallSuper {
|
public @interface OverrideCallSuper{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Marks a class as serializable. */
|
/** Marks a class as serializable. */
|
||||||
@@ -29,6 +59,9 @@ public class Annotations{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
//region struct
|
||||||
|
|
||||||
/** Marks a class as a special value type struct. Class name must end in 'Struct'. */
|
/** Marks a class as a special value type struct. Class name must end in 'Struct'. */
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@@ -44,6 +77,9 @@ public class Annotations{
|
|||||||
int value();
|
int value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
//region remote
|
||||||
|
|
||||||
public enum PacketPriority{
|
public enum PacketPriority{
|
||||||
/** Gets put in a queue and processed if not connected. */
|
/** Gets put in a queue and processed if not connected. */
|
||||||
normal,
|
normal,
|
||||||
@@ -138,4 +174,6 @@ public class Annotations{
|
|||||||
public @interface ReadClass{
|
public @interface ReadClass{
|
||||||
Class<?> value();
|
Class<?> value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
package mindustry.annotations;
|
package mindustry.annotations;
|
||||||
|
|
||||||
|
import arc.struct.*;
|
||||||
|
import com.squareup.javapoet.*;
|
||||||
|
import com.sun.source.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.util.*;
|
import javax.lang.model.util.*;
|
||||||
|
import javax.tools.Diagnostic.*;
|
||||||
|
import java.lang.annotation.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||||
@@ -15,8 +22,11 @@ public abstract class BaseProcessor extends AbstractProcessor{
|
|||||||
public static Elements 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;
|
||||||
|
|
||||||
protected int round;
|
protected int round;
|
||||||
|
protected int rounds = 1;
|
||||||
|
protected RoundEnvironment env;
|
||||||
|
|
||||||
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();
|
||||||
@@ -27,19 +37,48 @@ 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static void write(TypeSpec.Builder builder) throws Exception{
|
||||||
public synchronized void init(ProcessingEnvironment processingEnv){
|
JavaFile.builder(packageName, builder.build()).build().writeTo(BaseProcessor.filer);
|
||||||
super.init(processingEnv);
|
}
|
||||||
|
|
||||||
typeu = processingEnv.getTypeUtils();
|
public Array<Stype> types(Class<? extends Annotation> type){
|
||||||
elementu = processingEnv.getElementUtils();
|
return Array.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof TypeElement)
|
||||||
filer = processingEnv.getFiler();
|
.map(e -> new Stype((TypeElement)e));
|
||||||
messager = processingEnv.getMessager();
|
}
|
||||||
|
|
||||||
|
public Array<Svar> fields(Class<? extends Annotation> type){
|
||||||
|
return Array.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof VariableElement)
|
||||||
|
.map(e -> new Svar((VariableElement)e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Smethod> methods(Class<? extends Annotation> type){
|
||||||
|
return Array.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof ExecutableElement)
|
||||||
|
.map(e -> new Smethod((ExecutableElement)e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void err(String message){
|
||||||
|
messager.printMessage(Kind.ERROR, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void err(String message, Element elem){
|
||||||
|
messager.printMessage(Kind.ERROR, message, elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void init(ProcessingEnvironment env){
|
||||||
|
super.init(env);
|
||||||
|
|
||||||
|
trees = Trees.instance(env);
|
||||||
|
typeu = env.getTypeUtils();
|
||||||
|
elementu = env.getElementUtils();
|
||||||
|
filer = env.getFiler();
|
||||||
|
messager = env.getMessager();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
||||||
if(round++ != 0) return false; //only process 1 round
|
if(round++ >= rounds) return false; //only process 1 round
|
||||||
|
this.env = roundEnv;
|
||||||
try{
|
try{
|
||||||
process(roundEnv);
|
process(roundEnv);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
|
|||||||
@@ -0,0 +1,258 @@
|
|||||||
|
package mindustry.annotations.impl;
|
||||||
|
|
||||||
|
import arc.struct.*;
|
||||||
|
import arc.util.*;
|
||||||
|
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 javax.annotation.processing.*;
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
import javax.lang.model.type.*;
|
||||||
|
|
||||||
|
@SupportedAnnotationTypes({
|
||||||
|
"mindustry.annotations.Annotations.EntityDef",
|
||||||
|
"mindustry.annotations.Annotations.EntityInterface",
|
||||||
|
"mindustry.annotations.Annotations.BaseComponent"
|
||||||
|
})
|
||||||
|
public class EntityProcess extends BaseProcessor{
|
||||||
|
Array<Definition> definitions = new Array<>();
|
||||||
|
Array<Stype> baseComponents;
|
||||||
|
ObjectMap<Stype, Array<Stype>> componentDependencies = new ObjectMap<>();
|
||||||
|
ObjectMap<Stype, Array<Stype>> defComponents = new ObjectMap<>();
|
||||||
|
|
||||||
|
{
|
||||||
|
rounds = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(RoundEnvironment env) throws Exception{
|
||||||
|
|
||||||
|
//round 1: get component classes and generate interfaces for them
|
||||||
|
if(round == 1){
|
||||||
|
baseComponents = types(BaseComponent.class);
|
||||||
|
Array<Stype> allDefs = types(EntityDef.class);
|
||||||
|
|
||||||
|
ObjectSet<Stype> allComponents = new ObjectSet<>();
|
||||||
|
|
||||||
|
//find all components used...
|
||||||
|
for(Stype type : allDefs){
|
||||||
|
allComponents.addAll(allComponents(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
//add all components w/ dependencies
|
||||||
|
allComponents.addAll(types(Depends.class));
|
||||||
|
|
||||||
|
//create component interfaces
|
||||||
|
for(Stype component : allComponents){
|
||||||
|
TypeSpec.Builder inter = TypeSpec.interfaceBuilder(interfaceName(component)).addModifiers(Modifier.PUBLIC).addAnnotation(EntityInterface.class);
|
||||||
|
|
||||||
|
for(Stype extraInterface : component.interfaces()){
|
||||||
|
inter.addSuperinterface(extraInterface.mirror());
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<Stype> depends = getDependencies(component);
|
||||||
|
for(Stype type : depends){
|
||||||
|
inter.addSuperinterface(ClassName.get(packageName, interfaceName(type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Svar field : component.fields().select(e -> !e.is(Modifier.STATIC) && !e.is(Modifier.PRIVATE) && !e.is(Modifier.TRANSIENT))){
|
||||||
|
String cname = Strings.capitalize(field.name());
|
||||||
|
//getter
|
||||||
|
inter.addMethod(MethodSpec.methodBuilder("get" + cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC).returns(field.tname()).build());
|
||||||
|
//setter
|
||||||
|
if(!field.is(Modifier.FINAL)) inter.addMethod(MethodSpec.methodBuilder("set" + cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC).addParameter(field.tname(), field.name()).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
//add utility methods to interface
|
||||||
|
for(Smethod method : component.methods()){
|
||||||
|
inter.addMethod(MethodSpec.methodBuilder(method.name())
|
||||||
|
.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());
|
||||||
|
}
|
||||||
|
|
||||||
|
write(inter);
|
||||||
|
}
|
||||||
|
|
||||||
|
//look at each definition
|
||||||
|
for(Stype type : allDefs){
|
||||||
|
if(!type.name().endsWith("Def")){
|
||||||
|
err("All entity def names must end with 'Def'", type.e);
|
||||||
|
}
|
||||||
|
String name = type.name().replace("Def", "Gen"); //TODO remove 'gen'
|
||||||
|
TypeSpec.Builder builder = TypeSpec.classBuilder(name).addModifiers(Modifier.PUBLIC, Modifier.FINAL);
|
||||||
|
|
||||||
|
Array<Stype> components = allComponents(type);
|
||||||
|
ObjectMap<String, Array<Smethod>> methods = new ObjectMap<>();
|
||||||
|
|
||||||
|
//add all components
|
||||||
|
for(Stype comp : components){
|
||||||
|
|
||||||
|
//write fields to the class; ignoring transient ones
|
||||||
|
Array<Svar> fields = comp.fields().select(f -> !f.is(Modifier.TRANSIENT));
|
||||||
|
for(Svar f : fields){
|
||||||
|
VariableTree tree = f.tree();
|
||||||
|
FieldSpec.Builder fbuilder = FieldSpec.builder(f.tname(), f.name(), Modifier.PUBLIC);
|
||||||
|
//add initializer if it exists
|
||||||
|
if(tree.getInitializer() != null){
|
||||||
|
fbuilder.initializer(tree.getInitializer().toString());
|
||||||
|
}
|
||||||
|
builder.addField(fbuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
//get all utility methods from components
|
||||||
|
for(Smethod elem : comp.methods()){
|
||||||
|
methods.getOr(elem.toString(), Array::new).add(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//add all methods from components
|
||||||
|
for(ObjectMap.Entry<String, Array<Smethod>> entry : methods){
|
||||||
|
//representative method
|
||||||
|
Smethod first = entry.value.first();
|
||||||
|
//build method using same params/returns
|
||||||
|
MethodSpec.Builder mbuilder = MethodSpec.methodBuilder(first.name()).addModifiers(Modifier.PUBLIC, Modifier.FINAL);
|
||||||
|
mbuilder.addTypeVariables(first.typeVariables().map(TypeVariableName::get));
|
||||||
|
mbuilder.returns(first.retn());
|
||||||
|
|
||||||
|
for(Svar var : first.params()){
|
||||||
|
mbuilder.addParameter(var.tname(), var.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean returns = !first.ret().toString().equals("void");
|
||||||
|
|
||||||
|
for(Smethod elem : entry.value){
|
||||||
|
//wrap scope to prevent variable leakage
|
||||||
|
if(!returns) mbuilder.beginControlFlow("");
|
||||||
|
|
||||||
|
//get all statements in the method, copy them over
|
||||||
|
MethodTree methodTree = elem.tree();
|
||||||
|
BlockTree blockTree = methodTree.getBody();
|
||||||
|
for(StatementTree st : blockTree.getStatements()){
|
||||||
|
String state = st.toString();
|
||||||
|
mbuilder.addStatement(state.substring(0, state.length() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
//end scope
|
||||||
|
if(!returns) mbuilder.endControlFlow();
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.addMethod(mbuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
definitions.add(new Definition(builder, type));
|
||||||
|
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
//round 2: generate actual classes and implement interfaces
|
||||||
|
Array<Stype> interfaces = types(EntityInterface.class);
|
||||||
|
|
||||||
|
//implement each definition
|
||||||
|
for(Definition def : definitions){
|
||||||
|
Array<Stype> components = allComponents(def.base);
|
||||||
|
|
||||||
|
//get interface for each component
|
||||||
|
for(Stype comp : components){
|
||||||
|
//implement the interface
|
||||||
|
Stype inter = interfaces.find(i -> i.name().equals(interfaceName(comp)));
|
||||||
|
def.builder.addSuperinterface(inter.tname());
|
||||||
|
|
||||||
|
//generate getter/setter for each method
|
||||||
|
for(Smethod method : inter.methods()){
|
||||||
|
if(method.name().length() <= 3) continue;
|
||||||
|
|
||||||
|
String var = Strings.camelize(method.name().substring(3));
|
||||||
|
if(method.name().startsWith("get")){
|
||||||
|
def.builder.addMethod(MethodSpec.overriding(method.e).addStatement("return " + var).build());
|
||||||
|
}else if(method.name().startsWith("set")){
|
||||||
|
def.builder.addMethod(MethodSpec.overriding(method.e).addStatement("this." + var + " = " + var).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write(def.builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String interfaceName(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()) + "c";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return all components that a entity def has */
|
||||||
|
Array<Stype> allComponents(Stype type){
|
||||||
|
if(!defComponents.containsKey(type)){
|
||||||
|
//get base defs
|
||||||
|
Array<Stype> components = Array.with(mirrors(type)).map(Stype::of);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<Stype> getDependencies(Stype component){
|
||||||
|
if(!componentDependencies.containsKey(component)){
|
||||||
|
ObjectSet<Stype> out = new ObjectSet<>();
|
||||||
|
out.addAll(component.superclasses());
|
||||||
|
|
||||||
|
//get dependency classes
|
||||||
|
if(component.annotation(Depends.class) != null){
|
||||||
|
try{
|
||||||
|
component.annotation(Depends.class).value();
|
||||||
|
}catch(MirroredTypesException e){
|
||||||
|
out.addAll(Array.with(e.getTypeMirrors()).map(Stype::of));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//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);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDependencies.put(component, result.asArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
return componentDependencies.get(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeMirror[] mirrors(Stype type){
|
||||||
|
try{
|
||||||
|
type.annotation(EntityDef.class).value();
|
||||||
|
}catch(MirroredTypesException e){
|
||||||
|
return e.getTypeMirrors().toArray(new TypeMirror[0]);
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Missing components: " + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Definition{
|
||||||
|
final TypeSpec.Builder builder;
|
||||||
|
final Stype base;
|
||||||
|
|
||||||
|
public Definition(Builder builder, Stype base){
|
||||||
|
this.builder = builder;
|
||||||
|
this.base = base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,96 +42,83 @@ public class RemoteProcess extends BaseProcessor{
|
|||||||
//list of all method entries
|
//list of all method entries
|
||||||
private ArrayList<ClassEntry> classes;
|
private ArrayList<ClassEntry> classes;
|
||||||
|
|
||||||
|
{
|
||||||
|
rounds = 2;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
public void process(RoundEnvironment roundEnv) throws Exception{
|
||||||
if(round > 1) return false; //only process 2 rounds
|
//round 1: find all annotations, generate *writers*
|
||||||
|
if(round == 1){
|
||||||
|
//get serializers
|
||||||
|
serializers = new IOFinder().findSerializers(roundEnv);
|
||||||
|
//last method ID used
|
||||||
|
int lastMethodID = 0;
|
||||||
|
//find all elements with the Remote annotation
|
||||||
|
elements = roundEnv.getElementsAnnotatedWith(Remote.class);
|
||||||
|
//map of all classes to generate by name
|
||||||
|
classMap = new HashMap<>();
|
||||||
|
//list of all method entries
|
||||||
|
methods = new ArrayList<>();
|
||||||
|
//list of all method entries
|
||||||
|
classes = new ArrayList<>();
|
||||||
|
|
||||||
round++;
|
List<Element> orderedElements = new ArrayList<>(elements);
|
||||||
|
orderedElements.sort(Comparator.comparing(Object::toString));
|
||||||
|
|
||||||
try{
|
//create methods
|
||||||
|
for(Element element : orderedElements){
|
||||||
|
Remote annotation = element.getAnnotation(Remote.class);
|
||||||
|
|
||||||
//round 1: find all annotations, generate *writers*
|
//check for static
|
||||||
if(round == 1){
|
if(!element.getModifiers().contains(Modifier.STATIC) || !element.getModifiers().contains(Modifier.PUBLIC)){
|
||||||
//get serializers
|
BaseProcessor.messager.printMessage(Kind.ERROR, "All @Remote methods must be public and static: ", element);
|
||||||
serializers = new IOFinder().findSerializers(roundEnv);
|
|
||||||
//last method ID used
|
|
||||||
int lastMethodID = 0;
|
|
||||||
//find all elements with the Remote annotation
|
|
||||||
elements = roundEnv.getElementsAnnotatedWith(Remote.class);
|
|
||||||
//map of all classes to generate by name
|
|
||||||
classMap = new HashMap<>();
|
|
||||||
//list of all method entries
|
|
||||||
methods = new ArrayList<>();
|
|
||||||
//list of all method entries
|
|
||||||
classes = new ArrayList<>();
|
|
||||||
|
|
||||||
List<Element> orderedElements = new ArrayList<>(elements);
|
|
||||||
orderedElements.sort(Comparator.comparing(Object::toString));
|
|
||||||
|
|
||||||
//create methods
|
|
||||||
for(Element element : orderedElements){
|
|
||||||
Remote annotation = element.getAnnotation(Remote.class);
|
|
||||||
|
|
||||||
//check for static
|
|
||||||
if(!element.getModifiers().contains(Modifier.STATIC) || !element.getModifiers().contains(Modifier.PUBLIC)){
|
|
||||||
BaseProcessor.messager.printMessage(Kind.ERROR, "All @Remote methods must be public and static: ", element);
|
|
||||||
}
|
|
||||||
|
|
||||||
//can't generate none methods
|
|
||||||
if(annotation.targets() == Loc.none){
|
|
||||||
BaseProcessor.messager.printMessage(Kind.ERROR, "A @Remote method's targets() cannot be equal to 'none':", element);
|
|
||||||
}
|
|
||||||
|
|
||||||
//get and create class entry if needed
|
|
||||||
if(!classMap.containsKey(callLocation)){
|
|
||||||
ClassEntry clas = new ClassEntry(callLocation);
|
|
||||||
classMap.put(callLocation, clas);
|
|
||||||
classes.add(clas);
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassEntry entry = classMap.get(callLocation);
|
|
||||||
|
|
||||||
//create and add entry
|
|
||||||
MethodEntry method = new MethodEntry(entry.name, BaseProcessor.getMethodName(element), annotation.targets(), annotation.variants(),
|
|
||||||
annotation.called(), annotation.unreliable(), annotation.forward(), lastMethodID++, (ExecutableElement)element, annotation.priority());
|
|
||||||
|
|
||||||
entry.methods.add(method);
|
|
||||||
methods.add(method);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//create read/write generators
|
//can't generate none methods
|
||||||
RemoteWriteGenerator writegen = new RemoteWriteGenerator(serializers);
|
if(annotation.targets() == Loc.none){
|
||||||
|
BaseProcessor.messager.printMessage(Kind.ERROR, "A @Remote method's targets() cannot be equal to 'none':", element);
|
||||||
|
}
|
||||||
|
|
||||||
//generate the methods to invoke (write)
|
//get and create class entry if needed
|
||||||
writegen.generateFor(classes, packageName);
|
if(!classMap.containsKey(callLocation)){
|
||||||
|
ClassEntry clas = new ClassEntry(callLocation);
|
||||||
|
classMap.put(callLocation, clas);
|
||||||
|
classes.add(clas);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
ClassEntry entry = classMap.get(callLocation);
|
||||||
}else if(round == 2){ //round 2: generate all *readers*
|
|
||||||
RemoteReadGenerator readgen = new RemoteReadGenerator(serializers);
|
|
||||||
|
|
||||||
//generate server readers
|
//create and add entry
|
||||||
readgen.generateFor(methods.stream().filter(method -> method.where.isClient).collect(Collectors.toList()), readServerName, packageName, true);
|
MethodEntry method = new MethodEntry(entry.name, BaseProcessor.getMethodName(element), annotation.targets(), annotation.variants(),
|
||||||
//generate client readers
|
annotation.called(), annotation.unreliable(), annotation.forward(), lastMethodID++, (ExecutableElement)element, annotation.priority());
|
||||||
readgen.generateFor(methods.stream().filter(method -> method.where.isServer).collect(Collectors.toList()), readClientName, packageName, false);
|
|
||||||
|
|
||||||
//create class for storing unique method hash
|
entry.methods.add(method);
|
||||||
TypeSpec.Builder hashBuilder = TypeSpec.classBuilder("MethodHash").addModifiers(Modifier.PUBLIC);
|
methods.add(method);
|
||||||
hashBuilder.addJavadoc(autogenWarning);
|
|
||||||
hashBuilder.addField(FieldSpec.builder(int.class, "HASH", Modifier.STATIC, Modifier.PUBLIC, Modifier.FINAL)
|
|
||||||
.initializer("$1L", Objects.hash(methods)).build());
|
|
||||||
|
|
||||||
//build and write resulting hash class
|
|
||||||
TypeSpec spec = hashBuilder.build();
|
|
||||||
JavaFile.builder(packageName, spec).build().writeTo(BaseProcessor.filer);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}catch(Exception e){
|
//create read/write generators
|
||||||
e.printStackTrace();
|
RemoteWriteGenerator writegen = new RemoteWriteGenerator(serializers);
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
//generate the methods to invoke (write)
|
||||||
|
writegen.generateFor(classes, packageName);
|
||||||
|
}else if(round == 2){ //round 2: generate all *readers*
|
||||||
|
RemoteReadGenerator readgen = new RemoteReadGenerator(serializers);
|
||||||
|
|
||||||
|
//generate server readers
|
||||||
|
readgen.generateFor(methods.stream().filter(method -> method.where.isClient).collect(Collectors.toList()), readServerName, packageName, true);
|
||||||
|
//generate client readers
|
||||||
|
readgen.generateFor(methods.stream().filter(method -> method.where.isServer).collect(Collectors.toList()), readClientName, packageName, false);
|
||||||
|
|
||||||
|
//create class for storing unique method hash
|
||||||
|
TypeSpec.Builder hashBuilder = TypeSpec.classBuilder("MethodHash").addModifiers(Modifier.PUBLIC);
|
||||||
|
hashBuilder.addJavadoc(autogenWarning);
|
||||||
|
hashBuilder.addField(FieldSpec.builder(int.class, "HASH", Modifier.STATIC, Modifier.PUBLIC, Modifier.FINAL)
|
||||||
|
.initializer("$1L", Objects.hash(methods)).build());
|
||||||
|
|
||||||
|
//build and write resulting hash class
|
||||||
|
TypeSpec spec = hashBuilder.build();
|
||||||
|
JavaFile.builder(packageName, spec).build().writeTo(BaseProcessor.filer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package mindustry.annotations.util;
|
||||||
|
|
||||||
|
import com.squareup.javapoet.*;
|
||||||
|
import mindustry.annotations.*;
|
||||||
|
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
import javax.lang.model.type.*;
|
||||||
|
|
||||||
|
public class Selement<T extends Element>{
|
||||||
|
public final T e;
|
||||||
|
|
||||||
|
public Selement(T e){
|
||||||
|
this.e = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeMirror mirror(){
|
||||||
|
return e.asType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeName tname(){
|
||||||
|
return TypeName.get(mirror());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassName cname(){
|
||||||
|
return ClassName.get((TypeElement)BaseProcessor.typeu.asElement(mirror()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String name(){
|
||||||
|
return e.getSimpleName().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(){
|
||||||
|
return e.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode(){
|
||||||
|
return e.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o){
|
||||||
|
return o != null && o.getClass() == getClass() && ((Selement)o).e.equals(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package mindustry.annotations.util;
|
||||||
|
|
||||||
|
import arc.struct.*;
|
||||||
|
import com.squareup.javapoet.*;
|
||||||
|
import com.sun.source.tree.*;
|
||||||
|
import mindustry.annotations.*;
|
||||||
|
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
import javax.lang.model.type.*;
|
||||||
|
|
||||||
|
public class Smethod extends Selement<ExecutableElement>{
|
||||||
|
|
||||||
|
public Smethod(ExecutableElement executableElement){
|
||||||
|
super(executableElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<TypeParameterElement> typeVariables(){
|
||||||
|
return Array.with(e.getTypeParameters()).as(TypeParameterElement.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Svar> params(){
|
||||||
|
return Array.with(e.getParameters()).map(Svar::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeMirror ret(){
|
||||||
|
return e.getReturnType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeName retn(){
|
||||||
|
return TypeName.get(ret());
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodTree tree(){
|
||||||
|
return BaseProcessor.trees.getTree(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package mindustry.annotations.util;
|
||||||
|
|
||||||
|
import arc.struct.*;
|
||||||
|
import mindustry.annotations.*;
|
||||||
|
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
import javax.lang.model.type.*;
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
public class Stype extends Selement<TypeElement>{
|
||||||
|
|
||||||
|
public Stype(TypeElement typeElement){
|
||||||
|
super(typeElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stype of(TypeMirror mirror){
|
||||||
|
return new Stype((TypeElement)BaseProcessor.typeu.asElement(mirror));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Stype> interfaces(){
|
||||||
|
return Array.with(e.getInterfaces()).map(Stype::of);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Stype> superclasses(){
|
||||||
|
Array<Stype> out = new Array<>();
|
||||||
|
Stype sup = superclass();
|
||||||
|
while(!sup.name().equals("Object")){
|
||||||
|
out.add(sup);
|
||||||
|
sup = sup.superclass();
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stype superclass(){
|
||||||
|
return new Stype((TypeElement)BaseProcessor.typeu.asElement(BaseProcessor.typeu.directSupertypes(mirror()).get(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <A extends Annotation> A annotation(Class<A> annotation){
|
||||||
|
return e.getAnnotation(annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Svar> fields(){
|
||||||
|
return Array.with(e.getEnclosedElements()).select(e -> e instanceof VariableElement).map(e -> new Svar((VariableElement)e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Smethod> methods(){
|
||||||
|
return Array.with(e.getEnclosedElements()).select(e -> e instanceof ExecutableElement
|
||||||
|
&& !e.getSimpleName().toString().contains("<")).map(e -> new Smethod((ExecutableElement)e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Smethod> constructors(){
|
||||||
|
return Array.with(e.getEnclosedElements()).select(e -> e instanceof ExecutableElement
|
||||||
|
&& e.getSimpleName().toString().contains("<")).map(e -> new Smethod((ExecutableElement)e));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeMirror mirror(){
|
||||||
|
return e.asType();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package mindustry.annotations.util;
|
||||||
|
|
||||||
|
import com.sun.source.tree.*;
|
||||||
|
import mindustry.annotations.*;
|
||||||
|
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
|
||||||
|
public class Svar extends Selement<VariableElement>{
|
||||||
|
|
||||||
|
public Svar(VariableElement e){
|
||||||
|
super(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean is(Modifier mod){
|
||||||
|
return e.getModifiers().contains(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableTree tree(){
|
||||||
|
return (VariableTree)BaseProcessor.trees.getTree(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
package mindustry.entities;
|
package mindustry.entities;
|
||||||
|
|
||||||
import arc.Core;
|
import arc.*;
|
||||||
import arc.struct.Array;
|
import arc.func.*;
|
||||||
import arc.func.Cons;
|
import arc.graphics.*;
|
||||||
import arc.graphics.Color;
|
|
||||||
import arc.graphics.g2d.*;
|
import arc.graphics.g2d.*;
|
||||||
import arc.math.Mathf;
|
import arc.math.*;
|
||||||
import arc.math.geom.Position;
|
import arc.math.geom.*;
|
||||||
import arc.util.pooling.Pools;
|
import arc.struct.*;
|
||||||
import mindustry.entities.type.EffectEntity;
|
import arc.util.pooling.*;
|
||||||
import mindustry.entities.traits.ScaleTrait;
|
import mindustry.entities.type.*;
|
||||||
|
|
||||||
public class Effects{
|
public class Effects{
|
||||||
private static final EffectContainer container = new EffectContainer();
|
private static final EffectContainer container = new EffectContainer();
|
||||||
@@ -126,7 +125,7 @@ public class Effects{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class EffectContainer implements ScaleTrait{
|
public static class EffectContainer implements Scaled{
|
||||||
public float x, y, time, lifetime, rotation;
|
public float x, y, time, lifetime, rotation;
|
||||||
public Color color;
|
public Color color;
|
||||||
public int id;
|
public int id;
|
||||||
|
|||||||
102
core/src/mindustry/entities/def/EntityDefs.java
Normal file
102
core/src/mindustry/entities/def/EntityDefs.java
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package mindustry.entities.def;
|
||||||
|
|
||||||
|
import arc.math.geom.*;
|
||||||
|
import mindustry.annotations.Annotations.*;
|
||||||
|
import mindustry.entities.bullet.*;
|
||||||
|
import mindustry.entities.units.*;
|
||||||
|
import mindustry.gen.*;
|
||||||
|
import mindustry.net.*;
|
||||||
|
|
||||||
|
class EntityDefs{
|
||||||
|
|
||||||
|
@EntityDef({UnitComp.class, ConnectionComp.class})
|
||||||
|
class PlayerDef{}
|
||||||
|
|
||||||
|
@EntityDef({BulletComp.class, VelComp.class})
|
||||||
|
class BulletDef{}
|
||||||
|
|
||||||
|
@Depends({HealthComp.class, VelComp.class, StatusComp.class})
|
||||||
|
class UnitComp{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class HealthComp{
|
||||||
|
float health, maxHealth;
|
||||||
|
boolean dead;
|
||||||
|
|
||||||
|
float healthf(){
|
||||||
|
return health / maxHealth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class PosComp implements Position{
|
||||||
|
float x, y;
|
||||||
|
|
||||||
|
void set(float x, float y){
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Depends(PosComp.class)
|
||||||
|
class VelComp{
|
||||||
|
//transient fields act as imports from any other component clases; these are ignored by the generator
|
||||||
|
transient float x, y;
|
||||||
|
|
||||||
|
final Vec2 vel = new Vec2();
|
||||||
|
|
||||||
|
void update(){
|
||||||
|
x += vel.x;
|
||||||
|
y += vel.y;
|
||||||
|
vel.scl(0.9f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Depends(PosComp.class)
|
||||||
|
class HitboxComp{
|
||||||
|
transient float x, y;
|
||||||
|
|
||||||
|
float hitSize;
|
||||||
|
|
||||||
|
boolean collides(Hitboxc other){
|
||||||
|
return Intersector.overlapsRect(x - hitSize/2f, y - hitSize/2f, hitSize, hitSize,
|
||||||
|
other.getX() - other.getHitSize()/2f, other.getY() - other.getHitSize()/2f, other.getHitSize(), other.getHitSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StatusComp{
|
||||||
|
final Statuses statuses = new Statuses();
|
||||||
|
|
||||||
|
void update(){
|
||||||
|
statuses.update(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConnectionComp{
|
||||||
|
NetConnection connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BulletComp{
|
||||||
|
BulletType bullet;
|
||||||
|
|
||||||
|
void init(){
|
||||||
|
bullet.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@BaseComponent
|
||||||
|
class EntityComp{
|
||||||
|
int id;
|
||||||
|
|
||||||
|
void init(){}
|
||||||
|
|
||||||
|
<T> T as(Class<T> type){
|
||||||
|
return (T)this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testing(){
|
||||||
|
Entityc abullet = new BulletGen();
|
||||||
|
Entityc aplayer = new PlayerGen();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package mindustry.entities.traits;
|
|
||||||
|
|
||||||
import arc.math.Interpolation;
|
|
||||||
|
|
||||||
public interface ScaleTrait{
|
|
||||||
/** 0 to 1. */
|
|
||||||
float fin();
|
|
||||||
|
|
||||||
/** 1 to 0 */
|
|
||||||
default float fout(){
|
|
||||||
return 1f - fin();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 1 to 0 */
|
|
||||||
default float fout(Interpolation i){
|
|
||||||
return i.apply(fout());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 1 to 0, ending at the specified margin */
|
|
||||||
default float fout(float margin){
|
|
||||||
float f = fin();
|
|
||||||
if(f >= 1f - margin){
|
|
||||||
return 1f - (f - (1f - margin)) / margin;
|
|
||||||
}else{
|
|
||||||
return 1f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 0 to 1 **/
|
|
||||||
default float fin(Interpolation i){
|
|
||||||
return i.apply(fin());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 0 to 1 */
|
|
||||||
default float finpow(){
|
|
||||||
return Interpolation.pow3Out.apply(fin());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 0 to 1 to 0 */
|
|
||||||
default float fslope(){
|
|
||||||
return (0.5f - Math.abs(fin() - 0.5f)) * 2f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package mindustry.entities.traits;
|
package mindustry.entities.traits;
|
||||||
|
|
||||||
import arc.math.Mathf;
|
import arc.math.*;
|
||||||
import arc.util.Time;
|
import arc.util.Time;
|
||||||
|
|
||||||
public interface TimeTrait extends ScaleTrait, Entity{
|
public interface TimeTrait extends Scaled, Entity{
|
||||||
|
|
||||||
float lifetime();
|
float lifetime();
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import mindustry.world.*;
|
|||||||
|
|
||||||
import static mindustry.Vars.*;
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
public class Bullet extends SolidEntity implements DamageTrait, ScaleTrait, Poolable, DrawTrait, VelocityTrait, TimeTrait, TeamTrait, AbsorbTrait{
|
public class Bullet extends SolidEntity implements DamageTrait, Scaled, Poolable, DrawTrait, VelocityTrait, TimeTrait, TeamTrait, AbsorbTrait{
|
||||||
public Interval timer = new Interval(3);
|
public Interval timer = new Interval(3);
|
||||||
|
|
||||||
private float lifeScl;
|
private float lifeScl;
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
package mindustry.entities.type;
|
package mindustry.entities.type;
|
||||||
|
|
||||||
import arc.util.pooling.Pool.Poolable;
|
import arc.util.pooling.Pool.*;
|
||||||
import mindustry.entities.traits.ScaleTrait;
|
import mindustry.entities.traits.*;
|
||||||
import mindustry.entities.traits.TimeTrait;
|
|
||||||
|
|
||||||
public abstract class TimedEntity extends BaseEntity implements ScaleTrait, TimeTrait, Poolable{
|
public abstract class TimedEntity extends BaseEntity implements TimeTrait, Poolable{
|
||||||
public float time;
|
public float time;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user