BlockGenerator rework

This commit is contained in:
TheMode 2021-06-23 22:09:19 +02:00
parent 48b1aa90db
commit 4fb6e37622
2 changed files with 20 additions and 228 deletions

View File

@ -33,11 +33,7 @@ public class Generators {
File inputFolder = new File(args[1]); // This will be ignored if resourceMode = true File inputFolder = new File(args[1]); // This will be ignored if resourceMode = true
File outputFolder = new File(args[2]); File outputFolder = new File(args[2]);
// Generate blocks // Generate blocks
new BlockGenerator( new BlockGenerator(Generators.class.getResourceAsStream("/blocks.json"), outputFolder).generate();
resourceMode ? Generators.class.getResourceAsStream("/" + targetVersion + "_blocks.json") : new FileInputStream(new File(inputFolder, targetVersion + "_blocks.json")),
resourceMode ? Generators.class.getResourceAsStream("/" + targetVersion + "_block_properties.json") : new FileInputStream(new File(inputFolder, targetVersion + "_block_properties.json")),
outputFolder
).generate();
// Generate fluids // Generate fluids
new FluidGenerator( new FluidGenerator(
resourceMode ? Generators.class.getResourceAsStream("/" + targetVersion + "_fluids.json") : new FileInputStream(new File(inputFolder, targetVersion + "_fluids.json")), resourceMode ? Generators.class.getResourceAsStream("/" + targetVersion + "_fluids.json") : new FileInputStream(new File(inputFolder, targetVersion + "_fluids.json")),

View File

@ -1,6 +1,6 @@
package net.minestom.codegen.blocks; package net.minestom.codegen.blocks;
import com.google.gson.*; import com.google.gson.JsonObject;
import com.squareup.javapoet.*; import com.squareup.javapoet.*;
import net.minestom.codegen.MinestomCodeGenerator; import net.minestom.codegen.MinestomCodeGenerator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -12,17 +12,16 @@ import javax.lang.model.element.Modifier;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.*; import java.util.List;
import java.util.Locale;
public final class BlockGenerator extends MinestomCodeGenerator { public final class BlockGenerator extends MinestomCodeGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(BlockGenerator.class); private static final Logger LOGGER = LoggerFactory.getLogger(BlockGenerator.class);
private final InputStream blocksFile; private final InputStream blocksFile;
private final InputStream blockPropertyFile;
private final File outputFolder; private final File outputFolder;
public BlockGenerator(@Nullable InputStream blocksFile, @Nullable InputStream blockPropertyFile, @NotNull File outputFolder) { public BlockGenerator(@Nullable InputStream blocksFile, @NotNull File outputFolder) {
this.blocksFile = blocksFile; this.blocksFile = blocksFile;
this.blockPropertyFile = blockPropertyFile;
this.outputFolder = outputFolder; this.outputFolder = outputFolder;
} }
@ -33,97 +32,10 @@ public final class BlockGenerator extends MinestomCodeGenerator {
LOGGER.error("Stopped code generation for blocks."); LOGGER.error("Stopped code generation for blocks.");
return; return;
} }
if (blockPropertyFile == null) {
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 namespaceIDCN = ClassName.get("net.minestom.server.utils", "NamespaceID");
ClassName blockPropertyCN = ClassName.get("net.minestom.server.instance.block", "BlockProperty");
ClassName blockCN = ClassName.get("net.minestom.server.instance.block", "Block"); ClassName blockCN = ClassName.get("net.minestom.server.instance.block", "Block");
ClassName blockImplCN = ClassName.get("net.minestom.server.instance.block", "BlockImpl");
JsonArray blockProperties; JsonObject blocks;
blockProperties = GSON.fromJson(new InputStreamReader(blockPropertyFile), JsonArray.class); blocks = GSON.fromJson(new InputStreamReader(blocksFile), JsonObject.class);
ClassName blockPropertiesCN = ClassName.get("net.minestom.server.instance.block", "BlockProperties");
// Particle
TypeSpec.Builder blockPropertiesClass = TypeSpec.classBuilder(blockPropertiesCN)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
// Add @SuppressWarnings("unused")
.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "$S", "unused").build())
.addJavadoc("AUTOGENERATED by " + getClass().getSimpleName());
// Add private constructor
blockPropertiesClass.addMethod(
MethodSpec.constructorBuilder()
.addModifiers(Modifier.PRIVATE)
.build()
);
// This stores the classes of the field names, e.g. WATERLOGGED --> Boolean
Map<String, Class<?>> propertyClassMap = new HashMap<>();
Map<String, String> propertyKeyMap = new HashMap<>();
Map<String, String[]> propertyValueMap = new HashMap<>();
// Use data
for (JsonElement e : blockProperties) {
JsonObject blockProperty = e.getAsJsonObject();
String propertyName = blockProperty.get("name").getAsString();
JsonArray blockValues = blockProperty.get("values").getAsJsonArray();
JsonPrimitive firstElement = blockValues.get(0).getAsJsonPrimitive();
Class<?> type;
StringBuilder values = new StringBuilder();
if (firstElement.isBoolean()) {
type = Boolean.class;
values = new StringBuilder("true, false");
} else if (firstElement.isNumber()) {
type = Integer.class;
for (JsonElement blockValue : blockValues) {
int i = blockValue.getAsInt();
values.append(i).append(", ");
}
// Delete final ', '
values.delete(values.lastIndexOf(","), values.length());
} else {
type = String.class;
for (JsonElement blockValue : blockValues) {
String s = blockValue.getAsString();
values.append("\"").append(s).append("\", ");
}
// Delete final ', '
values.delete(values.lastIndexOf(","), values.length());
}
String propertyKey = blockProperty.get("key").getAsString();
propertyKeyMap.put(propertyName, propertyKey);
propertyClassMap.put(propertyName, type);
propertyValueMap.put(propertyName, values.toString().split(", "));
// Adds the field to the main class
// e.g. BlockProperty<Boolean> WATERLOGGED = new BlockProperty<Boolean>("waterlogged", true, false)
blockPropertiesClass.addField(
FieldSpec.builder(
ParameterizedTypeName.get(blockPropertyCN, TypeName.get(type)),
propertyName
).initializer(
"new $T<>($S, $L)",
blockPropertyCN,
propertyKey,
values
).addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).build()
);
}
JsonArray blocks;
blocks = GSON.fromJson(new InputStreamReader(blocksFile), JsonArray.class);
ClassName blocksCN = ClassName.get("net.minestom.server.instance.block", "BlockConstants"); ClassName blocksCN = ClassName.get("net.minestom.server.instance.block", "BlockConstants");
// BlockConstants class // BlockConstants class
TypeSpec.Builder blockConstantsClass = TypeSpec.interfaceBuilder(blocksCN) TypeSpec.Builder blockConstantsClass = TypeSpec.interfaceBuilder(blocksCN)
@ -132,138 +44,22 @@ public final class BlockGenerator extends MinestomCodeGenerator {
.addJavadoc("AUTOGENERATED by " + getClass().getSimpleName()); .addJavadoc("AUTOGENERATED by " + getClass().getSimpleName());
// Use data // Use data
for (JsonElement b : blocks) { blocks.keySet().forEach(namespace -> {
JsonObject block = b.getAsJsonObject(); final String constantName = namespace.replace("minecraft:", "").toUpperCase(Locale.ROOT);
blockConstantsClass.addField(
String blockName = block.get("name").getAsString(); FieldSpec.builder(blockCN, constantName)
// Handle the properties
// Create a subclass for each Block and reference the main BlockProperty.
JsonArray properties = block.get("properties").getAsJsonArray();
if (properties.size() != 0) {
// Create a subclass called "blockName"
// e.g. subclass AIR in BlockProperties
TypeSpec.Builder subClass = TypeSpec.classBuilder(blockPropertiesCN.nestedClass(blockName))
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.addJavadoc(
"Represents the $L {@link $T $L} that {@link $T#$N} can have:\n",
properties.size(),
blockPropertyCN,
properties.size() > 1 ? "properties" : "property",
blocksCN,
blockName
).addJavadoc("<ul>\n");
// Store a list of values for the getProperties() method.
StringBuilder values = new StringBuilder();
// Go through all properties the block has.
for (JsonElement property : properties) {
String propertyName = property.getAsString().toUpperCase(Locale.ROOT);
// Add a static field that delegates to the BlockProperties static definition
FieldSpec.Builder field = FieldSpec.builder(
ParameterizedTypeName.get(blockPropertyCN, TypeName.get(propertyClassMap.get(propertyName))),
propertyName)
.initializer("$T.$N", blockPropertiesCN, propertyName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.addJavadoc("Definition: \"$L\" = [", propertyKeyMap.get(propertyName)); .initializer(
// Results in "key" = ["value1", "value2"...] // Blocks.STONE = Block.fromNamespaceId("minecraft:stone")
String[] propertyVals = propertyValueMap.get(propertyName); "$T.fromNamespaceId($S)",
for (int i = 0; i < propertyVals.length; i++) { blockCN,
if (i == propertyVals.length - 1) { namespace
field.addJavadoc("$L]", propertyVals[i].toLowerCase()); )
} else { .build()
field.addJavadoc("$L, ", propertyVals[i].toLowerCase()); );
} });
}
// Add field to subclass
subClass.addField(field.build());
values.append(propertyName).append(", ");
subClass.addJavadoc("<li>{@link $T#$N}</li>\n", blockPropertiesCN, propertyName);
}
subClass.addJavadoc("</ul>");
// Delete final ', '
values.delete(values.lastIndexOf(","), values.length());
// Add a static method to get all the properties
subClass.addMethod(
MethodSpec.methodBuilder("getProperties")
.returns(
// List<BlockProperty<?>>
ParameterizedTypeName.get(
ClassName.get(List.class),
// BlockProperty<?>
ParameterizedTypeName.get(blockPropertyCN, TypeVariableName.get("?"))
)
)
.addStatement(
"return $T.of($L)",
// If it has multiple properties --> Arrays.asList() else Collections.singletonList()
ClassName.get(List.class),
values
)
.addModifiers(Modifier.STATIC)
.build()
);
blockPropertiesClass.addType(subClass.build());
}
JsonArray states = block.getAsJsonArray("states");
// Now handle the fields in Blocks.
// If we don't have properties
if (properties.size() == 0) {
// This is a block like Stone that only has 1 BlockState.
blockConstantsClass.addField(
FieldSpec.builder(blockCN, blockName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.initializer(
// Blocks.STONE = new BlockImpl(NamespaceID.from("minecraft:stone"), 1, Collections.emptyList())
"$T.create($T.from($S), (short) $L, (short) $L, (short) $L, (short) $L, $T.emptyList())",
blockImplCN,
namespaceIDCN,
block.get("id").getAsString(),
// Block id
block.get("numericalID").getAsShort(),
// First state id
states.get(0).getAsJsonObject().get("id").getAsShort(),
// Last state id
states.get(states.size() - 1).getAsJsonObject().get("id").getAsShort(),
// Default state id
block.get("defaultBlockState").getAsShort(),
ClassName.get(Collections.class)
)
.build()
);
} else {
// This is a block that has multiple properties.
blockConstantsClass.addField(
FieldSpec.builder(blockCN, blockName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.initializer(
// Blocks.GRASS_BLOCK = new BlockImpl(NamespaceID.from("minecraft:grass_block"), 9, 8, varargsProperty)
"$T.create($T.from($S), (short) $L, (short) $L, (short) $L, (short) $L, $T.$N.getProperties())",
blockImplCN,
namespaceIDCN,
block.get("id").getAsString(),
// Block id
block.get("numericalID").getAsShort(),
// First id
block.getAsJsonArray("states").get(0).getAsJsonObject().get("id").getAsShort(),
// Last state id
states.get(states.size() - 1).getAsJsonObject().get("id").getAsShort(),
// DefaultBlockStateId
block.get("defaultBlockState").getAsShort(),
blockPropertiesCN,
blockName
)
.build()
);
}
}
writeFiles( writeFiles(
List.of( List.of(
JavaFile.builder("net.minestom.server.instance.block", blockPropertiesClass.build())
.indent(" ")
.skipJavaLangImports(true)
.build(),
JavaFile.builder("net.minestom.server.instance.block", blockConstantsClass.build()) JavaFile.builder("net.minestom.server.instance.block", blockConstantsClass.build())
.indent(" ") .indent(" ")
.skipJavaLangImports(true) .skipJavaLangImports(true)