mirror of
https://github.com/Anuken/Mindustry.git
synced 2026-01-22 12:33:35 -08:00
New tile edge algorithms / Struct value type generation
This commit is contained in:
parent
5a3ec8f407
commit
54bade668e
19 changed files with 7275 additions and 7459 deletions
|
|
@ -22,7 +22,7 @@ public class Annotations{
|
|||
}
|
||||
|
||||
/**Marks a field of a struct. Optional.*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface StructField{
|
||||
/**Size of a struct field in bits. Not valid on booleans or floating point numbers.*/
|
||||
|
|
|
|||
|
|
@ -9,14 +9,17 @@ import io.anuke.annotations.Annotations.StructField;
|
|||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.util.ElementFilter;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**Generates ""value types"" classes that are packed into integer primitives of the most aproppriate size.
|
||||
* It would be nice if Java didn't make crazy hacks like this necessary.*/
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||
@SupportedAnnotationTypes({
|
||||
"io.anuke.annotations.Annotations.Struct"
|
||||
|
|
@ -44,31 +47,114 @@ public class StructAnnotationProcessor extends AbstractProcessor{
|
|||
Set<TypeElement> elements = ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(Struct.class));
|
||||
|
||||
for(TypeElement elem : elements){
|
||||
TypeName type = TypeName.get(elem.asType());
|
||||
|
||||
if(!type.toString().endsWith("Struct")){
|
||||
if(!elem.getSimpleName().toString().endsWith("Struct")){
|
||||
Utils.messager.printMessage(Kind.ERROR, "All classes annotated with @Struct must have their class names end in 'Struct'.", elem);
|
||||
continue;
|
||||
}
|
||||
|
||||
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(packageName + "." + elem.getSimpleName().toString());
|
||||
String structName = elem.getSimpleName().toString().substring(0, elem.getSimpleName().toString().length() - "Struct".length());
|
||||
String structParam = structName.toLowerCase();
|
||||
|
||||
int offset = 0;
|
||||
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(structName)
|
||||
.addModifiers(Modifier.FINAL, Modifier.PUBLIC);
|
||||
|
||||
for(VariableElement var : ElementFilter.fieldsIn(Collections.singletonList(elem))){
|
||||
if(!var.asType().getKind().isPrimitive()){
|
||||
Utils.messager.printMessage(Kind.ERROR, "All struct fields must be primitives.", var);
|
||||
try{
|
||||
List<VariableElement> variables = ElementFilter.fieldsIn(elem.getEnclosedElements());
|
||||
int structSize = variables.stream().mapToInt(StructAnnotationProcessor::varSize).sum();
|
||||
int structTotalSize = (structSize <= 8 ? 8 : structSize <= 16 ? 16 : structSize <= 32 ? 32 : 64);
|
||||
|
||||
if(variables.size() == 0){
|
||||
Utils.messager.printMessage(Kind.ERROR, "making a struct with no fields is utterly pointles.", elem);
|
||||
continue;
|
||||
}
|
||||
|
||||
StructField an = var.getAnnotation(StructField.class);
|
||||
int size = an == null ? typeSize(var.asType().getKind()) : an.value();
|
||||
//obtain type which will be stored
|
||||
Class<?> structType = typeForSize(structSize);
|
||||
|
||||
MethodSpec.Builder getter = MethodSpec.methodBuilder(var.getSimpleName().toString());
|
||||
MethodSpec.Builder setter = MethodSpec.methodBuilder(var.getSimpleName().toString());
|
||||
//[constructor] get(fields...) : structType
|
||||
MethodSpec.Builder constructor = MethodSpec.methodBuilder("get") //TODO 'get'..?
|
||||
.addModifiers(Modifier.STATIC, Modifier.PUBLIC)
|
||||
.returns(structType);
|
||||
|
||||
StringBuilder cons = new StringBuilder();
|
||||
StringBuilder doc = new StringBuilder();
|
||||
doc.append("Bits used: ").append(structSize).append(" / ").append(structTotalSize).append("\n");
|
||||
|
||||
int offset = 0;
|
||||
for(VariableElement var : variables){
|
||||
int size = varSize(var);
|
||||
TypeName varType = TypeName.get(var.asType());
|
||||
String varName = var.getSimpleName().toString();
|
||||
|
||||
//add val param to constructor
|
||||
constructor.addParameter(varType, varName);
|
||||
|
||||
//[get] field(structType) : fieldType
|
||||
MethodSpec.Builder getter = MethodSpec.methodBuilder(var.getSimpleName().toString())
|
||||
.addModifiers(Modifier.STATIC, Modifier.PUBLIC)
|
||||
.returns(varType)
|
||||
.addParameter(structType, structParam);
|
||||
//[set] field(structType, fieldType) : structType
|
||||
MethodSpec.Builder setter = MethodSpec.methodBuilder(var.getSimpleName().toString())
|
||||
.addModifiers(Modifier.STATIC, Modifier.PUBLIC)
|
||||
.returns(structType)
|
||||
.addParameter(structType, structParam).addParameter(varType, "value");
|
||||
|
||||
//[getter]
|
||||
if(varType == TypeName.BOOLEAN){
|
||||
//bools: single bit, is simplified
|
||||
getter.addStatement("return ($L & (1L << $L)) != 0", structParam, offset);
|
||||
}else if(varType == TypeName.FLOAT){
|
||||
//floats: need conversion
|
||||
getter.addStatement("return Float.intBitsToFloat((int)(($L >> $L) & $L))", structParam, offset, bitString(size, structTotalSize));
|
||||
}else{
|
||||
//bytes, shorts, chars, ints
|
||||
getter.addStatement("return ($T)(($L >> $L) & $L)", varType, structParam, offset, bitString(size, structTotalSize));
|
||||
}
|
||||
|
||||
//[setter] + [constructor building]
|
||||
if(varType == TypeName.BOOLEAN){
|
||||
cons.append(" | (").append(varName).append(" ? ").append("1L << ").append(offset).append("L : 0)");
|
||||
|
||||
//bools: single bit, needs special case to clear things
|
||||
setter.beginControlFlow("if(value)");
|
||||
setter.addStatement("return ($T)(($L & ~(1L << $LL)))", structType, structParam, offset);
|
||||
setter.nextControlFlow("else");
|
||||
setter.addStatement("return ($T)(($L & ~(1L << $LL)) | (1L << $LL))", structType, structParam, offset, offset);
|
||||
setter.endControlFlow();
|
||||
}else if(varType == TypeName.FLOAT){
|
||||
cons.append(" | (").append("(").append(structType).append(")").append("Float.floatToIntBits(").append(varName).append(") << ").append(offset).append("L)");
|
||||
|
||||
//floats: need conversion
|
||||
setter.addStatement("return ($T)(($L & $L) | (($T)Float.floatToIntBits(value) << $LL))", structType, structParam, bitString(offset, size, structTotalSize), structType, offset);
|
||||
}else{
|
||||
cons.append(" | (").append("(").append(structType).append(")").append(varName).append(" << ").append(offset).append("L)");
|
||||
|
||||
//bytes, shorts, chars, ints
|
||||
setter.addStatement("return ($T)(($L & $L) | (($T)value << $LL))", structType, structParam, bitString(offset, size, structTotalSize), structType, offset);
|
||||
}
|
||||
|
||||
doc.append("<br> ").append(varName).append(" [").append(offset).append("..").append(size + offset).append("]\n");
|
||||
|
||||
//add finished methods
|
||||
classBuilder.addMethod(getter.build());
|
||||
classBuilder.addMethod(setter.build());
|
||||
|
||||
offset += size;
|
||||
}
|
||||
|
||||
classBuilder.addJavadoc(doc.toString());
|
||||
|
||||
//add constructor final statement + add to class and build
|
||||
constructor.addStatement("return ($T)($L)", structType, cons.toString().substring(3));
|
||||
classBuilder.addMethod(constructor.build());
|
||||
|
||||
JavaFile.builder(packageName, classBuilder.build()).build().writeTo(Utils.filer);
|
||||
}catch(IllegalArgumentException e){
|
||||
e.printStackTrace();
|
||||
Utils.messager.printMessage(Kind.ERROR, e.getMessage(), elem);
|
||||
}
|
||||
|
||||
JavaFile.builder(packageName, classBuilder.build()).build().writeTo(Utils.filer);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -78,8 +164,53 @@ public class StructAnnotationProcessor extends AbstractProcessor{
|
|||
}
|
||||
}
|
||||
|
||||
static String bitString(int offset, int size, int totalSize){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for(int i = 0; i < offset; i++) builder.append('0');
|
||||
for(int i = 0; i < size; i++) builder.append('1');
|
||||
for(int i = 0; i < totalSize - size - offset; i++) builder.append('0');
|
||||
return "0b" + builder.reverse().toString() + "L";
|
||||
}
|
||||
|
||||
static String bitString(int size, int totalSize){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for(int i = 0; i < size; i++) builder.append('1');
|
||||
for(int i = 0; i < totalSize - size; i++) builder.append('0');
|
||||
return "0b" + builder.reverse().toString() + "L";
|
||||
}
|
||||
|
||||
static int varSize(VariableElement var) throws IllegalArgumentException{
|
||||
if(!var.asType().getKind().isPrimitive()){
|
||||
throw new IllegalArgumentException("All struct fields must be primitives: " + var);
|
||||
}
|
||||
|
||||
StructField an = var.getAnnotation(StructField.class);
|
||||
if(var.asType().getKind() == TypeKind.BOOLEAN && an != null && an.value() != 1){
|
||||
throw new IllegalArgumentException("Booleans can only be one bit long... why would you do this?");
|
||||
}
|
||||
|
||||
if(var.asType().getKind() == TypeKind.FLOAT && an != null && an.value() != 32){
|
||||
throw new IllegalArgumentException("Float size can't be changed. Very sad.");
|
||||
}
|
||||
|
||||
return an == null ? typeSize(var.asType().getKind()) : an.value();
|
||||
}
|
||||
|
||||
static Class<?> typeForSize(int size) throws IllegalArgumentException{
|
||||
if(size <= 8){
|
||||
return byte.class;
|
||||
}else if(size <= 16){
|
||||
return short.class;
|
||||
}else if(size <= 32){
|
||||
return int.class;
|
||||
}else if(size <= 64){
|
||||
return long.class;
|
||||
}
|
||||
throw new IllegalArgumentException("Too many fields, must fit in 64 bits. Curent size: " + size);
|
||||
}
|
||||
|
||||
/**returns a type's element size in bits.*/
|
||||
static int typeSize(TypeKind kind){
|
||||
static int typeSize(TypeKind kind) throws IllegalArgumentException{
|
||||
switch(kind){
|
||||
case BOOLEAN:
|
||||
return 1;
|
||||
|
|
@ -91,11 +222,8 @@ public class StructAnnotationProcessor extends AbstractProcessor{
|
|||
case CHAR:
|
||||
case INT:
|
||||
return 32;
|
||||
case LONG:
|
||||
case DOUBLE:
|
||||
return 64;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid type kind: " + kind);
|
||||
throw new IllegalArgumentException("Invalid type kind: " + kind + ". Note that doubles and longs are not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue