mirror of https://github.com/Minestom/Minestom.git
Add the Block Data API and Block Generator.
This commit is contained in:
parent
a749f07a3f
commit
dba8b65c03
|
@ -32,7 +32,7 @@ hs_err_pid*
|
|||
|
||||
### Gradle template
|
||||
.gradle
|
||||
/build/
|
||||
**/build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
@ -45,6 +45,7 @@ gradle-app.setting
|
|||
|
||||
# IDEA files
|
||||
.idea/
|
||||
**/out/
|
||||
|
||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||
# gradle/wrapper/gradle-wrapper.properties
|
||||
|
@ -56,5 +57,7 @@ gradle-app.setting
|
|||
/.mixin.out/
|
||||
|
||||
# When compiling we get a docs folder
|
||||
/docs
|
||||
.mixin.out/
|
||||
**/docs
|
||||
|
||||
# The data for generation is included in this folder
|
||||
code-generators/data/
|
||||
|
|
81
build.gradle
81
build.gradle
|
@ -5,6 +5,8 @@ plugins {
|
|||
id 'maven-publish'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.5.0'
|
||||
id 'checkstyle'
|
||||
// Used to download our data
|
||||
id 'de.undercouch.download' version '4.1.1'
|
||||
}
|
||||
|
||||
group 'net.minestom.server'
|
||||
|
@ -217,6 +219,85 @@ configurations.all {
|
|||
exclude group: "org.checkerframework", module: "checker-qual"
|
||||
}
|
||||
|
||||
// UPDATE: Update the data to the required version
|
||||
task downloadData(type: Download) {
|
||||
String mainURL = "https://raw.githubusercontent.com/Minestom/MinestomDataGenerator/master/Minestom-data/1.16.5/1_16_5_";
|
||||
src([
|
||||
// attributes.json
|
||||
"${mainURL}attributes.json",
|
||||
// biomes.json
|
||||
"${mainURL}biomes.json",
|
||||
// block_entities
|
||||
"${mainURL}block_entities.json",
|
||||
// block_properties
|
||||
"${mainURL}block_properties.json",
|
||||
// blocks.json
|
||||
"${mainURL}blocks.json",
|
||||
// custom_statistics.json
|
||||
"${mainURL}custom_statistics.json",
|
||||
// dimension_types.json
|
||||
"${mainURL}dimension_types.json",
|
||||
// enchantments.json
|
||||
"${mainURL}enchantments.json",
|
||||
// fluids.json
|
||||
"${mainURL}fluids.json",
|
||||
// items.json
|
||||
"${mainURL}items.json",
|
||||
// map_colors.json
|
||||
"${mainURL}map_colors.json",
|
||||
// particles.json
|
||||
"${mainURL}particles.json",
|
||||
// potion_effects.json
|
||||
"${mainURL}potion_effects.json",
|
||||
// potions.json
|
||||
"${mainURL}potions.json",
|
||||
// sounds.json
|
||||
"${mainURL}sounds.json",
|
||||
// villager_professions.json
|
||||
"${mainURL}villager_professions.json",
|
||||
// villager_types.json
|
||||
"${mainURL}villager_types.json"
|
||||
])
|
||||
dest new File(processResources.destinationDir, "/minecraft_data/")
|
||||
overwrite true
|
||||
}
|
||||
|
||||
task downloadTags(type: Download) {
|
||||
String mainURL = "https://raw.githubusercontent.com/Minestom/MinestomDataGenerator/master/Minestom-data/1.16.5/1_16_5_tags/1_16_5_";
|
||||
src([
|
||||
// block_tags.json
|
||||
"${mainURL}block_tags.json",
|
||||
// entity_type_tags.json
|
||||
"${mainURL}entity_type_tags.json",
|
||||
// fluid_tags.json
|
||||
"${mainURL}fluid_tags.json",
|
||||
// item_tags.json
|
||||
"${mainURL}item_tags.json",
|
||||
])
|
||||
dest new File(processResources.destinationDir, "/minecraft_data/tags/")
|
||||
overwrite true
|
||||
}
|
||||
|
||||
task downloadLootTables(type: Download) {
|
||||
String mainURL = "https://raw.githubusercontent.com/Minestom/MinestomDataGenerator/master/Minestom-data/1.16.5/1_16_5_loot_tables/1_16_5_";
|
||||
src([
|
||||
// block_loot_tables.json.json
|
||||
"${mainURL}block_loot_tables.json",
|
||||
// chest_loot_tables.json
|
||||
"${mainURL}chest_loot_tables.json",
|
||||
// entity_loot_tables.json
|
||||
"${mainURL}entity_loot_tables.json",
|
||||
// gameplay_loot_tables.json
|
||||
"${mainURL}gameplay_loot_tables.json",
|
||||
])
|
||||
dest new File(processResources.destinationDir, "/minecraft_data/loot_tables/")
|
||||
overwrite true
|
||||
}
|
||||
|
||||
processResources {
|
||||
dependsOn(downloadData, downloadTags, downloadLootTables)
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
// Used to download our data
|
||||
id 'de.undercouch.download'
|
||||
}
|
||||
|
||||
group 'net.minestom.server'
|
||||
version '1.0'
|
||||
|
||||
sourceCompatibility = 1.11
|
||||
|
||||
def mcVersion = "1.16.5"
|
||||
def mcVersionUnderscored = mcVersion.replace('.', '_')
|
||||
def dataDest = new File(project.projectDir, "/data/")
|
||||
def dataDestTags = new File(project.projectDir, "/data/tags/")
|
||||
def dataDestLootTable = new File(project.projectDir, "/data/loot_tables/")
|
||||
|
||||
application {
|
||||
mainClass.set("net.minestom.codegen.Generators")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
implementation 'org.jetbrains:annotations:20.1.0'
|
||||
implementation 'com.squareup:javapoet:1.13.0'
|
||||
// Logging
|
||||
implementation 'org.apache.logging.log4j:log4j-core:2.14.0'
|
||||
// SLF4J is the base logger for most libraries, therefore we can hook it into log4j2.
|
||||
implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.14.0'
|
||||
// Kotlin stuff
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${project.kotlinVersion}"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:${project.kotlinVersion}"
|
||||
}
|
||||
|
||||
// UPDATE: Update the data to the required version and run the code generator.
|
||||
task downloadData(type: Download) {
|
||||
String mainURL = "https://raw.githubusercontent.com/Minestom/MinestomDataGenerator/master/Minestom-data/$mcVersion/${mcVersionUnderscored}_"
|
||||
src([
|
||||
// attributes.json
|
||||
"${mainURL}attributes.json",
|
||||
// biomes.json
|
||||
"${mainURL}biomes.json",
|
||||
// block_entities
|
||||
"${mainURL}block_entities.json",
|
||||
// block_properties
|
||||
"${mainURL}block_properties.json",
|
||||
// blocks.json
|
||||
"${mainURL}blocks.json",
|
||||
// custom_statistics.json
|
||||
"${mainURL}custom_statistics.json",
|
||||
// dimension_types.json
|
||||
"${mainURL}dimension_types.json",
|
||||
// enchantments.json
|
||||
"${mainURL}enchantments.json",
|
||||
// fluids.json
|
||||
"${mainURL}fluids.json",
|
||||
// items.json
|
||||
"${mainURL}items.json",
|
||||
// map_colors.json
|
||||
"${mainURL}map_colors.json",
|
||||
// particles.json
|
||||
"${mainURL}particles.json",
|
||||
// potion_effects.json
|
||||
"${mainURL}potion_effects.json",
|
||||
// potions.json
|
||||
"${mainURL}potions.json",
|
||||
// sounds.json
|
||||
"${mainURL}sounds.json",
|
||||
// villager_professions.json
|
||||
"${mainURL}villager_professions.json",
|
||||
// villager_types.json
|
||||
"${mainURL}villager_types.json"
|
||||
])
|
||||
dest dataDest
|
||||
overwrite true
|
||||
dependsOn("downloadLootTables")
|
||||
dependsOn("downloadTags")
|
||||
}
|
||||
|
||||
task downloadTags(type: Download) {
|
||||
String mainURL = "https://raw.githubusercontent.com/Minestom/MinestomDataGenerator/master/Minestom-data/$mcVersion/${mcVersionUnderscored}_tags/${mcVersionUnderscored}_"
|
||||
src([
|
||||
// block_tags.json
|
||||
"${mainURL}block_tags.json",
|
||||
// entity_type_tags.json
|
||||
"${mainURL}entity_type_tags.json",
|
||||
// fluid_tags.json
|
||||
"${mainURL}fluid_tags.json",
|
||||
// item_tags.json
|
||||
"${mainURL}item_tags.json",
|
||||
])
|
||||
dest dataDestTags
|
||||
overwrite true
|
||||
}
|
||||
|
||||
task downloadLootTables(type: Download) {
|
||||
String mainURL = "https://raw.githubusercontent.com/Minestom/MinestomDataGenerator/master/Minestom-data/$mcVersion/${mcVersionUnderscored}_loot_tables/${mcVersionUnderscored}_"
|
||||
src([
|
||||
// block_loot_tables.json.json
|
||||
"${mainURL}block_loot_tables.json",
|
||||
// chest_loot_tables.json
|
||||
"${mainURL}chest_loot_tables.json",
|
||||
// entity_loot_tables.json
|
||||
"${mainURL}entity_loot_tables.json",
|
||||
// gameplay_loot_tables.json
|
||||
"${mainURL}gameplay_loot_tables.json",
|
||||
])
|
||||
dest dataDestLootTable
|
||||
overwrite true
|
||||
}
|
||||
|
||||
|
||||
run {
|
||||
// Update version
|
||||
setArgs(
|
||||
List.of(
|
||||
mcVersion,
|
||||
dataDest.getAbsolutePath(),
|
||||
project.rootProject.projectDir.getPath() + "${File.separatorChar}src${File.separatorChar}autogenerated${File.separatorChar}java")
|
||||
)
|
||||
dependsOn(downloadData)
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package net.minestom.codegen;
|
||||
|
||||
import net.minestom.codegen.blocks.BlockGenerator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class Generators {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Generators.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length < 3) {
|
||||
LOGGER.error("Usage: <MC version> <source folder> <target folder>");
|
||||
return;
|
||||
}
|
||||
String targetVersion = args[0];
|
||||
File inputFolder = new File(args[1]);
|
||||
File outputFolder = new File(args[2]);
|
||||
// Generate blocks
|
||||
new BlockGenerator(
|
||||
new File(inputFolder, targetVersion.replaceAll("\\.", "_") + "_blocks.json"),
|
||||
new File(inputFolder, targetVersion.replaceAll("\\.", "_") + "_block_properties.json"),
|
||||
outputFolder
|
||||
).generate();
|
||||
LOGGER.info("Finished generating code");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package net.minestom.codegen;
|
||||
|
||||
import com.squareup.javapoet.JavaFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class MinestomCodeGenerator {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MinestomCodeGenerator.class);
|
||||
|
||||
public abstract void generate();
|
||||
|
||||
protected void writeFiles(@NotNull List<JavaFile> fileList, File outputFolder) {
|
||||
for (JavaFile javaFile : fileList) {
|
||||
try {
|
||||
javaFile.writeTo(outputFolder);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("An error occured while writing source code to the file system.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
package net.minestom.codegen.blocks;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.squareup.javapoet.*;
|
||||
import net.minestom.codegen.MinestomCodeGenerator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class BlockGenerator extends MinestomCodeGenerator {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(BlockGenerator.class);
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
||||
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 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 blockImplCN = ClassName.get("net.minestom.server.instance.block", "BlockImpl");
|
||||
|
||||
JsonArray blockProperties;
|
||||
try {
|
||||
blockProperties = GSON.fromJson(new JsonReader(new FileReader(blockPropertyFile)), JsonArray.class);
|
||||
} catch (FileNotFoundException e) {
|
||||
LOGGER.error("Failed to find block_properties.json.");
|
||||
LOGGER.error("Stopped code generation for block entities.");
|
||||
return;
|
||||
}
|
||||
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;
|
||||
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 blocksCN = ClassName.get("net.minestom.server.instance.block", "BlockConstants");
|
||||
// BlockConstants class
|
||||
TypeSpec.Builder blockConstantsClass = TypeSpec.interfaceBuilder(blocksCN)
|
||||
// Add @SuppressWarnings("unused")
|
||||
.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "$S", "unused").build())
|
||||
.addJavadoc("AUTOGENERATED by " + getClass().getSimpleName());
|
||||
|
||||
// Use data
|
||||
for (JsonElement b : blocks) {
|
||||
JsonObject block = b.getAsJsonObject();
|
||||
|
||||
String blockName = block.get("name").getAsString();
|
||||
// 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();
|
||||
|
||||
// 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)
|
||||
.addJavadoc("Definition: \"$L\" = [", propertyKeyMap.get(propertyName));
|
||||
// Results in "key" = ["value1", "value2"...]
|
||||
String[] propertyVals = propertyValueMap.get(propertyName);
|
||||
for (int i = 0; i < propertyVals.length; i++) {
|
||||
if (i == propertyVals.length - 1) {
|
||||
field.addJavadoc("$L]", propertyVals[i].toLowerCase());
|
||||
} else {
|
||||
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(
|
||||
List.of(
|
||||
JavaFile.builder("net.minestom.server.instance.block", blockPropertiesClass.build())
|
||||
.indent(" ")
|
||||
.skipJavaLangImports(true)
|
||||
.build(),
|
||||
JavaFile.builder("net.minestom.server.instance.block", blockConstantsClass.build())
|
||||
.indent(" ")
|
||||
.skipJavaLangImports(true)
|
||||
.build()
|
||||
),
|
||||
outputFolder
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,2 +1,2 @@
|
|||
rootProject.name = 'Minestom'
|
||||
|
||||
include 'code-generators'
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +0,0 @@
|
|||
package net.minestom.server.instance.block;
|
||||
|
||||
final class BlockArray {
|
||||
static final Block[] blocks = new Block[Short.MAX_VALUE];
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,11 +1,9 @@
|
|||
// AUTOGENERATED by net.minestom.codegen.RegistriesGenerator
|
||||
package net.minestom.server.registry;
|
||||
|
||||
import java.util.HashMap;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.fluids.Fluid;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.item.Enchantment;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.particle.Particle;
|
||||
|
@ -17,15 +15,12 @@ import net.minestom.server.utils.NamespaceID;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* AUTOGENERATED
|
||||
*/
|
||||
public final class Registries {
|
||||
/**
|
||||
* Should only be used for internal code, please use the get* methods.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final HashMap<NamespaceID, Block> blocks = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Should only be used for internal code, please use the get* methods.
|
||||
|
@ -81,30 +76,6 @@ public final class Registries {
|
|||
@Deprecated
|
||||
public static final HashMap<NamespaceID, Fluid> fluids = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Returns the corresponding Block matching the given id. Returns 'AIR' if none match.
|
||||
*/
|
||||
@NotNull
|
||||
public static Block getBlock(String id) {
|
||||
return getBlock(NamespaceID.from(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the corresponding Block matching the given id. Returns 'AIR' if none match.
|
||||
*/
|
||||
@NotNull
|
||||
public static Block getBlock(NamespaceID id) {
|
||||
return blocks.getOrDefault(id, Block.AIR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the corresponding Block matching the given key. Returns 'AIR' if none match.
|
||||
*/
|
||||
@NotNull
|
||||
public static Block getBlock(Key key) {
|
||||
return getBlock(NamespaceID.from(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the corresponding Material matching the given id. Returns 'AIR' if none match.
|
||||
*/
|
||||
|
|
|
@ -17,7 +17,6 @@ import net.minestom.server.gamedata.loottables.LootTableManager;
|
|||
import net.minestom.server.gamedata.tags.TagManager;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.InstanceManager;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.instance.block.BlockManager;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.instance.block.rule.BlockPlacementRule;
|
||||
|
@ -69,6 +68,7 @@ public final class MinecraftServer {
|
|||
public final static Logger LOGGER = LoggerFactory.getLogger(MinecraftServer.class);
|
||||
|
||||
public static final String VERSION_NAME = "1.16.5";
|
||||
public static final String VERSION_NAME_UNDERSCORED = VERSION_NAME.replace('.', '_');
|
||||
public static final int PROTOCOL_VERSION = 754;
|
||||
|
||||
// Threads
|
||||
|
@ -157,7 +157,6 @@ public final class MinecraftServer {
|
|||
// without this line, registry types that are not loaded explicitly will have an internal empty registry in Registries
|
||||
// That can happen with PotionType for instance, if no code tries to access a PotionType field
|
||||
// TODO: automate (probably with code generation)
|
||||
Block.values();
|
||||
Material.values();
|
||||
PotionType.values();
|
||||
PotionEffect.values();
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
package net.minestom.server.instance.block;
|
||||
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ObjectAVLTreeMap;
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ObjectSortedMap;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.key.Keyed;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
import net.minestom.server.utils.math.IntRange;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public interface Block extends Keyed, TagReadable, BlockConstants {
|
||||
|
||||
Registry REGISTRY = new Registry();
|
||||
|
||||
<T> @NotNull Block withProperty(@NotNull BlockProperty<T> property, @NotNull T value);
|
||||
|
||||
<T> @NotNull Block withTag(@NotNull Tag<T> tag, @Nullable T value);
|
||||
|
||||
@NotNull Block getDefaultBlock();
|
||||
|
||||
@NotNull NamespaceID getNamespaceId();
|
||||
|
||||
@Override
|
||||
default @NotNull Key key() {
|
||||
return getNamespaceId();
|
||||
}
|
||||
|
||||
default @NotNull String getName() {
|
||||
return getNamespaceId().asString();
|
||||
}
|
||||
|
||||
@NotNull Map<String, String> createPropertiesMap();
|
||||
|
||||
int getBlockId();
|
||||
|
||||
short getStateId();
|
||||
|
||||
@NotNull BlockData getData();
|
||||
|
||||
/**
|
||||
* Migrated to {@link #getData()}.{@link BlockData#isSolid()} method.
|
||||
*
|
||||
* @return True if the Block is solid.
|
||||
*/
|
||||
@Deprecated(
|
||||
forRemoval = true
|
||||
)
|
||||
default boolean isSolid() {
|
||||
return getData().isSolid();
|
||||
}
|
||||
|
||||
class Registry {
|
||||
|
||||
private final Map<NamespaceID, Block> namespaceMap = new HashMap<>();
|
||||
private final Short2ObjectSortedMap<BlockSupplier> stateSet = new Short2ObjectAVLTreeMap<>();
|
||||
|
||||
private Registry() {
|
||||
}
|
||||
|
||||
public synchronized @Nullable Block fromNamespaceId(@NotNull NamespaceID namespaceID) {
|
||||
return namespaceMap.get(namespaceID);
|
||||
}
|
||||
|
||||
public synchronized @Nullable Block fromNamespaceId(@NotNull String namespaceID) {
|
||||
return namespaceMap.get(NamespaceID.from(namespaceID));
|
||||
}
|
||||
|
||||
public synchronized @Nullable Block fromStateId(short stateId) {
|
||||
BlockSupplier supplier = stateSet.get(stateId);
|
||||
return supplier.get(stateId);
|
||||
}
|
||||
|
||||
public synchronized void register(@NotNull NamespaceID namespaceID, @NotNull Block block,
|
||||
@NotNull IntRange range, @NotNull BlockSupplier blockSupplier) {
|
||||
this.namespaceMap.put(namespaceID, block);
|
||||
IntStream.range(range.getMinimum(), range.getMaximum()).forEach(value -> stateSet.put((short) value, blockSupplier));
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface BlockSupplier {
|
||||
@NotNull Block get(short stateId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package net.minestom.server.instance.block;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class BlockAlternative {
|
||||
|
||||
private final short id;
|
||||
private final String[] properties;
|
||||
|
||||
public BlockAlternative(short id, String... properties) {
|
||||
this.id = id;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public short getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String[] getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public Map<String, String> createPropertiesMap() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (String p : properties) {
|
||||
String[] parts = p.split("=");
|
||||
map.put(parts[0], parts[1]);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public String getProperty(String key) {
|
||||
for (String p : properties) {
|
||||
String[] parts = p.split("=");
|
||||
if (parts.length > 1)
|
||||
if (parts[0].equals(key))
|
||||
return parts[1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BlockAlternative{" +
|
||||
"id=" + id +
|
||||
", properties=" + Arrays.toString(properties) +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package net.minestom.server.instance.block;
|
||||
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.map.MapColors;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface BlockData {
|
||||
// Block properties
|
||||
|
||||
/**
|
||||
* Gets the blast (explosion) resistance of a {@link Block}
|
||||
* @return a double denoting the blast resistance.
|
||||
*/
|
||||
double getExplosionResistance();
|
||||
|
||||
/**
|
||||
* Gets the corresponding {@link Material} of a {@link Block}
|
||||
* @return the corresponding {@link Material} or null if not applicable.
|
||||
*/
|
||||
@Nullable Material getCorrespondingItem();
|
||||
|
||||
/**
|
||||
* Gets the friction value of a {@link Block}
|
||||
* @return a double denoting the friction.
|
||||
*/
|
||||
double getFriction();
|
||||
|
||||
/**
|
||||
* Gets the speed factor of a {@link Block}
|
||||
* @return a double denoting the speed factor.
|
||||
*/
|
||||
double getSpeedFactor();
|
||||
/**
|
||||
* Gets the jump factor of a {@link Block}
|
||||
* @return a double denoting the jump factor.
|
||||
*/
|
||||
double getJumpFactor();
|
||||
|
||||
/**
|
||||
* Checks if a {@link Block} is a block entity.
|
||||
* @return a boolean, true when a Block is a block entity, false otherwise.
|
||||
*/
|
||||
boolean isBlockEntity();
|
||||
|
||||
// State properties
|
||||
|
||||
/**
|
||||
* Gets the hardness (destroy speed) of a {@link Block}.
|
||||
* @return a double denoting the hardness.
|
||||
*/
|
||||
double getHardness();
|
||||
|
||||
/**
|
||||
* Gets the light level emitted by a {@link Block}
|
||||
* @return an int representing the light emission.
|
||||
*/
|
||||
int getLightEmission();
|
||||
|
||||
/**
|
||||
* Checks if a {@link Block} is occluding.
|
||||
* @return a boolean, true if a Block is occluding, false otherwise.
|
||||
*/
|
||||
boolean isOccluding();
|
||||
|
||||
/**
|
||||
* Gets the piston push reaction of a {@link Block}
|
||||
* @return a {@link String} containing the push reaction of a Block.
|
||||
*/
|
||||
String getPushReaction(); // TODO: Dedicated object?
|
||||
/**
|
||||
* Checks if a {@link Block} is blocking motion
|
||||
* @return a boolean, true if a Block is blocking motion, false otherwise.
|
||||
*/
|
||||
boolean isBlockingMotion();
|
||||
/**
|
||||
* Checks if a {@link Block} is flammable.
|
||||
* @return a boolean, true if a Block is flammable, false otherwise.
|
||||
*/
|
||||
boolean isFlammable();
|
||||
/**
|
||||
* Checks if a {@link Block} is an instance of air.
|
||||
* @return a boolean, true if a Block is air, false otherwise.
|
||||
*/
|
||||
boolean isAir();
|
||||
/**
|
||||
* Checks if a {@link Block} is an instance of a fluid.
|
||||
* @return a boolean, true if a Block is a liquid, false otherwise.
|
||||
*/
|
||||
boolean isLiquid();
|
||||
/**
|
||||
* Checks if a {@link Block} is replaceable.
|
||||
* @return a boolean, true if a Block is replaceable, false otherwise.
|
||||
*/
|
||||
boolean isReplaceable();
|
||||
/**
|
||||
* Checks if a {@link Block} is solid.
|
||||
* @return a boolean, true if a Block is solid, false otherwise.
|
||||
*/
|
||||
boolean isSolid();
|
||||
/**
|
||||
* Checks if a {@link Block} is solid and blocking.
|
||||
* @return a boolean, true if a Block is solid and blocking, false otherwise.
|
||||
*/
|
||||
boolean isSolidBlocking();
|
||||
/**
|
||||
* Gets the corresponding {@link MapColors} of a {@link Block}
|
||||
* @return the corresponding {@link MapColors}.
|
||||
*/
|
||||
@NotNull MapColors getMapColor();
|
||||
/**
|
||||
* Gets the piston bounding box of a {@link Block}
|
||||
* @return a {@link String} containing the bounding box of a Block.
|
||||
*/
|
||||
String getBoundingBox(); // TODO: Dedicated object?
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
package net.minestom.server.instance.block;
|
||||
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.map.MapColors;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
class BlockDataImpl implements BlockData {
|
||||
private final double explosionResistance;
|
||||
private final @NotNull Material item;
|
||||
private final double friction;
|
||||
private final double speedFactor;
|
||||
private final double jumpFactor;
|
||||
private final double hardness;
|
||||
private final boolean blockEntity;
|
||||
|
||||
private final int lightEmission;
|
||||
private final boolean occluding;
|
||||
private final String pushReaction; // TODO: Dedicated object?
|
||||
private final boolean blockingMotion;
|
||||
private final boolean flammable;
|
||||
private final boolean air;
|
||||
private final boolean liquid;
|
||||
private final boolean replaceable;
|
||||
private final boolean solid;
|
||||
private final boolean solidBlocking;
|
||||
private final @NotNull MapColors mapColor;
|
||||
private final String boundingBox; // TODO: Dedicated object?
|
||||
|
||||
|
||||
BlockDataImpl(
|
||||
double explosionResistance,
|
||||
@NotNull Material item,
|
||||
double friction,
|
||||
double speedFactor,
|
||||
double jumpFactor,
|
||||
boolean blockEntity,
|
||||
|
||||
double hardness,
|
||||
int lightEmission,
|
||||
boolean occluding,
|
||||
String pushReaction,
|
||||
boolean blockingMotion,
|
||||
boolean flammable,
|
||||
boolean air,
|
||||
boolean liquid,
|
||||
boolean replaceable,
|
||||
boolean solid,
|
||||
boolean solidBlocking,
|
||||
@NotNull MapColors mapColor,
|
||||
String boundingBox
|
||||
) {
|
||||
this.explosionResistance = explosionResistance;
|
||||
this.item = item;
|
||||
this.friction = friction;
|
||||
this.speedFactor = speedFactor;
|
||||
this.jumpFactor = jumpFactor;
|
||||
this.hardness = hardness;
|
||||
this.blockEntity = blockEntity;
|
||||
this.lightEmission = lightEmission;
|
||||
this.occluding = occluding;
|
||||
this.pushReaction = pushReaction;
|
||||
this.blockingMotion = blockingMotion;
|
||||
this.air = air;
|
||||
this.flammable = flammable;
|
||||
this.liquid = liquid;
|
||||
this.replaceable = replaceable;
|
||||
this.solid = solid;
|
||||
this.solidBlocking = solidBlocking;
|
||||
this.mapColor = mapColor;
|
||||
this.boundingBox = boundingBox;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getExplosionResistance() {
|
||||
return explosionResistance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Material getCorrespondingItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFriction() {
|
||||
return friction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getSpeedFactor() {
|
||||
return speedFactor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getJumpFactor() {
|
||||
return jumpFactor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHardness() {
|
||||
return hardness;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBlockEntity() {
|
||||
return blockEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightEmission() {
|
||||
return lightEmission;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOccluding() {
|
||||
return occluding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPushReaction() {
|
||||
return pushReaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBlockingMotion() {
|
||||
return blockingMotion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFlammable() {
|
||||
return flammable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAir() {
|
||||
return air;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLiquid() {
|
||||
return liquid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReplaceable() {
|
||||
return replaceable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSolid() {
|
||||
return solid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSolidBlocking() {
|
||||
return solidBlocking;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull MapColors getMapColor() {
|
||||
return mapColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBoundingBox() {
|
||||
return boundingBox;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,269 @@
|
|||
package net.minestom.server.instance.block;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ObjectAVLTreeMap;
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ObjectSortedMap;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.map.MapColors;
|
||||
import net.minestom.server.registry.Registries;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
import net.minestom.server.utils.math.IntRange;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.*;
|
||||
|
||||
class BlockImpl implements Block {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(BlockImpl.class);
|
||||
private static final Short2ObjectSortedMap<BlockData> blockData = new Short2ObjectAVLTreeMap<>();
|
||||
|
||||
static {
|
||||
loadBlockData();
|
||||
}
|
||||
|
||||
private final NamespaceID namespaceID;
|
||||
private final int blockId;
|
||||
private final short minStateId, stateId;
|
||||
private final List<BlockProperty<?>> properties;
|
||||
protected BlockImpl original = null;
|
||||
private LinkedHashMap<BlockProperty<?>, Object> propertiesMap;
|
||||
private NBTCompound compound;
|
||||
|
||||
private BlockImpl(NamespaceID namespaceID,
|
||||
int blockId,
|
||||
short minStateId, short stateId,
|
||||
List<BlockProperty<?>> properties,
|
||||
LinkedHashMap<BlockProperty<?>, Object> propertiesMap,
|
||||
NBTCompound compound) {
|
||||
this.namespaceID = namespaceID;
|
||||
this.blockId = blockId;
|
||||
this.minStateId = minStateId;
|
||||
this.stateId = stateId;
|
||||
this.properties = properties;
|
||||
this.propertiesMap = propertiesMap;
|
||||
this.compound = compound;
|
||||
}
|
||||
|
||||
private BlockImpl(NamespaceID namespaceID,
|
||||
int blockId, short minStateId, short stateId,
|
||||
List<BlockProperty<?>> properties,
|
||||
LinkedHashMap<BlockProperty<?>, Object> propertiesMap) {
|
||||
this(namespaceID, blockId, minStateId, stateId, properties, propertiesMap, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull <T> Block withProperty(@NotNull BlockProperty<T> property, @NotNull T value) {
|
||||
if (properties.isEmpty()) {
|
||||
// This block doesn't have any state
|
||||
return this;
|
||||
}
|
||||
final int index = properties.indexOf(property);
|
||||
if (index == -1) {
|
||||
// Invalid state
|
||||
return this;
|
||||
}
|
||||
|
||||
// Find properties map
|
||||
LinkedHashMap<BlockProperty<?>, Object> map;
|
||||
if (propertiesMap == null) {
|
||||
// Represents the first id, create a new map
|
||||
map = new LinkedHashMap<>();
|
||||
properties.forEach(prop -> map.put(prop, prop.equals(property) ? value : null));
|
||||
} else {
|
||||
// Change property
|
||||
map = (LinkedHashMap<BlockProperty<?>, Object>) propertiesMap.clone();
|
||||
map.put(property, value);
|
||||
}
|
||||
|
||||
var block = new BlockImpl(namespaceID, blockId, minStateId, computeId(minStateId, properties, map), properties, map);
|
||||
block.original = original;
|
||||
return block;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
|
||||
return tag.read(compound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTag(@NotNull Tag<?> tag) {
|
||||
return compound.containsKey(tag.getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull <T> Block withTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
if ((compound == null || compound.getKeys().isEmpty()) && value == null) {
|
||||
// No change
|
||||
return this;
|
||||
}
|
||||
|
||||
// Apply tag
|
||||
NBTCompound compound = Objects.requireNonNullElseGet(this.compound, NBTCompound::new);
|
||||
tag.write(compound, value);
|
||||
if (compound.getKeys().isEmpty()) {
|
||||
compound = null;
|
||||
}
|
||||
|
||||
var block = new BlockImpl(namespaceID, blockId, minStateId, stateId, properties, propertiesMap, compound);
|
||||
block.original = original;
|
||||
return block;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Block getDefaultBlock() {
|
||||
return original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NamespaceID getNamespaceId() {
|
||||
return namespaceID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, String> createPropertiesMap() {
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
propertiesMap.forEach((blockProperty, o) -> properties.put(blockProperty.getName(), o.toString()));
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockId() {
|
||||
return blockId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getStateId() {
|
||||
return stateId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull BlockData getData() {
|
||||
return blockData.get(stateId);
|
||||
}
|
||||
|
||||
protected static BlockImpl create(NamespaceID namespaceID, short blockId, short minStateId, short maxStateId,
|
||||
short defaultStateId, List<BlockProperty<?>> properties) {
|
||||
var block = new BlockImpl(namespaceID, blockId, minStateId, defaultStateId, properties, computeMap(defaultStateId, properties));
|
||||
block.original = block;
|
||||
Block.REGISTRY.register(namespaceID, block,
|
||||
new IntRange((int) minStateId, (int) maxStateId), requestedStateId -> {
|
||||
var requestedBlock = new BlockImpl(namespaceID, blockId, minStateId, requestedStateId, properties, computeMap(requestedStateId, properties));
|
||||
requestedBlock.original = block;
|
||||
return requestedBlock;
|
||||
});
|
||||
return block;
|
||||
}
|
||||
|
||||
private static short computeId(short id, List<BlockProperty<?>> properties,
|
||||
LinkedHashMap<BlockProperty<?>, Object> propertiesMap) {
|
||||
int[] factors = computeFactors(properties);
|
||||
int index = 0;
|
||||
for (var entry : propertiesMap.entrySet()) {
|
||||
var property = entry.getKey();
|
||||
var value = entry.getValue();
|
||||
if (value != null) {
|
||||
var values = property.getPossibleValues();
|
||||
id += values.indexOf(value) * factors[index++];
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
private static LinkedHashMap<BlockProperty<?>, Object> computeMap(short deltaId, List<BlockProperty<?>> properties) {
|
||||
LinkedHashMap<BlockProperty<?>, Object> result = new LinkedHashMap<>();
|
||||
int[] factors = computeFactors(properties);
|
||||
int index = 0;
|
||||
for (var property : properties) {
|
||||
final int factor = factors[index++];
|
||||
final int valueIndex = deltaId / factor;
|
||||
final var possibilities = property.getPossibleValues();
|
||||
final var value = possibilities.get(valueIndex);
|
||||
result.put(property, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int[] computeFactors(List<BlockProperty<?>> properties) {
|
||||
final int size = properties.size();
|
||||
int[] result = new int[size];
|
||||
int factor = 1;
|
||||
ListIterator<BlockProperty<?>> li = properties.listIterator(properties.size());
|
||||
// Iterate in reverse.
|
||||
int i = size;
|
||||
while (li.hasPrevious()) {
|
||||
var property = li.previous();
|
||||
result[--i] = factor;
|
||||
factor *= property.getPossibleValues().size();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the {@link BlockData} from the JAR Resources to the Map.
|
||||
*/
|
||||
private static void loadBlockData() {
|
||||
// E.G. 1_16_5_blocks.json
|
||||
InputStream blocksIS = BlockImpl.class.getResourceAsStream("/minestom_data/" + MinecraftServer.VERSION_NAME_UNDERSCORED + "_blocks.json");
|
||||
if (blocksIS == null) {
|
||||
LOGGER.error("Failed to find blocks.json");
|
||||
return;
|
||||
}
|
||||
// Get map Colors as we will need these
|
||||
MapColors[] mapColors = MapColors.values();
|
||||
|
||||
JsonArray blocks = new Gson().fromJson(new InputStreamReader(blocksIS), JsonArray.class);
|
||||
for (JsonElement blockEntry : blocks) {
|
||||
// Load Data
|
||||
JsonObject block = blockEntry.getAsJsonObject();
|
||||
double explosionResistance = block.get("explosionResistance").getAsDouble();
|
||||
double friction = block.get("friction").getAsDouble();
|
||||
double speedFactor = block.get("speedFactor").getAsDouble();
|
||||
double jumpFactor = block.get("jumpFactor").getAsDouble();
|
||||
boolean blockEntity = block.get("blockEntity").getAsBoolean();
|
||||
Material item = Registries.getMaterial(block.get("itemId").getAsString());
|
||||
JsonArray states = block.get("states").getAsJsonArray();
|
||||
for (JsonElement stateEntry : states) {
|
||||
// Load Data
|
||||
JsonObject state = stateEntry.getAsJsonObject();
|
||||
|
||||
short stateId = state.get("id").getAsShort();
|
||||
blockData.put(
|
||||
stateId,
|
||||
new BlockDataImpl(
|
||||
explosionResistance,
|
||||
item,
|
||||
friction,
|
||||
speedFactor,
|
||||
jumpFactor,
|
||||
blockEntity,
|
||||
|
||||
state.get("destroySpeed").getAsDouble(),
|
||||
state.get("lightEmission").getAsInt(),
|
||||
state.get("doesOcclude").getAsBoolean(),
|
||||
state.get("pushReaction").getAsString(),
|
||||
state.get("blocksMotion").getAsBoolean(),
|
||||
state.get("isFlammable").getAsBoolean(),
|
||||
state.get("air").getAsBoolean(),
|
||||
state.get("isLiquid").getAsBoolean(),
|
||||
state.get("isReplaceable").getAsBoolean(),
|
||||
state.get("isSolid").getAsBoolean(),
|
||||
state.get("isSolidBlocking").getAsBoolean(),
|
||||
mapColors[state.get("mapColorId").getAsInt()],
|
||||
state.get("boundingBox").getAsString()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package net.minestom.server.instance.block;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class BlockProperty<T> {
|
||||
|
||||
private final String name;
|
||||
private final List<T> possibleValues;
|
||||
|
||||
@SafeVarargs
|
||||
public BlockProperty(@NotNull String name, @NotNull T... possibleValues) {
|
||||
this.name = name;
|
||||
this.possibleValues = Arrays.asList(possibleValues);
|
||||
}
|
||||
|
||||
public @NotNull String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public @NotNull List<T> getPossibleValues() {
|
||||
return possibleValues;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue