Annotation system bugs fixed

This commit is contained in:
Anuken
2018-06-06 20:56:59 -04:00
parent 9e136bad94
commit c443eee15d
9 changed files with 60 additions and 35 deletions

View File

@@ -15,10 +15,10 @@ public class Annotations {
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS) @Retention(RetentionPolicy.CLASS)
public @interface Remote { public @interface Remote {
/**Whether this method can be invoked on remote clients.*/ /**Whether this method can be invoked from remote clients.*/
boolean client() default true; boolean client() default false;
/**Whether this method can be invoked on the remote server.*/ /**Whether this method can be invoked from the remote server.*/
boolean server() default false; boolean server() default true;
/**Whether a client-specific method is generated that accepts a connecton ID and sends to only one player. Default is false. /**Whether a client-specific method is generated that accepts a connecton ID and sends to only one player. Default is false.
* Only affects client methods.*/ * Only affects client methods.*/
boolean one() default false; boolean one() default false;

View File

@@ -5,10 +5,10 @@ import io.anuke.annotations.Annotations.WriteClass;
import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.type.MirroredTypeException;
import javax.tools.Diagnostic.Kind; import javax.tools.Diagnostic.Kind;
import java.util.HashMap; import java.util.HashMap;
import java.util.Set; 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} /**This class finds reader and writer methods annotated by the {@link io.anuke.annotations.Annotations.WriteClass}
* and {@link io.anuke.annotations.Annotations.ReadClass} annotations.*/ * and {@link io.anuke.annotations.Annotations.ReadClass} annotations.*/
@@ -26,30 +26,48 @@ public class IOFinder {
//look for writers first //look for writers first
for(Element writer : writers){ for(Element writer : writers){
WriteClass writean = writer.getAnnotation(WriteClass.class); WriteClass writean = writer.getAnnotation(WriteClass.class);
Class<?> type = writean.value(); String typeName = getValue(writean);
//make sure there's only one read method //make sure there's only one read method
if(readers.stream().filter(elem -> elem.getAnnotation(ReadClass.class).value() == type).count() > 1){ if(readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).count() > 1){
Utils.messager.printMessage(Kind.ERROR, "Multiple writer methods for type: ", writer); Utils.messager.printMessage(Kind.ERROR, "Multiple writer methods for type: ", writer);
} }
//make sure there's only one write method //make sure there's only one write method
Stream<? extends Element> stream = readers.stream().filter(elem -> elem.getAnnotation(ReadClass.class).value() == type); long count = readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).count();
if(stream.count() == 0){ if(count == 0){
Utils.messager.printMessage(Kind.ERROR, "Writer method does not have an accompanying reader: ", writer); Utils.messager.printMessage(Kind.ERROR, "Writer method does not have an accompanying reader: ", writer);
}else if(stream.count() > 1){ }else if(count > 1){
Utils.messager.printMessage(Kind.ERROR, "Writer method has multiple reader for type: ", writer); Utils.messager.printMessage(Kind.ERROR, "Writer method has multiple reader for type: ", writer);
} }
Element reader = stream.findFirst().get(); Element reader = readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).findFirst().get();
//add to result list //add to result list
result.put(type.getName(), new ClassSerializer(Utils.getMethodName(reader), Utils.getMethodName(writer), type.getName())); result.put(typeName, new ClassSerializer(Utils.getMethodName(reader), Utils.getMethodName(writer), typeName));
} }
return result; return result;
} }
private String getValue(WriteClass write){
try {
Class<?> type = write.value();
return type.getName();
}catch (MirroredTypeException e){
return e.getTypeMirror().toString();
}
}
private String getValue(ReadClass read){
try {
Class<?> type = read.value();
return type.getName();
}catch (MirroredTypeException e){
return e.getTypeMirror().toString();
}
}
/**Information about read/write methods for a specific class type.*/ /**Information about read/write methods for a specific class type.*/
public static class ClassSerializer{ public static class ClassSerializer{
/**Fully qualified method name of the reader.*/ /**Fully qualified method name of the reader.*/

View File

@@ -19,23 +19,11 @@ import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
//TODO document
//TODO split up into more classes /**The annotation processor for generating remote method call code.*/
//TODO somehow use annotations to generate serializers for each type?
//TODO documentation
//TODO custom hash to verify server/client compatibility, just in case!
//TODO specify creation class for putting each method
//TODO unified Call.functionName() class for more unified usage
//TODO error reporting for invalid usage, e.g. method IDs
//TODO automatically disable calling on server/client when it's not necessary
//TODO autogenerate methods for calling functions for specific clients
@SupportedSourceVersion(SourceVersion.RELEASE_8) @SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({ @SupportedAnnotationTypes({
"io.anuke.annotations.Annotations.RemoteClient", "io.anuke.annotations.Annotations.Remote",
"io.anuke.annotations.Annotations.RemoteServer",
"io.anuke.annotations.Annotations.Local",
"io.anuke.annotations.Annotations.Unreliable",
"io.anuke.annotations.Annotations.In",
"io.anuke.annotations.Annotations.WriteClass", "io.anuke.annotations.Annotations.WriteClass",
"io.anuke.annotations.Annotations.ReadClass", "io.anuke.annotations.Annotations.ReadClass",
}) })
@@ -125,10 +113,10 @@ public class RemoteMethodAnnotationProcessor extends AbstractProcessor {
RemoteReadGenerator readgen = new RemoteReadGenerator(serializers); RemoteReadGenerator readgen = new RemoteReadGenerator(serializers);
RemoteWriteGenerator writegen = new RemoteWriteGenerator(serializers); RemoteWriteGenerator writegen = new RemoteWriteGenerator(serializers);
//generate client readers
readgen.generateFor(methods.stream().filter(method -> method.client).collect(Collectors.toList()), readClientName, packageName, false);
//generate server readers //generate server readers
readgen.generateFor(methods.stream().filter(method -> method.server).collect(Collectors.toList()), readServerName, packageName, true); readgen.generateFor(methods.stream().filter(method -> method.client).collect(Collectors.toList()), readServerName, packageName, true);
//generate client readers
readgen.generateFor(methods.stream().filter(method -> method.server).collect(Collectors.toList()), readClientName, packageName, false);
//generate the methods to invoke (write) //generate the methods to invoke (write)
writegen.generateFor(classes, packageName); writegen.generateFor(classes, packageName);
@@ -136,7 +124,7 @@ public class RemoteMethodAnnotationProcessor extends AbstractProcessor {
//create class for storing unique method hash //create class for storing unique method hash
TypeSpec.Builder hashBuilder = TypeSpec.classBuilder("MethodHash").addModifiers(Modifier.PUBLIC); TypeSpec.Builder hashBuilder = TypeSpec.classBuilder("MethodHash").addModifiers(Modifier.PUBLIC);
hashBuilder.addField(FieldSpec.builder(int.class, "HASH", Modifier.STATIC, Modifier.PUBLIC, Modifier.FINAL) hashBuilder.addField(FieldSpec.builder(int.class, "HASH", Modifier.STATIC, Modifier.PUBLIC, Modifier.FINAL)
.initializer("$1L)", Objects.hash(methods)).build()); .initializer("$1L", Objects.hash(methods)).build());
//build and write resulting hash class //build and write resulting hash class
TypeSpec spec = hashBuilder.build(); TypeSpec spec = hashBuilder.build();
@@ -145,6 +133,7 @@ public class RemoteMethodAnnotationProcessor extends AbstractProcessor {
return true; return true;
}catch (Exception e){ }catch (Exception e){
e.printStackTrace();
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }

View File

@@ -113,7 +113,7 @@ public class RemoteReadGenerator {
//end control flow if necessary //end control flow if necessary
if(started){ if(started){
readBlock.nextControlFlow("else"); readBlock.nextControlFlow("else");
readBlock.addStatement("throw new $1N(\"Invalid read method ID: \" + id + \"\")"); //handle invalid method IDs readBlock.addStatement("throw new $1N(\"Invalid read method ID: \" + id + \"\")", RuntimeException.class.getName()); //handle invalid method IDs
readBlock.endControlFlow(); readBlock.endControlFlow();
} }

View File

@@ -42,7 +42,7 @@ public class RemoteWriteGenerator {
//write the 'send even to one player' variant, which is only applicable on the server //write the 'send even to one player' variant, which is only applicable on the server
if(methodEntry.server && methodEntry.oneVariant){ if(methodEntry.server && methodEntry.oneVariant){
writeMethodVariant(classBuilder, methodEntry, true); writeMethodVariant(classBuilder, methodEntry, false);
} }
} }
@@ -52,6 +52,7 @@ public class RemoteWriteGenerator {
} }
} }
/**Creates a specific variant for a method entry.*/
private void writeMethodVariant(TypeSpec.Builder classBuilder, MethodEntry methodEntry, boolean toAll){ private void writeMethodVariant(TypeSpec.Builder classBuilder, MethodEntry methodEntry, boolean toAll){
ExecutableElement elem = methodEntry.element; ExecutableElement elem = methodEntry.element;
@@ -134,7 +135,7 @@ public class RemoteWriteGenerator {
} }
//add statement for writing it //add statement for writing it
method.addStatement(ser.writeMethod + "(buffer, " + varName +")"); method.addStatement(ser.writeMethod + "(TEMP_BUFFER, " + varName +")");
} }
} }

View File

@@ -80,6 +80,7 @@ public class NetClient extends Module {
Net.handleClient(InvokePacket.class, packet -> { Net.handleClient(InvokePacket.class, packet -> {
//TODO invoke it //TODO invoke it
}); });
} }

View File

@@ -4,6 +4,7 @@ import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.TimeUtils; import com.badlogic.gdx.utils.TimeUtils;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.gen.RemoteReadServer;
import io.anuke.mindustry.net.Administration; import io.anuke.mindustry.net.Administration;
import io.anuke.mindustry.net.Administration.PlayerInfo; import io.anuke.mindustry.net.Administration.PlayerInfo;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
@@ -49,7 +50,7 @@ public class NetServer extends Module{
Net.handleServer(InvokePacket.class, (id, packet) -> { Net.handleServer(InvokePacket.class, (id, packet) -> {
//TODO implement //TODO implement
//CallServer.readPacket(packet.writeBuffer, packet.type, connections.get(id)); RemoteReadServer.readPacket(packet.writeBuffer, packet.type, connections.get(id));
}); });
} }

View File

@@ -1,5 +1,18 @@
package io.anuke.mindustry.net; package io.anuke.mindustry.net;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.world.Tile;
public class NetEvents { public class NetEvents {
@Remote(unreliable = true, one = true)
public static void callClientMethod(int something, Player player, String str, boolean bool){
System.out.println("Called " + something + " ? " + bool);
}
@Remote(local = false)
public static void someOtherMethod(Tile tile){
System.out.println("Called with tile " + tile);
}
} }

View File

@@ -3,6 +3,7 @@ package io.anuke.mindustry.net;
import com.badlogic.gdx.utils.TimeUtils; import com.badlogic.gdx.utils.TimeUtils;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.gen.RemoteReadClient;
import io.anuke.mindustry.net.Packet.ImportantPacket; import io.anuke.mindustry.net.Packet.ImportantPacket;
import io.anuke.mindustry.net.Packet.UnimportantPacket; import io.anuke.mindustry.net.Packet.UnimportantPacket;
@@ -38,6 +39,7 @@ public class Packets {
if(Net.client()){ if(Net.client()){
//TODO implement //TODO implement
//CallClient.readPacket(buffer, type); //CallClient.readPacket(buffer, type);
RemoteReadClient.readPacket(buffer, type);
}else{ }else{
byte[] bytes = new byte[writeLength]; byte[] bytes = new byte[writeLength];
buffer.get(bytes); buffer.get(bytes);