More entity processor features

This commit is contained in:
Anuken
2020-02-02 11:51:58 -05:00
parent 7ddfcbfabd
commit acb3438cc8
5 changed files with 100 additions and 23 deletions

View File

@ -4,6 +4,15 @@ 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 an entity definition. */ /** Indicates an entity definition. */
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@ -17,11 +26,8 @@ public class Annotations{
public @interface EntityInterface{ public @interface EntityInterface{
} }
/** Indicates that a component logic method should be merged. */ //endregion
@Target(ElementType.METHOD) //region misc. utility
@Retention(RetentionPolicy.SOURCE)
public @interface Merge{
}
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@ -48,6 +54,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)
@ -63,6 +72,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,
@ -157,4 +169,6 @@ public class Annotations{
public @interface ReadClass{ public @interface ReadClass{
Class<?> value(); Class<?> value();
} }
//endregion
} }

View File

@ -12,7 +12,6 @@ import mindustry.annotations.util.*;
import javax.annotation.processing.*; import javax.annotation.processing.*;
import javax.lang.model.element.*; import javax.lang.model.element.*;
import javax.lang.model.type.*; import javax.lang.model.type.*;
import java.util.*;
@SupportedAnnotationTypes({ @SupportedAnnotationTypes({
"mindustry.annotations.Annotations.EntityDef", "mindustry.annotations.Annotations.EntityDef",
@ -20,6 +19,8 @@ import java.util.*;
}) })
public class EntityProcess extends BaseProcessor{ public class EntityProcess extends BaseProcessor{
Array<Definition> definitions = new Array<>(); Array<Definition> definitions = new Array<>();
ObjectMap<Stype, Array<Stype>> componentDependencies = new ObjectMap<>();
ObjectMap<Stype, Array<Stype>> defComponents = new ObjectMap<>();
{ {
rounds = 2; rounds = 2;
@ -43,7 +44,12 @@ public class EntityProcess extends BaseProcessor{
for(Stype component : allComponents){ for(Stype component : allComponents){
TypeSpec.Builder inter = TypeSpec.interfaceBuilder(component.name() + "c").addModifiers(Modifier.PUBLIC).addAnnotation(EntityInterface.class); TypeSpec.Builder inter = TypeSpec.interfaceBuilder(component.name() + "c").addModifiers(Modifier.PUBLIC).addAnnotation(EntityInterface.class);
for(Svar field : component.fields().select(e -> !e.is(Modifier.STATIC) && !e.is(Modifier.PRIVATE))){ Array<Stype> depends = getDependencies(component);
for(Stype type : depends){
inter.addSuperinterface(ClassName.get(packageName, type.name() + "c"));
}
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()); String cname = Strings.capitalize(field.name());
//getter //getter
inter.addMethod(MethodSpec.methodBuilder("get" + cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC).returns(field.tname()).build()); inter.addMethod(MethodSpec.methodBuilder("get" + cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC).returns(field.tname()).build());
@ -72,8 +78,8 @@ public class EntityProcess extends BaseProcessor{
//add all components //add all components
for(Stype comp : components){ for(Stype comp : components){
//write fields //write fields to the class; ignoring transient ones
Array<Svar> fields = comp.fields(); Array<Svar> fields = comp.fields().select(f -> !f.is(Modifier.TRANSIENT));
for(Svar f : fields){ for(Svar f : fields){
VariableTree tree = f.tree(); VariableTree tree = f.tree();
FieldSpec.Builder fbuilder = FieldSpec.builder(f.tname(), f.name(), Modifier.PUBLIC); FieldSpec.Builder fbuilder = FieldSpec.builder(f.tname(), f.name(), Modifier.PUBLIC);
@ -96,7 +102,8 @@ public class EntityProcess extends BaseProcessor{
Smethod first = entry.value.first(); Smethod first = entry.value.first();
//build method using same params/returns //build method using same params/returns
MethodSpec.Builder mbuilder = MethodSpec.methodBuilder(first.name()).addModifiers(Modifier.PUBLIC, Modifier.FINAL); MethodSpec.Builder mbuilder = MethodSpec.methodBuilder(first.name()).addModifiers(Modifier.PUBLIC, Modifier.FINAL);
mbuilder.returns(TypeName.get(first.ret())); mbuilder.returns(first.retn());
for(Svar var : first.params()){ for(Svar var : first.params()){
mbuilder.addParameter(var.tname(), var.name()); mbuilder.addParameter(var.tname(), var.name());
} }
@ -141,6 +148,8 @@ public class EntityProcess extends BaseProcessor{
//generate getter/setter for each method //generate getter/setter for each method
for(Smethod method : inter.methods()){ for(Smethod method : inter.methods()){
if(method.name().length() == 3) continue;
String var = Strings.camelize(method.name().substring(3)); String var = Strings.camelize(method.name().substring(3));
if(method.name().startsWith("get")){ if(method.name().startsWith("get")){
def.builder.addMethod(MethodSpec.overriding(method.e).addStatement("return " + var).build()); def.builder.addMethod(MethodSpec.overriding(method.e).addStatement("return " + var).build());
@ -157,16 +166,47 @@ public class EntityProcess extends BaseProcessor{
/** @return all components that a entity def has */ /** @return all components that a entity def has */
Array<Stype> allComponents(Stype type){ Array<Stype> allComponents(Stype type){
ObjectSet<TypeMirror> mirrors = ObjectSet.with(mirrors(type)); if(!defComponents.containsKey(type)){
for(TypeMirror curr : mirrors){ //get base defs
List<? extends TypeMirror> sub = typeu.directSupertypes(curr); Array<Stype> components = Array.with(mirrors(type)).map(Stype::of);
while(!sub.isEmpty() && !sub.get(0).toString().equals("java.lang.Object")){ ObjectSet<Stype> out = new ObjectSet<>();
mirrors.add(sub.get(0)); for(Stype comp : components){
curr = sub.get(0); //get dependencies for each def, add them
sub = typeu.directSupertypes(curr); 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));
} }
} }
return mirrors.asArray().map(m -> new Stype((TypeElement)typeu.asElement(m)));
//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));
}
componentDependencies.put(component, result.asArray());
}
return componentDependencies.get(component);
} }
TypeMirror[] mirrors(Stype type){ TypeMirror[] mirrors(Stype type){

View File

@ -33,4 +33,14 @@ public class Selement<T extends Element>{
public String toString(){ public String toString(){
return e.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);
}
} }

View File

@ -13,12 +13,16 @@ public class Stype extends Selement<TypeElement>{
super(typeElement); super(typeElement);
} }
public static Stype of(TypeMirror mirror){
return new Stype((TypeElement)BaseProcessor.typeu.asElement(mirror));
}
public Array<Stype> superclasses(){ public Array<Stype> superclasses(){
Array<Stype> out = new Array<>(); Array<Stype> out = new Array<>();
Stype sup = superclass(); Stype sup = superclass();
while(!sup.superclass().name().equals("java.lang.Object")){ while(!sup.name().equals("Object")){
out.add(sup); out.add(sup);
sup = superclass(); sup = sup.superclass();
} }
return out; return out;
} }

View File

@ -8,9 +8,14 @@ import mindustry.net.*;
public class EntityDefs{ public class EntityDefs{
@EntityDef({Health.class, Vel.class, Status.class, Connection.class}) @EntityDef({Unit.class, Connection.class})
class PlayerDef{} class PlayerDef{}
@Depends({Health.class, Vel.class, Status.class})
class Unit{
}
class Health{ class Health{
float health, maxHealth; float health, maxHealth;
boolean dead; boolean dead;
@ -24,7 +29,11 @@ public class EntityDefs{
float x, y; float x, y;
} }
class Vel extends Pos{ @Depends(Pos.class)
class Vel{
//transient fields act as imports from any other clases; these are ignored by the generator
transient float x, y;
final Vec2 vel = new Vec2(); final Vec2 vel = new Vec2();
void update(){ void update(){
@ -46,7 +55,7 @@ public class EntityDefs{
NetConnection connection; NetConnection connection;
} }
static <T extends Connectionc & Velc & Healthc & Posc> void doSomethingWithAConnection(T value){ static <T extends Connectionc & Unitc> void doSomethingWithAConnection(T value){
value.setX(0); value.setX(0);
value.setY(0); value.setY(0);
value.getVel().set(100, 100f); value.getVel().set(100, 100f);