it never ends

This commit is contained in:
Anuken
2020-02-05 13:03:22 -05:00
parent a7b39e56bd
commit da97aee8e4
111 changed files with 1327 additions and 1644 deletions

View File

@ -6,7 +6,7 @@ public class Annotations{
//region entity interfaces
/** Indicates that a component field is read-only. */
@Target(ElementType.FIELD)
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface ReadOnly{
}

View File

@ -10,6 +10,7 @@ import mindustry.annotations.util.*;
import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.lang.model.util.*;
import javax.tools.Diagnostic.*;
import javax.tools.*;
@ -42,6 +43,22 @@ public abstract class BaseProcessor extends AbstractProcessor{
|| type.equals("long") || type.equals("float") || type.equals("double") || type.equals("char");
}
public static String simpleName(String str){
return str.contains(".") ? str.substring(str.lastIndexOf('.') + 1) : str;
}
public static TypeVariableName getTVN(TypeParameterElement element) {
String name = element.getSimpleName().toString();
List<? extends TypeMirror> boundsMirrors = element.getBounds();
List<TypeName> boundsTypeNames = new ArrayList<>();
for (TypeMirror typeMirror : boundsMirrors) {
boundsTypeNames.add(TypeName.get(typeMirror));
}
return TypeVariableName.get(name, boundsTypeNames.toArray(new TypeName[0]));
}
public static void write(TypeSpec.Builder builder) throws Exception{
write(builder, null);
}
@ -87,12 +104,12 @@ public abstract class BaseProcessor extends AbstractProcessor{
.map(e -> new Smethod((ExecutableElement)e));
}
public void err(String message){
public static void err(String message){
messager.printMessage(Kind.ERROR, message);
Log.err("[CODEGEN ERROR] " +message);
}
public void err(String message, Element elem){
public static void err(String message, Element elem){
messager.printMessage(Kind.ERROR, message, elem);
Log.err("[CODEGEN ERROR] " + message + ": " + elem);
}

View File

@ -11,7 +11,6 @@ import mindustry.annotations.*;
import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.lang.model.element.*;
import javax.tools.Diagnostic.*;
import java.util.*;
@SupportedAnnotationTypes("mindustry.annotations.Annotations.StyleDefaults")
@ -98,7 +97,7 @@ public class AssetsProcess extends BaseProcessor{
String name = p.nameWithoutExtension();
if(names.contains(name)){
BaseProcessor.messager.printMessage(Kind.ERROR, "Duplicate file name: " + p.toString() + "!");
BaseProcessor.err("Duplicate file name: " + p.toString() + "!");
}else{
names.add(name);
}

View File

@ -63,7 +63,6 @@ public class EntityProcess extends BaseProcessor{
//create component interfaces
for(Stype component : allComponents){
Log.info("&yGenerating interface for " + component);
TypeSpec.Builder inter = TypeSpec.interfaceBuilder(interfaceName(component)).addModifiers(Modifier.PUBLIC).addAnnotation(EntityInterface.class);
//implement extra interfaces these components may have, e.g. position
@ -77,22 +76,16 @@ public class EntityProcess extends BaseProcessor{
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 = field.name();
//getter
inter.addMethod(MethodSpec.methodBuilder(cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC).returns(field.tname()).build());
//setter
if(!field.is(Modifier.FINAL) && !field.annotations().contains(f -> f.toString().equals("@mindustry.annotations.Annotations.ReadOnly"))){
inter.addMethod(MethodSpec.methodBuilder(cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC).addParameter(field.tname(), field.name()).build());
}
}
ObjectSet<String> signatures = new ObjectSet<>();
//add utility methods to interface
for(Smethod method : component.methods()){
//skip private methods, those are for internal use.
if(method.is(Modifier.PRIVATE)) continue;
//keep track of signatures used to prevent dupes
signatures.add(method.e.toString());
inter.addMethod(MethodSpec.methodBuilder(method.name())
.addExceptions(method.thrownt())
.addTypeVariables(method.typeVariables().map(TypeVariableName::get))
@ -101,7 +94,42 @@ public class EntityProcess extends BaseProcessor{
.build())).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT).build());
}
for(Svar field : component.fields().select(e -> !e.is(Modifier.STATIC) && !e.is(Modifier.PRIVATE) && !e.is(Modifier.TRANSIENT))){
String cname = field.name();
//getter
if(!signatures.contains(cname + "()")){
inter.addMethod(MethodSpec.methodBuilder(cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
.addAnnotations(Array.with(field.annotations()).select(a -> a.toString().contains("Null")).map(AnnotationSpec::get))
.returns(field.tname()).build());
}
//setter
if(!field.is(Modifier.FINAL) && !signatures.contains(cname + "(" + field.mirror().toString() + ")") &&
!field.annotations().contains(f -> f.toString().equals("@mindustry.annotations.Annotations.ReadOnly"))){
inter.addMethod(MethodSpec.methodBuilder(cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
.addParameter(ParameterSpec.builder(field.tname(), field.name())
.addAnnotations(Array.with(field.annotations())
.select(a -> a.toString().contains("Null")).map(AnnotationSpec::get)).build()).build());
}
}
write(inter);
//LOGGING
Log.info("&gGenerating interface for " + component.name());
for(TypeName tn : inter.superinterfaces){
Log.info("&g> &lbextends {0}", simpleName(tn.toString()));
}
//log methods generated
for(MethodSpec spec : inter.methodSpecs){
Log.info("&g> > &c{0} {1}({2})", simpleName(spec.returnType.toString()), spec.name, Array.with(spec.parameters).toString(", ", p -> simpleName(p.type.toString()) + " " + p.name));
}
Log.info("");
}
//look at each definition
@ -134,7 +162,7 @@ public class EntityProcess extends BaseProcessor{
fbuilder.initializer(tree.getInitializer().toString());
}
builder.addAnnotations(f.annotations().map(AnnotationSpec::get));
fbuilder.addAnnotations(f.annotations().map(AnnotationSpec::get));
builder.addField(fbuilder.build());
}
@ -150,6 +178,12 @@ public class EntityProcess extends BaseProcessor{
//representative method
Smethod first = entry.value.first();
//skip internal impl
if(first.has(InternalImpl.class)){
continue;
}
//build method using same params/returns
MethodSpec.Builder mbuilder = MethodSpec.methodBuilder(first.name()).addModifiers(first.is(Modifier.PRIVATE) ? Modifier.PRIVATE : Modifier.PUBLIC, Modifier.FINAL);
mbuilder.addTypeVariables(first.typeVariables().map(TypeVariableName::get));
@ -232,6 +266,18 @@ public class EntityProcess extends BaseProcessor{
//write the groups
groupsBuilder.addMethod(groupInit.build());
MethodSpec.Builder groupResize = MethodSpec.methodBuilder("resize")
.addParameter(TypeName.FLOAT, "x").addParameter(TypeName.FLOAT, "y").addParameter(TypeName.FLOAT, "w").addParameter(TypeName.FLOAT, "h")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
for(GroupDefinition group : groupDefs){
if(group.def.spatial()){
groupResize.addStatement("$L.resize(x, y, w, h)", group.name);
}
}
groupsBuilder.addMethod(groupResize.build());
write(groupsBuilder);
//load map of sync IDs
@ -302,16 +348,16 @@ public class EntityProcess extends BaseProcessor{
String var = method.name();
FieldSpec field = Array.with(def.builder.fieldSpecs).find(f -> f.name.equals(var));
//make sure it's a real variable AND that the component doesn't already implement it with custom logic
if(field == null || comp.methods().contains(m -> m.name().equals(method.name()))) continue;
if(field == null || comp.methods().contains(m -> m.simpleString().equals(method.simpleString()))) continue;
//getter
if(!method.isVoid()){
def.builder.addMethod(MethodSpec.overriding(method.e).addStatement("return " + var).build());
def.builder.addMethod(MethodSpec.overriding(method.e).addStatement("return " + var).addModifiers(Modifier.FINAL).build());
}
//setter
if(method.isVoid() && !Array.with(field.annotations).contains(f -> f.type.toString().equals("@mindustry.annotations.Annotations.ReadOnly"))){
def.builder.addMethod(MethodSpec.overriding(method.e).addStatement("this." + var + " = " + var).build());
def.builder.addMethod(MethodSpec.overriding(method.e).addModifiers(Modifier.FINAL).addStatement("this." + var + " = " + var).build());
}
}
}

View File

@ -29,7 +29,7 @@ public class StructProcess extends BaseProcessor{
for(TypeElement elem : elements){
if(!elem.getSimpleName().toString().endsWith("Struct")){
BaseProcessor.messager.printMessage(Kind.ERROR, "All classes annotated with @Struct must have their class names end in 'Struct'.", elem);
BaseProcessor.err("All classes annotated with @Struct must have their class names end in 'Struct'.", elem);
continue;
}
@ -45,7 +45,7 @@ public class StructProcess extends BaseProcessor{
int structTotalSize = (structSize <= 8 ? 8 : structSize <= 16 ? 16 : structSize <= 32 ? 32 : 64);
if(variables.size() == 0){
BaseProcessor.messager.printMessage(Kind.ERROR, "making a struct with no fields is utterly pointles.", elem);
BaseProcessor.err("making a struct with no fields is utterly pointles.", elem);
continue;
}
@ -133,7 +133,7 @@ public class StructProcess extends BaseProcessor{
JavaFile.builder(packageName, classBuilder.build()).build().writeTo(BaseProcessor.filer);
}catch(IllegalArgumentException e){
e.printStackTrace();
BaseProcessor.messager.printMessage(Kind.ERROR, e.getMessage(), elem);
BaseProcessor.err(e.getMessage(), elem);
}
}

View File

@ -1,15 +1,12 @@
package mindustry.annotations.remote;
import mindustry.annotations.Annotations.*;
import mindustry.annotations.*;
import mindustry.annotations.Annotations.ReadClass;
import mindustry.annotations.Annotations.WriteClass;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.type.MirroredTypeException;
import javax.tools.Diagnostic.Kind;
import java.util.HashMap;
import java.util.Set;
import javax.annotation.processing.*;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import java.util.*;
/**
* This class finds reader and writer methods annotated by the {@link WriteClass}
@ -35,15 +32,15 @@ public class IOFinder{
//make sure there's only one read method
if(readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).count() > 1){
BaseProcessor.messager.printMessage(Kind.ERROR, "Multiple writer methods for type '" + typeName + "'", writer);
BaseProcessor.err("Multiple writer methods for type '" + typeName + "'", writer);
}
//make sure there's only one write method
long count = readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).count();
if(count == 0){
BaseProcessor.messager.printMessage(Kind.ERROR, "Writer method does not have an accompanying reader: ", writer);
BaseProcessor.err("Writer method does not have an accompanying reader: ", writer);
}else if(count > 1){
BaseProcessor.messager.printMessage(Kind.ERROR, "Writer method has multiple reader for type: ", writer);
BaseProcessor.err("Writer method has multiple reader for type: ", writer);
}
Element reader = readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).findFirst().get();

View File

@ -2,16 +2,13 @@ package mindustry.annotations.remote;
import com.squareup.javapoet.*;
import mindustry.annotations.*;
import mindustry.annotations.remote.IOFinder.ClassSerializer;
import mindustry.annotations.remote.IOFinder.*;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.*;
import javax.tools.Diagnostic.Kind;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.lang.reflect.*;
import java.nio.*;
import java.util.*;
/** Generates code for reading remote invoke packets on the client and server. */
public class RemoteReadGenerator{
@ -29,8 +26,7 @@ public class RemoteReadGenerator{
* @param packageName Full target package name.
* @param needsPlayer Whether this read method requires a reference to the player sender.
*/
public void generateFor(List<MethodEntry> entries, String className, String packageName, boolean needsPlayer)
throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException, IOException{
public void generateFor(List<MethodEntry> entries, String className, String packageName, boolean needsPlayer) throws Exception{
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
classBuilder.addJavadoc(RemoteProcess.autogenWarning);
@ -48,7 +44,7 @@ public class RemoteReadGenerator{
Constructor<TypeName> cons = TypeName.class.getDeclaredConstructor(String.class);
cons.setAccessible(true);
TypeName playerType = cons.newInstance("mindustry.entities.type.Player");
TypeName playerType = cons.newInstance("mindustry.gen.Playerc");
//add player parameter
readMethod.addParameter(playerType, "player");
}
@ -91,10 +87,10 @@ public class RemoteReadGenerator{
}
}else{
//else, try and find a serializer
ClassSerializer ser = serializers.get(typeName);
ClassSerializer ser = serializers.getOrDefault(typeName, SerializerResolver.locate(entry.element, var.asType()));
if(ser == null){ //make sure a serializer exists!
BaseProcessor.messager.printMessage(Kind.ERROR, "No @ReadClass method to read class type: '" + typeName + "'", var);
BaseProcessor.err("No @ReadClass method to read class type '" + typeName + "' in method " + entry.targetMethod, var);
return;
}
@ -119,7 +115,7 @@ public class RemoteReadGenerator{
if(entry.forward && entry.where.isServer && needsPlayer){
//call forwarded method
readBlock.addStatement(packageName + "." + entry.className + "." + entry.element.getSimpleName() +
"__forward(player.con" + (varResult.length() == 0 ? "" : ", ") + varResult.toString() + ")");
"__forward(player.con()" + (varResult.length() == 0 ? "" : ", ") + varResult.toString() + ")");
}
readBlock.nextControlFlow("catch (java.lang.Exception e)");

View File

@ -1,16 +1,15 @@
package mindustry.annotations.remote;
import arc.struct.*;
import com.squareup.javapoet.*;
import mindustry.annotations.Annotations.*;
import mindustry.annotations.*;
import mindustry.annotations.Annotations.Loc;
import mindustry.annotations.remote.IOFinder.ClassSerializer;
import mindustry.annotations.remote.IOFinder.*;
import javax.lang.model.element.*;
import javax.tools.Diagnostic.Kind;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.io.*;
import java.nio.*;
import java.util.*;
/** Generates code for writing remote invoke packets on the client and server. */
public class RemoteWriteGenerator{
@ -74,12 +73,12 @@ public class RemoteWriteGenerator{
//validate client methods to make sure
if(methodEntry.where.isClient){
if(elem.getParameters().isEmpty()){
BaseProcessor.messager.printMessage(Kind.ERROR, "Client invoke methods must have a first parameter of type Player.", elem);
BaseProcessor.err("Client invoke methods must have a first parameter of type Player", elem);
return;
}
if(!elem.getParameters().get(0).asType().toString().equals("mindustry.entities.type.Player")){
BaseProcessor.messager.printMessage(Kind.ERROR, "Client invoke methods should have a first parameter of type Player.", elem);
if(!elem.getParameters().get(0).asType().toString().equals("Playerc")){
BaseProcessor.err("Client invoke methods should have a first parameter of type Playerc", elem);
return;
}
}
@ -138,6 +137,8 @@ public class RemoteWriteGenerator{
//rewind buffer
method.addStatement("TEMP_BUFFER.position(0)");
method.addTypeVariables(Array.with(elem.getTypeParameters()).map(BaseProcessor::getTVN));
for(int i = 0; i < elem.getParameters().size(); i++){
//first argument is skipped as it is always the player caller
if((!methodEntry.where.isServer/* || methodEntry.mode == Loc.both*/) && i == 0){
@ -176,10 +177,10 @@ public class RemoteWriteGenerator{
}
}else{
//else, try and find a serializer
ClassSerializer ser = serializers.get(typeName);
ClassSerializer ser = serializers.getOrDefault(typeName, SerializerResolver.locate(elem, var.asType()));
if(ser == null){ //make sure a serializer exists!
BaseProcessor.messager.printMessage(Kind.ERROR, "No @WriteClass method to write class type: '" + typeName + "'", var);
BaseProcessor.err("No @WriteClass method to write class type: '" + typeName + "'", var);
return;
}

View File

@ -0,0 +1,29 @@
package mindustry.annotations.remote;
import arc.struct.*;
import mindustry.annotations.remote.IOFinder.*;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
public class SerializerResolver{
private static final ClassSerializer entitySerializer = new ClassSerializer("mindustry.io.TypeIO.readEntity", "mindustry.io.TypeIO.writeEntity", "Entityc");
public static ClassSerializer locate(ExecutableElement elem, TypeMirror mirror){
//generic type
if(mirror.toString().equals("T")){
TypeParameterElement param = elem.getTypeParameters().get(0);
if(Array.with(param.getBounds()).contains(SerializerResolver::isEntity)){
return entitySerializer;
}
}
if(isEntity(mirror)){
return entitySerializer;
}
return null;
}
private static boolean isEntity(TypeMirror mirror){
return !mirror.toString().contains(".") && mirror.toString().endsWith("c");
}
}

View File

@ -49,4 +49,8 @@ public class Smethod extends Selement<ExecutableElement>{
public MethodTree tree(){
return BaseProcessor.trees.getTree(e);
}
public String simpleString(){
return name() + "(" + params().toString(", ", p -> BaseProcessor.simpleName(p.mirror().toString())) + ")";
}
}

View File

@ -1,5 +1,5 @@
#Maps entity names to IDs. Autogenerated.
#Tue Feb 04 17:41:17 EST 2020
#Wed Feb 05 12:35:36 EST 2020
mindustry.entities.def.EntityDefs.DecalDef=1
mindustry.entities.def.EntityDefs.EffectDef=2