mirror of https://github.com/Minestom/Minestom.git
403 lines
19 KiB
Java
403 lines
19 KiB
Java
package net.minestom.codegen.blocks;
|
|
|
|
import com.google.gson.JsonArray;
|
|
import com.google.gson.JsonElement;
|
|
import com.google.gson.JsonObject;
|
|
import com.google.gson.stream.JsonReader;
|
|
import com.squareup.javapoet.*;
|
|
import net.minestom.codegen.MinestomCodeGenerator;
|
|
import net.minestom.codegen.util.NameUtil;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.jetbrains.annotations.Nullable;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import javax.lang.model.element.Modifier;
|
|
import java.io.File;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FileReader;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
public final class BlockGenerator extends MinestomCodeGenerator {
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(BlockGenerator.class);
|
|
private final File blocksFile;
|
|
private final File blockPropertyFile;
|
|
private final File outputFolder;
|
|
|
|
public BlockGenerator(@NotNull File blocksFile, @NotNull File blockPropertyFile, @NotNull File outputFolder) {
|
|
this.blocksFile = blocksFile;
|
|
this.blockPropertyFile = blockPropertyFile;
|
|
this.outputFolder = outputFolder;
|
|
}
|
|
|
|
@Override
|
|
public void generate() {
|
|
if (!blocksFile.exists()) {
|
|
LOGGER.error("Failed to find blocks.json.");
|
|
LOGGER.error("Stopped code generation for blocks.");
|
|
return;
|
|
}
|
|
if (!blockPropertyFile.exists()) {
|
|
LOGGER.error("Failed to find block_properties.json.");
|
|
LOGGER.error("Stopped code generation for block properties.");
|
|
return;
|
|
}
|
|
|
|
if (!outputFolder.exists() && !outputFolder.mkdirs()) {
|
|
LOGGER.error("Output folder for code generation does not exist and could not be created.");
|
|
return;
|
|
}
|
|
if (!outputFolder.exists() && !outputFolder.mkdirs()) {
|
|
LOGGER.error("Output folder for code generation does not exist and could not be created.");
|
|
return;
|
|
}
|
|
// Important classes we use alot
|
|
ClassName namespaceIDClassName = ClassName.get("net.minestom.server.utils", "NamespaceID");
|
|
ClassName registriesClassName = ClassName.get("net.minestom.server.registry", "Registries");
|
|
|
|
JsonArray blocks;
|
|
try {
|
|
blocks = GSON.fromJson(new JsonReader(new FileReader(blocksFile)), JsonArray.class);
|
|
} catch (FileNotFoundException e) {
|
|
LOGGER.error("Failed to find blocks.json.");
|
|
LOGGER.error("Stopped code generation for blocks.");
|
|
return;
|
|
}
|
|
ClassName blockClassName = ClassName.get("net.minestom.server.instance.block", "Block");
|
|
ClassName blockAltClassName = ClassName.get("net.minestom.server.instance.block", "BlockAlternative");
|
|
List<JavaFile> filesToWrite = new ArrayList<>();
|
|
|
|
// Block
|
|
TypeSpec.Builder blockClass = TypeSpec.enumBuilder(blockClassName)
|
|
.addSuperinterface(ClassName.get("net.kyori.adventure.key", "Keyed"))
|
|
.addModifiers(Modifier.PUBLIC).addJavadoc("AUTOGENERATED by " + getClass().getSimpleName());
|
|
|
|
blockClass.addField(
|
|
FieldSpec.builder(namespaceIDClassName, "id")
|
|
.addModifiers(Modifier.PRIVATE, Modifier.FINAL).addAnnotation(NotNull.class).build()
|
|
);
|
|
blockClass.addField(
|
|
FieldSpec.builder(TypeName.SHORT, "defaultID")
|
|
.addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()
|
|
);
|
|
blockClass.addField(
|
|
FieldSpec.builder(TypeName.DOUBLE, "hardness")
|
|
.addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()
|
|
);
|
|
blockClass.addField(
|
|
FieldSpec.builder(TypeName.DOUBLE, "resistance")
|
|
.addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()
|
|
);
|
|
blockClass.addField(
|
|
FieldSpec.builder(TypeName.BOOLEAN, "isAir")
|
|
.addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()
|
|
);
|
|
blockClass.addField(
|
|
FieldSpec.builder(TypeName.BOOLEAN, "isSolid")
|
|
.addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()
|
|
);
|
|
blockClass.addField(
|
|
FieldSpec.builder(TypeName.BOOLEAN, "blockEntity")
|
|
.addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()
|
|
);
|
|
blockClass.addField(
|
|
FieldSpec.builder(TypeName.BOOLEAN, "singleState")
|
|
.addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()
|
|
);
|
|
blockClass.addField(
|
|
FieldSpec.builder(ParameterizedTypeName.get(ClassName.get("java.util", "List"), blockAltClassName), "alternatives")
|
|
.initializer("new $T<>()", ClassName.get("java.util", "ArrayList"))
|
|
.addModifiers(Modifier.PRIVATE, Modifier.FINAL)
|
|
.addAnnotation(NotNull.class)
|
|
.build()
|
|
);
|
|
// Block constructor
|
|
blockClass.addMethod(
|
|
MethodSpec.constructorBuilder()
|
|
.addParameter(ParameterSpec.builder(namespaceIDClassName, "id").addAnnotation(NotNull.class).build())
|
|
.addParameter(TypeName.SHORT, "defaultID")
|
|
.addParameter(TypeName.DOUBLE, "hardness")
|
|
.addParameter(TypeName.DOUBLE, "resistance")
|
|
.addParameter(TypeName.BOOLEAN, "isAir")
|
|
.addParameter(TypeName.BOOLEAN, "isSolid")
|
|
.addParameter(TypeName.BOOLEAN, "blockEntity")
|
|
.addParameter(TypeName.BOOLEAN, "singleState")
|
|
|
|
.addStatement("this.id = id")
|
|
.addStatement("this.defaultID = defaultID")
|
|
.addStatement("this.hardness = hardness")
|
|
.addStatement("this.resistance = resistance")
|
|
.addStatement("this.isAir = isAir")
|
|
.addStatement("this.isSolid = isSolid")
|
|
.addStatement("this.blockEntity = blockEntity")
|
|
.addStatement("this.singleState = singleState")
|
|
.beginControlFlow("if (singleState)")
|
|
.addStatement("addBlockAlternative(new $T(defaultID))", blockAltClassName)
|
|
.endControlFlow()
|
|
.addStatement("$T.blocks.put(id, this)", registriesClassName)
|
|
.build()
|
|
);
|
|
// Override key method (adventure)
|
|
blockClass.addMethod(
|
|
MethodSpec.methodBuilder("key")
|
|
.returns(ClassName.get("net.kyori.adventure.key", "Key"))
|
|
.addAnnotation(Override.class)
|
|
.addAnnotation(NotNull.class)
|
|
.addStatement("return this.id")
|
|
.addModifiers(Modifier.PUBLIC)
|
|
.build()
|
|
);
|
|
// addBlockState method
|
|
blockClass.addMethod(
|
|
MethodSpec.methodBuilder("addBlockAlternative")
|
|
.addParameter(ParameterSpec.builder(blockAltClassName, "alternative").addAnnotation(NotNull.class).build())
|
|
|
|
.addStatement("this.alternatives.add(alternative)")
|
|
.addStatement("BlockArray.blocks[alternative.getId()] = this")
|
|
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
|
|
.build()
|
|
);
|
|
// getId method
|
|
blockClass.addMethod(
|
|
MethodSpec.methodBuilder("getBlockId")
|
|
.returns(TypeName.SHORT)
|
|
.addStatement("return defaultID")
|
|
.addModifiers(Modifier.PUBLIC)
|
|
.build()
|
|
);
|
|
// getName method
|
|
blockClass.addMethod(
|
|
MethodSpec.methodBuilder("getName")
|
|
.addAnnotation(NotNull.class)
|
|
.returns(ClassName.get(String.class))
|
|
.addStatement("return this.id.asString()")
|
|
.addModifiers(Modifier.PUBLIC)
|
|
.build()
|
|
);
|
|
// getHardness method
|
|
blockClass.addMethod(
|
|
MethodSpec.methodBuilder("getHardness")
|
|
.returns(TypeName.DOUBLE)
|
|
.addStatement("return this.hardness")
|
|
.addModifiers(Modifier.PUBLIC)
|
|
.build()
|
|
);
|
|
// getResistance method
|
|
blockClass.addMethod(
|
|
MethodSpec.methodBuilder("getResistance")
|
|
.returns(TypeName.DOUBLE)
|
|
.addStatement("return this.resistance")
|
|
.addModifiers(Modifier.PUBLIC)
|
|
.build()
|
|
);
|
|
// breakInstantaneously method
|
|
blockClass.addMethod(
|
|
MethodSpec.methodBuilder("breaksInstantaneously")
|
|
.returns(TypeName.BOOLEAN)
|
|
.addStatement("return this.hardness == 0")
|
|
.addModifiers(Modifier.PUBLIC)
|
|
.build()
|
|
);
|
|
// getBlockStates method
|
|
blockClass.addMethod(
|
|
MethodSpec.methodBuilder("getAlternatives")
|
|
.returns(ParameterizedTypeName.get(ClassName.get(List.class), blockAltClassName))
|
|
.addAnnotation(NotNull.class)
|
|
.addStatement("return this.alternatives")
|
|
.addModifiers(Modifier.PUBLIC, Modifier.FINAL) // Should this be final, for now I guess so.
|
|
.build()
|
|
);
|
|
// isAir method
|
|
blockClass.addMethod(
|
|
MethodSpec.methodBuilder("isAir")
|
|
.returns(TypeName.BOOLEAN)
|
|
// UPDATE: Make sure they didn't add new AIR types.
|
|
.addStatement("return isAir")
|
|
.addModifiers(Modifier.PUBLIC)
|
|
.build()
|
|
);
|
|
// isLiquid method
|
|
blockClass.addMethod(
|
|
MethodSpec.methodBuilder("isLiquid")
|
|
.returns(TypeName.BOOLEAN)
|
|
// UPDATE: Make sure they didn't add new liquids.
|
|
.addStatement("return this == WATER || this == LAVA")
|
|
.addModifiers(Modifier.PUBLIC)
|
|
.build()
|
|
);
|
|
// isSolid method
|
|
blockClass.addMethod(
|
|
MethodSpec.methodBuilder("isSolid")
|
|
.returns(TypeName.BOOLEAN)
|
|
// UPDATE: Make sure they didn't add new liquids.
|
|
.addStatement("return isSolid")
|
|
.addModifiers(Modifier.PUBLIC)
|
|
.build()
|
|
);
|
|
// hasBlockEntity method
|
|
blockClass.addMethod(
|
|
MethodSpec.methodBuilder("hasBlockEntity")
|
|
.returns(TypeName.BOOLEAN)
|
|
.addStatement("return blockEntity")
|
|
.addModifiers(Modifier.PUBLIC)
|
|
.build()
|
|
);
|
|
// getAlternative method
|
|
blockClass.addMethod(
|
|
MethodSpec.methodBuilder("getAlternative")
|
|
.returns(blockAltClassName)
|
|
.addParameter(TypeName.SHORT, "blockId")
|
|
.addAnnotation(Nullable.class)
|
|
.beginControlFlow("for ($T alt : alternatives) ", blockAltClassName)
|
|
.beginControlFlow("if (alt.getId() == blockId)")
|
|
.addStatement("return alt")
|
|
.endControlFlow()
|
|
.endControlFlow()
|
|
.addStatement("return null")
|
|
.addModifiers(Modifier.PUBLIC)
|
|
.build()
|
|
);
|
|
// withProperties method
|
|
blockClass.addMethod(
|
|
MethodSpec.methodBuilder("withProperties")
|
|
.returns(TypeName.SHORT)
|
|
.addParameter(
|
|
ParameterSpec.builder(ArrayTypeName.of(String.class), "properties").addAnnotation(NotNull.class).build()
|
|
).varargs()
|
|
.beginControlFlow("for ($T alt : alternatives)", blockAltClassName)
|
|
.beginControlFlow("if ($T.equals(alt.getProperties(), properties))", Arrays.class)
|
|
.addStatement("return alt.getId()")
|
|
.endControlFlow()
|
|
.endControlFlow()
|
|
.addStatement("return this.defaultID")
|
|
.addModifiers(Modifier.PUBLIC)
|
|
.build()
|
|
);
|
|
// fromStateId method
|
|
blockClass.addMethod(
|
|
MethodSpec.methodBuilder("fromStateId")
|
|
.returns(blockClassName)
|
|
.addParameter(TypeName.SHORT, "blockStateId")
|
|
.addStatement("return BlockArray.blocks[blockStateId]")
|
|
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
|
.build()
|
|
);
|
|
// toString method
|
|
blockClass.addMethod(
|
|
MethodSpec.methodBuilder("toString")
|
|
.addAnnotation(NotNull.class)
|
|
.addAnnotation(Override.class)
|
|
.returns(String.class)
|
|
// this resolves to [Namespace]
|
|
.addStatement("return \"[\" + this.id + \"]\"")
|
|
.addModifiers(Modifier.PUBLIC)
|
|
.build()
|
|
);
|
|
// Staticblock in Block enum
|
|
CodeBlock.Builder staticBlock = CodeBlock.builder();
|
|
// Use data
|
|
for (JsonElement b : blocks) {
|
|
JsonObject block = b.getAsJsonObject();
|
|
|
|
String blockName = block.get("name").getAsString();
|
|
JsonArray states = block.get("states").getAsJsonArray();
|
|
// Enum constant in Block
|
|
blockClass.addEnumConstant(blockName, TypeSpec.anonymousClassBuilder(
|
|
"$T.from($S), (short) $L, $L, $L, $L, $L, $L, $L",
|
|
namespaceIDClassName,
|
|
block.get("id").getAsString(),
|
|
block.get("defaultBlockState").getAsShort(),
|
|
states.get(0).getAsJsonObject().get("destroySpeed").getAsDouble(),
|
|
block.get("explosionResistance").getAsDouble(),
|
|
states.get(0).getAsJsonObject().get("isAir").getAsBoolean(),
|
|
states.get(0).getAsJsonObject().get("isSolid").getAsBoolean(),
|
|
block.get("blockEntity").getAsBoolean(),
|
|
states.size() == 1
|
|
).build()
|
|
);
|
|
if (states.size() > 1) {
|
|
ClassName blockStateSpecificClassName = ClassName.get(
|
|
"net.minestom.server.instance.block.states",
|
|
NameUtil.convertSnakeCaseToCamelCase(blockName.toLowerCase())
|
|
);
|
|
|
|
// Common blockStateSpecificClass structure
|
|
TypeSpec.Builder blockStateSpecificClass = TypeSpec.classBuilder(blockStateSpecificClassName)
|
|
.addAnnotation(
|
|
AnnotationSpec.builder(Deprecated.class)
|
|
.addMember("since", "$S", "forever")
|
|
.addMember("forRemoval", "$L", false).build()
|
|
)
|
|
.addModifiers(Modifier.PUBLIC, Modifier.FINAL).addJavadoc("AUTOGENERATED by " + getClass().getSimpleName());
|
|
|
|
// initStates method
|
|
MethodSpec.Builder initStatesMethod = MethodSpec.methodBuilder("initStates")
|
|
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
|
|
|
// init States methods in block specific classes
|
|
for (JsonElement s : states) {
|
|
JsonObject state = s.getAsJsonObject();
|
|
|
|
// block state properties
|
|
JsonObject properties = state.get("properties").getAsJsonObject();
|
|
if (properties.size() != 0) {
|
|
StringBuilder propertiesStr = new StringBuilder();
|
|
// has properties
|
|
for (Map.Entry<String, JsonElement> entry : properties.entrySet()) {
|
|
String key = entry.getKey();
|
|
String value = entry.getValue().getAsString();
|
|
propertiesStr.append('"').append(key).append('=').append(value).append("\",");
|
|
}
|
|
// Delete last comma
|
|
propertiesStr.deleteCharAt(propertiesStr.length() - 1);
|
|
|
|
initStatesMethod.addStatement(
|
|
"$T.$N.addBlockAlternative(new $T((short) $L, **REPLACE**))".replace("**REPLACE**", propertiesStr.toString()),
|
|
blockClassName,
|
|
blockName,
|
|
blockAltClassName,
|
|
state.get("id").getAsShort()
|
|
);
|
|
} else {
|
|
// has no properties
|
|
initStatesMethod.addStatement(
|
|
"$T.$N.addBlockAlternative(new $T((short) $L))",
|
|
blockClassName,
|
|
blockName,
|
|
blockAltClassName,
|
|
state.get("id").getAsShort()
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
// Add initStates method
|
|
blockStateSpecificClass.addMethod(initStatesMethod.build());
|
|
|
|
// Add initStates method refence to static block
|
|
staticBlock.addStatement("$T.initStates()", blockStateSpecificClassName);
|
|
|
|
// Add BlockStates to list of files we need to write:
|
|
filesToWrite.add(JavaFile.builder("net.minestom.server.instance.block.states", blockStateSpecificClass.build()).indent(" ").build());
|
|
}
|
|
}
|
|
blockClass.addStaticBlock(staticBlock.build());
|
|
// Add ignore deprecations annotation
|
|
blockClass.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "$S", "deprecation").build());
|
|
|
|
|
|
// Add Block & BlockState to list of files we need to write:
|
|
filesToWrite.add(
|
|
JavaFile.builder("net.minestom.server.instance.block", blockClass.build())
|
|
.indent(" ")
|
|
.skipJavaLangImports(true)
|
|
.build()
|
|
);
|
|
|
|
// Write files to outputFolder
|
|
writeFiles(filesToWrite, outputFolder);
|
|
}
|
|
} |