mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-07-28 13:47:32 +07:00
it never ends
This commit is contained in:
@ -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{
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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)");
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
@ -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())) + ")";
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user