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)
@Retention(RetentionPolicy.CLASS)
public @interface Remote {
/**Whether this method can be invoked on remote clients.*/
boolean client() default true;
/**Whether this method can be invoked on the remote server.*/
boolean server() default false;
/**Whether this method can be invoked from remote clients.*/
boolean client() default false;
/**Whether this method can be invoked from the remote server.*/
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.
* Only affects client methods.*/
boolean one() default false;

View File

@ -5,10 +5,10 @@ import io.anuke.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 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.*/
@ -26,30 +26,48 @@ public class IOFinder {
//look for writers first
for(Element writer : writers){
WriteClass writean = writer.getAnnotation(WriteClass.class);
Class<?> type = writean.value();
String typeName = getValue(writean);
//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);
}
//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){
long count = readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).count();
if(count == 0){
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);
}
Element reader = stream.findFirst().get();
Element reader = readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).findFirst().get();
//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;
}
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.*/
public static class ClassSerializer{
/**Fully qualified method name of the reader.*/

View File

@ -19,23 +19,11 @@ import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
//TODO document
//TODO split up into more classes
//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
/**The annotation processor for generating remote method call code.*/
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({
"io.anuke.annotations.Annotations.RemoteClient",
"io.anuke.annotations.Annotations.RemoteServer",
"io.anuke.annotations.Annotations.Local",
"io.anuke.annotations.Annotations.Unreliable",
"io.anuke.annotations.Annotations.In",
"io.anuke.annotations.Annotations.Remote",
"io.anuke.annotations.Annotations.WriteClass",
"io.anuke.annotations.Annotations.ReadClass",
})
@ -125,10 +113,10 @@ public class RemoteMethodAnnotationProcessor extends AbstractProcessor {
RemoteReadGenerator readgen = new RemoteReadGenerator(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
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)
writegen.generateFor(classes, packageName);
@ -136,7 +124,7 @@ public class RemoteMethodAnnotationProcessor extends AbstractProcessor {
//create class for storing unique method hash
TypeSpec.Builder hashBuilder = TypeSpec.classBuilder("MethodHash").addModifiers(Modifier.PUBLIC);
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
TypeSpec spec = hashBuilder.build();
@ -145,6 +133,7 @@ public class RemoteMethodAnnotationProcessor extends AbstractProcessor {
return true;
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException(e);
}
}

View File

@ -113,7 +113,7 @@ public class RemoteReadGenerator {
//end control flow if necessary
if(started){
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();
}

View File

@ -42,7 +42,7 @@ public class RemoteWriteGenerator {
//write the 'send even to one player' variant, which is only applicable on the server
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){
ExecutableElement elem = methodEntry.element;
@ -134,7 +135,7 @@ public class RemoteWriteGenerator {
}
//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 -> {
//TODO invoke it
});
}

View File

@ -4,6 +4,7 @@ import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.TimeUtils;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.gen.RemoteReadServer;
import io.anuke.mindustry.net.Administration;
import io.anuke.mindustry.net.Administration.PlayerInfo;
import io.anuke.mindustry.net.Net;
@ -49,7 +50,7 @@ public class NetServer extends Module{
Net.handleServer(InvokePacket.class, (id, packet) -> {
//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;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.world.Tile;
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 io.anuke.mindustry.Vars;
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.UnimportantPacket;
@ -38,6 +39,7 @@ public class Packets {
if(Net.client()){
//TODO implement
//CallClient.readPacket(buffer, type);
RemoteReadClient.readPacket(buffer, type);
}else{
byte[] bytes = new byte[writeLength];
buffer.get(bytes);