Class ID mapping

This commit is contained in:
Anuken
2020-02-04 13:57:42 -05:00
parent be50997f94
commit 012421afcb
21 changed files with 106 additions and 63 deletions

View File

@@ -17,6 +17,12 @@ public class Annotations{
public @interface Component{
}
/** Indicates that a method is implemented by the annotation processor. */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface InternalImpl{
}
/** Indicates priority of a method in an entity. Methods with higher priority are done last. */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)

View File

@@ -1,5 +1,6 @@
package mindustry.annotations;
import arc.files.*;
import arc.struct.*;
import arc.util.*;
import com.squareup.javapoet.*;
@@ -30,6 +31,7 @@ public abstract class BaseProcessor extends AbstractProcessor{
protected int round;
protected int rounds = 1;
protected RoundEnvironment env;
protected Fi rootDirectory;
public static String getMethodName(Element element){
return ((TypeElement)element.getEnclosingElement()).getQualifiedName().toString() + "." + element.getSimpleName();
@@ -113,6 +115,17 @@ public abstract class BaseProcessor extends AbstractProcessor{
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
if(round++ >= rounds) return false; //only process 1 round
if(rootDirectory == null){
try{
String path = Fi.get(filer.getResource(StandardLocation.CLASS_OUTPUT, "no", "no")
.toUri().toURL().toString().substring(System.getProperty("os.name").contains("Windows") ? 6 : "file:".length()))
.parent().parent().parent().parent().parent().parent().parent().toString().replace("%20", " ");
rootDirectory = Fi.get(path);
}catch(IOException e){
throw new RuntimeException(e);
}
}
this.env = roundEnv;
try{
process(roundEnv);

View File

@@ -5,29 +5,22 @@ import arc.scene.style.*;
import arc.struct.*;
import arc.util.serialization.*;
import com.squareup.javapoet.*;
import mindustry.annotations.*;
import mindustry.annotations.Annotations.*;
import mindustry.annotations.*;
import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.lang.model.element.*;
import javax.tools.Diagnostic.*;
import javax.tools.*;
import java.util.*;
@SupportedAnnotationTypes("mindustry.annotations.Annotations.StyleDefaults")
public class AssetsProcess extends BaseProcessor{
private String path;
@Override
public void process(RoundEnvironment env) throws Exception{
path = Fi.get(BaseProcessor.filer.createResource(StandardLocation.CLASS_OUTPUT, "no", "no")
.toUri().toURL().toString().substring(System.getProperty("os.name").contains("Windows") ? 6 : "file:".length()))
.parent().parent().parent().parent().parent().parent().toString();
path = path.replace("%20", " ");
processSounds("Sounds", path + "/assets/sounds", "arc.audio.Sound");
processSounds("Musics", path + "/assets/music", "arc.audio.Music");
processSounds("Sounds", rootDirectory + "/core/assets/sounds", "arc.audio.Sound");
processSounds("Musics", rootDirectory + "/core/assets/music", "arc.audio.Music");
processUI(env.getElementsAnnotatedWith(StyleDefaults.class));
}
@@ -38,8 +31,8 @@ public class AssetsProcess extends BaseProcessor{
MethodSpec.Builder load = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
MethodSpec.Builder loadStyles = MethodSpec.methodBuilder("loadStyles").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
MethodSpec.Builder icload = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
String resources = path + "/assets-raw/sprites/ui";
Jval icons = Jval.read(Fi.get(path + "/assets-raw/fontgen/config.json").readString());
String resources = rootDirectory + "/core/assets-raw/sprites/ui";
Jval icons = Jval.read(Fi.get(rootDirectory + "/core/assets-raw/fontgen/config.json").readString());
ictype.addField(FieldSpec.builder(ParameterizedTypeName.get(ObjectMap.class, String.class, TextureRegionDrawable.class),
"icons", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("new ObjectMap<>()").build());

View File

@@ -1,8 +1,10 @@
package mindustry.annotations.impl;
import arc.files.*;
import arc.func.*;
import arc.struct.*;
import arc.util.*;
import arc.util.io.*;
import com.squareup.javapoet.*;
import com.squareup.javapoet.TypeSpec.*;
import com.sun.source.tree.*;
@@ -21,7 +23,7 @@ import java.lang.annotation.*;
"mindustry.annotations.Annotations.BaseComponent"
})
public class EntityProcess extends BaseProcessor{
Array<Definition> definitions = new Array<>();
Array<EntityDefinition> definitions = new Array<>();
Array<GroupDefinition> groupDefs = new Array<>();
Array<Stype> baseComponents;
ObjectMap<String, Stype> componentNames = new ObjectMap<>();
@@ -161,7 +163,7 @@ public class EntityProcess extends BaseProcessor{
//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.first().is(Modifier.NATIVE)) && entry.value.size == 1){
if((entry.value.first().is(Modifier.ABSTRACT) || entry.value.first().is(Modifier.NATIVE)) && entry.value.size == 1 && !entry.value.first().has(InternalImpl.class)){
err(entry.value.first().up().getSimpleName() + "#" + entry.value.first() + " is an abstract method and must be implemented in some component", type);
}
@@ -208,7 +210,7 @@ public class EntityProcess extends BaseProcessor{
builder.addMethod(mbuilder.build());
}
definitions.add(new Definition(builder, type, components, groups));
definitions.add(new EntityDefinition("mindustry.gen." + name, builder, type, components, groups));
}
//generate groups
@@ -216,6 +218,7 @@ public class EntityProcess extends BaseProcessor{
MethodSpec.Builder groupInit = MethodSpec.methodBuilder("init").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
for(GroupDefinition group : groupDefs){
Stype ctype = group.components.first();
//class names for interface/group
ClassName itype = ClassName.bestGuess("mindustry.gen." + interfaceName(ctype));
ClassName groupc = ClassName.bestGuess("mindustry.entities.EntityGroup");
@@ -226,16 +229,61 @@ public class EntityProcess extends BaseProcessor{
groupInit.addStatement("$L = new $T<>($L, $L)", group.name, groupc, group.def.spatial(), group.def.mapping());
}
//write the groups
groupsBuilder.addMethod(groupInit.build());
write(groupsBuilder);
//load map of sync IDs
StringMap map = new StringMap();
Fi idProps = rootDirectory.child("annotations/src/main/resources/classids.properties");
if(!idProps.exists()) idProps.writeString("");
PropertiesUtils.load(map, idProps.reader());
//next ID to be used in generation
Integer max = map.values().toArray().map(Integer::parseInt).max(i -> i);
int maxID = max == null ? 0 : max + 1;
//assign IDs
definitions.sort(Structs.comparing(t -> t.base.toString()));
for(EntityDefinition def : definitions){
String name = def.base.fullName();
if(map.containsKey(name)){
def.classID = map.getInt(name);
}else{
def.classID = maxID++;
map.put(name, def.classID + "");
}
}
//write assigned IDs
PropertiesUtils.store(map, idProps.writer(false), "Maps entity names to IDs. Autogenerated.");
//build mapping class for sync IDs
TypeSpec.Builder idBuilder = TypeSpec.classBuilder("ClassMapping").addModifiers(Modifier.PUBLIC)
.addField(FieldSpec.builder(TypeName.get(Prov[].class), "mapping", Modifier.PRIVATE, Modifier.STATIC).initializer("new Prov[256]").build())
.addMethod(MethodSpec.methodBuilder("map").addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(TypeName.get(Prov.class)).addParameter(int.class, "id").addStatement("return mapping[id]").build());
CodeBlock.Builder idStore = CodeBlock.builder();
//store the mappings
for(EntityDefinition def : definitions){
//store mapping
idStore.addStatement("mapping[$L] = $L::new", def.classID, def.name);
//return mapping
def.builder.addMethod(MethodSpec.methodBuilder("classId").addAnnotation(Override.class)
.returns(int.class).addModifiers(Modifier.PUBLIC).addStatement("return " + def.classID).build());
}
idBuilder.addStaticBlock(idStore.build());
write(idBuilder);
}else{
//round 2: generate actual classes and implement interfaces
Array<Stype> interfaces = types(EntityInterface.class);
//implement each definition
for(Definition def : definitions){
for(EntityDefinition def : definitions){
//get interface for each component
for(Stype comp : def.components){
@@ -369,14 +417,17 @@ public class EntityProcess extends BaseProcessor{
}
}
class Definition{
class EntityDefinition{
final Array<GroupDefinition> groups;
final Array<Stype> components;
final TypeSpec.Builder builder;
final Stype base;
final String name;
int classID;
public Definition(Builder builder, Stype base, Array<Stype> components, Array<GroupDefinition> groups){
public EntityDefinition(String name, Builder builder, Stype base, Array<Stype> components, Array<GroupDefinition> groups){
this.builder = builder;
this.name = name;
this.base = base;
this.groups = groups;
this.components = components;

View File

@@ -16,6 +16,10 @@ public class Stype extends Selement<TypeElement>{
return new Stype((TypeElement)BaseProcessor.typeu.asElement(mirror));
}
public String fullName(){
return mirror().toString();
}
public Array<Stype> interfaces(){
return Array.with(e.getInterfaces()).map(Stype::of);
}