diff --git a/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java b/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java deleted file mode 100644 index 80e294a0f3..0000000000 --- a/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java +++ /dev/null @@ -1,297 +0,0 @@ -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 definitions = new Array<>(); - Array baseComponents; - ObjectMap> componentDependencies = new ObjectMap<>(); - ObjectMap> defComponents = new ObjectMap<>(); - ObjectSet imports = new ObjectSet<>(); - - { - 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 allDefs = types(EntityDef.class); - - ObjectSet 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).map(s -> Array.withArrays(getDependencies(s), s)).flatten()); - - //add component imports - for(Stype comp : allComponents){ - imports.addAll(getImports(comp.e)); - } - - //create component interfaces - for(Stype component : allComponents){ - TypeSpec.Builder inter = TypeSpec.interfaceBuilder(interfaceName(component)).addModifiers(Modifier.PUBLIC).addAnnotation(EntityInterface.class); - - //implement extra interfaces these components may have, e.g. position - for(Stype extraInterface : component.interfaces()){ - inter.addSuperinterface(extraInterface.mirror()); - } - - //implement super interfaces - Array 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()) - .addExceptions(method.thrownt()) - .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 components = allComponents(type); - ObjectMap> methods = new ObjectMap<>(); - - //add all components - for(Stype comp : components){ - - //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()); - //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> 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()); - mbuilder.addExceptions(first.thrownt()); - - for(Svar var : first.params()){ - mbuilder.addParameter(var.tname(), var.name()); - } - - //only write the block if it's a void method with several entries - boolean writeBlock = first.ret().toString().equals("void") && entry.value.size > 1; - - if(entry.value.first().is(Modifier.ABSTRACT) && entry.value.size == 1){ - err(entry.value.first().up().getSimpleName() + " declares an abstract method. This method must be implemented in another component", entry.value.first()); - } - - for(Smethod elem : entry.value){ - if(elem.is(Modifier.ABSTRACT)) continue; - - //get all statements in the method, copy them over - MethodTree methodTree = elem.tree(); - BlockTree blockTree = methodTree.getBody(); - String str = blockTree.toString(); - //name for code blocks in the methods - String blockName = elem.up().getSimpleName().toString().toLowerCase().replace("comp", ""); - - //skip empty blocks - if(str.replace("{", "").replace("\n", "").replace("}", "").replace("\t", "").replace(" ", "").isEmpty()){ - continue; - } - - //wrap scope to prevent variable leakage - if(writeBlock){ - //replace return; with block break - str = str.replace("return;", "break " + blockName + ";"); - mbuilder.addCode(blockName + ": {\n"); - } - - //trim block - str = str.substring(2, str.length() - 1); - - //make sure to remove braces here - mbuilder.addCode(str); - - //end scope - if(writeBlock) mbuilder.addCode("}\n"); - } - - builder.addMethod(mbuilder.build()); - } - - definitions.add(new Definition(builder, type)); - - } - }else{ - //round 2: generate actual classes and implement interfaces - Array interfaces = types(EntityInterface.class); - - //implement each definition - for(Definition def : definitions){ - Array 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)); - //make sure it's a real variable - if(!Array.with(def.builder.fieldSpecs).contains(f -> f.name.equals(var))) continue; - - 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, imports.asArray()); - } - } - } - - Array getImports(Element elem){ - return Array.with(trees.getPath(elem).getCompilationUnit().getImports()).map(Object::toString); - } - - /** @return interface for a component type */ - 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 allComponents(Stype type){ - 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 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)); - } - - 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; - } - } -} diff --git a/core/src/mindustry/entities/def/EntityComps.java b/core/src/mindustry/entities/def/EntityComps.java deleted file mode 100644 index 3dea444169..0000000000 --- a/core/src/mindustry/entities/def/EntityComps.java +++ /dev/null @@ -1,231 +0,0 @@ -package mindustry.entities.def; - -import arc.graphics.*; -import arc.math.*; -import arc.math.geom.*; -import arc.struct.Bits; -import arc.struct.*; -import arc.util.*; -import arc.util.pooling.*; -import mindustry.annotations.Annotations.*; -import mindustry.content.*; -import mindustry.ctype.*; -import mindustry.entities.bullet.*; -import mindustry.entities.units.*; -import mindustry.gen.*; -import mindustry.type.*; - -import java.io.*; - -import static mindustry.Vars.content; - -public class EntityComps{ - - @Depends({HealthComp.class, VelComp.class, StatusComp.class}) - class UnitComp{ - - } - - class OwnerComp{ - Entityc owner; - } - - @Depends({TimedComp.class}) - class BulletComp{ - BulletType bullet; - - void init(){ - bullet.init(); - } - } - - abstract class TimedComp extends EntityComp implements Scaled{ - float time, lifetime; - - void update(){ - time = Math.min(time + Time.delta(), lifetime); - - if(time >= lifetime){ - remove(); - } - } - - @Override - public float fin(){ - return time / lifetime; - } - } - - 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()); - } - } - - @Depends(PosComp.class) - class StatusComp{ - private Array statuses = new Array<>(); - private Bits applied = new Bits(content.getBy(ContentType.status).size); - - private float speedMultiplier; - private float damageMultiplier; - private float armorMultiplier; - - void apply(StatusEffect effect, float duration){ - if(effect == StatusEffects.none || effect == null || isImmune(effect)) return; //don't apply empty or immune effects - - if(statuses.size > 0){ - //check for opposite effects - for(StatusEntry entry : statuses){ - //extend effect - if(entry.effect == effect){ - entry.time = Math.max(entry.time, duration); - return; - }else if(entry.effect.reactsWith(effect)){ //find opposite - StatusEntry.tmp.effect = entry.effect; - //TODO unit cannot be null here - entry.effect.getTransition(null, effect, entry.time, duration, StatusEntry.tmp); - entry.time = StatusEntry.tmp.time; - - if(StatusEntry.tmp.effect != entry.effect){ - entry.effect = StatusEntry.tmp.effect; - } - - //stop looking when one is found - return; - } - } - } - - //otherwise, no opposites found, add direct effect - StatusEntry entry = Pools.obtain(StatusEntry.class, StatusEntry::new); - entry.set(effect, duration); - statuses.add(entry); - } - - boolean isImmune(StatusEffect effect){ - return false; - } - - Color getStatusColor(){ - if(statuses.size == 0){ - return Tmp.c1.set(Color.white); - } - - float r = 0f, g = 0f, b = 0f; - for(StatusEntry entry : statuses){ - r += entry.effect.color.r; - g += entry.effect.color.g; - b += entry.effect.color.b; - } - return Tmp.c1.set(r / statuses.size, g / statuses.size, b / statuses.size, 1f); - } - - void update(){ - applied.clear(); - speedMultiplier = damageMultiplier = armorMultiplier = 1f; - - if(statuses.isEmpty()) return; - - statuses.eachFilter(entry -> { - entry.time = Math.max(entry.time - Time.delta(), 0); - applied.set(entry.effect.id); - - if(entry.time <= 0){ - Pools.free(entry); - return true; - }else{ - speedMultiplier *= entry.effect.speedMultiplier; - armorMultiplier *= entry.effect.armorMultiplier; - damageMultiplier *= entry.effect.damageMultiplier; - //TODO unit can't be null - entry.effect.update(null, entry.time); - } - - return false; - }); - } - - boolean hasEffect(StatusEffect effect){ - return applied.get(effect.id); - } - - void writeSave(DataOutput stream) throws IOException{ - stream.writeByte(statuses.size); - for(StatusEntry entry : statuses){ - stream.writeByte(entry.effect.id); - stream.writeFloat(entry.time); - } - } - - void readSave(DataInput stream, byte version) throws IOException{ - for(StatusEntry effect : statuses){ - Pools.free(effect); - } - - statuses.clear(); - - byte amount = stream.readByte(); - for(int i = 0; i < amount; i++){ - byte id = stream.readByte(); - float time = stream.readFloat(); - StatusEntry entry = Pools.obtain(StatusEntry.class, StatusEntry::new); - entry.set(content.getByID(ContentType.status, id), time); - statuses.add(entry); - } - } - } - - @BaseComponent - class EntityComp{ - int id; - - void init(){} - - void update(){} - - void remove(){} - - T as(Class type){ - return (T)this; - } - } -} diff --git a/core/src/mindustry/entities/def/EntityDefs.java b/core/src/mindustry/entities/def/EntityDefs.java deleted file mode 100644 index d538cec4a8..0000000000 --- a/core/src/mindustry/entities/def/EntityDefs.java +++ /dev/null @@ -1,10 +0,0 @@ -package mindustry.entities.def; - -import mindustry.annotations.Annotations.*; -import mindustry.entities.def.EntityComps.*; - -class EntityDefs{ - - @EntityDef({BulletComp.class, VelComp.class, TimedComp.class}) - class BulletDef{} -}