mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-02-02 20:33:50 +07:00
Interface + base component support
This commit is contained in:
parent
acb3438cc8
commit
5eb3f0f3de
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user