Registry Updates (Villagers, Entity) (#2465)

* Add VillagerType and VillagerProfession generators and implementation, add a few new entity registry fields

* Fix NBT serialization and remove unused code

* Bump DataGen version and change VillagerType to GenericEnumGenerator
This commit is contained in:
GreatWyrm 2024-11-28 04:49:11 -08:00 committed by Matt Worzala
parent 10e69be65e
commit c44fe9db46
11 changed files with 242 additions and 473 deletions

View File

@ -36,6 +36,8 @@ public class Generators {
resource("consume_effects.json"), outputFolder).packagePrivate().generate();
new GenericEnumGenerator("net.minestom.server.command", "ArgumentParserType",
resource("command_arguments.json"), outputFolder).generate();
new GenericEnumGenerator("net.minestom.server.entity", "VillagerType",
resource("villager_types.json"), outputFolder).generate();
var generator = new CodeGenerator(outputFolder);
@ -49,6 +51,8 @@ public class Generators {
generator.generate(resource("custom_statistics.json"), "net.minestom.server.statistic", "StatisticType", "StatisticTypeImpl", "StatisticTypes");
generator.generate(resource("attributes.json"), "net.minestom.server.entity.attribute", "Attribute", "AttributeImpl", "Attributes");
generator.generate(resource("feature_flags.json"), "net.minestom.server", "FeatureFlag", "FeatureFlagImpl", "FeatureFlags");
generator.generate(resource("villager_professions.json"), "net.minestom.server.entity", "VillagerProfession", "VillagerProfessionImpl", "VillagerProfessions");
// Dynamic registries
generator.generateKeys(resource("chat_types.json"), "net.minestom.server.message", "ChatType", "ChatTypes");
@ -66,16 +70,6 @@ public class Generators {
// Generate fluids
new FluidGenerator(resource("fluids.json"), outputFolder).generate();
// TODO: Generate villager professions
// new VillagerProfessionGenerator(
// new File(inputFolder, targetVersion + "_villager_professions.json"),
// outputFolder
// ).generate();
// TODO: Generate villager types
// new VillagerTypeGenerator(
// new File(inputFolder, targetVersion + "_villager_types.json"),
// outputFolder
// ).generate();
LOGGER.info("Finished generating code");
}

View File

@ -1,212 +0,0 @@
package net.minestom.codegen.entity;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.squareup.javapoet.*;
import net.minestom.codegen.MinestomCodeGenerator;
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.InputStream;
import java.io.InputStreamReader;
import java.util.List;
public final class VillagerProfessionGenerator extends MinestomCodeGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(VillagerProfessionGenerator.class);
private final InputStream villagerProfessionsFile;
private final File outputFolder;
public VillagerProfessionGenerator(@Nullable InputStream villagerProfessionsFile, @NotNull File outputFolder) {
this.villagerProfessionsFile = villagerProfessionsFile;
this.outputFolder = outputFolder;
}
@Override
public void generate() {
if (villagerProfessionsFile == null) {
LOGGER.error("Failed to find villager_professions.json.");
LOGGER.error("Stopped code generation for villager professions.");
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 rawVillagerProfessionDataClassName = ClassName.get("net.minestom.server.raw_data", "RawVillagerProfessionData");
ClassName registryClassName = ClassName.get("net.minestom.server.registry", "Registry");
JsonArray villagerProfessions = GSON.fromJson(new InputStreamReader(villagerProfessionsFile), JsonArray.class);
ClassName villagerProfessionClassName = ClassName.get("net.minestom.server.entity.metadata.villager", "VillagerProfession");
// Particle
TypeSpec.Builder villagerProfessionClass = TypeSpec.classBuilder(villagerProfessionClassName)
.addSuperinterface(ClassName.get("net.kyori.adventure.key", "Keyed"))
.addModifiers(Modifier.PUBLIC).addJavadoc("AUTOGENERATED by " + getClass().getSimpleName());
villagerProfessionClass.addField(
FieldSpec.builder(namespaceIDClassName, "id")
.addModifiers(Modifier.PRIVATE, Modifier.FINAL).addAnnotation(NotNull.class).build()
);
villagerProfessionClass.addField(
FieldSpec.builder(rawVillagerProfessionDataClassName, "villagerProfessionData")
.addModifiers(Modifier.PRIVATE, Modifier.VOLATILE)
.addAnnotation(NotNull.class)
.build()
);
villagerProfessionClass.addMethod(
MethodSpec.constructorBuilder()
.addParameter(ParameterSpec.builder(namespaceIDClassName, "id").addAnnotation(NotNull.class).build())
.addParameter(ParameterSpec.builder(rawVillagerProfessionDataClassName, "villagerProfessionData").addAnnotation(NotNull.class).build())
.addStatement("this.id = id")
.addStatement("this.villagerProfessionData = villagerProfessionData")
.addModifiers(Modifier.PROTECTED)
.build()
);
// Override key method (adventure)
villagerProfessionClass.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()
);
// getId method
villagerProfessionClass.addMethod(
MethodSpec.methodBuilder("getId")
.returns(namespaceIDClassName)
.addAnnotation(NotNull.class)
.addStatement("return this.id")
.addModifiers(Modifier.PUBLIC)
.build()
);
// getVillagerProfessionData method
villagerProfessionClass.addMethod(
MethodSpec.methodBuilder("getVillagerProfessionData")
.returns(rawVillagerProfessionDataClassName)
.addAnnotation(NotNull.class)
.addStatement("return this.villagerProfessionData")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.build()
);
// setVillagerProfessionData method
villagerProfessionClass.addMethod(
MethodSpec.methodBuilder("setVillagerProfessionData")
.addParameter(ParameterSpec.builder(rawVillagerProfessionDataClassName, "villagerProfessionData").addAnnotation(NotNull.class).build())
.addStatement("this.villagerProfessionData = villagerProfessionData")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.build()
);
// getNumericalId
villagerProfessionClass.addMethod(
MethodSpec.methodBuilder("getNumericalId")
.returns(TypeName.INT)
.addStatement("return $T.VILLAGER_PROFESSION_REGISTRY.getId(this)", registryClassName)
.addModifiers(Modifier.PUBLIC)
.build()
);
// fromId Method
villagerProfessionClass.addMethod(
MethodSpec.methodBuilder("fromId")
.returns(villagerProfessionClassName)
.addAnnotation(Nullable.class)
.addParameter(TypeName.INT, "id")
.addStatement("return $T.VILLAGER_PROFESSION_REGISTRY.get((short) id)", registryClassName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.build()
);
// fromId Method
villagerProfessionClass.addMethod(
MethodSpec.methodBuilder("fromId")
.returns(villagerProfessionClassName)
.addAnnotation(NotNull.class)
.addParameter(ClassName.get("net.kyori.adventure.key", "Key"), "id")
.addStatement("return $T.VILLAGER_PROFESSION_REGISTRY.get(id)", registryClassName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.build()
);
// toString method
villagerProfessionClass.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()
);
// values method
villagerProfessionClass.addMethod(
MethodSpec.methodBuilder("values")
.addAnnotation(NotNull.class)
.returns(ParameterizedTypeName.get(ClassName.get(List.class), villagerProfessionClassName))
.addStatement("return $T.VILLAGER_PROFESSION_REGISTRY.values()", registryClassName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.build()
);
CodeBlock.Builder staticBlock = CodeBlock.builder();
// Use data
for (JsonElement vp : villagerProfessions) {
JsonObject villagerProfession = vp.getAsJsonObject();
String villagerProfessionName = villagerProfession.get("name").getAsString();
JsonElement workSound = villagerProfession.get("workSound");
if (workSound == null) {
villagerProfessionClass.addField(
FieldSpec.builder(
villagerProfessionClassName,
villagerProfessionName
).initializer(
"new $T($T.from($S), new $T(() -> null))",
villagerProfessionClassName,
namespaceIDClassName,
villagerProfession.get("id").getAsString(),
rawVillagerProfessionDataClassName
).addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).build()
);
} else {
villagerProfessionClass.addField(
FieldSpec.builder(
villagerProfessionClassName,
villagerProfessionName
).initializer(
"new $T($T.from($S), new $T(() -> $T.SOUND_EVENT_REGISTRY.get($S)))",
villagerProfessionClassName,
namespaceIDClassName,
villagerProfession.get("id").getAsString(),
rawVillagerProfessionDataClassName,
registryClassName,
workSound.getAsString()
).addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).build()
);
}
// Add to static init.
staticBlock.addStatement("$T.VILLAGER_PROFESSION_REGISTRY.register($N)", registryClassName, villagerProfessionName);
}
villagerProfessionClass.addStaticBlock(staticBlock.build());
// Write files to outputFolder
writeFiles(
List.of(
JavaFile.builder("net.minestom.server.entity.metadata.villager", villagerProfessionClass.build())
.indent(" ")
.skipJavaLangImports(true)
.build()
),
outputFolder
);
}
}

View File

@ -1,164 +0,0 @@
package net.minestom.codegen.entity;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.squareup.javapoet.*;
import net.minestom.codegen.MinestomCodeGenerator;
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.InputStream;
import java.io.InputStreamReader;
import java.util.List;
public final class VillagerTypeGenerator extends MinestomCodeGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(VillagerTypeGenerator.class);
private final InputStream villagerTypesFile;
private final File outputFolder;
public VillagerTypeGenerator(@Nullable InputStream villagerTypesFile, @NotNull File outputFolder) {
this.villagerTypesFile = villagerTypesFile;
this.outputFolder = outputFolder;
}
@Override
public void generate() {
if (villagerTypesFile == null) {
LOGGER.error("Failed to find villager_types.json.");
LOGGER.error("Stopped code generation for villager types.");
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 registryClassName = ClassName.get("net.minestom.server.registry", "Registry");
JsonArray villagerTypes = GSON.fromJson(new InputStreamReader(villagerTypesFile), JsonArray.class);
ClassName villagerTypeClassName = ClassName.get("net.minestom.server.entity.metadata.villager", "VillagerType");
// Particle
TypeSpec.Builder villagerTypeClass = TypeSpec.classBuilder(villagerTypeClassName)
.addSuperinterface(ClassName.get("net.kyori.adventure.key", "Keyed"))
.addModifiers(Modifier.PUBLIC).addJavadoc("AUTOGENERATED by " + getClass().getSimpleName());
villagerTypeClass.addField(
FieldSpec.builder(namespaceIDClassName, "id")
.addModifiers(Modifier.PRIVATE, Modifier.FINAL).addAnnotation(NotNull.class).build()
);
villagerTypeClass.addMethod(
MethodSpec.constructorBuilder()
.addParameter(ParameterSpec.builder(namespaceIDClassName, "id").addAnnotation(NotNull.class).build())
.addStatement("this.id = id")
.addModifiers(Modifier.PROTECTED)
.build()
);
// Override key method (adventure)
villagerTypeClass.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()
);
// getId method
villagerTypeClass.addMethod(
MethodSpec.methodBuilder("getId")
.returns(namespaceIDClassName)
.addAnnotation(NotNull.class)
.addStatement("return this.id")
.addModifiers(Modifier.PUBLIC)
.build()
);
// getNumericalId
villagerTypeClass.addMethod(
MethodSpec.methodBuilder("getNumericalId")
.returns(TypeName.INT)
.addStatement("return $T.VILLAGER_TYPE_REGISTRY.getId(this)", registryClassName)
.addModifiers(Modifier.PUBLIC)
.build()
);
// fromId Method
villagerTypeClass.addMethod(
MethodSpec.methodBuilder("fromId")
.returns(villagerTypeClassName)
.addAnnotation(Nullable.class)
.addParameter(TypeName.INT, "id")
.addStatement("return $T.VILLAGER_TYPE_REGISTRY.get((short) id)", registryClassName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.build()
);
// fromId Method
villagerTypeClass.addMethod(
MethodSpec.methodBuilder("fromId")
.returns(villagerTypeClassName)
.addAnnotation(NotNull.class)
.addParameter(ClassName.get("net.kyori.adventure.key", "Key"), "id")
.addStatement("return $T.VILLAGER_TYPE_REGISTRY.get(id)", registryClassName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.build()
);
// toString method
villagerTypeClass.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()
);
// values method
villagerTypeClass.addMethod(
MethodSpec.methodBuilder("values")
.addAnnotation(NotNull.class)
.returns(ParameterizedTypeName.get(ClassName.get(List.class), villagerTypeClassName))
.addStatement("return $T.VILLAGER_TYPE_REGISTRY.values()", registryClassName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.build()
);
CodeBlock.Builder staticBlock = CodeBlock.builder();
// Use data
for (JsonElement vp : villagerTypes) {
JsonObject villagerProfession = vp.getAsJsonObject();
String villagerProfessionName = villagerProfession.get("name").getAsString();
villagerTypeClass.addField(
FieldSpec.builder(
villagerTypeClassName,
villagerProfessionName
).initializer(
"new $T($T.from($S))",
villagerTypeClassName,
namespaceIDClassName,
villagerProfession.get("id").getAsString()
).addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).build()
);
// Add to static init.
staticBlock.addStatement("$T.VILLAGER_TYPE_REGISTRY.register($N)", registryClassName, villagerProfessionName);
}
villagerTypeClass.addStaticBlock(staticBlock.build());
// Write files to outputFolder
writeFiles(
List.of(
JavaFile.builder("net.minestom.server.entity.metadata.villager", villagerTypeClass.build())
.indent(" ")
.skipJavaLangImports(true)
.build()
),
outputFolder
);
}
}

View File

@ -3,7 +3,7 @@ metadata.format.version = "1.1"
[versions]
# Important dependencies
data = "1.21.3-rv1"
data = "1.21.3-rv2"
adventure = "4.17.0"
jetbrainsAnnotations = "24.1.0"
slf4j = "2.0.7"

View File

@ -0,0 +1,37 @@
package net.minestom.server.entity;
/**
* Code autogenerated, do not edit!
*/
@SuppressWarnings("unused")
interface VillagerProfessions {
VillagerProfession NONE = VillagerProfessionImpl.get("minecraft:none");
VillagerProfession ARMORER = VillagerProfessionImpl.get("minecraft:armorer");
VillagerProfession BUTCHER = VillagerProfessionImpl.get("minecraft:butcher");
VillagerProfession CARTOGRAPHER = VillagerProfessionImpl.get("minecraft:cartographer");
VillagerProfession CLERIC = VillagerProfessionImpl.get("minecraft:cleric");
VillagerProfession FARMER = VillagerProfessionImpl.get("minecraft:farmer");
VillagerProfession FISHERMAN = VillagerProfessionImpl.get("minecraft:fisherman");
VillagerProfession FLETCHER = VillagerProfessionImpl.get("minecraft:fletcher");
VillagerProfession LEATHERWORKER = VillagerProfessionImpl.get("minecraft:leatherworker");
VillagerProfession LIBRARIAN = VillagerProfessionImpl.get("minecraft:librarian");
VillagerProfession MASON = VillagerProfessionImpl.get("minecraft:mason");
VillagerProfession NITWIT = VillagerProfessionImpl.get("minecraft:nitwit");
VillagerProfession SHEPHERD = VillagerProfessionImpl.get("minecraft:shepherd");
VillagerProfession TOOLSMITH = VillagerProfessionImpl.get("minecraft:toolsmith");
VillagerProfession WEAPONSMITH = VillagerProfessionImpl.get("minecraft:weaponsmith");
}

View File

@ -0,0 +1,47 @@
package net.minestom.server.entity;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.registry.StaticProtocolObject;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.NotNull;
/**
* AUTOGENERATED by GenericEnumGenerator
*/
public enum VillagerType implements StaticProtocolObject {
DESERT(NamespaceID.from("minecraft:desert")),
JUNGLE(NamespaceID.from("minecraft:jungle")),
PLAINS(NamespaceID.from("minecraft:plains")),
SAVANNA(NamespaceID.from("minecraft:savanna")),
SNOW(NamespaceID.from("minecraft:snow")),
SWAMP(NamespaceID.from("minecraft:swamp")),
TAIGA(NamespaceID.from("minecraft:taiga"));
public static final NetworkBuffer.Type<VillagerType> NETWORK_TYPE = NetworkBuffer.Enum(VillagerType.class);
public static final BinaryTagSerializer<VillagerType> NBT_TYPE = BinaryTagSerializer.fromEnumKeyed(VillagerType.class);
private final NamespaceID namespace;
VillagerType(@NotNull NamespaceID namespace) {
this.namespace = namespace;
}
@NotNull
@Override
public NamespaceID namespace() {
return this.namespace;
}
@Override
public int id() {
return this.ordinal();
}
}

View File

@ -0,0 +1,34 @@
package net.minestom.server.entity;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.registry.Registry;
import net.minestom.server.registry.StaticProtocolObject;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public sealed interface VillagerProfession extends StaticProtocolObject, VillagerProfessions permits VillagerProfessionImpl {
NetworkBuffer.Type<VillagerProfession> NETWORK_TYPE = NetworkBuffer.VAR_INT.transform(VillagerProfession::fromId, VillagerProfession::id);
BinaryTagSerializer<VillagerProfession> NBT_TYPE = BinaryTagSerializer.STRING.map(VillagerProfessionImpl::getSafe, VillagerProfession::name);
@Contract(pure = true)
@NotNull Registry.VillagerProfessionEntry registry();
@Override
default @NotNull NamespaceID namespace() {
return registry().namespace();
}
@Override
default int id() {
return registry().id();
}
static @Nullable VillagerProfession fromId(int id) {
return VillagerProfessionImpl.getId(id);
}
}

View File

@ -0,0 +1,32 @@
package net.minestom.server.entity;
import net.minestom.server.registry.Registry;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
public record VillagerProfessionImpl(Registry.VillagerProfessionEntry registry) implements VillagerProfession {
private static final Registry.Container<VillagerProfession> CONTAINER = Registry.createStaticContainer(Registry.Resource.VILLAGER_PROFESSIONS,
(namespace, properties) -> new VillagerProfessionImpl(Registry.villagerProfession(namespace, properties)));
static VillagerProfession get(@NotNull String namespace) {
return CONTAINER.get(namespace);
}
static VillagerProfession getSafe(@NotNull String namespace) {
return CONTAINER.getSafe(namespace);
}
static VillagerProfession getId(int id) {
return CONTAINER.getId(id);
}
static Collection<VillagerProfession> values() {
return CONTAINER.values();
}
@Override
public String toString() {
return name();
}
}

View File

@ -3,6 +3,7 @@ package net.minestom.server.entity.metadata.monster.zombie;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.MetadataDef;
import net.minestom.server.entity.MetadataHolder;
import net.minestom.server.entity.*;
import net.minestom.server.entity.metadata.villager.VillagerMeta;
import org.jetbrains.annotations.NotNull;
@ -22,14 +23,14 @@ public class ZombieVillagerMeta extends ZombieMeta {
public VillagerMeta.VillagerData getVillagerData() {
int[] data = metadata.get(MetadataDef.ZombieVillager.VILLAGER_DATA);
if (data == null) {
return new VillagerMeta.VillagerData(VillagerMeta.Type.PLAINS, VillagerMeta.Profession.NONE, VillagerMeta.Level.NOVICE);
return new VillagerMeta.VillagerData(VillagerType.PLAINS, VillagerProfession.NONE, VillagerMeta.Level.NOVICE);
}
return new VillagerMeta.VillagerData(VillagerMeta.Type.VALUES[data[0]], VillagerMeta.Profession.VALUES[data[1]], VillagerMeta.Level.VALUES[data[2] - 1]);
return new VillagerMeta.VillagerData(VillagerType.values()[data[0]], VillagerProfession.fromId(data[1]), VillagerMeta.Level.VALUES[data[2] - 1]);
}
public void setVillagerData(VillagerMeta.VillagerData data) {
int[] value = new int[]{data.getType().ordinal(), data.getProfession().ordinal(), data.getLevel().ordinal() + 1};
metadata.set(MetadataDef.ZombieVillager.VILLAGER_DATA, value);
public void setVillagerData(@NotNull VillagerMeta.VillagerData data) {
int[] value = new int[]{data.villagerType().ordinal(), data.villagerProfession().id(), data.level().ordinal() + 1};
metadata.set(MetadataDef.Villager.VARIANT, value);
}
}

View File

@ -1,8 +1,6 @@
package net.minestom.server.entity.metadata.villager;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.MetadataDef;
import net.minestom.server.entity.MetadataHolder;
import net.minestom.server.entity.*;
import org.jetbrains.annotations.NotNull;
public class VillagerMeta extends AbstractVillagerMeta {
@ -14,88 +12,17 @@ public class VillagerMeta extends AbstractVillagerMeta {
public VillagerData getVillagerData() {
int[] data = metadata.get(MetadataDef.Villager.VARIANT);
if (data == null) {
return new VillagerData(Type.PLAINS, Profession.NONE, Level.NOVICE);
return new VillagerData(VillagerType.PLAINS, VillagerProfession.NONE, Level.NOVICE);
}
return new VillagerData(Type.VALUES[data[0]], Profession.VALUES[data[1]], Level.VALUES[data[2] - 1]);
return new VillagerData(VillagerType.values()[data[0]], VillagerProfession.fromId(data[1]), Level.VALUES[data[2] - 1]);
}
public void setVillagerData(@NotNull VillagerData data) {
int[] value = new int[]{data.type.ordinal(), data.profession.ordinal(), data.level.ordinal() + 1};
int[] value = new int[]{data.villagerType.ordinal(), data.villagerProfession.id(), data.level.ordinal() + 1};
metadata.set(MetadataDef.Villager.VARIANT, value);
}
public static class VillagerData {
private Type type;
private Profession profession;
private Level level;
public VillagerData(@NotNull Type type, @NotNull Profession profession, @NotNull Level level) {
this.type = type;
this.profession = profession;
this.level = level;
}
@NotNull
public Type getType() {
return this.type;
}
public void setType(@NotNull Type type) {
this.type = type;
}
@NotNull
public Profession getProfession() {
return this.profession;
}
public void setProfession(@NotNull Profession profession) {
this.profession = profession;
}
@NotNull
public Level getLevel() {
return level;
}
public void setLevel(@NotNull Level level) {
this.level = level;
}
}
public enum Type {
DESERT,
JUNGLE,
PLAINS,
SAVANNA,
SNOW,
SWAMP,
TAIGA;
public final static Type[] VALUES = values();
}
public enum Profession {
NONE,
ARMORER,
BUTCHER,
CARTOGRAPHER,
CLERIC,
FARMER,
FISHERMAN,
FLETCHER,
LEATHERWORKER,
LIBRARIAN,
NITWIT,
UNEMPLOYED,
MASON,
SHEPHERD,
TOOLSMITH,
WEAPONSMITH;
public final static Profession[] VALUES = values();
}
public record VillagerData(@NotNull VillagerType villagerType, @NotNull VillagerProfession villagerProfession, @NotNull Level level) { }
public enum Level {
NOVICE,

View File

@ -70,6 +70,11 @@ public final class Registry {
return new EntityEntry(namespace, main, null);
}
@ApiStatus.Internal
public static VillagerProfessionEntry villagerProfession(String namespace, @NotNull Properties main) {
return new VillagerProfessionEntry(namespace, main, null);
}
@ApiStatus.Internal
public static FeatureFlagEntry featureFlag(String namespace, @NotNull Properties main) {
return new FeatureFlagEntry(namespace, main, null);
@ -236,7 +241,8 @@ public final class Registry {
CHAT_TYPES("chat_types.json"),
ENCHANTMENTS("enchantments.snbt"),
PAINTING_VARIANTS("painting_variants.json"),
JUKEBOX_SONGS("jukebox_songs.json");
JUKEBOX_SONGS("jukebox_songs.json"),
VILLAGER_PROFESSIONS("villager_professions.json");
private final String name;
@ -648,6 +654,9 @@ public final class Registry {
private final double width;
private final double height;
private final double eyeHeight;
private final int clientTrackingRange;
private final boolean fireImmune;
private final Map<String, List<Double>> entityOffsets;
private final BoundingBox boundingBox;
private final Properties custom;
@ -658,6 +667,8 @@ public final class Registry {
this.drag = main.getDouble("drag", 0.02);
this.acceleration = main.getDouble("acceleration", 0.08);
this.spawnType = EntitySpawnType.valueOf(main.getString("packetType").toUpperCase(Locale.ROOT));
this.fireImmune = main.getBoolean("fireImmune", false);
this.clientTrackingRange = main.getInt("clientTrackingRange");
// Dimensions
this.width = main.getDouble("width");
@ -666,9 +677,14 @@ public final class Registry {
this.boundingBox = new BoundingBox(this.width, this.height, this.width);
// Attachments
this.entityOffsets = new HashMap<>();
Properties attachments = main.section("attachments");
if (attachments != null) {
//todo
var allAttachments = attachments.asMap().keySet();
for (String key : allAttachments) {
var offset = attachments.getNestedDoubleArray(key);
this.entityOffsets.put(key, offset.getFirst()); // It's an array of an array with a single element, as of 1.21.3 we only need to grab a single array of 3 doubles
}
}
this.custom = custom;
@ -710,6 +726,20 @@ public final class Registry {
return eyeHeight;
}
public boolean fireImmune() { return fireImmune; }
public int clientTrackingRange() { return clientTrackingRange; }
/**
*
* Gets the entity attachment by name. Typically, will be PASSENGER or VEHICLE, but some entities have custom attachments (e.g. WARDEN_CHEST, NAMETAG)
* @param attachmentName The attachment to retrieve
* @return A list of 3 doubles if the attachment is defined for this entity, or null if it is not defined
*/
public @Nullable List<Double> entityAttachment(@NotNull String attachmentName) {
return entityOffsets.get(attachmentName);
}
public @NotNull BoundingBox boundingBox() {
return boundingBox;
}
@ -720,6 +750,41 @@ public final class Registry {
}
}
public static final class VillagerProfessionEntry implements Entry {
private final NamespaceID namespace;
private final int id;
private final SoundEvent workSound;
private final Properties custom;
public VillagerProfessionEntry(String namespace, Properties main, Properties custom) {
this.namespace = NamespaceID.from(namespace);
this.id = main.getInt("id");
if (main.containsKey("workSound")) {
this.workSound = SoundEvent.fromNamespaceId(main.getString("workSound"));
} else {
this.workSound = null;
}
this.custom = custom;
}
public @NotNull NamespaceID namespace() {
return namespace;
}
public int id() {
return id;
}
public @Nullable SoundEvent workSound() {
return workSound;
}
@Override
public Properties custom() {
return custom;
}
}
public record FeatureFlagEntry(NamespaceID namespace, int id, Properties custom) implements Entry {
public FeatureFlagEntry(String namespace, Properties main, Properties custom) {
this(NamespaceID.from(namespace),
@ -1007,6 +1072,12 @@ public final class Registry {
return element != null ? (boolean) element : defaultValue;
}
@Override
public List<List<Double>> getNestedDoubleArray(String name) {
var element = element(name);
return element != null ? (List<List<Double>>) element : List.of();
}
@Override
public boolean getBoolean(String name) {
return element(name);
@ -1068,6 +1139,8 @@ public final class Registry {
boolean getBoolean(String name);
List<List<Double>> getNestedDoubleArray(String name);
Properties section(String name);
boolean containsKey(String name);