diff --git a/src/main/java/org/mockbukkit/metaminer/MetaMiner.java b/src/main/java/org/mockbukkit/metaminer/MetaMiner.java index 82b42bb..05fb6df 100644 --- a/src/main/java/org/mockbukkit/metaminer/MetaMiner.java +++ b/src/main/java/org/mockbukkit/metaminer/MetaMiner.java @@ -4,6 +4,7 @@ import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.plugin.java.JavaPlugin; +import org.mockbukkit.metaminer.internal.entity.EntityTypeGenerator; import org.mockbukkit.metaminer.internal.MaterialDataGenerator; import org.mockbukkit.metaminer.internal.potion.PotionDataGenerator; import org.mockbukkit.metaminer.internal.tags.InternalTagDataGenerator; @@ -55,7 +56,7 @@ private List getDataGenerators() return List.of(new KeyedDataGenerator(this.getDataFolder()), new InternalTagDataGenerator(this.getDataFolder()), new PotionDataGenerator(this.getDataFolder()), new TagDataGenerator(this.getDataFolder()), new MaterialDataGenerator(this.getDataFolder()), new RegistryKeyClassDataGenerator(this.getDataFolder()), - new TranslationDataGenerator(this.getDataFolder())); + new TranslationDataGenerator(this.getDataFolder()), new EntityTypeGenerator(this.getDataFolder())); } } diff --git a/src/main/java/org/mockbukkit/metaminer/internal/entity/EntityInformationExtractor.java b/src/main/java/org/mockbukkit/metaminer/internal/entity/EntityInformationExtractor.java new file mode 100644 index 0000000..7c9d157 --- /dev/null +++ b/src/main/java/org/mockbukkit/metaminer/internal/entity/EntityInformationExtractor.java @@ -0,0 +1,11 @@ +package org.mockbukkit.metaminer.internal.entity; + +import com.google.gson.JsonObject; +import org.bukkit.entity.EntityType; + +public interface EntityInformationExtractor +{ + + JsonObject extractInformation(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType); + +} diff --git a/src/main/java/org/mockbukkit/metaminer/internal/entity/EntityKeys.java b/src/main/java/org/mockbukkit/metaminer/internal/entity/EntityKeys.java new file mode 100644 index 0000000..5f4e8cb --- /dev/null +++ b/src/main/java/org/mockbukkit/metaminer/internal/entity/EntityKeys.java @@ -0,0 +1,16 @@ +package org.mockbukkit.metaminer.internal.entity; + +public class EntityKeys +{ + public static final String WIDTH = "width"; + public static final String HEIGHT = "height"; + public static final String SCALE = "scale"; + public static final String EYE_HEIGHT = "eyeHeight"; + public static final String STATES = "states"; + public static final String BASE_DAMAGE = "baseDamage"; + + private EntityKeys() + { + // Hide the public constructor + } +} diff --git a/src/main/java/org/mockbukkit/metaminer/internal/entity/EntitySubType.java b/src/main/java/org/mockbukkit/metaminer/internal/entity/EntitySubType.java new file mode 100644 index 0000000..dc673a3 --- /dev/null +++ b/src/main/java/org/mockbukkit/metaminer/internal/entity/EntitySubType.java @@ -0,0 +1,48 @@ +package org.mockbukkit.metaminer.internal.entity; + +/** + * A type of a entity, it still has the same + * {@link org.bukkit.entity.EntityType}. + */ +public enum EntitySubType +{ + /** + * The default subtype (adult or ageless) + */ + DEFAULT("default"), + + /** + * Baby + */ + BABY("baby"), + + /** + * Big slime/magma cube + */ + BIG("big"), + + /** + * Medium slime/magma cube + */ + MEDIUM("medium"), + + /** + * Small slime/magma cube + */ + SMALL("small"); + + private final String subTypeName; + + EntitySubType(String subTypeName) + { + this.subTypeName = subTypeName; + } + + /** + * @return The key this property is assigned to + */ + public String getName() + { + return this.subTypeName; + } +} diff --git a/src/main/java/org/mockbukkit/metaminer/internal/entity/EntityTypeGenerator.java b/src/main/java/org/mockbukkit/metaminer/internal/entity/EntityTypeGenerator.java new file mode 100644 index 0000000..5ad5613 --- /dev/null +++ b/src/main/java/org/mockbukkit/metaminer/internal/entity/EntityTypeGenerator.java @@ -0,0 +1,191 @@ +package org.mockbukkit.metaminer.internal.entity; + +import com.google.common.base.Preconditions; +import com.google.gson.JsonObject; +import net.kyori.adventure.key.Key; +import net.minecraft.world.entity.Entity; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Ageable; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Frog; +import org.bukkit.entity.Parrot; +import org.bukkit.entity.PiglinBrute; +import org.bukkit.entity.Slime; +import org.bukkit.entity.WanderingTrader; +import org.mockbukkit.metaminer.DataGenerator; +import org.mockbukkit.metaminer.internal.entity.extractor.BabyEntityInformationExtractor; +import org.mockbukkit.metaminer.internal.entity.extractor.BigEntityInformationExtractor; +import org.mockbukkit.metaminer.internal.entity.extractor.DefaultEntityInformationExtractor; +import org.mockbukkit.metaminer.internal.entity.extractor.MediumEntityInformationExtractor; +import org.mockbukkit.metaminer.internal.entity.extractor.SmallEntityInformationExtractor; +import org.mockbukkit.metaminer.util.JsonUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Collectors; + +public class EntityTypeGenerator implements DataGenerator +{ + private static final Logger LOGGER = LoggerFactory.getLogger(EntityTypeGenerator.class); + + private final File workDirectory; + + public EntityTypeGenerator(File workDirectory) + { + Preconditions.checkNotNull(workDirectory, "The work directory can't be null!"); + this.workDirectory = workDirectory; + } + + @Override + public void generateData() throws IOException + { + File entitiesDirectory = new File(workDirectory, "entities"); + Map> entities = getEntities(); + + int entityCounter = 0; + int entityTotal = entities.size(); + + for (Map.Entry> entry : entities.entrySet()) { + + String name = entry.getKey(); + net.minecraft.world.entity.EntityType mojangEntityType = entry.getValue(); + if (mojangEntityType == null) + { + throw new IllegalArgumentException(String.format("Entity with name %s was not found in minecraft entity. Possible values are: %s", + name, entities.keySet())); + } + + EntityType entityType = EntityType.fromName(name); + if (entityType == null) + { + throw new IllegalArgumentException(String.format("Entity with name %s was not found in bukkit entity. Possible values are: %s", + name, Set.of(EntityType.values()).stream() + .map(EntityType::getKey) + .map(NamespacedKey::getKey) + .collect(Collectors.toSet()))); + } + + if (LOGGER.isInfoEnabled()) + { + LOGGER.info("[{}/{}] Working on entity {}...", String.format("%3d", ++entityCounter), entityTotal, name); + } + + JsonObject root = extractInformation(entityType, mojangEntityType); + + String fileName = String.format("%s.json", name); + File file = new File(entitiesDirectory, fileName); + JsonUtil.dump(root, file); + } + } + + private JsonObject extractInformation(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType) + { + JsonObject root = new JsonObject(); + + Set possibleStates = getPossibleStatesForEntity(entityType, mojangEntityType); + for (EntitySubType state : possibleStates) { + switch (state) + { + case DEFAULT -> root.add(EntitySubType.DEFAULT.getName(), DefaultEntityInformationExtractor.process(entityType, mojangEntityType)); + case BABY -> root.add(EntitySubType.BABY.getName(), BabyEntityInformationExtractor.process(entityType, mojangEntityType)); + case SMALL -> root.add(EntitySubType.SMALL.getName(), SmallEntityInformationExtractor.process(entityType, mojangEntityType)); + case MEDIUM -> root.add(EntitySubType.MEDIUM.getName(), MediumEntityInformationExtractor.process(entityType, mojangEntityType)); + case BIG -> root.add(EntitySubType.BIG.getName(), BigEntityInformationExtractor.process(entityType, mojangEntityType)); + default -> throw new UnsupportedOperationException("Unknown entity state: " + state); + } + } + + return root; + } + + private static Set getPossibleStatesForEntity(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType) + { + Class entityClass = entityType.getEntityClass(); + if (entityClass == null) + { + throw new IllegalArgumentException(String.format("Entity type %s does not have a entity class", entityType.name())); + } + + Set possibleStates = new HashSet<>(); + + // Add the default + possibleStates.add(EntitySubType.DEFAULT); + + // Add the baby state + if (Ageable.class.isAssignableFrom(entityClass)) + { + Set> ignoredClasses = Set.of( + // Frog is a special mob, unlike all other mobs with baby variants, the tadpole is treated + // by the game as a completely different mob from the frog. + Frog.class, + // Parrot's don't breed and don't have baby variant + Parrot.class, + // Piglin Brute don't breed and don't have baby variant + PiglinBrute.class, + // Wandering Trader don't breed and don't have baby variant + WanderingTrader.class + ); + if (ignoredClasses.stream().noneMatch(c -> c.isAssignableFrom(entityClass))) { + possibleStates.add(EntitySubType.BABY); + } + } + + // Add the armor stand + if (ArmorStand.class.isAssignableFrom(entityClass)) + { + possibleStates.add(EntitySubType.SMALL); + } + + // Add the Slime/Magma cube + if (Slime.class.isAssignableFrom(entityClass)) + { + possibleStates.add(EntitySubType.BIG); + possibleStates.add(EntitySubType.MEDIUM); + possibleStates.add(EntitySubType.SMALL); + + possibleStates.remove(EntitySubType.DEFAULT); + } + + return possibleStates; + } + + public static Map> getEntities() { + + Map> entityTypes = new TreeMap<>(); + + for (EntityType entityType : EntityType.values()) { + + if (EntityType.UNKNOWN.equals(entityType)) + { + // Unknown entity should be ignored + continue; + } + + NamespacedKey key = entityType.getKey(); + if (!Key.MINECRAFT_NAMESPACE.equalsIgnoreCase(key.getNamespace())) + { + throw new IllegalArgumentException(String.format("Entity type %s has namespace %s while the expected value would be %s", + key.getNamespace(), key, Key.MINECRAFT_NAMESPACE)); + } + + String entityName =key.getKey(); + Optional> minecraftEntityType = net.minecraft.world.entity.EntityType.byString(entityName); + if (minecraftEntityType.isEmpty()) + { + throw new IllegalArgumentException(String.format("Entity %s was not found in minecraft registry.", entityName)); + } + entityTypes.put(entityName, minecraftEntityType.get()); + } + + return entityTypes; + } + +} diff --git a/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/BabyEntityInformationExtractor.java b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/BabyEntityInformationExtractor.java new file mode 100644 index 0000000..c13f269 --- /dev/null +++ b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/BabyEntityInformationExtractor.java @@ -0,0 +1,35 @@ +package org.mockbukkit.metaminer.internal.entity.extractor; + +import com.google.gson.JsonObject; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.animal.armadillo.Armadillo; +import net.minecraft.world.entity.animal.camel.Camel; +import org.bukkit.entity.EntityType; + +public class BabyEntityInformationExtractor +{ + + public static JsonObject process(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType) + { + double scale = getBabyScaleAsDouble(entityType, mojangEntityType); + return ScaleEntityInformationExtractor.process(entityType, mojangEntityType, scale); + } + + private static double getBabyScaleAsDouble(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType) + { + return switch (entityType) + { + case ARMADILLO -> Armadillo.BABY_SCALE; + case TURTLE -> 0.30D; // Constant is not public + case CAMEL -> Camel.BABY_SCALE; + default -> LivingEntity.DEFAULT_BABY_SCALE; + }; + } + + private BabyEntityInformationExtractor() + { + // Hide the public constructor + } + +} diff --git a/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/BaseDamageInformationExtractor.java b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/BaseDamageInformationExtractor.java new file mode 100644 index 0000000..3b1ab82 --- /dev/null +++ b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/BaseDamageInformationExtractor.java @@ -0,0 +1,27 @@ +package org.mockbukkit.metaminer.internal.entity.extractor; + +import org.bukkit.entity.EntityType; + +public class BaseDamageInformationExtractor +{ + private BaseDamageInformationExtractor() + { + // Hide public constructor + } + + public static double process(EntityType entityType) + { + if (EntityType.ARROW.equals(entityType) || EntityType.SPECTRAL_ARROW.equals(entityType)) + { + return 2.0D; + } + if (EntityType.TRIDENT.equals(entityType)) + { + return 8.0D; + } + + String className = (entityType.getEntityClass() != null ? entityType.getEntityClass().getName() : "null"); + throw new UnsupportedOperationException(String.format("Unknown base damage for entity %s (%s)", entityType.name(), className)); + } + +} diff --git a/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/BigEntityInformationExtractor.java b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/BigEntityInformationExtractor.java new file mode 100644 index 0000000..43d6143 --- /dev/null +++ b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/BigEntityInformationExtractor.java @@ -0,0 +1,19 @@ +package org.mockbukkit.metaminer.internal.entity.extractor; + +import com.google.gson.JsonObject; +import org.bukkit.entity.EntityType; + +public class BigEntityInformationExtractor +{ + + public static JsonObject process(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType) + { + return ScaleEntityInformationExtractor.process(entityType, mojangEntityType, 4); + } + + private BigEntityInformationExtractor() + { + // Hide the public constructor + } + +} diff --git a/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/DefaultEntityInformationExtractor.java b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/DefaultEntityInformationExtractor.java new file mode 100644 index 0000000..76f56cb --- /dev/null +++ b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/DefaultEntityInformationExtractor.java @@ -0,0 +1,48 @@ +package org.mockbukkit.metaminer.internal.entity.extractor; + +import com.google.common.base.Preconditions; +import com.google.gson.JsonObject; +import org.bukkit.entity.AbstractArrow; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.mockbukkit.metaminer.internal.entity.EntityInformationExtractor; +import org.mockbukkit.metaminer.internal.entity.EntityKeys; + +public class DefaultEntityInformationExtractor implements EntityInformationExtractor +{ + private static final DefaultEntityInformationExtractor INSTANCE = new DefaultEntityInformationExtractor(); + + public static JsonObject process(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType) + { + return INSTANCE.extractInformation(entityType, mojangEntityType); + } + + private DefaultEntityInformationExtractor() + { + // Hide the public constructor + } + + @Override + public JsonObject extractInformation(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType) + { + double scale = 1; + + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty(EntityKeys.WIDTH, mojangEntityType.getWidth()); + jsonObject.addProperty(EntityKeys.HEIGHT, mojangEntityType.getHeight()); + jsonObject.addProperty(EntityKeys.SCALE, scale); + jsonObject.addProperty(EntityKeys.EYE_HEIGHT, mojangEntityType.getDimensions().eyeHeight()); + + StatesEntityInformationExtractor.process(entityType, mojangEntityType, scale).ifPresent(object -> jsonObject.add(EntityKeys.STATES, object)); + + // Process the class + Class entityClass = entityType.getEntityClass(); + Preconditions.checkState(entityClass != null, "The entity {} does not have a entity class.", entityType.name()); + if (AbstractArrow.class.isAssignableFrom(entityClass)) { + jsonObject.addProperty(EntityKeys.BASE_DAMAGE, BaseDamageInformationExtractor.process(entityType)); + } + + return jsonObject; + } + +} diff --git a/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/HeightDifferenceEntityInformationExtractor.java b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/HeightDifferenceEntityInformationExtractor.java new file mode 100644 index 0000000..3c9dfd3 --- /dev/null +++ b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/HeightDifferenceEntityInformationExtractor.java @@ -0,0 +1,41 @@ +package org.mockbukkit.metaminer.internal.entity.extractor; + +import com.google.gson.JsonObject; +import org.bukkit.entity.EntityType; +import org.mockbukkit.metaminer.internal.entity.EntityInformationExtractor; +import org.mockbukkit.metaminer.internal.entity.EntityKeys; +import org.mockbukkit.metaminer.util.NumberUtils; + +import java.math.BigDecimal; + +public class HeightDifferenceEntityInformationExtractor implements EntityInformationExtractor +{ + public static JsonObject process(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType, double difference, BigDecimal scale) + { + return new HeightDifferenceEntityInformationExtractor(difference, scale).extractInformation(entityType, mojangEntityType); + } + + private final BigDecimal difference; + private final BigDecimal scale; + + private HeightDifferenceEntityInformationExtractor(double difference, BigDecimal scale) + { + this.difference = BigDecimal.valueOf(difference); + this.scale = scale; + } + + @Override + public JsonObject extractInformation(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType) + { + BigDecimal width = BigDecimal.valueOf(mojangEntityType.getWidth()).multiply(scale); + BigDecimal height = BigDecimal.valueOf(mojangEntityType.getHeight()).add(difference).multiply(scale); + BigDecimal eyeHeight = BigDecimal.valueOf(mojangEntityType.getDimensions().eyeHeight()).add(difference).multiply(scale); + + JsonObject newHeightData = new JsonObject(); + newHeightData.addProperty(EntityKeys.WIDTH, NumberUtils.toDouble(width)); + newHeightData.addProperty(EntityKeys.HEIGHT, NumberUtils.toDouble(height)); + newHeightData.addProperty(EntityKeys.EYE_HEIGHT, NumberUtils.toDouble(eyeHeight)); + return newHeightData; + } + +} diff --git a/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/MediumEntityInformationExtractor.java b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/MediumEntityInformationExtractor.java new file mode 100644 index 0000000..af1f6d6 --- /dev/null +++ b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/MediumEntityInformationExtractor.java @@ -0,0 +1,19 @@ +package org.mockbukkit.metaminer.internal.entity.extractor; + +import com.google.gson.JsonObject; +import org.bukkit.entity.EntityType; + +public class MediumEntityInformationExtractor +{ + + public static JsonObject process(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType) + { + return ScaleEntityInformationExtractor.process(entityType, mojangEntityType, 2); + } + + private MediumEntityInformationExtractor() + { + // Hide the public constructor + } + +} diff --git a/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/ScaleEntityInformationExtractor.java b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/ScaleEntityInformationExtractor.java new file mode 100644 index 0000000..28f3f86 --- /dev/null +++ b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/ScaleEntityInformationExtractor.java @@ -0,0 +1,57 @@ +package org.mockbukkit.metaminer.internal.entity.extractor; + +import com.google.gson.JsonObject; +import org.bukkit.entity.EntityType; +import org.mockbukkit.metaminer.internal.entity.EntityInformationExtractor; +import org.mockbukkit.metaminer.internal.entity.EntityKeys; +import org.mockbukkit.metaminer.util.NumberUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +public class ScaleEntityInformationExtractor implements EntityInformationExtractor +{ + public static JsonObject process(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType, double scale) + { + JsonObject jsonObject = processWithoutRecursion(entityType, mojangEntityType, scale); + StatesEntityInformationExtractor.process(entityType, mojangEntityType, scale).ifPresent(object -> jsonObject.add(EntityKeys.STATES, object)); + return jsonObject; + } + + public static JsonObject processWithoutRecursion(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType, double scale) + { + return new ScaleEntityInformationExtractor(scale).extractInformation(entityType, mojangEntityType); + } + + private final BigDecimal scale; + + private ScaleEntityInformationExtractor(double scale) + { + this.scale = BigDecimal.valueOf(scale); + } + + @Override + public JsonObject extractInformation(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType) + { + BigDecimal normalWidth = BigDecimal.valueOf(mojangEntityType.getWidth()); + BigDecimal normalHeight = BigDecimal.valueOf(mojangEntityType.getHeight()); + BigDecimal normalEyeHeight = BigDecimal.valueOf(mojangEntityType.getDimensions().eyeHeight()); + + BigDecimal scaledWidth = normalWidth.multiply(scale); + BigDecimal scaledHeight = normalHeight.multiply(scale); + BigDecimal scaledEyeHeight = BigDecimal.ZERO; + if (BigDecimal.ZERO.compareTo(normalHeight) != 0) + { + scaledEyeHeight = scaledHeight.multiply(normalEyeHeight).divide(normalHeight, RoundingMode.HALF_UP); + } + + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty(EntityKeys.WIDTH, NumberUtils.toDouble(scaledWidth)); + jsonObject.addProperty(EntityKeys.HEIGHT, NumberUtils.toDouble(scaledHeight)); + jsonObject.addProperty(EntityKeys.SCALE, NumberUtils.toDouble(scale)); + jsonObject.addProperty(EntityKeys.EYE_HEIGHT, NumberUtils.toDouble(scaledEyeHeight)); + + return jsonObject; + } + +} diff --git a/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/SmallEntityInformationExtractor.java b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/SmallEntityInformationExtractor.java new file mode 100644 index 0000000..fedebb9 --- /dev/null +++ b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/SmallEntityInformationExtractor.java @@ -0,0 +1,24 @@ +package org.mockbukkit.metaminer.internal.entity.extractor; + +import com.google.gson.JsonObject; +import org.bukkit.entity.EntityType; + +public class SmallEntityInformationExtractor +{ + + public static JsonObject process(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType) + { + if (EntityType.ARMOR_STAND.equals(entityType)) + { + return ScaleEntityInformationExtractor.process(entityType, mojangEntityType, 0.5); + } + + return ScaleEntityInformationExtractor.process(entityType, mojangEntityType, 1); + } + + private SmallEntityInformationExtractor() + { + // Hide the public constructor + } + +} diff --git a/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/StatesEntityInformationExtractor.java b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/StatesEntityInformationExtractor.java new file mode 100644 index 0000000..f0ec881 --- /dev/null +++ b/src/main/java/org/mockbukkit/metaminer/internal/entity/extractor/StatesEntityInformationExtractor.java @@ -0,0 +1,113 @@ +package org.mockbukkit.metaminer.internal.entity.extractor; + +import com.google.common.base.Preconditions; +import com.google.gson.JsonObject; +import org.bukkit.entity.Camel; +import org.bukkit.entity.Enderman; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.entity.PufferFish; +import org.bukkit.entity.Shulker; +import org.mockbukkit.metaminer.internal.entity.EntityInformationExtractor; +import org.mockbukkit.metaminer.internal.entity.EntityKeys; + +import javax.annotation.Nonnull; +import java.math.BigDecimal; +import java.util.Optional; + +public class StatesEntityInformationExtractor implements EntityInformationExtractor +{ + public static Optional process(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType, BigDecimal scale) + { + JsonObject jsonObject = new StatesEntityInformationExtractor(scale).extractInformation(entityType, mojangEntityType); + return jsonObject.isEmpty() ? Optional.empty() : Optional.of(jsonObject); + } + + public static Optional process(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType, double scale) + { + return process(entityType, mojangEntityType, BigDecimal.valueOf(scale)); + } + + private final BigDecimal scale; + + private StatesEntityInformationExtractor(@Nonnull BigDecimal scale) + { + Preconditions.checkNotNull(scale, "The scale cannot be null."); + this.scale = scale; + } + + @Override + public JsonObject extractInformation(EntityType entityType, net.minecraft.world.entity.EntityType mojangEntityType) + { + Class entityClass = entityType.getEntityClass(); + assert entityClass != null; + + JsonObject jsonObject = new JsonObject(); + + if (Camel.class.isAssignableFrom(entityClass)) + { + JsonObject camelSitting = HeightDifferenceEntityInformationExtractor.process(entityType, mojangEntityType, -1.43D, scale); + jsonObject.add("sitting", camelSitting); + } + + if (Enderman.class.isAssignableFrom(entityClass)) + { + JsonObject enderManAngry = HeightDifferenceEntityInformationExtractor.process(entityType, mojangEntityType, 0.35D, scale); + jsonObject.add("angry", enderManAngry); + } + + if (Player.class.isAssignableFrom(entityClass)) + { + // Sneaking + JsonObject camelSitting = HeightDifferenceEntityInformationExtractor.process(entityType, mojangEntityType, -0.3D, scale); + jsonObject.add("sneaking", camelSitting); + + // Gliding / Swimming / Crawling + JsonObject gliding = HeightDifferenceEntityInformationExtractor.process(entityType, mojangEntityType, -1.2D, scale); + jsonObject.add("gliding", gliding); + jsonObject.add("swimming", gliding); + jsonObject.add("crawling", gliding); + + // Sneaking + JsonObject sleeping = HeightDifferenceEntityInformationExtractor.process(entityType, mojangEntityType, -1.6D, scale); + jsonObject.add("sleeping", sleeping); + } + + if (PufferFish.class.isAssignableFrom(entityClass)) + { + // UNPUFFED + JsonObject unPuffed = ScaleEntityInformationExtractor.processWithoutRecursion(entityType, mojangEntityType, 0.5); + jsonObject.add("unpuffed", unPuffed); + + // SEMI_PUFFED + JsonObject semiPuffed = ScaleEntityInformationExtractor.processWithoutRecursion(entityType, mojangEntityType, 0.7); + jsonObject.add("semi_puffed", semiPuffed); + + // PUFFED + JsonObject puffed = ScaleEntityInformationExtractor.processWithoutRecursion(entityType, mojangEntityType, 1.0); + jsonObject.add("puffed", puffed); + } + + if (Shulker.class.isAssignableFrom(entityClass)) + { + // Closed + JsonObject closed = HeightDifferenceEntityInformationExtractor.process(entityType, mojangEntityType, 0.0, scale); + jsonObject.add("closed", closed); + + // Peeking + JsonObject peeking = HeightDifferenceEntityInformationExtractor.process(entityType, mojangEntityType, 0.2, scale); + jsonObject.add("peeking", peeking); + + // Open + JsonObject open = HeightDifferenceEntityInformationExtractor.process(entityType, mojangEntityType, 1.0, scale); + jsonObject.add("open", open); + + // The eye level is always fixed. + peeking.addProperty(EntityKeys.EYE_HEIGHT, closed.get(EntityKeys.EYE_HEIGHT).getAsBigDecimal()); + open.addProperty(EntityKeys.EYE_HEIGHT, closed.get(EntityKeys.EYE_HEIGHT).getAsBigDecimal()); + } + + return jsonObject; + } + +} diff --git a/src/main/java/org/mockbukkit/metaminer/util/NumberUtils.java b/src/main/java/org/mockbukkit/metaminer/util/NumberUtils.java new file mode 100644 index 0000000..2b35442 --- /dev/null +++ b/src/main/java/org/mockbukkit/metaminer/util/NumberUtils.java @@ -0,0 +1,31 @@ +package org.mockbukkit.metaminer.util; + +import com.google.common.base.Preconditions; + +import javax.annotation.Nonnull; +import java.math.BigDecimal; +import java.math.RoundingMode; + +public class NumberUtils +{ + private static final int MAXIMUM_DECIMAL_PLACES = 6; + + /** + * Output the Big Decimal with a maximum of 6-digit decimal places. + * + * @param bigDecimal The value to be formatted. + * + * @return The formatted value. + */ + public static double toDouble(@Nonnull BigDecimal bigDecimal) + { + Preconditions.checkNotNull(bigDecimal, "The big decimal cannot be null!"); + return bigDecimal.setScale(MAXIMUM_DECIMAL_PLACES, RoundingMode.HALF_UP).doubleValue(); + } + + private NumberUtils() + { + // Hide the public constructor + } + +}