mirror of
https://github.com/Anuken/Mindustry.git
synced 2024-11-13 07:15:28 +03:00
Entity components based on code generation - experiment
This commit is contained in:
parent
9f20ff151c
commit
da2aee7d31
@ -4,9 +4,28 @@ import java.lang.annotation.*;
|
||||
|
||||
public class Annotations{
|
||||
|
||||
/** Indicates an entity definition. */
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface StyleDefaults {
|
||||
public @interface EntityDef{
|
||||
Class[] value();
|
||||
}
|
||||
|
||||
/** Indicates an internal interface for entity components. */
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface EntityInterface{
|
||||
}
|
||||
|
||||
/** Indicates that a component logic method should be merged. */
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Merge{
|
||||
}
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface StyleDefaults{
|
||||
}
|
||||
|
||||
/** Indicates that a method should always call its super version. */
|
||||
@ -16,10 +35,10 @@ public class Annotations{
|
||||
|
||||
}
|
||||
|
||||
/** Annotation that allows overriding CallSuper annotation. To be used on method that overrides method with CallSuper annotation from parent class.*/
|
||||
/** Annotation that allows overriding CallSuper annotation. To be used on method that overrides method with CallSuper annotation from parent class. */
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface OverrideCallSuper {
|
||||
public @interface OverrideCallSuper{
|
||||
}
|
||||
|
||||
/** Marks a class as serializable. */
|
||||
|
@ -1,9 +1,15 @@
|
||||
package mindustry.annotations;
|
||||
|
||||
import arc.struct.*;
|
||||
import com.squareup.javapoet.*;
|
||||
import com.sun.source.util.*;
|
||||
import mindustry.annotations.util.*;
|
||||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.*;
|
||||
import javax.lang.model.element.*;
|
||||
import javax.lang.model.util.*;
|
||||
import java.lang.annotation.*;
|
||||
import java.util.*;
|
||||
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||
@ -15,8 +21,11 @@ public abstract class BaseProcessor extends AbstractProcessor{
|
||||
public static Elements elementu;
|
||||
public static Filer filer;
|
||||
public static Messager messager;
|
||||
public static Trees trees;
|
||||
|
||||
protected int round;
|
||||
protected int rounds = 1;
|
||||
protected RoundEnvironment env;
|
||||
|
||||
public static String getMethodName(Element element){
|
||||
return ((TypeElement)element.getEnclosingElement()).getQualifiedName().toString() + "." + element.getSimpleName();
|
||||
@ -27,19 +36,40 @@ public abstract class BaseProcessor extends AbstractProcessor{
|
||||
|| type.equals("long") || type.equals("float") || type.equals("double") || type.equals("char");
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment processingEnv){
|
||||
super.init(processingEnv);
|
||||
public static void write(TypeSpec.Builder builder) throws Exception{
|
||||
JavaFile.builder(packageName, builder.build()).build().writeTo(BaseProcessor.filer);
|
||||
}
|
||||
|
||||
typeu = processingEnv.getTypeUtils();
|
||||
elementu = processingEnv.getElementUtils();
|
||||
filer = processingEnv.getFiler();
|
||||
messager = processingEnv.getMessager();
|
||||
public Array<Stype> types(Class<? extends Annotation> type){
|
||||
return Array.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof TypeElement)
|
||||
.map(e -> new Stype((TypeElement)e));
|
||||
}
|
||||
|
||||
public Array<Svar> fields(Class<? extends Annotation> type){
|
||||
return Array.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof VariableElement)
|
||||
.map(e -> new Svar((VariableElement)e));
|
||||
}
|
||||
|
||||
public Array<Smethod> methods(Class<? extends Annotation> type){
|
||||
return Array.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof ExecutableElement)
|
||||
.map(e -> new Smethod((ExecutableElement)e));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment env){
|
||||
super.init(env);
|
||||
|
||||
trees = Trees.instance(env);
|
||||
typeu = env.getTypeUtils();
|
||||
elementu = env.getElementUtils();
|
||||
filer = env.getFiler();
|
||||
messager = env.getMessager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
||||
if(round++ != 0) return false; //only process 1 round
|
||||
if(round++ >= rounds) return false; //only process 1 round
|
||||
this.env = roundEnv;
|
||||
try{
|
||||
process(roundEnv);
|
||||
}catch(Exception e){
|
||||
|
@ -0,0 +1,183 @@
|
||||
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.*;
|
||||
import java.util.*;
|
||||
|
||||
@SupportedAnnotationTypes({
|
||||
"mindustry.annotations.Annotations.EntityDef",
|
||||
"mindustry.annotations.Annotations.EntityInterface"
|
||||
})
|
||||
public class EntityProcess extends BaseProcessor{
|
||||
Array<Definition> definitions = new Array<>();
|
||||
|
||||
{
|
||||
rounds = 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(RoundEnvironment env) throws Exception{
|
||||
|
||||
//round 1: get component classes and generate interfaces for them
|
||||
if(round == 1){
|
||||
Array<Stype> allDefs = types(EntityDef.class);
|
||||
|
||||
ObjectSet<Stype> allComponents = new ObjectSet<>();
|
||||
|
||||
//find all components used...
|
||||
for(Stype type : allDefs){
|
||||
allComponents.addAll(allComponents(type));
|
||||
}
|
||||
|
||||
//create component interfaces
|
||||
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))){
|
||||
String cname = Strings.capitalize(field.name());
|
||||
//getter
|
||||
inter.addMethod(MethodSpec.methodBuilder("get" + cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC).returns(field.tname()).build());
|
||||
//setter
|
||||
inter.addMethod(MethodSpec.methodBuilder("set" + cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC).addParameter(field.tname(), field.name()).build());
|
||||
}
|
||||
|
||||
write(inter);
|
||||
}
|
||||
|
||||
//look at each definition
|
||||
for(Stype type : allDefs){
|
||||
String name = type.name().replace("Def", "Gen");
|
||||
TypeSpec.Builder builder = TypeSpec.classBuilder(name).addModifiers(Modifier.PUBLIC, Modifier.FINAL);
|
||||
|
||||
Array<Stype> components = allComponents(type);
|
||||
ObjectMap<String, Array<Smethod>> methods = new ObjectMap<>();
|
||||
|
||||
//add all components
|
||||
for(Stype comp : components){
|
||||
|
||||
//write fields
|
||||
Array<Svar> fields = comp.fields();
|
||||
for(Svar f : fields){
|
||||
VariableTree tree = f.tree();
|
||||
FieldSpec.Builder fbuilder = FieldSpec.builder(f.tname(), f.name(), Modifier.PUBLIC);
|
||||
//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<String, Array<Smethod>> 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.returns(TypeName.get(first.ret()));
|
||||
for(Svar var : first.params()){
|
||||
mbuilder.addParameter(var.tname(), var.name());
|
||||
}
|
||||
|
||||
boolean returns = !first.ret().toString().equals("void");
|
||||
|
||||
for(Smethod elem : entry.value){
|
||||
//wrap scope to prevent variable leakage
|
||||
if(!returns) mbuilder.beginControlFlow("");
|
||||
|
||||
//get all statements in the method, copy them over
|
||||
MethodTree methodTree = elem.tree();
|
||||
BlockTree blockTree = methodTree.getBody();
|
||||
for(StatementTree st : blockTree.getStatements()){
|
||||
String state = st.toString();
|
||||
mbuilder.addStatement(state.substring(0, state.length() - 1));
|
||||
}
|
||||
|
||||
//end scope
|
||||
if(!returns) mbuilder.endControlFlow();
|
||||
}
|
||||
|
||||
builder.addMethod(mbuilder.build());
|
||||
}
|
||||
|
||||
definitions.add(new Definition(builder, type));
|
||||
|
||||
}
|
||||
}else{
|
||||
//round 2: generate actual classes and implement interfaces
|
||||
Array<Stype> interfaces = types(EntityInterface.class);
|
||||
|
||||
//implement each definition
|
||||
for(Definition def : definitions){
|
||||
Array<Stype> components = allComponents(def.base);
|
||||
|
||||
//get interface for each component
|
||||
for(Stype comp : components){
|
||||
//implement the interface
|
||||
Stype inter = interfaces.find(i -> i.name().equals(comp.name() + "c"));
|
||||
def.builder.addSuperinterface(inter.tname());
|
||||
|
||||
//generate getter/setter for each method
|
||||
for(Smethod method : inter.methods()){
|
||||
String var = Strings.camelize(method.name().substring(3));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return all components that a entity def has */
|
||||
Array<Stype> allComponents(Stype type){
|
||||
ObjectSet<TypeMirror> mirrors = ObjectSet.with(mirrors(type));
|
||||
for(TypeMirror curr : mirrors){
|
||||
List<? extends TypeMirror> 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);
|
||||
}
|
||||
}
|
||||
return mirrors.asArray().map(m -> new Stype((TypeElement)typeu.asElement(m)));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -42,96 +42,83 @@ public class RemoteProcess extends BaseProcessor{
|
||||
//list of all method entries
|
||||
private ArrayList<ClassEntry> classes;
|
||||
|
||||
{
|
||||
rounds = 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
||||
if(round > 1) return false; //only process 2 rounds
|
||||
public void process(RoundEnvironment roundEnv) throws Exception{
|
||||
//round 1: find all annotations, generate *writers*
|
||||
if(round == 1){
|
||||
//get serializers
|
||||
serializers = new IOFinder().findSerializers(roundEnv);
|
||||
//last method ID used
|
||||
int lastMethodID = 0;
|
||||
//find all elements with the Remote annotation
|
||||
elements = roundEnv.getElementsAnnotatedWith(Remote.class);
|
||||
//map of all classes to generate by name
|
||||
classMap = new HashMap<>();
|
||||
//list of all method entries
|
||||
methods = new ArrayList<>();
|
||||
//list of all method entries
|
||||
classes = new ArrayList<>();
|
||||
|
||||
round++;
|
||||
List<Element> orderedElements = new ArrayList<>(elements);
|
||||
orderedElements.sort(Comparator.comparing(Object::toString));
|
||||
|
||||
try{
|
||||
//create methods
|
||||
for(Element element : orderedElements){
|
||||
Remote annotation = element.getAnnotation(Remote.class);
|
||||
|
||||
//round 1: find all annotations, generate *writers*
|
||||
if(round == 1){
|
||||
//get serializers
|
||||
serializers = new IOFinder().findSerializers(roundEnv);
|
||||
//last method ID used
|
||||
int lastMethodID = 0;
|
||||
//find all elements with the Remote annotation
|
||||
elements = roundEnv.getElementsAnnotatedWith(Remote.class);
|
||||
//map of all classes to generate by name
|
||||
classMap = new HashMap<>();
|
||||
//list of all method entries
|
||||
methods = new ArrayList<>();
|
||||
//list of all method entries
|
||||
classes = new ArrayList<>();
|
||||
|
||||
List<Element> orderedElements = new ArrayList<>(elements);
|
||||
orderedElements.sort(Comparator.comparing(Object::toString));
|
||||
|
||||
//create methods
|
||||
for(Element element : orderedElements){
|
||||
Remote annotation = element.getAnnotation(Remote.class);
|
||||
|
||||
//check for static
|
||||
if(!element.getModifiers().contains(Modifier.STATIC) || !element.getModifiers().contains(Modifier.PUBLIC)){
|
||||
BaseProcessor.messager.printMessage(Kind.ERROR, "All @Remote methods must be public and static: ", element);
|
||||
}
|
||||
|
||||
//can't generate none methods
|
||||
if(annotation.targets() == Loc.none){
|
||||
BaseProcessor.messager.printMessage(Kind.ERROR, "A @Remote method's targets() cannot be equal to 'none':", element);
|
||||
}
|
||||
|
||||
//get and create class entry if needed
|
||||
if(!classMap.containsKey(callLocation)){
|
||||
ClassEntry clas = new ClassEntry(callLocation);
|
||||
classMap.put(callLocation, clas);
|
||||
classes.add(clas);
|
||||
}
|
||||
|
||||
ClassEntry entry = classMap.get(callLocation);
|
||||
|
||||
//create and add entry
|
||||
MethodEntry method = new MethodEntry(entry.name, BaseProcessor.getMethodName(element), annotation.targets(), annotation.variants(),
|
||||
annotation.called(), annotation.unreliable(), annotation.forward(), lastMethodID++, (ExecutableElement)element, annotation.priority());
|
||||
|
||||
entry.methods.add(method);
|
||||
methods.add(method);
|
||||
//check for static
|
||||
if(!element.getModifiers().contains(Modifier.STATIC) || !element.getModifiers().contains(Modifier.PUBLIC)){
|
||||
BaseProcessor.messager.printMessage(Kind.ERROR, "All @Remote methods must be public and static: ", element);
|
||||
}
|
||||
|
||||
//create read/write generators
|
||||
RemoteWriteGenerator writegen = new RemoteWriteGenerator(serializers);
|
||||
//can't generate none methods
|
||||
if(annotation.targets() == Loc.none){
|
||||
BaseProcessor.messager.printMessage(Kind.ERROR, "A @Remote method's targets() cannot be equal to 'none':", element);
|
||||
}
|
||||
|
||||
//generate the methods to invoke (write)
|
||||
writegen.generateFor(classes, packageName);
|
||||
//get and create class entry if needed
|
||||
if(!classMap.containsKey(callLocation)){
|
||||
ClassEntry clas = new ClassEntry(callLocation);
|
||||
classMap.put(callLocation, clas);
|
||||
classes.add(clas);
|
||||
}
|
||||
|
||||
return true;
|
||||
}else if(round == 2){ //round 2: generate all *readers*
|
||||
RemoteReadGenerator readgen = new RemoteReadGenerator(serializers);
|
||||
ClassEntry entry = classMap.get(callLocation);
|
||||
|
||||
//generate server readers
|
||||
readgen.generateFor(methods.stream().filter(method -> method.where.isClient).collect(Collectors.toList()), readServerName, packageName, true);
|
||||
//generate client readers
|
||||
readgen.generateFor(methods.stream().filter(method -> method.where.isServer).collect(Collectors.toList()), readClientName, packageName, false);
|
||||
//create and add entry
|
||||
MethodEntry method = new MethodEntry(entry.name, BaseProcessor.getMethodName(element), annotation.targets(), annotation.variants(),
|
||||
annotation.called(), annotation.unreliable(), annotation.forward(), lastMethodID++, (ExecutableElement)element, annotation.priority());
|
||||
|
||||
//create class for storing unique method hash
|
||||
TypeSpec.Builder hashBuilder = TypeSpec.classBuilder("MethodHash").addModifiers(Modifier.PUBLIC);
|
||||
hashBuilder.addJavadoc(autogenWarning);
|
||||
hashBuilder.addField(FieldSpec.builder(int.class, "HASH", Modifier.STATIC, Modifier.PUBLIC, Modifier.FINAL)
|
||||
.initializer("$1L", Objects.hash(methods)).build());
|
||||
|
||||
//build and write resulting hash class
|
||||
TypeSpec spec = hashBuilder.build();
|
||||
JavaFile.builder(packageName, spec).build().writeTo(BaseProcessor.filer);
|
||||
|
||||
return true;
|
||||
entry.methods.add(method);
|
||||
methods.add(method);
|
||||
}
|
||||
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
//create read/write generators
|
||||
RemoteWriteGenerator writegen = new RemoteWriteGenerator(serializers);
|
||||
|
||||
return false;
|
||||
//generate the methods to invoke (write)
|
||||
writegen.generateFor(classes, packageName);
|
||||
}else if(round == 2){ //round 2: generate all *readers*
|
||||
RemoteReadGenerator readgen = new RemoteReadGenerator(serializers);
|
||||
|
||||
//generate server readers
|
||||
readgen.generateFor(methods.stream().filter(method -> method.where.isClient).collect(Collectors.toList()), readServerName, packageName, true);
|
||||
//generate client readers
|
||||
readgen.generateFor(methods.stream().filter(method -> method.where.isServer).collect(Collectors.toList()), readClientName, packageName, false);
|
||||
|
||||
//create class for storing unique method hash
|
||||
TypeSpec.Builder hashBuilder = TypeSpec.classBuilder("MethodHash").addModifiers(Modifier.PUBLIC);
|
||||
hashBuilder.addJavadoc(autogenWarning);
|
||||
hashBuilder.addField(FieldSpec.builder(int.class, "HASH", Modifier.STATIC, Modifier.PUBLIC, Modifier.FINAL)
|
||||
.initializer("$1L", Objects.hash(methods)).build());
|
||||
|
||||
//build and write resulting hash class
|
||||
TypeSpec spec = hashBuilder.build();
|
||||
JavaFile.builder(packageName, spec).build().writeTo(BaseProcessor.filer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
package mindustry.annotations.util;
|
||||
|
||||
import com.squareup.javapoet.*;
|
||||
import mindustry.annotations.*;
|
||||
|
||||
import javax.lang.model.element.*;
|
||||
import javax.lang.model.type.*;
|
||||
|
||||
public class Selement<T extends Element>{
|
||||
public final T e;
|
||||
|
||||
public Selement(T e){
|
||||
this.e = e;
|
||||
}
|
||||
|
||||
public TypeMirror mirror(){
|
||||
return e.asType();
|
||||
}
|
||||
|
||||
public TypeName tname(){
|
||||
return TypeName.get(mirror());
|
||||
}
|
||||
|
||||
public ClassName cname(){
|
||||
return ClassName.get((TypeElement)BaseProcessor.typeu.asElement(mirror()));
|
||||
}
|
||||
|
||||
public String name(){
|
||||
return e.getSimpleName().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return e.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package mindustry.annotations.util;
|
||||
|
||||
import arc.struct.*;
|
||||
import com.sun.source.tree.*;
|
||||
import mindustry.annotations.*;
|
||||
|
||||
import javax.lang.model.element.*;
|
||||
import javax.lang.model.type.*;
|
||||
|
||||
public class Smethod extends Selement<ExecutableElement>{
|
||||
|
||||
public Smethod(ExecutableElement executableElement){
|
||||
super(executableElement);
|
||||
}
|
||||
|
||||
public Array<Svar> params(){
|
||||
return Array.with(e.getParameters()).map(Svar::new);
|
||||
}
|
||||
|
||||
public TypeMirror ret(){
|
||||
return e.getReturnType();
|
||||
}
|
||||
|
||||
public MethodTree tree(){
|
||||
return BaseProcessor.trees.getTree(e);
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package mindustry.annotations.util;
|
||||
|
||||
import arc.struct.*;
|
||||
|
||||
import javax.lang.model.element.*;
|
||||
import javax.lang.model.type.*;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
public class Stype extends Selement<TypeElement>{
|
||||
|
||||
public Stype(TypeElement typeElement){
|
||||
super(typeElement);
|
||||
}
|
||||
|
||||
public <A extends Annotation> A annotation(Class<A> annotation){
|
||||
return e.getAnnotation(annotation);
|
||||
}
|
||||
|
||||
public Array<Svar> fields(){
|
||||
return Array.with(e.getEnclosedElements()).select(e -> e instanceof VariableElement).map(e -> new Svar((VariableElement)e));
|
||||
}
|
||||
|
||||
public Array<Smethod> methods(){
|
||||
return Array.with(e.getEnclosedElements()).select(e -> e instanceof ExecutableElement
|
||||
&& !e.getSimpleName().toString().contains("<")).map(e -> new Smethod((ExecutableElement)e));
|
||||
}
|
||||
|
||||
public Array<Smethod> constructors(){
|
||||
return Array.with(e.getEnclosedElements()).select(e -> e instanceof ExecutableElement
|
||||
&& e.getSimpleName().toString().contains("<")).map(e -> new Smethod((ExecutableElement)e));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeMirror mirror(){
|
||||
return e.asType();
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package mindustry.annotations.util;
|
||||
|
||||
import com.sun.source.tree.*;
|
||||
import mindustry.annotations.*;
|
||||
|
||||
import javax.lang.model.element.*;
|
||||
|
||||
public class Svar extends Selement<VariableElement>{
|
||||
|
||||
public Svar(VariableElement e){
|
||||
super(e);
|
||||
}
|
||||
|
||||
public boolean is(Modifier mod){
|
||||
return e.getModifiers().contains(mod);
|
||||
}
|
||||
|
||||
public VariableTree tree(){
|
||||
return (VariableTree)BaseProcessor.trees.getTree(e);
|
||||
}
|
||||
}
|
48
core/src/mindustry/entities/def/EntityDefs.java
Normal file
48
core/src/mindustry/entities/def/EntityDefs.java
Normal file
@ -0,0 +1,48 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.math.geom.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.net.*;
|
||||
|
||||
public class EntityDefs{
|
||||
|
||||
@EntityDef({Health.class, Vel.class, Status.class, Connection.class})
|
||||
class PlayerDef{}
|
||||
|
||||
class Health{
|
||||
float health, maxHealth;
|
||||
boolean dead;
|
||||
|
||||
float healthf(){
|
||||
return health / maxHealth;
|
||||
}
|
||||
}
|
||||
|
||||
class Pos{
|
||||
float x, y;
|
||||
}
|
||||
|
||||
class Vel extends Pos{
|
||||
Vec2 vel = new Vec2();
|
||||
|
||||
void update(){
|
||||
x += vel.x;
|
||||
y += vel.y;
|
||||
vel.scl(0.9f);
|
||||
}
|
||||
}
|
||||
|
||||
class Status{
|
||||
Statuses statuses = new Statuses();
|
||||
|
||||
void update(){
|
||||
statuses.update(null);
|
||||
}
|
||||
}
|
||||
|
||||
class Connection{
|
||||
NetConnection connection;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user