Skip to content

Commit

Permalink
Annotation processing done, more work on local multiplayer input
Browse files Browse the repository at this point in the history
  • Loading branch information
Anuken committed May 13, 2018
1 parent 00e70cb commit d1a3752
Show file tree
Hide file tree
Showing 16 changed files with 355 additions and 155 deletions.
Binary file modified annotations/build/libs/annotations-release.jar
Binary file not shown.
199 changes: 193 additions & 6 deletions annotations/src/io/anuke/annotations/AnnotationProcessor.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,47 @@
package io.anuke.annotations;

import com.squareup.javapoet.*;
import io.anuke.annotations.Annotations.Local;
import io.anuke.annotations.Annotations.Remote;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.*;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({
"java.lang.Override"
"io.anuke.annotations.Annotations.Remote",
"io.anuke.annotations.Annotations.Local"
})
public class AnnotationProcessor extends AbstractProcessor {
private static final int maxPacketSize = 128;
private static final String fullClassName = "io.anuke.mindustry.gen.CallEvent";
private static final String className = fullClassName.substring(1 + fullClassName.lastIndexOf('.'));
private static final String packageName = fullClassName.substring(0, fullClassName.lastIndexOf('.'));
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(buffer.getInt())"
}
});
}};

private Types typeUtils;
private Elements elementUtils;
private Filer filer;
private Messager messager;
private boolean done;

@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
Expand All @@ -30,11 +54,174 @@ public synchronized void init(ProcessingEnvironment processingEnv) {

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(Override.class)) {
messager.printMessage(Kind.ERROR, "an element has the override class: ", annotatedElement);
if(done) return false;
done = true;

ArrayList<Element> elements = new ArrayList<>();

for (Element element : roundEnv.getElementsAnnotatedWith(Remote.class)) {
if(!element.getModifiers().contains(Modifier.STATIC)) {
messager.printMessage(Kind.ERROR, "All local/remote methods must be static: ", element);
}else if(element.getKind() != ElementKind.METHOD){
messager.printMessage(Kind.ERROR, "All local/remote annotations must be on methods: ", element);
}else{
elements.add(element);
}
}
return false;

try {

TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className)
.addModifiers(Modifier.PUBLIC);

int id = 0;

classBuilder.addField(FieldSpec.builder(ByteBuffer.class, "TEMP_BUFFER", Modifier.STATIC, Modifier.PRIVATE, Modifier.FINAL)
.initializer("ByteBuffer.allocate($1L)", maxPacketSize).build());

MethodSpec.Builder readMethod = MethodSpec.methodBuilder("readPacket")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(ByteBuffer.class, "buffer")
.addParameter(int.class, "id")
.returns(void.class);

CodeBlock.Builder writeSwitch = CodeBlock.builder();
boolean started = false;

readMethod.addJavadoc("This method reads and executes a method by ID. For internal use only!");

for (Element e : elements) {
boolean local = e.getAnnotation(Local.class) != null;

ExecutableElement exec = (ExecutableElement)e;

MethodSpec.Builder method = MethodSpec.methodBuilder(e.getSimpleName().toString())
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class);

for(VariableElement var : exec.getParameters()){
method.addParameter(TypeName.get(var.asType()), var.getSimpleName().toString());
}

if(local){
//todo
int index = 0;
StringBuilder results = new StringBuilder();
for(VariableElement var : exec.getParameters()){
results.append(var.getSimpleName());
if(index != exec.getParameters().size() - 1) results.append(", ");
index ++;
}

method.addStatement("$N." + exec.getSimpleName() + "(" + results.toString() + ")",
((TypeElement)e.getEnclosingElement()).getQualifiedName().toString());
}

if(!started){
writeSwitch.beginControlFlow("if(id == "+id+")");
}else{
writeSwitch.nextControlFlow("else if(id == "+id+")");
}
started = true;

method.addStatement("$1N packet = new $1N()", "io.anuke.mindustry.net.Packets.InvokePacket");
method.addStatement("packet.writeBuffer = TEMP_BUFFER");
method.addStatement("TEMP_BUFFER.position(0)");

for(VariableElement var : exec.getParameters()){
String varName = var.getSimpleName().toString();
String typeName = var.asType().toString();
String bufferName = "TEMP_BUFFER";
String simpleTypeName = typeName.contains(".") ? typeName.substring(1 + typeName.lastIndexOf('.')) : typeName;
String capName = simpleTypeName.equals("byte") ? "" : Character.toUpperCase(simpleTypeName.charAt(0)) + simpleTypeName.substring(1);

if(typeUtils.isAssignable(var.asType(), elementUtils.getTypeElement("java.lang.Enum").asType())) {
method.addStatement(bufferName + ".put(" + varName + ".ordinal())");
}else if(isPrimitive(typeName)) {
if(simpleTypeName.equals("boolean")){
method.addStatement(bufferName + ".put(" + varName + " ? (byte)1 : 0)");
}else{
method.addStatement(bufferName + ".put" +
capName + "(" + varName + ")");
}
}else if(writeMap.get(simpleTypeName) != null){
String[] values = writeMap.get(simpleTypeName)[0];
for(String str : values){
method.addStatement(str.replaceAll("rbuffer", bufferName)
.replaceAll("rvalue", varName));
}
}else{
messager.printMessage(Kind.ERROR, "No method for writing type: " + typeName, var);
}

if(typeUtils.isAssignable(var.asType(), elementUtils.getTypeElement("java.lang.Enum").asType())) {
writeSwitch.addStatement(typeName + " " + varName + " = " + typeName + ".values()["+bufferName +".getInt()]");
}else if(isPrimitive(typeName)) {
if(simpleTypeName.equals("boolean")){
writeSwitch.addStatement("boolean " + varName + " = " + bufferName + ".get() == 1");
}else{
writeSwitch.addStatement(typeName + " " + varName + " = " + bufferName + ".get" + capName + "()");
}
}else if(writeMap.get(simpleTypeName) != null){
String[] values = writeMap.get(simpleTypeName)[1];
for(String str : values){
writeSwitch.addStatement(str.replaceAll("rbuffer", bufferName)
.replaceAll("rvalue", varName)
.replaceAll("rtype", simpleTypeName));
}
}else{
messager.printMessage(Kind.ERROR, "No method for writing type: " + typeName, var);
}
}
method.addStatement("packet.writeLength = TEMP_BUFFER.position()");
method.addStatement("io.anuke.mindustry.net.Net.send(packet, io.anuke.mindustry.net.Net.SendMode.tcp)");

classBuilder.addMethod(method.build());

FieldSpec var = FieldSpec.builder(TypeName.INT, "ID_METHOD_" + exec.getSimpleName().toString().toUpperCase())
.initializer("$1L", id).addModifiers(Modifier.FINAL, Modifier.PRIVATE, Modifier.STATIC).build();

classBuilder.addField(var);

int index = 0;
StringBuilder results = new StringBuilder();
for(VariableElement writevar : exec.getParameters()){
results.append(writevar.getSimpleName());
if(index != exec.getParameters().size() - 1) results.append(", ");
index ++;
}

writeSwitch.addStatement("com.badlogic.gdx.Gdx.app.postRunnable(() -> $N." + exec.getSimpleName() + "(" + results.toString() + "))",
((TypeElement)e.getEnclosingElement()).getQualifiedName().toString());

id ++;

//TODO add params from the method and invoke it
}

if(started){
writeSwitch.endControlFlow();
}

readMethod.addCode(writeSwitch.build());
classBuilder.addMethod(readMethod.build());

TypeSpec spec = classBuilder.build();

JavaFile.builder(packageName, spec).build().writeTo(filer);


}catch (Exception e){
throw new RuntimeException(e);
}

return true;
}

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");

}

}
19 changes: 19 additions & 0 deletions annotations/src/io/anuke/annotations/Annotations.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.anuke.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class Annotations {

/**Marks a method as invokable remotely.*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface Remote{}

/**Marks a method to be locally invoked as well as remotely invoked.*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface Local{}
}
4 changes: 4 additions & 0 deletions annotations/src/io/anuke/annotations/Serializers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package io.anuke.annotations;

public class Serializers {
}
10 changes: 6 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ project(":desktop") {
compile "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion"
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
compile "com.badlogicgames.gdx:gdx-controllers-lwjgl3:$gdxVersion"
compile 'com.github.MinnDevelopment:java-discord-rpc:v1.3.2'
//todo uncomment
//compile 'com.github.MinnDevelopment:java-discord-rpc:v1.3.2'
}
}

Expand Down Expand Up @@ -174,8 +175,7 @@ project(":core") {
}

compileJava.options.compilerArgs = [
"-proc:only",
"-processor", "io.anuke.annotations.AnnotationProcessor"
"-processor", "io.anuke.annotations.AnnotationProcessor"
]
}

Expand All @@ -193,7 +193,9 @@ project(":server") {
project(":annotations") {
apply plugin: "java"

dependencies {}
dependencies {
compile 'com.squareup:javapoet:1.11.0'
}
}

project(":kryonet") {
Expand Down
10 changes: 3 additions & 7 deletions core/src/io/anuke/mindustry/core/Control.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.Input.Buttons;
import com.badlogic.gdx.graphics.Color;
import io.anuke.mindustry.content.Mechs;
import io.anuke.mindustry.content.Weapons;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.Tutorial;
import io.anuke.mindustry.input.AndroidInput;
import io.anuke.mindustry.input.DefaultKeybinds;
import io.anuke.mindustry.input.DesktopInput;
Expand All @@ -20,12 +16,10 @@
import io.anuke.mindustry.io.Saves;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.resource.Item;
import io.anuke.ucore.UCore;
import io.anuke.ucore.core.*;
import io.anuke.ucore.core.Inputs.DeviceType;
import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.input.InputProxy;
import io.anuke.ucore.modules.Module;
import io.anuke.ucore.scene.ui.layout.Unit;
import io.anuke.ucore.util.*;

import static io.anuke.mindustry.Vars.*;
Expand Down Expand Up @@ -271,6 +265,8 @@ public void update(){

saves.update();

triggerUpdateInput();

if(!state.is(State.menu)){
for(InputHandler input : inputs){
input.update();
Expand Down
7 changes: 1 addition & 6 deletions core/src/io/anuke/mindustry/core/NetClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,10 @@ public NetClient(){
});

Net.handleClient(InvokePacket.class, packet -> {
try{
packet.method.invoke(null, packet.args);
}catch (ReflectionException e){
throw new RuntimeException(e);
}
//TODO invoke it
});

Net.handleClient(StateSyncPacket.class, packet -> {

System.arraycopy(packet.items, 0, state.inventory.writeItems(), 0, packet.items.length);

state.enemies = packet.enemies;
Expand Down
1 change: 1 addition & 0 deletions core/src/io/anuke/mindustry/core/NetServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.anuke.mindustry.entities.BulletType;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.SyncEntity;
import io.anuke.mindustry.gen.CallEvent;
import io.anuke.mindustry.io.Platform;
import io.anuke.mindustry.io.Version;
import io.anuke.mindustry.net.*;
Expand Down
2 changes: 1 addition & 1 deletion core/src/io/anuke/mindustry/editor/MapEditorDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import io.anuke.ucore.scene.ui.layout.Stack;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Input;
import io.anuke.ucore.input.Input;
import io.anuke.ucore.util.Log;
import io.anuke.ucore.util.Strings;

Expand Down
2 changes: 1 addition & 1 deletion core/src/io/anuke/mindustry/editor/MapView.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import io.anuke.ucore.scene.event.Touchable;
import io.anuke.ucore.scene.ui.TextField;
import io.anuke.ucore.scene.ui.layout.Unit;
import io.anuke.ucore.util.Input;
import io.anuke.ucore.input.Input;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Tmp;

Expand Down
Loading

0 comments on commit d1a3752

Please sign in to comment.