Skip to content

Pig Variants #7846

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: dev/feature
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 75 additions & 14 deletions src/main/java/ch/njol/skript/classes/data/BukkitClasses.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,15 @@
import ch.njol.skript.classes.*;
import ch.njol.skript.classes.registry.RegistryClassInfo;
import ch.njol.skript.entity.EntityData;
import ch.njol.skript.entity.WolfData;
import ch.njol.skript.entity.PigData.PigVariantDummy;
import ch.njol.skript.entity.WolfData.WolfVariantDummy;
import ch.njol.skript.expressions.ExprDamageCause;
import ch.njol.skript.expressions.base.EventValueExpression;
import ch.njol.skript.lang.ParseContext;
import ch.njol.skript.lang.util.SimpleLiteral;
import ch.njol.skript.localization.Language;
import ch.njol.skript.registrations.Classes;
import ch.njol.skript.util.BlockUtils;
import ch.njol.skript.util.PotionEffectUtils;
import ch.njol.skript.util.StringMode;
import ch.njol.skript.util.Utils;
import ch.njol.skript.util.*;
import ch.njol.yggdrasil.Fields;
import io.papermc.paper.world.MoonPhase;
import org.bukkit.*;
Expand Down Expand Up @@ -52,6 +50,7 @@
import org.bukkit.event.player.PlayerQuitEvent.QuitReason;
import org.bukkit.event.player.PlayerResourcePackStatusEvent.Status;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.inventory.BlockInventoryHolder;
import org.bukkit.inventory.*;
import org.bukkit.metadata.Metadatable;
import org.bukkit.potion.PotionEffect;
Expand Down Expand Up @@ -1410,14 +1409,15 @@ public String toVariableNameString(EnchantmentOffer eo) {
.description("Represents the cause of the action of a potion effect on an entity, e.g. arrow, command")
.since("2.10"));

ClassInfo<?> wolfVariantClassInfo;
if (Skript.classExists("org.bukkit.entity.Wolf$Variant") && BukkitUtils.registryExists("WOLF_VARIANT")) {
wolfVariantClassInfo = new RegistryClassInfo<>(Wolf.Variant.class, Registry.WOLF_VARIANT, "wolfvariant", "wolf variants");
} else {
/*
* Registers a dummy/placeholder class to ensure working operation on MC versions that do not have `Wolf.Variant`
*/
wolfVariantClassInfo = new ClassInfo<>(WolfData.VariantDummy.class, "wolfvariant");
ClassInfo<?> wolfVariantClassInfo = getRegistryClassInfo(
"org.bukkit.entity.Wolf$Variant",
"WOLF_VARIANT",
"wolfvariant",
"wolf variants"
);
if (wolfVariantClassInfo == null) {
// Registers a dummy/placeholder class to ensure working operation on MC versions that do not have 'Wolf.Variant' (1.20.4-)
wolfVariantClassInfo = new ClassInfo<>(WolfVariantDummy.class, "wolfvariant");
}
Classes.registerClass(wolfVariantClassInfo
.user("wolf ?variants?")
Expand Down Expand Up @@ -1550,14 +1550,75 @@ public String toVariableNameString(WorldBorder border) {
.changer(DefaultChangers.entityChanger)
);


Classes.registerClass(new EnumClassInfo<>(EquipmentSlot.class, "equipmentslot", "equipment slots")
.user("equipment ?slots?")
.name("Equipment Slot")
.description("Represents an equipment slot of an entity.")
.since("2.11")
);

ClassInfo<?> pigVariantClassInfo = getRegistryClassInfo(
"org.bukkit.entity.Pig$Variant",
"PIG_VARIANT",
"pigvariant",
"pig variants"
);
if (pigVariantClassInfo == null) {
// Registers a dummy/placeholder class to ensure working operation on MC versions that do not have 'Pig.Variant' (1.21.4-)
pigVariantClassInfo = new ClassInfo<>(PigVariantDummy.class, "pigvariant");
}
Classes.registerClass(pigVariantClassInfo
.user("pig ?variants?")
.name("Pig Variant")
.description("Represents the variant of a pig entity.",
"NOTE: Minecraft namespaces are supported, ex: 'minecraft:warm'.")
.since("INSERT VERSION")
.requiredPlugins("Minecraft 1.21.5+")
.documentationId("PigVariant"));

}

/**
* Gets a {@link RegistryClassInfo} by checking if the {@link Class} from {@code classPath} exists
* and {@link Registry} or {@link io.papermc.paper.registry.RegistryKey} contains {@code registryName}.
* @param classPath The {@link String} representation of the desired {@link Class}.
* @param registryName The {@link String} representation of the desired {@link Registry}.
* @param codeName The name used in patterns.
* @param languageNode The language node of the type.
* @return {@link RegistryClassInfo} if the class and registry exists, otherwise {@code null}.
*/
private static <R extends Keyed> @Nullable RegistryClassInfo<?> getRegistryClassInfo(
String classPath,
String registryName,
String codeName,
String languageNode
) {
if (!Skript.classExists(classPath))
return null;
Registry<R> registry = null;
if (BukkitUtils.registryExists(registryName)) {
try {
//noinspection unchecked
registry = (Registry<R>) Registry.class.getField(registryName).get(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
} else if (PaperUtils.registryExists(registryName)) {
registry = PaperUtils.getBukkitRegistry(registryName);
}
if (registry != null) {
Class<R> registryClass;
try {
//noinspection unchecked
registryClass = (Class<R>) Class.forName(classPath);
} catch (ClassNotFoundException e) {
Skript.debug("Could not retrieve the class with the path: '" + classPath + "'.");
throw new RuntimeException(e);
}
return new RegistryClassInfo<>(registryClass, registry, codeName, languageNode);
}
Skript.debug("There were no registries found for '" + registryName + "'.");
return null;
}

}
106 changes: 72 additions & 34 deletions src/main/java/ch/njol/skript/entity/PigData.java
Original file line number Diff line number Diff line change
@@ -1,53 +1,83 @@
package ch.njol.skript.entity;

import ch.njol.skript.Skript;
import ch.njol.skript.lang.Literal;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.registrations.Classes;
import ch.njol.util.coll.CollectionUtils;
import com.google.common.collect.Iterators;
import org.bukkit.entity.Pig;
import org.jetbrains.annotations.Nullable;

import ch.njol.skript.lang.Literal;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import java.util.Objects;

/**
* @author Peter Güttinger
*/
public class PigData extends EntityData<Pig> {

public enum SaddleState {
NOT_SADDLED, UNKNOWN, SADDLED
}

private static final SaddleState[] SADDLE_STATES = SaddleState.values();
private static boolean variantsEnabled = false;
private static Object[] variants;

static {
EntityData.register(PigData.class, "pig", Pig.class, 1, "unsaddled pig", "pig", "saddled pig");
if (Skript.classExists("org.bukkit.entity.Pig$Variant")) {
variantsEnabled = true;
variants = Iterators.toArray(Classes.getExactClassInfo(Pig.Variant.class).getSupplier().get(), Pig.Variant.class);
}
}

private int saddled = 0;
private SaddleState saddled = SaddleState.UNKNOWN;
private @Nullable Object variant = null;

public PigData() {}

// TODO: When safe, 'variant' should have the type changed to 'Pig.Variant'
public PigData(SaddleState saddled, @Nullable Object variant) {
this.saddled = saddled;
this.variant = variant;
}

@Override
protected boolean init(final Literal<?>[] exprs, final int matchedPattern, final ParseResult parseResult) {
saddled = matchedPattern - 1;
protected boolean init(Literal<?>[] exprs, int matchedPattern, ParseResult parseResult) {
saddled = SADDLE_STATES[matchedPattern];
if (exprs[0] != null && variantsEnabled)
//noinspection unchecked
variant = ((Literal<Pig.Variant>) exprs[0]).getSingle();
return true;
}

@Override
protected boolean init(final @Nullable Class<? extends Pig> c, final @Nullable Pig e) {
saddled = e == null ? 0 : e.hasSaddle() ? 1 : -1;
protected boolean init(@Nullable Class<? extends Pig> entityClass, @Nullable Pig pig) {
saddled = SaddleState.UNKNOWN;
if (pig != null) {
saddled = pig.hasSaddle() ? SaddleState.SADDLED : SaddleState.NOT_SADDLED;
if (variantsEnabled)
variant = pig.getVariant();
}
return true;
}

// return "" + saddled;

@Override
protected boolean deserialize(final String s) {
try {
saddled = Integer.parseInt(s);
return Math.abs(saddled) <= 1;
} catch (final NumberFormatException e) {
return false;
}
protected boolean deserialize(String string) {
return true;
}

@Override
public void set(final Pig entity) {
if (saddled != 0)
entity.setSaddle(saddled == 1);
public void set(Pig pig) {
pig.setSaddle(saddled == SaddleState.SADDLED);
if (variantsEnabled) {
Object finalVariant = variant != null ? variant : CollectionUtils.getRandom(variants);
pig.setVariant((Pig.Variant) finalVariant);
}
}

@Override
protected boolean match(final Pig entity) {
return saddled == 0 || entity.hasSaddle() == (saddled == 1);
protected boolean match(Pig pig) {
return (saddled == SaddleState.UNKNOWN || (pig.hasSaddle() ? SaddleState.SADDLED : SaddleState.NOT_SADDLED) == saddled)
&& (variant == null || variant == pig.getVariant());
}

@Override
Expand All @@ -56,28 +86,36 @@ public Class<? extends Pig> getType() {
}

@Override
protected boolean equals_i(final EntityData<?> obj) {
if (!(obj instanceof PigData))
protected boolean equals_i(EntityData<?> obj) {
if (!(obj instanceof PigData other))
return false;
if (saddled != other.saddled)
return false;
final PigData other = (PigData) obj;
return other.saddled == saddled;
return variant == null || variant == other.variant;
}

@Override
protected int hashCode_i() {
return saddled;
return saddled.ordinal() + Objects.hashCode(variant);
}

@Override
public boolean isSupertypeOf(final EntityData<?> e) {
if (e instanceof PigData)
return saddled == 0 || ((PigData) e).saddled == saddled;
return false;
public boolean isSupertypeOf(EntityData<?> entityData) {
if (!(entityData instanceof PigData other))
return false;
if (saddled != SaddleState.UNKNOWN && saddled != other.saddled)
return false;
return variant == null || variant == other.variant;
}

@Override
public EntityData getSuperType() {
public EntityData<Pig> getSuperType() {
return new PigData();
}

/**
* A dummy/placeholder class to ensure working operation on MC versions that do not have `Pig.Variant`
*/
public static class PigVariantDummy {}

}
4 changes: 2 additions & 2 deletions src/main/java/ch/njol/skript/entity/WolfData.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class WolfData extends EntityData<Wolf> {
EntityData.register(WolfData.class, "wolf", Wolf.class, 1,
"peaceful wolf", "wolf", "angry wolf",
"wild wolf", "tamed wolf");
if (Skript.classExists("org.bukkit.entity.Wolf$Variant") && BukkitUtils.registryExists("WOLF_VARIANT")) {
if (Skript.classExists("org.bukkit.entity.Wolf$Variant")) {
variantsEnabled = true;
variants = Iterators.toArray(Classes.getExactClassInfo(Wolf.Variant.class).getSupplier().get(), Wolf.Variant.class);
}
Expand Down Expand Up @@ -152,6 +152,6 @@ public EntityData<Wolf> getSuperType() {
/**
* A dummy/placeholder class to ensure working operation on MC versions that do not have `Wolf.Variant`
*/
public static class VariantDummy {};
public static class WolfVariantDummy {};

}
44 changes: 44 additions & 0 deletions src/main/java/ch/njol/skript/util/PaperUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ch.njol.skript.util;

import ch.njol.skript.Skript;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryKey;
import org.bukkit.Keyed;
import org.bukkit.Registry;
import org.jetbrains.annotations.Nullable;

public class PaperUtils {

private static final boolean REGISTRY_ACCESS_EXISTS = Skript.classExists("io.papermc.paper.registry.RegistryAccess");
private static final boolean REGISTRY_KEY_EXISTS = Skript.classExists("io.papermc.paper.registry.RegistryKey");

/**
* Check if a registry exists within {@link RegistryKey}
* @param registry Registry to check for (Fully qualified name of registry)
* @return True if registry exists else false
*/
public static boolean registryExists(String registry) {
return REGISTRY_ACCESS_EXISTS
&& REGISTRY_KEY_EXISTS
&& Skript.fieldExists(RegistryKey.class, registry);
}

/**
* Gets the Bukkit {@link Registry} from Paper's {@link RegistryKey}.
* @param registry Registry to get (Fully qualified name of registry).
* @return The Bukkit {@link Registry} if registry exists else {@code null}.
*/
public static <T extends Keyed> @Nullable Registry<T> getBukkitRegistry(String registry) {
if (!registryExists(registry))
return null;
RegistryKey registryKey;
try {
registryKey = (RegistryKey) RegistryKey.class.getField(registry).get(null);
} catch (NoSuchFieldException | IllegalAccessException ignored) {
return null;
}
//noinspection unchecked
return (Registry<T>) RegistryAccess.registryAccess().getRegistry(registryKey);
}

}
14 changes: 10 additions & 4 deletions src/main/resources/lang/default.lang
Original file line number Diff line number Diff line change
Expand Up @@ -701,13 +701,13 @@ entities:
pattern: painting(|1¦s)
pig:
name: pig¦s
pattern: <age> pig(|1¦s)|(4¦)piglet(|1¦s)
pattern: <age> [%-pigvariant%] pig(|1¦s)|(4¦)piglet(|1¦s)
saddled pig:
name: saddled pig¦s
pattern: saddled pig(|1¦s)
pattern: saddled [%-pigvariant%] pig(|1¦s)
unsaddled pig:
name: unsaddled pig¦s @an
pattern: unsaddled pig(|1¦s)
pattern: unsaddled [%-pigvariant%] pig(|1¦s)
player:
name: player¦s
pattern: player(|1¦s)
Expand Down Expand Up @@ -2586,6 +2586,12 @@ equipment slots:
off_hand: off hand slot, off hand
body: body slot, body armor slot, body armour slot

# -- Pig Variants --
pig variants:
cold: cold
temperate: temperate
warm: warm

# -- Boolean --
boolean:
true:
Expand Down Expand Up @@ -2689,7 +2695,7 @@ types:
vehicle: vehicle¦s @a
fishingstate: fishing state¦s @a
equipmentslot: equipment slot¦s @an

pigvariant: pig variant¦s @a

# Skript
weathertype: weather type¦s @a
Expand Down
Loading