Codegen for an inheritance tree
This commit is contained in:
@@ -50,6 +50,9 @@ public class Annotations{
|
|||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface Component{
|
public @interface Component{
|
||||||
|
/** Whether to generate a base class for this components.
|
||||||
|
* An entity cannot have two base classes, so only one component can have base be true. */
|
||||||
|
boolean base() default false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Indicates that a method is implemented by the annotation processor. */
|
/** Indicates that a method is implemented by the annotation processor. */
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package mindustry.annotations;
|
|||||||
|
|
||||||
import arc.files.*;
|
import arc.files.*;
|
||||||
import arc.struct.*;
|
import arc.struct.*;
|
||||||
import arc.util.*;
|
|
||||||
import arc.util.Log;
|
import arc.util.Log;
|
||||||
import arc.util.Log.*;
|
import arc.util.Log.*;
|
||||||
|
import arc.util.*;
|
||||||
import com.squareup.javapoet.*;
|
import com.squareup.javapoet.*;
|
||||||
import com.sun.source.util.*;
|
import com.sun.source.util.*;
|
||||||
import com.sun.tools.javac.model.*;
|
import com.sun.tools.javac.model.*;
|
||||||
@@ -22,9 +22,8 @@ import javax.tools.Diagnostic.*;
|
|||||||
import javax.tools.*;
|
import javax.tools.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||||
public abstract class BaseProcessor extends AbstractProcessor{
|
public abstract class BaseProcessor extends AbstractProcessor{
|
||||||
@@ -100,10 +99,16 @@ public abstract class BaseProcessor extends AbstractProcessor{
|
|||||||
return str.contains(".") ? str.substring(str.lastIndexOf('.') + 1) : str;
|
return str.contains(".") ? str.substring(str.lastIndexOf('.') + 1) : str;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TypeName tname(String name) throws Exception{
|
public static TypeName tname(String pack, String simple){
|
||||||
Constructor<TypeName> cons = TypeName.class.getDeclaredConstructor(String.class);
|
return ClassName.get(pack, simple );
|
||||||
cons.setAccessible(true);
|
}
|
||||||
return cons.newInstance(name);
|
|
||||||
|
public static TypeName tname(String name){
|
||||||
|
if(!name.contains(".")) return ClassName.get(packageName, name);
|
||||||
|
|
||||||
|
String pack = name.substring(0, name.lastIndexOf("."));
|
||||||
|
String simple = name.substring(name.lastIndexOf(".") + 1);
|
||||||
|
return ClassName.get(pack, simple);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TypeName tname(Class<?> c){
|
public static TypeName tname(Class<?> c){
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public class EntityIO{
|
|||||||
MethodSpec.Builder method;
|
MethodSpec.Builder method;
|
||||||
ObjectSet<String> presentFields = new ObjectSet<>();
|
ObjectSet<String> presentFields = new ObjectSet<>();
|
||||||
|
|
||||||
EntityIO(String name, TypeSpec.Builder type, ClassSerializer serializer, Fi directory){
|
EntityIO(String name, TypeSpec.Builder type, Seq<FieldSpec> typeFields, ClassSerializer serializer, Fi directory){
|
||||||
this.directory = directory;
|
this.directory = directory;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.serializer = serializer;
|
this.serializer = serializer;
|
||||||
@@ -49,7 +49,7 @@ public class EntityIO{
|
|||||||
int nextRevision = revisions.isEmpty() ? 0 : revisions.max(r -> r.version).version + 1;
|
int nextRevision = revisions.isEmpty() ? 0 : revisions.max(r -> r.version).version + 1;
|
||||||
|
|
||||||
//resolve preferred field order based on fields that fit
|
//resolve preferred field order based on fields that fit
|
||||||
Seq<FieldSpec> fields = Seq.with(type.fieldSpecs).select(spec ->
|
Seq<FieldSpec> fields = typeFields.select(spec ->
|
||||||
!spec.hasModifier(Modifier.TRANSIENT) &&
|
!spec.hasModifier(Modifier.TRANSIENT) &&
|
||||||
!spec.hasModifier(Modifier.STATIC) &&
|
!spec.hasModifier(Modifier.STATIC) &&
|
||||||
!spec.hasModifier(Modifier.FINAL)/* &&
|
!spec.hasModifier(Modifier.FINAL)/* &&
|
||||||
|
|||||||
@@ -38,10 +38,12 @@ public class EntityProcess extends BaseProcessor{
|
|||||||
ObjectMap<Selement, Seq<Stype>> defComponents = new ObjectMap<>();
|
ObjectMap<Selement, Seq<Stype>> defComponents = new ObjectMap<>();
|
||||||
ObjectMap<String, String> varInitializers = new ObjectMap<>();
|
ObjectMap<String, String> varInitializers = new ObjectMap<>();
|
||||||
ObjectMap<String, String> methodBlocks = new ObjectMap<>();
|
ObjectMap<String, String> methodBlocks = new ObjectMap<>();
|
||||||
|
ObjectMap<Stype, ObjectSet<Stype>> baseClassDeps = new ObjectMap<>();
|
||||||
ObjectSet<String> imports = new ObjectSet<>();
|
ObjectSet<String> imports = new ObjectSet<>();
|
||||||
Seq<Selement> allGroups = new Seq<>();
|
Seq<Selement> allGroups = new Seq<>();
|
||||||
Seq<Selement> allDefs = new Seq<>();
|
Seq<Selement> allDefs = new Seq<>();
|
||||||
Seq<Stype> allInterfaces = new Seq<>();
|
Seq<Stype> allInterfaces = new Seq<>();
|
||||||
|
Seq<TypeSpec.Builder> baseClasses = new Seq<>();
|
||||||
ClassSerializer serializer;
|
ClassSerializer serializer;
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -151,12 +153,53 @@ public class EntityProcess extends BaseProcessor{
|
|||||||
|
|
||||||
write(inter);
|
write(inter);
|
||||||
|
|
||||||
|
//generate base class if necessary
|
||||||
|
//SPECIAL CASE: components with EntityDefs don't get a base class! the generated class becomes the base class itself
|
||||||
|
if(component.annotation(Component.class).base()){
|
||||||
|
|
||||||
|
Seq<Stype> deps = depends.copy().and(component);
|
||||||
|
baseClassDeps.get(component, ObjectSet::new).addAll(deps);
|
||||||
|
|
||||||
|
//do not generate base classes when the component will generate one itself
|
||||||
|
if(!component.has(EntityDef.class)){
|
||||||
|
TypeSpec.Builder base = TypeSpec.classBuilder(baseName(component)).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT);
|
||||||
|
|
||||||
|
//go through all the fields.
|
||||||
|
for(Stype type : deps){
|
||||||
|
//add public fields
|
||||||
|
for(Svar field : type.fields().select(e -> !e.is(Modifier.STATIC) && !e.is(Modifier.PRIVATE) && !e.has(Import.class) && !e.has(ReadOnly.class))){
|
||||||
|
FieldSpec.Builder builder = FieldSpec.builder(field.tname(),field.name(), Modifier.PUBLIC);
|
||||||
|
|
||||||
|
//keep transience
|
||||||
|
if(field.is(Modifier.TRANSIENT)) builder.addModifiers(Modifier.TRANSIENT);
|
||||||
|
//keep all annotations
|
||||||
|
builder.addAnnotations(field.annotations().map(AnnotationSpec::get));
|
||||||
|
|
||||||
|
//add initializer if it exists
|
||||||
|
if(varInitializers.containsKey(field.descString())){
|
||||||
|
builder.initializer(varInitializers.get(field.descString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
base.addField(builder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//add interfaces
|
||||||
|
for(Stype type : depends){
|
||||||
|
base.addSuperinterface(tname(packageName, interfaceName(type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//add to queue to be written later
|
||||||
|
baseClasses.add(base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//LOGGING
|
//LOGGING
|
||||||
|
|
||||||
Log.debug("&gGenerating interface for " + component.name());
|
Log.debug("&gGenerating interface for " + component.name());
|
||||||
|
|
||||||
for(TypeName tn : inter.superinterfaces){
|
for(TypeName tn : inter.superinterfaces){
|
||||||
Log.debug("&g> &lbextends @", simpleName(tn.toString()));
|
Log.debug("&g> &lbimplements @", simpleName(tn.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
//log methods generated
|
//log methods generated
|
||||||
@@ -173,7 +216,12 @@ public class EntityProcess extends BaseProcessor{
|
|||||||
//this needs to be done before the entity interfaces are generated, as the entity classes need to know which groups to add themselves to
|
//this needs to be done before the entity interfaces are generated, as the entity classes need to know which groups to add themselves to
|
||||||
for(Selement<?> group : allGroups){
|
for(Selement<?> group : allGroups){
|
||||||
GroupDef an = group.annotation(GroupDef.class);
|
GroupDef an = group.annotation(GroupDef.class);
|
||||||
Seq<Stype> types = types(an, GroupDef::value).map(this::interfaceToComp);
|
Seq<Stype> types = types(an, GroupDef::value).map(stype -> {
|
||||||
|
Stype result = interfaceToComp(stype);
|
||||||
|
if(result == null) throw new IllegalArgumentException("Interface " + stype + " does not have an associated component!");
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
boolean collides = an.collide();
|
boolean collides = an.collide();
|
||||||
groupDefs.add(new GroupDefinition(group.name().startsWith("g") ? group.name().substring(1) : group.name(),
|
groupDefs.add(new GroupDefinition(group.name().startsWith("g") ? group.name().substring(1) : group.name(),
|
||||||
ClassName.bestGuess(packageName + "." + interfaceName(types.first())), types, an.spatial(), an.mapping(), collides));
|
ClassName.bestGuess(packageName + "." + interfaceName(types.first())), types, an.spatial(), an.mapping(), collides));
|
||||||
@@ -211,29 +259,45 @@ public class EntityProcess extends BaseProcessor{
|
|||||||
TypeSpec.Builder builder = TypeSpec.classBuilder(name).addModifiers(Modifier.PUBLIC);
|
TypeSpec.Builder builder = TypeSpec.classBuilder(name).addModifiers(Modifier.PUBLIC);
|
||||||
if(isFinal) builder.addModifiers(Modifier.FINAL);
|
if(isFinal) builder.addModifiers(Modifier.FINAL);
|
||||||
|
|
||||||
|
//all component classes (not interfaces)
|
||||||
Seq<Stype> components = allComponents(type);
|
Seq<Stype> components = allComponents(type);
|
||||||
Seq<GroupDefinition> groups = groupDefs.select(g -> (!g.components.isEmpty() && !g.components.contains(s -> !components.contains(s))) || g.manualInclusions.contains(type));
|
Seq<GroupDefinition> groups = groupDefs.select(g -> (!g.components.isEmpty() && !g.components.contains(s -> !components.contains(s))) || g.manualInclusions.contains(type));
|
||||||
ObjectMap<String, Seq<Smethod>> methods = new ObjectMap<>();
|
ObjectMap<String, Seq<Smethod>> methods = new ObjectMap<>();
|
||||||
ObjectMap<FieldSpec, Svar> specVariables = new ObjectMap<>();
|
ObjectMap<FieldSpec, Svar> specVariables = new ObjectMap<>();
|
||||||
ObjectSet<String> usedFields = new ObjectSet<>();
|
ObjectSet<String> usedFields = new ObjectSet<>();
|
||||||
|
|
||||||
|
//make sure there's less than 2 base classes
|
||||||
|
Seq<Stype> baseClasses = components.select(s -> s.annotation(Component.class).base());
|
||||||
|
if(baseClasses.size > 2){
|
||||||
|
err("No entity may have more than 2 base classes. Base classes: " + baseClasses, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
//get base class type name for extension
|
||||||
|
Stype baseClassType = baseClasses.any() ? baseClasses.first() : null;
|
||||||
|
@Nullable TypeName baseClass = baseClasses.any() ? tname(packageName + "." + baseName(baseClassType)) : null;
|
||||||
|
//whether the main class is the base itself
|
||||||
|
boolean typeIsBase = baseClassType != null && type.has(Component.class) && type.annotation(Component.class).base();
|
||||||
|
|
||||||
//add serialize() boolean
|
//add serialize() boolean
|
||||||
builder.addMethod(MethodSpec.methodBuilder("serialize").addModifiers(Modifier.PUBLIC, Modifier.FINAL).returns(boolean.class).addStatement("return " + ann.serialize()).build());
|
builder.addMethod(MethodSpec.methodBuilder("serialize").addModifiers(Modifier.PUBLIC, Modifier.FINAL).returns(boolean.class).addStatement("return " + ann.serialize()).build());
|
||||||
|
|
||||||
//all SyncField fields
|
//all SyncField fields
|
||||||
Seq<Svar> syncedFields = new Seq<>();
|
Seq<Svar> syncedFields = new Seq<>();
|
||||||
Seq<Svar> allFields = new Seq<>();
|
Seq<Svar> allFields = new Seq<>();
|
||||||
|
Seq<FieldSpec> allFieldSpecs = new Seq<>();
|
||||||
|
|
||||||
boolean isSync = components.contains(s -> s.name().contains("Sync"));
|
boolean isSync = components.contains(s -> s.name().contains("Sync"));
|
||||||
|
|
||||||
//add all components
|
//add all components
|
||||||
for(Stype comp : components){
|
for(Stype comp : components){
|
||||||
|
//whether this component's fields are defined in the base class
|
||||||
|
boolean isShadowed = baseClass != null && !typeIsBase && baseClassDeps.get(baseClassType).contains(comp);
|
||||||
|
|
||||||
//write fields to the class; ignoring transient/imported ones
|
//write fields to the class; ignoring transient/imported ones
|
||||||
Seq<Svar> fields = comp.fields().select(f -> !f.has(Import.class));
|
Seq<Svar> fields = comp.fields().select(f -> !f.has(Import.class));
|
||||||
for(Svar f : fields){
|
for(Svar f : fields){
|
||||||
if(!usedFields.add(f.name())){
|
if(!usedFields.add(f.name())){
|
||||||
err("Field '" + f.name() + "' of component '" + comp.name() + "' re-defines a field in entity '" + type.name() + "'");
|
err("Field '" + f.name() + "' of component '" + comp.name() + "' redefines a field in entity '" + type.name() + "'");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,9 +319,19 @@ public class EntityProcess extends BaseProcessor{
|
|||||||
|
|
||||||
fbuilder.addModifiers(f.has(ReadOnly.class) ? Modifier.PROTECTED : Modifier.PUBLIC);
|
fbuilder.addModifiers(f.has(ReadOnly.class) ? Modifier.PROTECTED : Modifier.PUBLIC);
|
||||||
fbuilder.addAnnotations(f.annotations().map(AnnotationSpec::get));
|
fbuilder.addAnnotations(f.annotations().map(AnnotationSpec::get));
|
||||||
builder.addField(fbuilder.build());
|
FieldSpec spec = fbuilder.build();
|
||||||
specVariables.put(builder.fieldSpecs.get(builder.fieldSpecs.size() - 1), f);
|
|
||||||
|
|
||||||
|
//whether this field would be added to the superclass
|
||||||
|
boolean isVisible = !f.is(Modifier.STATIC) && !f.is(Modifier.PRIVATE) && !f.has(ReadOnly.class);
|
||||||
|
|
||||||
|
//add the field only if it isn't visible or it wasn't implemented by the base class
|
||||||
|
if(!isShadowed || !isVisible){
|
||||||
|
builder.addField(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
specVariables.put(spec, f);
|
||||||
|
|
||||||
|
allFieldSpecs.add(spec);
|
||||||
allFields.add(f);
|
allFields.add(f);
|
||||||
|
|
||||||
//add extra sync fields
|
//add extra sync fields
|
||||||
@@ -294,7 +368,7 @@ public class EntityProcess extends BaseProcessor{
|
|||||||
.addModifiers(Modifier.PUBLIC)
|
.addModifiers(Modifier.PUBLIC)
|
||||||
.addStatement("return $S + $L", name + "#", "id").build());
|
.addStatement("return $S + $L", name + "#", "id").build());
|
||||||
|
|
||||||
EntityIO io = new EntityIO(type.name(), builder, serializer, rootDirectory.child("annotations/src/main/resources/revisions").child(name));
|
EntityIO io = new EntityIO(type.name(), builder, allFieldSpecs, serializer, rootDirectory.child("annotations/src/main/resources/revisions").child(name));
|
||||||
//entities with no sync comp and no serialization gen no code
|
//entities with no sync comp and no serialization gen no code
|
||||||
boolean hasIO = ann.genio() && (components.contains(s -> s.name().contains("Sync")) || ann.serialize());
|
boolean hasIO = ann.genio() && (components.contains(s -> s.name().contains("Sync")) || ann.serialize());
|
||||||
|
|
||||||
@@ -435,7 +509,7 @@ public class EntityProcess extends BaseProcessor{
|
|||||||
builder.addSuperinterface(Poolable.class);
|
builder.addSuperinterface(Poolable.class);
|
||||||
//implement reset()
|
//implement reset()
|
||||||
MethodSpec.Builder resetBuilder = MethodSpec.methodBuilder("reset").addModifiers(Modifier.PUBLIC);
|
MethodSpec.Builder resetBuilder = MethodSpec.methodBuilder("reset").addModifiers(Modifier.PUBLIC);
|
||||||
for(FieldSpec spec : builder.fieldSpecs){
|
for(FieldSpec spec : allFieldSpecs){
|
||||||
@Nullable Svar variable = specVariables.get(spec);
|
@Nullable Svar variable = specVariables.get(spec);
|
||||||
if(variable != null && variable.isAny(Modifier.STATIC, Modifier.FINAL)) continue;
|
if(variable != null && variable.isAny(Modifier.STATIC, Modifier.FINAL)) continue;
|
||||||
String desc = variable.descString();
|
String desc = variable.descString();
|
||||||
@@ -462,7 +536,7 @@ public class EntityProcess extends BaseProcessor{
|
|||||||
.returns(tname(packageName + "." + name))
|
.returns(tname(packageName + "." + name))
|
||||||
.addStatement(ann.pooled() ? "return Pools.obtain($L.class, " +name +"::new)" : "return new $L()", name).build());
|
.addStatement(ann.pooled() ? "return Pools.obtain($L.class, " +name +"::new)" : "return new $L()", name).build());
|
||||||
|
|
||||||
definitions.add(new EntityDefinition(packageName + "." + name, builder, type, components, groups));
|
definitions.add(new EntityDefinition(packageName + "." + name, builder, type, baseClass, components, groups, allFieldSpecs));
|
||||||
}
|
}
|
||||||
|
|
||||||
//generate groups
|
//generate groups
|
||||||
@@ -522,9 +596,9 @@ public class EntityProcess extends BaseProcessor{
|
|||||||
int maxID = max == null ? 0 : max + 1;
|
int maxID = max == null ? 0 : max + 1;
|
||||||
|
|
||||||
//assign IDs
|
//assign IDs
|
||||||
definitions.sort(Structs.comparing(t -> t.base.toString()));
|
definitions.sort(Structs.comparing(t -> t.naming.toString()));
|
||||||
for(EntityDefinition def : definitions){
|
for(EntityDefinition def : definitions){
|
||||||
String name = def.base.fullName();
|
String name = def.naming.fullName();
|
||||||
if(map.containsKey(name)){
|
if(map.containsKey(name)){
|
||||||
def.classID = map.getInt(name);
|
def.classID = map.getInt(name);
|
||||||
}else{
|
}else{
|
||||||
@@ -557,7 +631,7 @@ public class EntityProcess extends BaseProcessor{
|
|||||||
for(EntityDefinition def : definitions){
|
for(EntityDefinition def : definitions){
|
||||||
//store mapping
|
//store mapping
|
||||||
idStore.addStatement("idMap[$L] = $L::new", def.classID, def.name);
|
idStore.addStatement("idMap[$L] = $L::new", def.classID, def.name);
|
||||||
extraNames.get(def.base).each(extra -> {
|
extraNames.get(def.naming).each(extra -> {
|
||||||
idStore.addStatement("nameMap.put($S, $L::new)", extra, def.name);
|
idStore.addStatement("nameMap.put($S, $L::new)", extra, def.name);
|
||||||
if(!Strings.camelToKebab(extra).equals(extra)){
|
if(!Strings.camelToKebab(extra).equals(extra)){
|
||||||
idStore.addStatement("nameMap.put($S, $L::new)", Strings.camelToKebab(extra), def.name);
|
idStore.addStatement("nameMap.put($S, $L::new)", Strings.camelToKebab(extra), def.name);
|
||||||
@@ -576,11 +650,21 @@ public class EntityProcess extends BaseProcessor{
|
|||||||
}else{
|
}else{
|
||||||
//round 3: generate actual classes and implement interfaces
|
//round 3: generate actual classes and implement interfaces
|
||||||
|
|
||||||
|
//write base classes
|
||||||
|
for(TypeSpec.Builder b : baseClasses){
|
||||||
|
write(b, imports.asArray());
|
||||||
|
}
|
||||||
|
|
||||||
//implement each definition
|
//implement each definition
|
||||||
for(EntityDefinition def : definitions){
|
for(EntityDefinition def : definitions){
|
||||||
|
|
||||||
ObjectSet<String> methodNames = def.components.flatMap(type -> type.methods().map(Smethod::simpleString)).<String>as().asSet();
|
ObjectSet<String> methodNames = def.components.flatMap(type -> type.methods().map(Smethod::simpleString)).<String>as().asSet();
|
||||||
|
|
||||||
|
//add base class extension if it exists
|
||||||
|
if(def.extend != null){
|
||||||
|
def.builder.superclass(def.extend);
|
||||||
|
}
|
||||||
|
|
||||||
//get interface for each component
|
//get interface for each component
|
||||||
for(Stype comp : def.components){
|
for(Stype comp : def.components){
|
||||||
|
|
||||||
@@ -596,7 +680,7 @@ public class EntityProcess extends BaseProcessor{
|
|||||||
//generate getter/setter for each method
|
//generate getter/setter for each method
|
||||||
for(Smethod method : inter.methods()){
|
for(Smethod method : inter.methods()){
|
||||||
String var = method.name();
|
String var = method.name();
|
||||||
FieldSpec field = Seq.with(def.builder.fieldSpecs).find(f -> f.name.equals(var));
|
FieldSpec field = Seq.with(def.fieldSpecs).find(f -> f.name.equals(var));
|
||||||
//make sure it's a real variable AND that the component doesn't already implement it somewhere with custom logic
|
//make sure it's a real variable AND that the component doesn't already implement it somewhere with custom logic
|
||||||
if(field == null || methodNames.contains(method.simpleString())) continue;
|
if(field == null || methodNames.contains(method.simpleString())) continue;
|
||||||
|
|
||||||
@@ -681,12 +765,27 @@ public class EntityProcess extends BaseProcessor{
|
|||||||
/** @return interface for a component type */
|
/** @return interface for a component type */
|
||||||
String interfaceName(Stype comp){
|
String interfaceName(Stype comp){
|
||||||
String suffix = "Comp";
|
String suffix = "Comp";
|
||||||
if(!comp.name().endsWith(suffix)){
|
if(!comp.name().endsWith(suffix)) err("All components must have names that end with 'Comp'", comp.e);
|
||||||
err("All components must have names that end with 'Comp'", comp.e);
|
|
||||||
}
|
//example: BlockComp -> IBlock
|
||||||
return comp.name().substring(0, comp.name().length() - suffix.length()) + "c";
|
return comp.name().substring(0, comp.name().length() - suffix.length()) + "c";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return base class name for a component type */
|
||||||
|
String baseName(Stype comp){
|
||||||
|
String suffix = "Comp";
|
||||||
|
if(!comp.name().endsWith(suffix)) err("All components must have names that end with 'Comp'", comp.e);
|
||||||
|
boolean isConcrete = comp.has(EntityDef.class); //concrete base implementations have no "Base" suffix
|
||||||
|
|
||||||
|
return comp.name().substring(0, comp.name().length() - suffix.length()) + (isConcrete ? "" : "Base");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable Stype interfaceToComp(Stype type){
|
||||||
|
//example: IBlock -> BlockComp
|
||||||
|
String name = type.name().substring(0, type.name().length() - 1) + "Comp";
|
||||||
|
return componentNames.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
/** @return all components that a entity def has */
|
/** @return all components that a entity def has */
|
||||||
Seq<Stype> allComponents(Selement<?> type){
|
Seq<Stype> allComponents(Selement<?> type){
|
||||||
if(!defComponents.containsKey(type)){
|
if(!defComponents.containsKey(type)){
|
||||||
@@ -746,19 +845,10 @@ public class EntityProcess extends BaseProcessor{
|
|||||||
return interfaceToComp(type) != null;
|
return interfaceToComp(type) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable Stype interfaceToComp(Stype type){
|
|
||||||
String name = type.name().substring(0, type.name().length() - 1) + "Comp";
|
|
||||||
return componentNames.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
String createName(Selement<?> elem){
|
String createName(Selement<?> elem){
|
||||||
Seq<Stype> comps = types(elem.annotation(EntityDef.class), EntityDef::value).map(this::interfaceToComp);;
|
Seq<Stype> comps = types(elem.annotation(EntityDef.class), EntityDef::value).map(this::interfaceToComp);;
|
||||||
comps.sortComparing(Selement::name);
|
comps.sortComparing(Selement::name);
|
||||||
return comps.toString("", s -> s.name().replace("Comp", "")) + "Entity";
|
return comps.toString("", s -> s.name().replace("Comp", ""));
|
||||||
}
|
|
||||||
|
|
||||||
boolean isComponent(Stype type){
|
|
||||||
return type.annotation(Component.class) != null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<T extends Annotation> Seq<Stype> types(T t, Cons<T> consumer){
|
<T extends Annotation> Seq<Stype> types(T t, Cons<T> consumer){
|
||||||
@@ -795,17 +885,21 @@ public class EntityProcess extends BaseProcessor{
|
|||||||
class EntityDefinition{
|
class EntityDefinition{
|
||||||
final Seq<GroupDefinition> groups;
|
final Seq<GroupDefinition> groups;
|
||||||
final Seq<Stype> components;
|
final Seq<Stype> components;
|
||||||
|
final Seq<FieldSpec> fieldSpecs;
|
||||||
final TypeSpec.Builder builder;
|
final TypeSpec.Builder builder;
|
||||||
final Selement base;
|
final Selement naming;
|
||||||
final String name;
|
final String name;
|
||||||
|
final @Nullable TypeName extend;
|
||||||
int classID;
|
int classID;
|
||||||
|
|
||||||
public EntityDefinition(String name, Builder builder, Selement base, Seq<Stype> components, Seq<GroupDefinition> groups){
|
public EntityDefinition(String name, Builder builder, Selement naming, TypeName extend, Seq<Stype> components, Seq<GroupDefinition> groups, Seq<FieldSpec> fieldSpec){
|
||||||
this.builder = builder;
|
this.builder = builder;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.base = base;
|
this.naming = naming;
|
||||||
this.groups = groups;
|
this.groups = groups;
|
||||||
this.components = components;
|
this.components = components;
|
||||||
|
this.extend = extend;
|
||||||
|
this.fieldSpecs = fieldSpec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -813,7 +907,7 @@ public class EntityProcess extends BaseProcessor{
|
|||||||
return "Definition{" +
|
return "Definition{" +
|
||||||
"groups=" + groups +
|
"groups=" + groups +
|
||||||
"components=" + components +
|
"components=" + components +
|
||||||
", base=" + base +
|
", base=" + naming +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ block=1
|
|||||||
cix=2
|
cix=2
|
||||||
draug=3
|
draug=3
|
||||||
mace=4
|
mace=4
|
||||||
|
mindustry.entities.comp.BuildingComp=22
|
||||||
mindustry.entities.comp.BulletComp=5
|
mindustry.entities.comp.BulletComp=5
|
||||||
mindustry.entities.comp.DecalComp=6
|
mindustry.entities.comp.DecalComp=6
|
||||||
mindustry.entities.comp.EffectComp=7
|
mindustry.entities.comp.EffectComp=7
|
||||||
|
|||||||
@@ -183,7 +183,6 @@ project(":desktop"){
|
|||||||
apply plugin: "java"
|
apply plugin: "java"
|
||||||
|
|
||||||
compileJava.options.fork = true
|
compileJava.options.fork = true
|
||||||
compileJava.options.compilerArgs += ["-XDignore.symbol.file"]
|
|
||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
implementation project(":core")
|
implementation project(":core")
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
|||||||
//debug GL information
|
//debug GL information
|
||||||
Log.info("[GL] Version: @", graphics.getGLVersion());
|
Log.info("[GL] Version: @", graphics.getGLVersion());
|
||||||
Log.info("[GL] Max texture size: @", Gl.getInt(Gl.maxTextureSize));
|
Log.info("[GL] Max texture size: @", Gl.getInt(Gl.maxTextureSize));
|
||||||
Log.info("[GL] OpenGL 3.0 context: @", gl30 != null);
|
Log.info("[GL] Using @ context.", gl30 != null ? "OpenGL 3" : "OpenGL 2");
|
||||||
Log.info("[JAVA] Version: @", System.getProperty("java.version"));
|
Log.info("[JAVA] Version: @", System.getProperty("java.version"));
|
||||||
|
|
||||||
Time.setDeltaProvider(() -> {
|
Time.setDeltaProvider(() -> {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class GroupDefs<G>{
|
|||||||
@GroupDef(value = Playerc.class, mapping = true) G player;
|
@GroupDef(value = Playerc.class, mapping = true) G player;
|
||||||
@GroupDef(value = Bulletc.class, spatial = true, collide = true) G bullet;
|
@GroupDef(value = Bulletc.class, spatial = true, collide = true) G bullet;
|
||||||
@GroupDef(value = Unitc.class, spatial = true, mapping = true) G unit;
|
@GroupDef(value = Unitc.class, spatial = true, mapping = true) G unit;
|
||||||
@GroupDef(value = Tilec.class) G tile;
|
@GroupDef(value = Buildingc.class) G tile;
|
||||||
@GroupDef(value = Syncc.class, mapping = true) G sync;
|
@GroupDef(value = Syncc.class, mapping = true) G sync;
|
||||||
@GroupDef(value = Drawc.class) G draw;
|
@GroupDef(value = Drawc.class) G draw;
|
||||||
@GroupDef(value = Weatherc.class) G weather;
|
@GroupDef(value = Weatherc.class) G weather;
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ import mindustry.world.modules.*;
|
|||||||
|
|
||||||
import static mindustry.Vars.*;
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
@EntityDef(value = {Tilec.class}, isFinal = false, genio = false, serialize = false)
|
@EntityDef(value = {Buildingc.class}, isFinal = false, genio = false, serialize = false)
|
||||||
@Component
|
@Component(base = true)
|
||||||
abstract class TileComp implements Posc, Teamc, Healthc, Tilec, Timerc, QuadTreeObject, Displayable{
|
abstract class BuildingComp implements Posc, Teamc, Healthc, Tilec, Timerc, QuadTreeObject, Displayable{
|
||||||
//region vars and initialization
|
//region vars and initialization
|
||||||
static final float timeToSleep = 60f * 1;
|
static final float timeToSleep = 60f * 1;
|
||||||
static final ObjectSet<Tilec> tmpTiles = new ObjectSet<>();
|
static final ObjectSet<Tilec> tmpTiles = new ObjectSet<>();
|
||||||
@@ -15,7 +15,7 @@ import mindustry.graphics.*;
|
|||||||
import static mindustry.Vars.*;
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
@EntityDef(value = {Bulletc.class}, pooled = true, serialize = false)
|
@EntityDef(value = {Bulletc.class}, pooled = true, serialize = false)
|
||||||
@Component
|
@Component(base = true)
|
||||||
abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Drawc, Shielderc, Ownerc, Velc, Bulletc, Timerc{
|
abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Drawc, Shielderc, Ownerc, Velc, Bulletc, Timerc{
|
||||||
@Import Team team;
|
@Import Team team;
|
||||||
@Import Entityc owner;
|
@Import Entityc owner;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import mindustry.world.blocks.storage.CoreBlock.*;
|
|||||||
import static mindustry.Vars.*;
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
@EntityDef(value = {Playerc.class}, serialize = false)
|
@EntityDef(value = {Playerc.class}, serialize = false)
|
||||||
@Component
|
@Component(base = true)
|
||||||
abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Drawc{
|
abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Drawc{
|
||||||
static final float deathDelay = 30f;
|
static final float deathDelay = 30f;
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import mindustry.world.blocks.environment.*;
|
|||||||
|
|
||||||
import static mindustry.Vars.*;
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
@Component
|
@Component(base = true)
|
||||||
abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, Itemsc, Rotc, Unitc, Weaponsc, Drawc, Boundedc, Syncc, Shieldc, Displayable{
|
abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, Itemsc, Rotc, Unitc, Weaponsc, Drawc, Boundedc, Syncc, Shieldc, Displayable{
|
||||||
|
|
||||||
@Import float x, y, rotation, elevation, maxHealth, drag, armor, hitSize, health;
|
@Import float x, y, rotation, elevation, maxHealth, drag, armor, hitSize, health;
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public class Mods implements Loadable{
|
|||||||
|
|
||||||
/** @return the loaded mod found by class, or null if not found. */
|
/** @return the loaded mod found by class, or null if not found. */
|
||||||
public @Nullable LoadedMod getMod(Class<? extends Mod> type){
|
public @Nullable LoadedMod getMod(Class<? extends Mod> type){
|
||||||
return mods.find(m -> m.enabled() && m.main != null && m.main.getClass() == type);//loaded.find(l -> l.mod != null && l.mod.getClass() == type);
|
return mods.find(m -> m.enabled() && m.main != null && m.main.getClass() == type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Imports an external mod file.*/
|
/** Imports an external mod file.*/
|
||||||
|
|||||||
Reference in New Issue
Block a user