Moved many block files; work on annotation processor
This commit is contained in:
@@ -6,7 +6,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Goal: To create a system to send events to the server from the client and vice versa.<br>
|
||||
* Goal: To create a system to send events to the server from the client and vice versa, without creating a new packet type each time.<br>
|
||||
* These events may optionally also trigger on the caller client/server as well.<br>
|
||||
*<br>
|
||||
* Three annotations are used for this purpose.<br>
|
||||
@@ -22,7 +22,12 @@ public class Annotations {
|
||||
/**Marks a method as invokable remotely from a server on a client.*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
public @interface RemoteClient {}
|
||||
public @interface RemoteClient {
|
||||
/**Whether a client-specific method is generated that accepts a connecton ID and sends to only one player. Default is false.*/
|
||||
boolean one() default false;
|
||||
/**Whether a 'global' method is generated that sends the event to all players. Default is true.*/
|
||||
boolean all() default true;
|
||||
}
|
||||
|
||||
/**Marks a method as invokable remotely from a client on a server.
|
||||
* All RemoteServer methods must have their first formal parameter be of type Player.
|
||||
@@ -42,4 +47,30 @@ public class Annotations {
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
public @interface Unreliable{}
|
||||
|
||||
/**Specifies that this method will be placed in the class specified by its value.
|
||||
* Only use constants for this value!*/ //TODO enforce this
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
public @interface In{
|
||||
String value();
|
||||
}
|
||||
|
||||
/**Specifies that this method will be used to write classes of the type returned by {@link #value()}.<br>
|
||||
* This method must return void and have two parameters, the first being of type {@link java.nio.ByteBuffer} and the second
|
||||
* being the type returned by {@link #value()}.*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
public @interface WriteClass {
|
||||
Class<?> value();
|
||||
}
|
||||
|
||||
/**Specifies that this method will be used to read classes of the type returned by {@link #value()}. <br>
|
||||
* This method must return the type returned by {@link #value()},
|
||||
* and have one parameter, being of type {@link java.nio.ByteBuffer}.*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
public @interface ReadClass {
|
||||
Class<?> value();
|
||||
}
|
||||
}
|
||||
|
||||
15
annotations/src/io/anuke/annotations/ClassEntry.java
Normal file
15
annotations/src/io/anuke/annotations/ClassEntry.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package io.anuke.annotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**Represents a class witha list method entries to include in it.*/
|
||||
public class ClassEntry {
|
||||
/**All methods in this generated class.*/
|
||||
public final ArrayList<MethodEntry> methods = new ArrayList<>();
|
||||
/**Simple class name.*/
|
||||
public final String name;
|
||||
|
||||
public ClassEntry(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
72
annotations/src/io/anuke/annotations/IOFinder.java
Normal file
72
annotations/src/io/anuke/annotations/IOFinder.java
Normal file
@@ -0,0 +1,72 @@
|
||||
package io.anuke.annotations;
|
||||
|
||||
import io.anuke.annotations.Annotations.ReadClass;
|
||||
import io.anuke.annotations.Annotations.WriteClass;
|
||||
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**This class finds reader and writer methods annotated by the {@link io.anuke.annotations.Annotations.WriteClass}
|
||||
* and {@link io.anuke.annotations.Annotations.ReadClass} annotations.*/
|
||||
public class IOFinder {
|
||||
|
||||
/**Finds all class serializers for all types and returns them. Logs errors when necessary.
|
||||
* Maps full class names to their serializers.*/
|
||||
public HashMap<String, ClassSerializer> findSerializers(RoundEnvironment env){
|
||||
HashMap<String, ClassSerializer> result = new HashMap<>();
|
||||
|
||||
//get methods with the types
|
||||
Set<? extends Element> writers = env.getElementsAnnotatedWith(WriteClass.class);
|
||||
Set<? extends Element> readers = env.getElementsAnnotatedWith(ReadClass.class);
|
||||
|
||||
//look for writers first
|
||||
for(Element writer : writers){
|
||||
WriteClass writean = writer.getAnnotation(WriteClass.class);
|
||||
Class<?> type = writean.value();
|
||||
|
||||
//make sure there's only one read method
|
||||
if(readers.stream().filter(elem -> elem.getAnnotation(ReadClass.class).value() == type).count() > 1){
|
||||
Utils.messager.printMessage(Kind.ERROR, "Multiple writer methods for type: ", writer);
|
||||
}
|
||||
|
||||
//make sure there's only one write method
|
||||
Stream<? extends Element> stream = readers.stream().filter(elem -> elem.getAnnotation(ReadClass.class).value() == type);
|
||||
if(stream.count() == 0){
|
||||
Utils.messager.printMessage(Kind.ERROR, "Writer method does not have an accompanying reader: ", writer);
|
||||
}else if(stream.count() > 1){
|
||||
Utils.messager.printMessage(Kind.ERROR, "Writer method has multiple reader for type: ", writer);
|
||||
}
|
||||
|
||||
Element reader = stream.findFirst().get();
|
||||
|
||||
//add to result list
|
||||
result.put(type.getName(), new ClassSerializer(getFullMethod(reader), getFullMethod(writer), type.getName()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getFullMethod(Element element){
|
||||
return element.getEnclosingElement().asType().toString() + "." + element.getSimpleName();
|
||||
}
|
||||
|
||||
/**Information about read/write methods for a specific class type.*/
|
||||
public static class ClassSerializer{
|
||||
/**Fully qualified method name of the reader.*/
|
||||
public final String readMethod;
|
||||
/**Fully qualified method name of the writer.*/
|
||||
public final String writeMethod;
|
||||
/**Fully qualified class type name.*/
|
||||
public final String classType;
|
||||
|
||||
public ClassSerializer(String readMethod, String writeMethod, String classType) {
|
||||
this.readMethod = readMethod;
|
||||
this.writeMethod = writeMethod;
|
||||
this.classType = classType;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
annotations/src/io/anuke/annotations/MethodEntry.java
Normal file
23
annotations/src/io/anuke/annotations/MethodEntry.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package io.anuke.annotations;
|
||||
|
||||
/**Class that repesents a remote method to be constructed and put into a class.*/
|
||||
public class MethodEntry {
|
||||
/**Simple target class name.*/
|
||||
public final String className;
|
||||
/**Fully qualified target method to call.*/
|
||||
public final String targetMethod;
|
||||
/**Whether this method can be called on a client/server.*/
|
||||
public final boolean client, server;
|
||||
/**Whether an additional 'one' and 'all' method variant is generated. At least one of these must be true.
|
||||
* Only applicable to client (server-invoked) methods.*/
|
||||
public final boolean allVariant, oneVariant;
|
||||
|
||||
public MethodEntry(String className, String targetMethod, boolean client, boolean server, boolean allVariant, boolean oneVariant) {
|
||||
this.className = className;
|
||||
this.targetMethod = targetMethod;
|
||||
this.client = client;
|
||||
this.server = server;
|
||||
this.allVariant = allVariant;
|
||||
this.oneVariant = oneVariant;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import io.anuke.annotations.Annotations.Local;
|
||||
import io.anuke.annotations.Annotations.RemoteClient;
|
||||
import io.anuke.annotations.Annotations.RemoteServer;
|
||||
import io.anuke.annotations.Annotations.Unreliable;
|
||||
import io.anuke.annotations.IOFinder.ClassSerializer;
|
||||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.SourceVersion;
|
||||
@@ -34,60 +35,30 @@ import java.util.Set;
|
||||
"io.anuke.annotations.Annotations.RemoteClient",
|
||||
"io.anuke.annotations.Annotations.RemoteServer",
|
||||
"io.anuke.annotations.Annotations.Local",
|
||||
"io.anuke.annotations.Annotations.Unreliable"
|
||||
"io.anuke.annotations.Annotations.Unreliable",
|
||||
"io.anuke.annotations.Annotations.In",
|
||||
"io.anuke.annotations.Annotations.WriteClass",
|
||||
"io.anuke.annotations.Annotations.ReadClass",
|
||||
})
|
||||
public class AnnotationProcessor extends AbstractProcessor {
|
||||
private static final int maxPacketSize = 128;
|
||||
public class RemoteMethodAnnotationProcessor extends AbstractProcessor {
|
||||
/**Maximum size of each event packet.*/
|
||||
private static final int maxPacketSize = 512;
|
||||
/**Name of the base package to put all the generated classes.*/
|
||||
private static final String packageClassName = "io.anuke.mindustry.gen";
|
||||
|
||||
private static final String clientFullClassName = "io.anuke.mindustry.gen.CallClient";
|
||||
private static final String serverFullClassName = "io.anuke.mindustry.gen.CallServer";
|
||||
|
||||
private static final HashMap<String, String[][]> writeMap = new HashMap<String, String[][]>(){{
|
||||
put("Player", new String[][]{
|
||||
{
|
||||
"rbuffer.putInt(rvalue.id)"
|
||||
},
|
||||
{
|
||||
"rtype rvalue = io.anuke.mindustry.Vars.playerGroup.getByID(rbuffer.getInt())"
|
||||
}
|
||||
});
|
||||
|
||||
put("Tile", new String[][]{
|
||||
{
|
||||
"rbuffer.putInt(rvalue.packedPosition())"
|
||||
},
|
||||
{
|
||||
"rtype rvalue = io.anuke.mindustry.Vars.world.tile(rbuffer.getInt())"
|
||||
}
|
||||
});
|
||||
|
||||
put("String", new String[][]{
|
||||
{
|
||||
"rbuffer.putShort((short)rvalue.getBytes().length)",
|
||||
"rbuffer.put(rvalue.getBytes())"
|
||||
},
|
||||
{
|
||||
"short __rvalue_length = rbuffer.getShort()",
|
||||
"byte[] __rvalue_bytes = new byte[__rvalue_length]",
|
||||
"rbuffer.get(__rvalue_bytes)",
|
||||
"rtype rvalue = new rtype(__rvalue_bytes)"
|
||||
}
|
||||
});
|
||||
}};
|
||||
|
||||
private Types typeUtils;
|
||||
private Elements elementUtils;
|
||||
private Filer filer;
|
||||
private Messager messager;
|
||||
/**Maps fully qualified class names to serializers.*/
|
||||
private HashMap<String, ClassSerializer> serializers;
|
||||
/**Whether the initial round is done.*/
|
||||
private boolean done;
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment processingEnv) {
|
||||
super.init(processingEnv);
|
||||
typeUtils = processingEnv.getTypeUtils();
|
||||
elementUtils = processingEnv.getElementUtils();
|
||||
filer = processingEnv.getFiler();
|
||||
messager = processingEnv.getMessager();
|
||||
//put all relevant utils into utils class
|
||||
Utils.typeUtils = processingEnv.getTypeUtils();
|
||||
Utils.elementUtils = processingEnv.getElementUtils();
|
||||
Utils.filer = processingEnv.getFiler();
|
||||
Utils.messager = processingEnv.getMessager();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,13 +66,15 @@ public class AnnotationProcessor extends AbstractProcessor {
|
||||
if(done) return false;
|
||||
done = true;
|
||||
|
||||
serializers = new IOFinder().findSerializers(roundEnv);
|
||||
|
||||
writeElements(roundEnv, clientFullClassName, RemoteClient.class);
|
||||
writeElements(roundEnv, serverFullClassName, RemoteServer.class);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void writeElements(RoundEnvironment env, String fullClassName, Class<? extends Annotation> annotation){
|
||||
private void writeElements(RoundEnvironment env){
|
||||
try {
|
||||
boolean client = annotation == RemoteServer.class;
|
||||
String className = fullClassName.substring(1 + fullClassName.lastIndexOf('.'));
|
||||
@@ -279,15 +252,14 @@ public class AnnotationProcessor extends AbstractProcessor {
|
||||
TypeSpec spec = classBuilder.build();
|
||||
|
||||
JavaFile.builder(packageName, spec).build().writeTo(filer);
|
||||
|
||||
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPrimitive(String type){
|
||||
return type.equals("boolean") || type.equals("byte") || type.equals("short") || type.equals("int")
|
||||
|| type.equals("long") || type.equals("float") || type.equals("double") || type.equals("char");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package io.anuke.annotations;
|
||||
|
||||
public class Serializers {
|
||||
}
|
||||
18
annotations/src/io/anuke/annotations/Utils.java
Normal file
18
annotations/src/io/anuke/annotations/Utils.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package io.anuke.annotations;
|
||||
|
||||
import javax.annotation.processing.Filer;
|
||||
import javax.annotation.processing.Messager;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
|
||||
public class Utils {
|
||||
public static Types typeUtils;
|
||||
public static Elements elementUtils;
|
||||
public static Filer filer;
|
||||
public static Messager messager;
|
||||
|
||||
public static boolean isPrimitive(String type){
|
||||
return type.equals("boolean") || type.equals("byte") || type.equals("short") || type.equals("int")
|
||||
|| type.equals("long") || type.equals("float") || type.equals("double") || type.equals("char");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user