Improvements to annotation code generation system, client RMI

This commit is contained in:
Anuken
2018-05-18 17:21:18 -07:00
parent ae3bcac3b7
commit b3e188a5f4
12 changed files with 130 additions and 53 deletions

View File

@ -2,15 +2,17 @@ package io.anuke.annotations;
import com.squareup.javapoet.*; import com.squareup.javapoet.*;
import io.anuke.annotations.Annotations.Local; import io.anuke.annotations.Annotations.Local;
import io.anuke.annotations.Annotations.Remote; import io.anuke.annotations.Annotations.RemoteClient;
import io.anuke.annotations.Annotations.RemoteServer;
import javax.annotation.processing.*; import javax.annotation.processing.*;
import javax.lang.model.SourceVersion; import javax.lang.model.SourceVersion;
import javax.lang.model.element.*; import javax.lang.model.element.*;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements; import javax.lang.model.util.Elements;
import javax.lang.model.util.Types; import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind; import javax.tools.Diagnostic.Kind;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -18,14 +20,16 @@ import java.util.Set;
@SupportedSourceVersion(SourceVersion.RELEASE_8) @SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({ @SupportedAnnotationTypes({
"io.anuke.annotations.Annotations.Remote", "io.anuke.annotations.Annotations.RemoteClient",
"io.anuke.annotations.Annotations.RemoteServer",
"io.anuke.annotations.Annotations.Local" "io.anuke.annotations.Annotations.Local"
}) })
public class AnnotationProcessor extends AbstractProcessor { public class AnnotationProcessor extends AbstractProcessor {
private static final int maxPacketSize = 128; 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 clientFullClassName = "io.anuke.mindustry.gen.CallClient";
private static final String packageName = fullClassName.substring(0, fullClassName.lastIndexOf('.')); private static final String serverFullClassName = "io.anuke.mindustry.gen.CallServer";
private static final HashMap<String, String[][]> writeMap = new HashMap<String, String[][]>(){{ private static final HashMap<String, String[][]> writeMap = new HashMap<String, String[][]>(){{
put("Player", new String[][]{ put("Player", new String[][]{
{ {
@ -57,22 +61,24 @@ public class AnnotationProcessor extends AbstractProcessor {
if(done) return false; if(done) return false;
done = true; done = true;
ArrayList<Element> elements = new ArrayList<>(); writeElements(roundEnv, clientFullClassName, RemoteClient.class);
writeElements(roundEnv, serverFullClassName, RemoteServer.class);
for (Element element : roundEnv.getElementsAnnotatedWith(Remote.class)) { return true;
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);
}
} }
private void writeElements(RoundEnvironment env, String fullClassName, Class<? extends Annotation> annotation){
try { try {
boolean client = annotation == RemoteServer.class;
String className = fullClassName.substring(1 + fullClassName.lastIndexOf('.'));
String packageName = fullClassName.substring(0, fullClassName.lastIndexOf('.'));
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className) Constructor<TypeName> cons = TypeName.class.getDeclaredConstructor(String.class);
.addModifiers(Modifier.PUBLIC); cons.setAccessible(true);
TypeName playerType = cons.newInstance("io.anuke.mindustry.entities.Player");
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
int id = 0; int id = 0;
@ -85,12 +91,23 @@ public class AnnotationProcessor extends AbstractProcessor {
.addParameter(int.class, "id") .addParameter(int.class, "id")
.returns(void.class); .returns(void.class);
if(client){
readMethod.addParameter(playerType, "player");
}
CodeBlock.Builder writeSwitch = CodeBlock.builder(); CodeBlock.Builder writeSwitch = CodeBlock.builder();
boolean started = false; boolean started = false;
readMethod.addJavadoc("This method reads and executes a method by ID. For internal use only!"); readMethod.addJavadoc("This method reads and executes a method by ID. For internal use only!");
for (Element e : elements) { for (Element e : env.getElementsAnnotatedWith(annotation)) {
if(!e.getModifiers().contains(Modifier.STATIC)) {
messager.printMessage(Kind.ERROR, "All local/remote methods must be static: ", e);
}else if(e.getKind() != ElementKind.METHOD){
messager.printMessage(Kind.ERROR, "All local/remote annotations must be on methods: ", e);
}
if(e.getAnnotation(annotation) == null) continue;
boolean local = e.getAnnotation(Local.class) != null; boolean local = e.getAnnotation(Local.class) != null;
ExecutableElement exec = (ExecutableElement)e; ExecutableElement exec = (ExecutableElement)e;
@ -99,12 +116,24 @@ public class AnnotationProcessor extends AbstractProcessor {
.addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class); .returns(void.class);
if(client){
if(exec.getParameters().isEmpty()){
messager.printMessage(Kind.ERROR, "Client invoke methods must have a first parameter of type Player.", e);
return;
}
VariableElement var = exec.getParameters().get(0);
if(!var.asType().toString().equals("io.anuke.mindustry.entities.Player")){
messager.printMessage(Kind.ERROR, "Client invoke methods should have a first parameter of type Player.", e);
}
}
for(VariableElement var : exec.getParameters()){ for(VariableElement var : exec.getParameters()){
method.addParameter(TypeName.get(var.asType()), var.getSimpleName().toString()); method.addParameter(TypeName.get(var.asType()), var.getSimpleName().toString());
} }
if(local){ if(local){
//todo
int index = 0; int index = 0;
StringBuilder results = new StringBuilder(); StringBuilder results = new StringBuilder();
for(VariableElement var : exec.getParameters()){ for(VariableElement var : exec.getParameters()){
@ -124,11 +153,17 @@ public class AnnotationProcessor extends AbstractProcessor {
} }
started = true; started = true;
method.addStatement("$1N packet = new $1N()", "io.anuke.mindustry.net.Packets.InvokePacket"); method.addStatement("$1N packet = $2N.obtain($1N.class)", "io.anuke.mindustry.net.Packets.InvokePacket",
"com.badlogic.gdx.utils.Pools");
method.addStatement("packet.writeBuffer = TEMP_BUFFER"); method.addStatement("packet.writeBuffer = TEMP_BUFFER");
method.addStatement("TEMP_BUFFER.position(0)"); method.addStatement("TEMP_BUFFER.position(0)");
for(VariableElement var : exec.getParameters()){ ArrayList<VariableElement> parameters = new ArrayList<>(exec.getParameters());
if(client){
parameters.remove(0);
}
for(VariableElement var : parameters){
String varName = var.getSimpleName().toString(); String varName = var.getSimpleName().toString();
String typeName = var.asType().toString(); String typeName = var.asType().toString();
String bufferName = "TEMP_BUFFER"; String bufferName = "TEMP_BUFFER";
@ -178,13 +213,9 @@ public class AnnotationProcessor extends AbstractProcessor {
classBuilder.addMethod(method.build()); 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; int index = 0;
StringBuilder results = new StringBuilder(); StringBuilder results = new StringBuilder();
for(VariableElement writevar : exec.getParameters()){ for(VariableElement writevar : exec.getParameters()){
results.append(writevar.getSimpleName()); results.append(writevar.getSimpleName());
if(index != exec.getParameters().size() - 1) results.append(", "); if(index != exec.getParameters().size() - 1) results.append(", ");
@ -195,8 +226,6 @@ public class AnnotationProcessor extends AbstractProcessor {
((TypeElement)e.getEnclosingElement()).getQualifiedName().toString()); ((TypeElement)e.getEnclosingElement()).getQualifiedName().toString());
id ++; id ++;
//TODO add params from the method and invoke it
} }
if(started){ if(started){
@ -209,13 +238,10 @@ public class AnnotationProcessor extends AbstractProcessor {
TypeSpec spec = classBuilder.build(); TypeSpec spec = classBuilder.build();
JavaFile.builder(packageName, spec).build().writeTo(filer); JavaFile.builder(packageName, spec).build().writeTo(filer);
}catch (Exception e){ }catch (Exception e){
e.printStackTrace();
throw new RuntimeException(e); throw new RuntimeException(e);
} }
return true;
} }
private boolean isPrimitive(String type){ private boolean isPrimitive(String type){

View File

@ -5,14 +5,33 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/**
* Goal: To create a system to send events to the server from the client and vice versa.<br>
* These events may optionally also trigger on the caller client/server as well.<br>
*<br>
* Three annotations are used for this purpose.<br>
* {@link RemoteClient}: Marks a method as able to be invoked remotely on a client from a server.<br>
* {@link RemoteServer}: Marks a method as able to be invoked remotely on a server from a client.<br>
* {@link Local}: Makes this method get invoked locally as well as remotely.<br>
*<br>
* All RemoteClient methods are put in the class CallClient, and all RemoteServer methods are put in the class CallServer.<br>
*/
public class Annotations { public class Annotations {
/**Marks a method as invokable remotely.*/ /**Marks a method as invokable remotely from a server on a client.*/
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS) @Retention(RetentionPolicy.CLASS)
public @interface Remote{} public @interface RemoteClient {}
/**Marks a method to be locally invoked as well as remotely invoked.*/ /**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.
* This player is the invoker of the method.*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface RemoteServer {}
/**Marks a method to be locally invoked as well as remotely invoked on the caller
* Must be used with {@link RemoteClient}/{@link RemoteServer} annotations.*/
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS) @Retention(RetentionPolicy.CLASS)
public @interface Local{} public @interface Local{}

View File

@ -10,6 +10,7 @@ import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.BulletType; import io.anuke.mindustry.entities.BulletType;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.SyncEntity; import io.anuke.mindustry.entities.SyncEntity;
import io.anuke.mindustry.gen.CallServer;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.io.Platform;
import io.anuke.mindustry.io.Version; import io.anuke.mindustry.io.Version;
import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.*;
@ -162,6 +163,10 @@ public class NetServer extends Module{
//...don't do anything here as it's already handled by the packet itself //...don't do anything here as it's already handled by the packet itself
}); });
Net.handleServer(InvokePacket.class, (id, packet) -> {
CallServer.readPacket(packet.writeBuffer, packet.type, connections.get(id));
});
Net.handleServer(EntityShootPacket.class, (id, packet) -> { Net.handleServer(EntityShootPacket.class, (id, packet) -> {
Player player = connections.get(id); Player player = connections.get(id);

View File

@ -27,6 +27,7 @@ import io.anuke.ucore.scene.builders.table;
import io.anuke.ucore.scene.ui.*; import io.anuke.ucore.scene.ui.*;
import io.anuke.ucore.scene.ui.layout.Stack; import io.anuke.ucore.scene.ui.layout.Stack;
import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.scene.utils.UIUtils;
import io.anuke.ucore.util.Bundles; import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.input.Input; import io.anuke.ucore.input.Input;
import io.anuke.ucore.util.Log; import io.anuke.ucore.util.Log;
@ -387,15 +388,14 @@ public class MapEditorDialog extends Dialog{
private void doInput(){ private void doInput(){
//tool select //tool select
for(int i = 0; i < EditorTool.values().length; i ++){ for(int i = 0; i < EditorTool.values().length; i ++){
int code = i == 0 ? 5 : i; if(Inputs.keyTap(Input.valueOf("NUM_" + (i+1)))){
if(Inputs.keyTap("weapon_" + code)){
view.setTool(EditorTool.values()[i]); view.setTool(EditorTool.values()[i]);
break; break;
} }
} }
//ctrl keys (undo, redo, save) //ctrl keys (undo, redo, save)
if(Inputs.keyDown(Input.CONTROL_LEFT)){ if(UIUtils.ctrl()){
if(Inputs.keyTap(Input.Z)){ if(Inputs.keyTap(Input.Z)){
view.undo(); view.undo();
} }

View File

@ -10,6 +10,7 @@ import io.anuke.mindustry.content.Weapons;
import io.anuke.mindustry.content.fx.ExplosionFx; import io.anuke.mindustry.content.fx.ExplosionFx;
import io.anuke.mindustry.entities.effect.DamageArea; import io.anuke.mindustry.entities.effect.DamageArea;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.CallClient;
import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetEvents; import io.anuke.mindustry.net.NetEvents;
@ -338,7 +339,8 @@ public class Player extends Unit implements BlockPlacer{
currentPlace = null; currentPlace = null;
}else if(distanceTo(check) <= placeDistance){ }else if(distanceTo(check) <= placeDistance){
BuildEntity entity = check.entity(); BuildEntity entity = check.entity();
entity.progress += 1f / entity.result.health;
entity.progress += 1f / entity.recipe.cost;
rotation = Mathf.slerpDelta(rotation, angleTo(entity), 0.4f); rotation = Mathf.slerpDelta(rotation, angleTo(entity), 0.4f);
} }

View File

@ -2,7 +2,8 @@ package io.anuke.mindustry.net;
import com.badlogic.gdx.utils.Pools; import com.badlogic.gdx.utils.Pools;
import io.anuke.annotations.Annotations.Local; import io.anuke.annotations.Annotations.Local;
import io.anuke.annotations.Annotations.Remote; import io.anuke.annotations.Annotations.RemoteClient;
import io.anuke.annotations.Annotations.RemoteServer;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.SyncEntity; import io.anuke.mindustry.entities.SyncEntity;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
@ -17,7 +18,7 @@ import static io.anuke.mindustry.Vars.*;
public class NetEvents { public class NetEvents {
@Remote @RemoteClient
@Local @Local
public static void friendlyFireChange(boolean enabled){ public static void friendlyFireChange(boolean enabled){
state.friendlyFire = enabled; state.friendlyFire = enabled;
@ -25,6 +26,17 @@ public class NetEvents {
if(Net.server()) netCommon.sendMessage(enabled ? "[accent]Friendly fire enabled." : "[accent]Friendly fire disabled."); if(Net.server()) netCommon.sendMessage(enabled ? "[accent]Friendly fire enabled." : "[accent]Friendly fire disabled.");
} }
@RemoteServer
public static void notifySomethingFromClient(Player player, int x, float y){
}
@RemoteClient
@Local
public static void notifySomethingFromServerLocal(int y, float x, boolean w){
}
public static void handleGameOver(){ public static void handleGameOver(){
Net.send(Pools.obtain(GameOverPacket.class), SendMode.tcp); Net.send(Pools.obtain(GameOverPacket.class), SendMode.tcp);
} }
@ -116,7 +128,7 @@ public class NetEvents {
Net.send(packet, SendMode.tcp); Net.send(packet, SendMode.tcp);
} }
@Remote @RemoteClient
@Local @Local
public static void adminSet(Player player, boolean admin){ public static void adminSet(Player player, boolean admin){
player.isAdmin = admin; player.isAdmin = admin;

View File

@ -7,7 +7,7 @@ import com.badlogic.gdx.utils.reflect.ReflectionException;
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.entities.SyncEntity; import io.anuke.mindustry.entities.SyncEntity;
import io.anuke.mindustry.gen.CallEvent; import io.anuke.mindustry.gen.CallClient;
import io.anuke.mindustry.io.Version; import io.anuke.mindustry.io.Version;
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;
@ -43,9 +43,11 @@ public class Packets {
type = buffer.get(); type = buffer.get();
if(Net.client()){ if(Net.client()){
CallEvent.readPacket(buffer, type); CallClient.readPacket(buffer, type);
}else{ }else{
buffer.position(buffer.position() + writeLength); byte[] bytes = new byte[writeLength];
buffer.get(bytes);
writeBuffer = ByteBuffer.wrap(bytes);
} }
} }

View File

@ -28,6 +28,9 @@ public class Item implements Comparable<Item>{
public int hardness = 0; public int hardness = 0;
/**the burning color of this item*/ /**the burning color of this item*/
public Color flameColor = Palette.darkFlame.cpy(); public Color flameColor = Palette.darkFlame.cpy();
/**base material cost of this item, used for calculating place times
* 1 cost = 1 tick added to build time*/
public float cost = 1f;
public Item(String name, Color color) { public Item(String name, Color color) {
this.id = items.size; this.id = items.size;

View File

@ -13,6 +13,7 @@ public class Recipe {
public final Block result; public final Block result;
public final ItemStack[] requirements; public final ItemStack[] requirements;
public final Section section; public final Section section;
public final float cost;
public boolean desktopOnly = false, debugOnly = false; public boolean desktopOnly = false, debugOnly = false;
@ -22,6 +23,13 @@ public class Recipe {
this.requirements = requirements; this.requirements = requirements;
this.section = section; this.section = section;
float timeToPlace = 0f;
for(ItemStack stack : requirements){
timeToPlace += stack.amount * stack.item.cost;
}
this.cost = timeToPlace;
allRecipes.add(this); allRecipes.add(this);
recipeMap.put(result, this); recipeMap.put(result, this);
} }

View File

@ -1,7 +1,7 @@
package io.anuke.mindustry.ui.dialogs; package io.anuke.mindustry.ui.dialogs;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.gen.CallEvent; import io.anuke.mindustry.gen.CallClient;
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;
import io.anuke.mindustry.net.NetConnection; import io.anuke.mindustry.net.NetConnection;
@ -49,7 +49,7 @@ public class AdminsDialog extends FloatingDialog {
for(Player player : playerGroup.all()){ for(Player player : playerGroup.all()){
NetConnection c = Net.getConnection(player.clientid); NetConnection c = Net.getConnection(player.clientid);
if(c != null){ if(c != null){
CallEvent.adminSet(player, false); CallClient.adminSet(player, false);
break; break;
} }
} }

View File

@ -3,7 +3,7 @@ package io.anuke.mindustry.ui.fragments;
import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.ObjectMap;
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.CallEvent; import io.anuke.mindustry.gen.CallClient;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetConnection; import io.anuke.mindustry.net.NetConnection;
import io.anuke.mindustry.net.NetEvents; import io.anuke.mindustry.net.NetEvents;
@ -49,7 +49,7 @@ public class PlayerListFragment implements Fragment{
margin(12f); margin(12f);
get().addCheck("$text.server.friendlyfire", b -> { get().addCheck("$text.server.friendlyfire", b -> {
CallEvent.friendlyFireChange(b); // CallClient.friendlyFireChange(b);
}).growX().update(i -> i.setChecked(state.friendlyFire)).disabled(b -> Net.client()).padRight(5); }).growX().update(i -> i.setChecked(state.friendlyFire)).disabled(b -> Net.client()).padRight(5);
new button("$text.server.bans", () -> { new button("$text.server.bans", () -> {
@ -160,12 +160,12 @@ public class PlayerListFragment implements Fragment{
if(netServer.admins.isAdmin(id, connection.address)){ if(netServer.admins.isAdmin(id, connection.address)){
ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> { ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> {
netServer.admins.unAdminPlayer(id); netServer.admins.unAdminPlayer(id);
CallEvent.adminSet(player, false); CallClient.adminSet(player, false);
}); });
}else{ }else{
ui.showConfirm("$text.confirm", "$text.confirmadmin", () -> { ui.showConfirm("$text.confirm", "$text.confirmadmin", () -> {
netServer.admins.adminPlayer(id, connection.address); netServer.admins.adminPlayer(id, connection.address);
CallEvent.adminSet(player, true); CallClient.adminSet(player, true);
}); });
} }
}).update(b ->{ }).update(b ->{