Make Enchantment an interface

This commit is contained in:
TheMode 2021-07-28 13:27:49 +02:00
parent 8dff4227eb
commit a6a657b9b8
12 changed files with 294 additions and 260 deletions

View File

@ -164,7 +164,7 @@ dependencies {
}
api "com.github.Minestom:DependencyGetter:v1.0.1"
implementation 'com.github.Minestom:MinestomDataGenerator:9f8b6a3748'
implementation 'com.github.Minestom:MinestomDataGenerator:3d2d73e383'
// Adventure, for user-interface
api "net.kyori:adventure-api:$adventureVersion"

View File

@ -15,6 +15,8 @@ import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
public final class EnchantmentGenerator extends MinestomCodeGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(EnchantmentGenerator.class);
@ -37,106 +39,35 @@ public final class EnchantmentGenerator extends MinestomCodeGenerator {
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");
ClassName enchantmentCN = ClassName.get("net.minestom.server.item", "Enchantment");
JsonObject enchantments = GSON.fromJson(new InputStreamReader(enchantmentsFile), JsonObject.class);
ClassName enchantmentClassName = ClassName.get("net.minestom.server.item", "Enchantment");
// Enchantment
TypeSpec.Builder enchantmentClass = TypeSpec.enumBuilder(enchantmentClassName)
.addSuperinterface(ClassName.get("net.kyori.adventure.key", "Keyed"))
.addModifiers(Modifier.PUBLIC).addJavadoc("AUTOGENERATED by " + getClass().getSimpleName());
enchantmentClass.addField(
FieldSpec.builder(namespaceIDClassName, "id")
.addModifiers(Modifier.PRIVATE, Modifier.FINAL).addAnnotation(NotNull.class).build()
);
// static field
enchantmentClass.addField(
FieldSpec.builder(ArrayTypeName.of(enchantmentClassName), "VALUES")
.addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
.initializer("values()")
.build()
);
enchantmentClass.addMethod(
MethodSpec.constructorBuilder()
.addParameter(ParameterSpec.builder(namespaceIDClassName, "id").addAnnotation(NotNull.class).build())
.addStatement("this.id = id")
.addStatement("$T.enchantments.put(id, this)", registriesClassName)
.build()
);
// Override key method (adventure)
enchantmentClass.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
enchantmentClass.addMethod(
MethodSpec.methodBuilder("getId")
.returns(TypeName.SHORT)
.addStatement("return (short) ordinal()")
.addModifiers(Modifier.PUBLIC)
.build()
);
// getNamespaceID method
enchantmentClass.addMethod(
MethodSpec.methodBuilder("getNamespaceID")
.returns(namespaceIDClassName)
.addAnnotation(NotNull.class)
.addStatement("return this.id")
.addModifiers(Modifier.PUBLIC)
.build()
);
// fromId Method
enchantmentClass.addMethod(
MethodSpec.methodBuilder("fromId")
.returns(enchantmentClassName)
.addAnnotation(Nullable.class)
.addParameter(TypeName.SHORT, "id")
.beginControlFlow("if(id >= 0 && id < VALUES.length)")
.addStatement("return VALUES[id]")
.endControlFlow()
.addStatement("return null")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.build()
);
// toString method
enchantmentClass.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()
);
JsonObject items;
items = GSON.fromJson(new InputStreamReader(enchantmentsFile), JsonObject.class);
ClassName materialsCN = ClassName.get("net.minestom.server.item", "EnchantmentConstants");
// BlockConstants class
TypeSpec.Builder blockConstantsClass = TypeSpec.interfaceBuilder(materialsCN)
// Add @SuppressWarnings("unused")
.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "$S", "unused").build())
.addJavadoc("AUTOGENERATED by " + getClass().getSimpleName());
// Use data
enchantments.entrySet().forEach(entry -> {
final String enchantmentNamespace = entry.getKey();
final String enchantmentConstant = toConstant(enchantmentNamespace);
enchantmentClass.addEnumConstant(enchantmentConstant, TypeSpec.anonymousClassBuilder(
"$T.from($S)",
namespaceIDClassName,
enchantmentNamespace
).build()
items.keySet().forEach(namespace -> {
final String constantName = namespace.replace("minecraft:", "").toUpperCase(Locale.ROOT);
blockConstantsClass.addField(
FieldSpec.builder(enchantmentCN, constantName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.initializer(
// Material.STONE = Material.fromNamespaceId("minecraft:stone")
"$T.fromNamespaceId($S)",
enchantmentCN,
namespace
)
.build()
);
});
// Write files to outputFolder
writeFiles(
Collections.singletonList(
JavaFile.builder("net.minestom.server.item", enchantmentClass.build())
List.of(
JavaFile.builder("net.minestom.server.item", blockConstantsClass.build())
.indent(" ")
.skipJavaLangImports(true)
.build()

View File

@ -1,128 +0,0 @@
package net.minestom.server.item;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.minestom.server.registry.Registries;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* AUTOGENERATED by EnchantmentGenerator
*/
public enum Enchantment implements Keyed {
PROTECTION(NamespaceID.from("minecraft:protection")),
FIRE_PROTECTION(NamespaceID.from("minecraft:fire_protection")),
FEATHER_FALLING(NamespaceID.from("minecraft:feather_falling")),
BLAST_PROTECTION(NamespaceID.from("minecraft:blast_protection")),
PROJECTILE_PROTECTION(NamespaceID.from("minecraft:projectile_protection")),
RESPIRATION(NamespaceID.from("minecraft:respiration")),
AQUA_AFFINITY(NamespaceID.from("minecraft:aqua_affinity")),
THORNS(NamespaceID.from("minecraft:thorns")),
DEPTH_STRIDER(NamespaceID.from("minecraft:depth_strider")),
FROST_WALKER(NamespaceID.from("minecraft:frost_walker")),
BINDING_CURSE(NamespaceID.from("minecraft:binding_curse")),
SOUL_SPEED(NamespaceID.from("minecraft:soul_speed")),
SHARPNESS(NamespaceID.from("minecraft:sharpness")),
SMITE(NamespaceID.from("minecraft:smite")),
BANE_OF_ARTHROPODS(NamespaceID.from("minecraft:bane_of_arthropods")),
KNOCKBACK(NamespaceID.from("minecraft:knockback")),
FIRE_ASPECT(NamespaceID.from("minecraft:fire_aspect")),
LOOTING(NamespaceID.from("minecraft:looting")),
SWEEPING(NamespaceID.from("minecraft:sweeping")),
EFFICIENCY(NamespaceID.from("minecraft:efficiency")),
SILK_TOUCH(NamespaceID.from("minecraft:silk_touch")),
UNBREAKING(NamespaceID.from("minecraft:unbreaking")),
FORTUNE(NamespaceID.from("minecraft:fortune")),
POWER(NamespaceID.from("minecraft:power")),
PUNCH(NamespaceID.from("minecraft:punch")),
FLAME(NamespaceID.from("minecraft:flame")),
INFINITY(NamespaceID.from("minecraft:infinity")),
LUCK_OF_THE_SEA(NamespaceID.from("minecraft:luck_of_the_sea")),
LURE(NamespaceID.from("minecraft:lure")),
LOYALTY(NamespaceID.from("minecraft:loyalty")),
IMPALING(NamespaceID.from("minecraft:impaling")),
RIPTIDE(NamespaceID.from("minecraft:riptide")),
CHANNELING(NamespaceID.from("minecraft:channeling")),
MULTISHOT(NamespaceID.from("minecraft:multishot")),
QUICK_CHARGE(NamespaceID.from("minecraft:quick_charge")),
PIERCING(NamespaceID.from("minecraft:piercing")),
MENDING(NamespaceID.from("minecraft:mending")),
VANISHING_CURSE(NamespaceID.from("minecraft:vanishing_curse"));
private static final Enchantment[] VALUES = values();
@NotNull
private final NamespaceID id;
Enchantment(@NotNull NamespaceID id) {
this.id = id;
Registries.enchantments.put(id, this);
}
@Override
@NotNull
public Key key() {
return this.id;
}
public short getId() {
return (short) ordinal();
}
@NotNull
public NamespaceID getNamespaceID() {
return this.id;
}
@Nullable
public static Enchantment fromId(short id) {
if(id >= 0 && id < VALUES.length) {
return VALUES[id];
}
return null;
}
@NotNull
@Override
public String toString() {
return "[" + this.id + "]";
}
}

View File

@ -0,0 +1,83 @@
package net.minestom.server.item;
/**
* AUTOGENERATED by EnchantmentGenerator
*/
@SuppressWarnings("unused")
interface EnchantmentConstants {
Enchantment PROTECTION = Enchantment.fromNamespaceId("minecraft:protection");
Enchantment FIRE_PROTECTION = Enchantment.fromNamespaceId("minecraft:fire_protection");
Enchantment FEATHER_FALLING = Enchantment.fromNamespaceId("minecraft:feather_falling");
Enchantment BLAST_PROTECTION = Enchantment.fromNamespaceId("minecraft:blast_protection");
Enchantment PROJECTILE_PROTECTION = Enchantment.fromNamespaceId("minecraft:projectile_protection");
Enchantment RESPIRATION = Enchantment.fromNamespaceId("minecraft:respiration");
Enchantment AQUA_AFFINITY = Enchantment.fromNamespaceId("minecraft:aqua_affinity");
Enchantment THORNS = Enchantment.fromNamespaceId("minecraft:thorns");
Enchantment DEPTH_STRIDER = Enchantment.fromNamespaceId("minecraft:depth_strider");
Enchantment FROST_WALKER = Enchantment.fromNamespaceId("minecraft:frost_walker");
Enchantment BINDING_CURSE = Enchantment.fromNamespaceId("minecraft:binding_curse");
Enchantment SOUL_SPEED = Enchantment.fromNamespaceId("minecraft:soul_speed");
Enchantment SHARPNESS = Enchantment.fromNamespaceId("minecraft:sharpness");
Enchantment SMITE = Enchantment.fromNamespaceId("minecraft:smite");
Enchantment BANE_OF_ARTHROPODS = Enchantment.fromNamespaceId("minecraft:bane_of_arthropods");
Enchantment KNOCKBACK = Enchantment.fromNamespaceId("minecraft:knockback");
Enchantment FIRE_ASPECT = Enchantment.fromNamespaceId("minecraft:fire_aspect");
Enchantment LOOTING = Enchantment.fromNamespaceId("minecraft:looting");
Enchantment SWEEPING = Enchantment.fromNamespaceId("minecraft:sweeping");
Enchantment EFFICIENCY = Enchantment.fromNamespaceId("minecraft:efficiency");
Enchantment SILK_TOUCH = Enchantment.fromNamespaceId("minecraft:silk_touch");
Enchantment UNBREAKING = Enchantment.fromNamespaceId("minecraft:unbreaking");
Enchantment FORTUNE = Enchantment.fromNamespaceId("minecraft:fortune");
Enchantment POWER = Enchantment.fromNamespaceId("minecraft:power");
Enchantment PUNCH = Enchantment.fromNamespaceId("minecraft:punch");
Enchantment FLAME = Enchantment.fromNamespaceId("minecraft:flame");
Enchantment INFINITY = Enchantment.fromNamespaceId("minecraft:infinity");
Enchantment LUCK_OF_THE_SEA = Enchantment.fromNamespaceId("minecraft:luck_of_the_sea");
Enchantment LURE = Enchantment.fromNamespaceId("minecraft:lure");
Enchantment LOYALTY = Enchantment.fromNamespaceId("minecraft:loyalty");
Enchantment IMPALING = Enchantment.fromNamespaceId("minecraft:impaling");
Enchantment RIPTIDE = Enchantment.fromNamespaceId("minecraft:riptide");
Enchantment CHANNELING = Enchantment.fromNamespaceId("minecraft:channeling");
Enchantment MULTISHOT = Enchantment.fromNamespaceId("minecraft:multishot");
Enchantment QUICK_CHARGE = Enchantment.fromNamespaceId("minecraft:quick_charge");
Enchantment PIERCING = Enchantment.fromNamespaceId("minecraft:piercing");
Enchantment MENDING = Enchantment.fromNamespaceId("minecraft:mending");
Enchantment VANISHING_CURSE = Enchantment.fromNamespaceId("minecraft:vanishing_curse");
}

View File

@ -3,7 +3,6 @@ package net.minestom.server.registry;
import net.kyori.adventure.key.Key;
import net.minestom.server.fluid.Fluid;
import net.minestom.server.item.Enchantment;
import net.minestom.server.particle.Particle;
import net.minestom.server.potion.PotionEffect;
import net.minestom.server.potion.PotionType;
@ -20,12 +19,6 @@ import java.util.HashMap;
*/
public final class Registries {
/**
* Should only be used for internal code, please use the get* methods.
*/
@Deprecated
public static final HashMap<NamespaceID, Enchantment> enchantments = new HashMap<>();
/**
* Should only be used for internal code, please use the get* methods.
*/
@ -62,30 +55,6 @@ public final class Registries {
@Deprecated
public static final HashMap<NamespaceID, Fluid> fluids = new HashMap<>();
/**
* Returns the corresponding Enchantment matching the given id. Returns null if none match.
*/
@Nullable
public static Enchantment getEnchantment(String id) {
return getEnchantment(NamespaceID.from(id));
}
/**
* Returns the corresponding Enchantment matching the given id. Returns null if none match.
*/
@Nullable
public static Enchantment getEnchantment(NamespaceID id) {
return enchantments.get(id);
}
/**
* Returns the corresponding Enchantment matching the given key. Returns null if none match.
*/
@Nullable
public static Enchantment getEnchantment(Key key) {
return getEnchantment(NamespaceID.from(key));
}
/**
* Returns the corresponding Particle matching the given id. Returns null if none match.
*/

View File

@ -3,7 +3,6 @@ package net.minestom.server.command.builder.arguments.minecraft.registry;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.item.Enchantment;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.registry.Registries;
import org.jetbrains.annotations.NotNull;
/**
@ -17,7 +16,7 @@ public class ArgumentEnchantment extends ArgumentRegistry<Enchantment> {
@Override
public Enchantment getRegistry(@NotNull String value) {
return Registries.getEnchantment(value);
return Enchantment.fromNamespaceId(value);
}
@Override

View File

@ -94,7 +94,7 @@ public class EnchantmentTableInventory extends Inventory {
* @param enchantment the enchantment
*/
public void setEnchantmentShown(EnchantmentSlot enchantmentSlot, Enchantment enchantment) {
final short id = enchantment == null ? -1 : (short) enchantment.getId();
final short id = enchantment == null ? -1 : (short) enchantment.id();
switch (enchantmentSlot) {
case TOP:
sendProperty(InventoryProperty.ENCHANTMENT_TABLE_ENCH_ID_TOP, id);

View File

@ -0,0 +1,49 @@
package net.minestom.server.item;
import net.minestom.server.registry.ProtocolObject;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
@ApiStatus.NonExtendable
public interface Enchantment extends ProtocolObject {
/**
* Returns the enchantment registry.
*
* @return the enchantment registry
*/
@Contract(pure = true)
@NotNull Registry.EnchantmentEntry registry();
@Override
default @NotNull NamespaceID namespace() {
return registry().namespace();
}
@Override
default int id() {
return registry().id();
}
static @NotNull Collection<@NotNull Enchantment> values() {
return EnchantmentLoader.values();
}
static Enchantment fromNamespaceId(@NotNull String namespaceID) {
return EnchantmentLoader.get(namespaceID);
}
static Enchantment fromNamespaceId(@NotNull NamespaceID namespaceID) {
return fromNamespaceId(namespaceID.asString());
}
static @Nullable Enchantment fromId(int id) {
return EnchantmentLoader.getId(id);
}
}

View File

@ -0,0 +1,17 @@
package net.minestom.server.item;
import net.minestom.server.registry.Registry;
import org.jetbrains.annotations.NotNull;
final class EnchantmentImpl implements Enchantment {
private final Registry.EnchantmentEntry registry;
EnchantmentImpl(Registry.EnchantmentEntry registry) {
this.registry = registry;
}
@Override
public @NotNull Registry.EnchantmentEntry registry() {
return registry;
}
}

View File

@ -0,0 +1,55 @@
package net.minestom.server.item;
import com.google.gson.JsonObject;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minestom.server.registry.Registry;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ApiStatus.Internal
final class EnchantmentLoader {
// Maps do not need to be thread-safe as they are fully populated
// in the static initializer, should not be modified during runtime
// Block namespace -> registry data
private static final Map<String, Enchantment> NAMESPACE_MAP = new HashMap<>();
// Block id -> registry data
private static final Int2ObjectMap<Enchantment> ID_MAP = new Int2ObjectOpenHashMap<>();
static @Nullable Enchantment get(@NotNull String namespace) {
if (namespace.indexOf(':') == -1) {
// Default to minecraft namespace
namespace = "minecraft:" + namespace;
}
return NAMESPACE_MAP.get(namespace);
}
static Enchantment getId(int id) {
return ID_MAP.get(id);
}
static Collection<Enchantment> values() {
return Collections.unmodifiableCollection(NAMESPACE_MAP.values());
}
static {
// Load data from file
JsonObject enchantments = Registry.load(Registry.Resource.ENCHANTMENTS);
enchantments.entrySet().forEach(entry -> {
final String namespace = entry.getKey();
final JsonObject enchantmentObject = entry.getValue().getAsJsonObject();
final var enchantment = new EnchantmentImpl(Registry.enchantment(namespace, enchantmentObject, null));
ID_MAP.put(enchantment.id(), enchantment);
NAMESPACE_MAP.put(namespace, enchantment);
});
}
}

View File

@ -33,6 +33,10 @@ public class Registry {
return new EntityEntry(namespace, jsonObject, override);
}
public static EnchantmentEntry enchantment(String namespace, @NotNull JsonObject jsonObject, JsonObject override) {
return new EnchantmentEntry(namespace, jsonObject, override);
}
public static JsonObject load(Resource resource) {
final String path = String.format("/%s.json", resource.name);
final var resourceStream = Registry.class.getResourceAsStream(path);
@ -42,7 +46,8 @@ public class Registry {
public enum Resource {
BLOCKS("blocks"),
ITEMS("items"),
ENTITIES("entities");
ENTITIES("entities"),
ENCHANTMENTS("enchantments");
private final String name;
@ -273,6 +278,61 @@ public class Registry {
}
}
public static class EnchantmentEntry extends Entry {
private final NamespaceID namespace;
private final int id;
private final String translationKey;
private final double maxLevel;
private final boolean isCursed;
private final boolean isDiscoverable;
private final boolean isTradeable;
private final boolean isTreasureOnly;
private EnchantmentEntry(String namespace, JsonObject main, JsonObject override) {
super(main, override);
this.namespace = NamespaceID.from(namespace);
this.id = getInt("id");
this.translationKey = getString("translationKey");
this.maxLevel = getDouble("maxLevel");
this.isCursed = getBoolean("curse", false);
this.isDiscoverable = getBoolean("discoverable", true);
this.isTradeable = getBoolean("tradeable", true);
this.isTreasureOnly = getBoolean("treasureOnly", false);
}
public @NotNull NamespaceID namespace() {
return namespace;
}
public int id() {
return id;
}
public String translationKey() {
return translationKey;
}
public double maxLevel() {
return maxLevel;
}
public boolean isCursed() {
return isCursed;
}
public boolean isDiscoverable() {
return isDiscoverable;
}
public boolean isTradeable() {
return isTradeable;
}
public boolean isTreasureOnly() {
return isTreasureOnly;
}
}
public static class Entry {
private final JsonObject main, override;

View File

@ -16,7 +16,6 @@ import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.item.attribute.AttributeSlot;
import net.minestom.server.item.attribute.ItemAttribute;
import net.minestom.server.registry.Registries;
import net.minestom.server.utils.binary.BinaryReader;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@ -254,7 +253,7 @@ public final class NBTUtils {
for (NBTCompound enchantment : enchantments) {
final short level = enchantment.getAsShort("lvl");
final String id = enchantment.getString("id");
final Enchantment enchant = Registries.getEnchantment(id);
final Enchantment enchant = Enchantment.fromNamespaceId(id);
if (enchant != null) {
setter.applyEnchantment(enchant, level);
} else {