Interface + base component support

This commit is contained in:
Anuken 2020-02-02 12:25:46 -05:00
parent acb3438cc8
commit 5eb3f0f3de
6 changed files with 97 additions and 26 deletions

View File

@ -3,7 +3,6 @@ package mindustry.annotations;
import java.lang.annotation.*; import java.lang.annotation.*;
public class Annotations{ public class Annotations{
//region entity interfaces //region entity interfaces
/** Indicates multiple inheritance on a component type. */ /** Indicates multiple inheritance on a component type. */
@ -13,6 +12,12 @@ public class Annotations{
Class[] value(); 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. */ /** Indicates an entity definition. */
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)

View File

@ -9,6 +9,7 @@ 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.lang.annotation.*;
import java.util.*; import java.util.*;
@ -55,6 +56,14 @@ public abstract class BaseProcessor extends AbstractProcessor{
.map(e -> new Smethod((ExecutableElement)e)); .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 @Override
public synchronized void init(ProcessingEnvironment env){ public synchronized void init(ProcessingEnvironment env){
super.init(env); super.init(env);

View File

@ -15,10 +15,12 @@ import javax.lang.model.type.*;
@SupportedAnnotationTypes({ @SupportedAnnotationTypes({
"mindustry.annotations.Annotations.EntityDef", "mindustry.annotations.Annotations.EntityDef",
"mindustry.annotations.Annotations.EntityInterface" "mindustry.annotations.Annotations.EntityInterface",
"mindustry.annotations.Annotations.BaseComponent"
}) })
public class EntityProcess extends BaseProcessor{ public class EntityProcess extends BaseProcessor{
Array<Definition> definitions = new Array<>(); Array<Definition> definitions = new Array<>();
Array<Stype> baseComponents;
ObjectMap<Stype, Array<Stype>> componentDependencies = new ObjectMap<>(); ObjectMap<Stype, Array<Stype>> componentDependencies = new ObjectMap<>();
ObjectMap<Stype, Array<Stype>> defComponents = new ObjectMap<>(); ObjectMap<Stype, Array<Stype>> defComponents = new ObjectMap<>();
@ -31,6 +33,7 @@ public class EntityProcess extends BaseProcessor{
//round 1: get component classes and generate interfaces for them //round 1: get component classes and generate interfaces for them
if(round == 1){ if(round == 1){
baseComponents = types(BaseComponent.class);
Array<Stype> allDefs = types(EntityDef.class); Array<Stype> allDefs = types(EntityDef.class);
ObjectSet<Stype> allComponents = new ObjectSet<>(); ObjectSet<Stype> allComponents = new ObjectSet<>();
@ -42,11 +45,15 @@ public class EntityProcess extends BaseProcessor{
//create component interfaces //create component interfaces
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(interfaceName(component)).addModifiers(Modifier.PUBLIC).addAnnotation(EntityInterface.class);
for(Stype extraInterface : component.interfaces()){
inter.addSuperinterface(extraInterface.mirror());
}
Array<Stype> depends = getDependencies(component); Array<Stype> depends = getDependencies(component);
for(Stype type : depends){ for(Stype type : depends){
inter.addSuperinterface(ClassName.get(packageName, type.name() + "c")); 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))){ for(Svar field : component.fields().select(e -> !e.is(Modifier.STATIC) && !e.is(Modifier.PRIVATE) && !e.is(Modifier.TRANSIENT))){
@ -59,7 +66,9 @@ public class EntityProcess extends BaseProcessor{
//add utility methods to interface //add utility methods to interface
for(Smethod method : component.methods()){ for(Smethod method : component.methods()){
inter.addMethod(MethodSpec.methodBuilder(method.name()).returns(method.ret().toString().equals("void") ? TypeName.VOID : method.retn()) 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()) .addParameters(method.params().map(v -> ParameterSpec.builder(v.tname(), v.name())
.build())).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT).build()); .build())).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT).build());
} }
@ -102,6 +111,7 @@ 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.addTypeVariables(first.typeVariables().map(TypeVariableName::get));
mbuilder.returns(first.retn()); mbuilder.returns(first.retn());
for(Svar var : first.params()){ for(Svar var : first.params()){
@ -143,12 +153,12 @@ public class EntityProcess extends BaseProcessor{
//get interface for each component //get interface for each component
for(Stype comp : components){ for(Stype comp : components){
//implement the interface //implement the interface
Stype inter = interfaces.find(i -> i.name().equals(comp.name() + "c")); Stype inter = interfaces.find(i -> i.name().equals(interfaceName(comp)));
def.builder.addSuperinterface(inter.tname()); def.builder.addSuperinterface(inter.tname());
//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; 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")){
@ -164,6 +174,13 @@ public class EntityProcess extends BaseProcessor{
} }
} }
String interfaceName(Stype comp){
if(!comp.name().endsWith("c")){
err("All components must have names that end with 'c'.", comp.e);
}
return comp.name().substring(0, comp.name().length() - 1) + "t";
}
/** @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){
if(!defComponents.containsKey(type)){ if(!defComponents.containsKey(type)){
@ -203,6 +220,10 @@ public class EntityProcess extends BaseProcessor{
result.addAll(getDependencies(type)); result.addAll(getDependencies(type));
} }
if(component.annotation(BaseComponent.class) == null){
result.addAll(baseComponents);
}
componentDependencies.put(component, result.asArray()); componentDependencies.put(component, result.asArray());
} }

View File

@ -14,6 +14,10 @@ public class Smethod extends Selement<ExecutableElement>{
super(executableElement); super(executableElement);
} }
public Array<TypeParameterElement> typeVariables(){
return Array.with(e.getTypeParameters()).as(TypeParameterElement.class);
}
public Array<Svar> params(){ public Array<Svar> params(){
return Array.with(e.getParameters()).map(Svar::new); return Array.with(e.getParameters()).map(Svar::new);
} }

View File

@ -17,6 +17,10 @@ public class Stype extends Selement<TypeElement>{
return new Stype((TypeElement)BaseProcessor.typeu.asElement(mirror)); return new Stype((TypeElement)BaseProcessor.typeu.asElement(mirror));
} }
public Array<Stype> interfaces(){
return Array.with(e.getInterfaces()).map(Stype::of);
}
public Array<Stype> superclasses(){ public Array<Stype> superclasses(){
Array<Stype> out = new Array<>(); Array<Stype> out = new Array<>();
Stype sup = superclass(); Stype sup = superclass();

View File

@ -1,22 +1,27 @@
package mindustry.entities.def; package mindustry.entities.def;
import arc.math.geom.*; import arc.math.geom.*;
import arc.util.*;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
import mindustry.entities.bullet.*;
import mindustry.entities.units.*; import mindustry.entities.units.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.net.*; import mindustry.net.*;
public class EntityDefs{ class EntityDefs{
@EntityDef({Unit.class, Connection.class}) @EntityDef({Unitc.class, Connectionc.class})
class PlayerDef{} class PlayerDef{}
@Depends({Health.class, Vel.class, Status.class}) @EntityDef({Bulletc.class, Velc.class})
class Unit{ class BulletDef{}
@Depends({Healthc.class, Velc.class, Statusc.class})
class Unitc{
} }
class Health{ class Healthc{
float health, maxHealth; float health, maxHealth;
boolean dead; boolean dead;
@ -25,13 +30,18 @@ public class EntityDefs{
} }
} }
class Pos{ abstract class Posc implements Position{
float x, y; float x, y;
void set(float x, float y){
this.x = x;
this.y = y;
}
} }
@Depends(Pos.class) @Depends(Posc.class)
class Vel{ class Velc{
//transient fields act as imports from any other clases; these are ignored by the generator //transient fields act as imports from any other component clases; these are ignored by the generator
transient float x, y; transient float x, y;
final Vec2 vel = new Vec2(); final Vec2 vel = new Vec2();
@ -43,7 +53,7 @@ public class EntityDefs{
} }
} }
class Status{ class Statusc{
final Statuses statuses = new Statuses(); final Statuses statuses = new Statuses();
void update(){ void update(){
@ -51,19 +61,37 @@ public class EntityDefs{
} }
} }
class Connection{ class Connectionc{
NetConnection connection; NetConnection connection;
} }
static <T extends Connectionc & Unitc> void doSomethingWithAConnection(T value){ class Bulletc{
value.setX(0); BulletType bullet;
value.setY(0);
value.getVel().set(100, 100f); void init(){
value.setDead(true); bullet.init();
value.getConnection().kick("you are dead"); }
} }
static void test(){ @BaseComponent
doSomethingWithAConnection(new PlayerGen()); class Entityc{
int id;
void init(){}
<T> T as(Class<T> type){
return (T)this;
}
}
static void testing(){
Entityt abullet = new BulletGen();
Entityt aplayer = new PlayerGen();
if(abullet instanceof Post){
Log.info("Pos: " + abullet.as(Post.class).getX());
}
Log.info(abullet.as(Post.class).dst(aplayer.as(Post.class)));
} }
} }