diff --git a/annotations/src/main/java/mindustry/annotations/Annotations.java b/annotations/src/main/java/mindustry/annotations/Annotations.java index b30581b431..df917b4137 100644 --- a/annotations/src/main/java/mindustry/annotations/Annotations.java +++ b/annotations/src/main/java/mindustry/annotations/Annotations.java @@ -4,6 +4,15 @@ import java.lang.annotation.*; 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. */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) @@ -17,11 +26,8 @@ public class Annotations{ public @interface EntityInterface{ } - /** Indicates that a component logic method should be merged. */ - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.SOURCE) - public @interface Merge{ - } + //endregion + //region misc. utility @Target(ElementType.TYPE) @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'. */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) @@ -63,6 +72,9 @@ public class Annotations{ int value(); } + //endregion + //region remote + public enum PacketPriority{ /** Gets put in a queue and processed if not connected. */ normal, @@ -157,4 +169,6 @@ public class Annotations{ public @interface ReadClass{ Class value(); } + + //endregion } diff --git a/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java b/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java index 0978f2b554..5877a2876b 100644 --- a/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java +++ b/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java @@ -12,7 +12,6 @@ import mindustry.annotations.util.*; import javax.annotation.processing.*; import javax.lang.model.element.*; import javax.lang.model.type.*; -import java.util.*; @SupportedAnnotationTypes({ "mindustry.annotations.Annotations.EntityDef", @@ -20,6 +19,8 @@ import java.util.*; }) public class EntityProcess extends BaseProcessor{ Array definitions = new Array<>(); + ObjectMap> componentDependencies = new ObjectMap<>(); + ObjectMap> defComponents = new ObjectMap<>(); { rounds = 2; @@ -43,7 +44,12 @@ public class EntityProcess extends BaseProcessor{ for(Stype component : allComponents){ 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 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()); //getter 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 for(Stype comp : components){ - //write fields - Array fields = comp.fields(); + //write fields to the class; ignoring transient ones + Array 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); @@ -96,7 +102,8 @@ public class EntityProcess extends BaseProcessor{ Smethod first = entry.value.first(); //build method using same params/returns 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()){ mbuilder.addParameter(var.tname(), var.name()); } @@ -141,6 +148,8 @@ public class EntityProcess extends BaseProcessor{ //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()); @@ -157,16 +166,47 @@ public class EntityProcess extends BaseProcessor{ /** @return all components that a entity def has */ Array allComponents(Stype type){ - ObjectSet mirrors = ObjectSet.with(mirrors(type)); - for(TypeMirror curr : mirrors){ - List sub = typeu.directSupertypes(curr); - while(!sub.isEmpty() && !sub.get(0).toString().equals("java.lang.Object")){ - mirrors.add(sub.get(0)); - curr = sub.get(0); - sub = typeu.directSupertypes(curr); + if(!defComponents.containsKey(type)){ + //get base defs + Array components = Array.with(mirrors(type)).map(Stype::of); + ObjectSet 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 mirrors.asArray().map(m -> new Stype((TypeElement)typeu.asElement(m))); + + return defComponents.get(type); + } + + Array getDependencies(Stype component){ + if(!componentDependencies.containsKey(component)){ + ObjectSet 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 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){ diff --git a/annotations/src/main/java/mindustry/annotations/util/Selement.java b/annotations/src/main/java/mindustry/annotations/util/Selement.java index aca793cc72..b9d8aa4e32 100644 --- a/annotations/src/main/java/mindustry/annotations/util/Selement.java +++ b/annotations/src/main/java/mindustry/annotations/util/Selement.java @@ -33,4 +33,14 @@ public class Selement{ 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); + } } diff --git a/annotations/src/main/java/mindustry/annotations/util/Stype.java b/annotations/src/main/java/mindustry/annotations/util/Stype.java index 9f49d0b3b7..b13f13a65d 100644 --- a/annotations/src/main/java/mindustry/annotations/util/Stype.java +++ b/annotations/src/main/java/mindustry/annotations/util/Stype.java @@ -13,12 +13,16 @@ public class Stype extends Selement{ super(typeElement); } + public static Stype of(TypeMirror mirror){ + return new Stype((TypeElement)BaseProcessor.typeu.asElement(mirror)); + } + public Array superclasses(){ Array out = new Array<>(); Stype sup = superclass(); - while(!sup.superclass().name().equals("java.lang.Object")){ + while(!sup.name().equals("Object")){ out.add(sup); - sup = superclass(); + sup = sup.superclass(); } return out; } diff --git a/core/src/mindustry/entities/def/EntityDefs.java b/core/src/mindustry/entities/def/EntityDefs.java index b44a906e80..6f11e2c47a 100644 --- a/core/src/mindustry/entities/def/EntityDefs.java +++ b/core/src/mindustry/entities/def/EntityDefs.java @@ -8,9 +8,14 @@ import mindustry.net.*; public class EntityDefs{ - @EntityDef({Health.class, Vel.class, Status.class, Connection.class}) + @EntityDef({Unit.class, Connection.class}) class PlayerDef{} + @Depends({Health.class, Vel.class, Status.class}) + class Unit{ + + } + class Health{ float health, maxHealth; boolean dead; @@ -24,7 +29,11 @@ public class EntityDefs{ 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(); void update(){ @@ -46,7 +55,7 @@ public class EntityDefs{ NetConnection connection; } - static void doSomethingWithAConnection(T value){ + static void doSomethingWithAConnection(T value){ value.setX(0); value.setY(0); value.getVel().set(100, 100f);