Merge pull request #81 from Minestom/feature/item-meta

Implements player head and firework metas
This commit is contained in:
TheMode 2020-12-25 10:44:49 +01:00 committed by GitHub
commit 610c1dd85b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 622 additions and 38 deletions

View File

@ -9,6 +9,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.Objects;
/**
* Contains all the data required to store a skin.
@ -26,24 +27,6 @@ public class PlayerSkin {
this.signature = signature;
}
/**
* Gets the skin textures value.
*
* @return the textures value
*/
public String getTextures() {
return textures;
}
/**
* Gets the skin signature.
*
* @return the skin signature
*/
public String getSignature() {
return signature;
}
/**
* Gets a skin from a Mojang UUID.
*
@ -98,4 +81,42 @@ public class PlayerSkin {
}
}
/**
* Gets the skin textures value.
*
* @return the textures value
*/
public String getTextures() {
return textures;
}
/**
* Gets the skin signature.
*
* @return the skin signature
*/
public String getSignature() {
return signature;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object object) {
if (this == object) return true;
if (object == null || getClass() != object.getClass()) return false;
PlayerSkin that = (PlayerSkin) object;
return Objects.equals(textures, that.textures) &&
Objects.equals(signature, that.signature);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects.hash(textures, signature);
}
}

View File

@ -690,6 +690,9 @@ public class ItemStack implements DataContainer, PublicCloneable<ItemStack> {
if (material == Material.WRITTEN_BOOK)
return new WrittenBookMeta();
if(material == Material.FIREWORK_STAR)
return new FireworkEffectMeta();
if (material == Material.FIREWORK_ROCKET)
return new FireworkMeta();

View File

@ -0,0 +1,166 @@
package net.minestom.server.item.firework;
import net.minestom.server.chat.ChatColor;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.Objects;
public class FireworkEffect {
private final boolean flicker;
private final boolean trail;
private final FireworkEffectType type;
private final ChatColor color;
private final ChatColor fadeColor;
/**
* Initializes a new firework effect.
*
* @param flicker {@code true} if this explosion has the Twinkle effect (glowstone dust), otherwise {@code false}.
* @param trail {@code true} if this explosion hsa the Trail effect (diamond), otherwise {@code false}.
* @param type The shape of this firework's explosion.
* @param color The primary color of this firework effect.
* @param fadeColor The secondary color of this firework effect.
*/
public FireworkEffect(boolean flicker, boolean trail, FireworkEffectType type, ChatColor color, ChatColor fadeColor) {
this.flicker = flicker;
this.trail = trail;
this.type = type;
this.color = color;
this.fadeColor = fadeColor;
}
/**
* Retrieves a firework effect from the given {@code compound}.
*
* @param compound The NBT connection, which should be a fireworks effect.
* @return A new created firework effect.
*/
public static FireworkEffect fromCompound(@NotNull NBTCompound compound) {
ChatColor primaryColor = null;
ChatColor secondaryColor = null;
if (compound.containsKey("Colors")) {
int[] color = compound.getIntArray("Colors");
primaryColor = ChatColor.fromRGB((byte) color[0], (byte) color[1], (byte) color[2]);
}
if (compound.containsKey("FadeColors")) {
int[] fadeColor = compound.getIntArray("FadeColors");
secondaryColor = ChatColor.fromRGB((byte) fadeColor[0], (byte) fadeColor[1], (byte) fadeColor[2]);
}
boolean flicker = compound.containsKey("Flicker") && compound.getByte("Flicker") == 1;
boolean trail = compound.containsKey("Trail") && compound.getByte("Trail") == 1;
FireworkEffectType type = compound.containsKey("Type") ? FireworkEffectType.byId(compound.getByte("Type")) : FireworkEffectType.SMALL_BALL;
return new FireworkEffect(
flicker,
trail,
type,
primaryColor,
secondaryColor);
}
/**
* Whether the firework has a flicker effect.
*
* @return {@code 1} if this explosion has the flicker effect, otherwise {@code 0}.
*/
public byte getFlicker() {
return (byte) (this.flicker ? 1 : 0);
}
/**
* Whether the firework has a trail effect.
*
* @return {@code 1} if this explosion has the trail effect, otherwise {@code 0};
*/
public byte getTrail() {
return (byte) (this.trail ? 1 : 0);
}
/**
* Retrieves type of the firework effect.
*
* @return The firework type as a byte.
*/
public byte getType() {
return this.type.getType();
}
/**
* Retrieves array of integer values corresponding to the primary colors of this firework's explosion.
* <p>
* If custom color codes are used, the game renders is as "Custom" in the tooltip, but the proper color is used
* in the explosion.
*
* @return An array of integer values corresponding to the primary colors of this firework's explosion.
*/
public int[] getColors() {
return new int[]{
this.color.getRed(),
this.color.getGreen(),
this.color.getBlue()
};
}
/**
* Retrieves array of integer values corresponding to the fading colors of this firework's explosion.
* <p>
* Same handling of custom colors as Colors.
*
* @return An array of integer values corresponding to the fading colors of this firework's explosion.
*/
public int[] getFadeColors() {
return new int[]{
this.fadeColor.getRed(),
this.fadeColor.getGreen(),
this.fadeColor.getBlue()
};
}
/**
* Retrieves the {@link FireworkEffect} as a {@link NBTCompound}.
*
* @return The firework effect as a nbt compound.
*/
public NBTCompound asCompound() {
NBTCompound explosionCompound = new NBTCompound();
explosionCompound.setByte("Flicker", this.getFlicker());
explosionCompound.setByte("Trail", this.getTrail());
explosionCompound.setByte("Type", this.getType());
explosionCompound.setIntArray("Colors", this.getColors());
explosionCompound.setIntArray("FadeColors", this.getFadeColors());
return explosionCompound;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FireworkEffect that = (FireworkEffect) o;
return flicker == that.flicker &&
trail == that.trail &&
type == that.type &&
Objects.equals(color, that.color) &&
Objects.equals(fadeColor, that.fadeColor);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects.hash(flicker, trail, type, color, fadeColor);
}
}

View File

@ -0,0 +1,52 @@
package net.minestom.server.item.firework;
import java.util.HashMap;
import java.util.Map;
/**
* An enumeration that representing all available firework types.
*/
public enum FireworkEffectType {
SMALL_BALL((byte) 0),
LARGE_BAR((byte) 1),
STAR_SHAPED((byte) 2),
CREEPER_SHAPED((byte) 3),
BURST((byte) 4),
;
private static final Map<Byte, FireworkEffectType> BY_ID = new HashMap<>();
static {
for (FireworkEffectType value : values()) {
BY_ID.put(value.type, value);
}
}
private final byte type;
FireworkEffectType(byte type) {
this.type = type;
}
/**
* Retrieves a {@link FireworkEffectType} by the given {@code id}.
*
* @param id The identifier of the firework effect type.
* @return A firework effect type or {@code null}.
*/
public static FireworkEffectType byId(byte id) {
return BY_ID.get(id);
}
/**
* Retrieves the type of the firework effect.
*
* @return The type of the firework effect as a byte.
*/
public byte getType() {
return type;
}
}

View File

@ -0,0 +1,92 @@
package net.minestom.server.item.metadata;
import net.minestom.server.item.firework.FireworkEffect;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
public class FireworkEffectMeta extends ItemMeta {
private FireworkEffect fireworkEffect;
/**
* Retrieves the firework effect for this meta.
*
* @return The current firework effect, or {@code null} if none.
*/
@Nullable
public FireworkEffect getFireworkEffect() {
return fireworkEffect;
}
/**
* Changes the {@link FireworkEffect} for this item meta.
*
* @param fireworkEffect The new firework effect, or {@code null}.
*/
public void setFireworkEffect(@Nullable FireworkEffect fireworkEffect) {
this.fireworkEffect = fireworkEffect;
}
/**
* Whether if this item meta has an effect.
*
* @return {@code true} if this item meta has an effect, otherwise {@code false}.
*/
public boolean hasFireworkEffect() {
return this.fireworkEffect != null;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasNbt() {
return this.hasFireworkEffect();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
if (!(itemMeta instanceof FireworkEffectMeta)) {
return false;
}
FireworkEffectMeta fireworkEffectMeta = (FireworkEffectMeta) itemMeta;
return fireworkEffectMeta.fireworkEffect == this.fireworkEffect;
}
/**
* {@inheritDoc}
*/
@Override
public void read(@NotNull NBTCompound compound) {
if (compound.containsKey("Explosion")) {
this.fireworkEffect = FireworkEffect.fromCompound(compound.getCompound("Explosion"));
}
}
/**
* {@inheritDoc}
*/
@Override
public void write(@NotNull NBTCompound compound) {
if (this.fireworkEffect != null) {
compound.set("Explosion", this.fireworkEffect.asCompound());
}
}
/**
* {@inheritDoc}
*/
@NotNull
@Override
public ItemMeta clone() {
FireworkEffectMeta fireworkEffectMeta = (FireworkEffectMeta) super.clone();
fireworkEffectMeta.fireworkEffect = this.fireworkEffect;
return fireworkEffectMeta;
}
}

View File

@ -1,47 +1,171 @@
package net.minestom.server.item.metadata;
import net.minestom.server.item.firework.FireworkEffect;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTList;
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Represents a firework rocket meta data and its effects.
*/
public class FireworkMeta extends ItemMeta {
private boolean flicker;
private boolean trail;
private FireworkType type;
private int[] colors;
private int[] fadeColors;
private List<FireworkEffect> effects = new CopyOnWriteArrayList<>();
private byte flightDuration;
// TODO Explosions list
@Override
public boolean hasNbt() {
return false;
/**
* Adds a firework effect to this firework.
*
* @param effect The firework effect to be added.
*/
public void addFireworkEffect(FireworkEffect effect) {
this.effects.add(effect);
}
/**
* Adds an array of firework effects to this firework.
*
* @param effects An array of firework effects to be added.
*/
public void addFireworkEffects(FireworkEffect... effects) {
this.effects.addAll(Arrays.asList(effects));
}
/**
* Removes a firework effect from this firework.
*
* @param index The index of the firework effect to be removed.
* @throws IndexOutOfBoundsException If index {@literal < 0 or index >} {@link #getEffectSize()}
*/
public void removeFireworkEffect(int index) throws IndexOutOfBoundsException {
this.effects.remove(index);
}
/**
* Removes a firework effects from this firework.
*
* @param effect The effect to be removed.
*/
public void removeFireworkEffect(FireworkEffect effect) {
this.effects.remove(effect);
}
/**
* Retrieves a collection with all effects in this firework.
*
* @return A collection with all effects in this firework.
*/
public List<FireworkEffect> getEffects() {
return effects;
}
/**
* Retrieves the size of effects in this firework.
*
* @return The size of the effects.
*/
public int getEffectSize() {
return this.effects.size();
}
/**
* Removes all effects from this firework.
*/
public void clearEffects() {
this.effects.clear();
}
/**
* Whether this firework has any effects.
*
* @return {@code true} if this firework has any effects, otherwise {@code false}.
*/
public boolean hasEffects() {
return this.effects.isEmpty();
}
/**
* Changes the flight duration of this firework.
*
* @param flightDuration The new flight duration for this firework.
*/
public void setFlightDuration(byte flightDuration) {
this.flightDuration = flightDuration;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasNbt() {
return this.flightDuration == 0 || !this.effects.isEmpty();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public void read(@NotNull NBTCompound compound) {
this.effects.clear();
if (compound.containsKey("Fireworks")) {
NBTCompound fireworksCompound = compound.getCompound("Fireworks");
if (fireworksCompound.containsKey("Flight")) {
this.flightDuration = fireworksCompound.getAsByte("Flight");
}
if (fireworksCompound.containsKey("Explosions")) {
NBTList<NBTCompound> explosions = fireworksCompound.getList("Explosions");
for (NBTCompound explosion : explosions) {
this.effects.add(FireworkEffect.fromCompound(explosion));
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void write(@NotNull NBTCompound compound) {
NBTCompound fireworksCompound = new NBTCompound();
fireworksCompound.setByte("Flight", this.flightDuration);
NBTList<NBTCompound> explosions = new NBTList<>(NBTTypes.TAG_Compound);
for (FireworkEffect effect : this.effects) {
explosions.add(effect.asCompound());
}
fireworksCompound.set("Explosions", explosions);
compound.set("Fireworks", fireworksCompound);
}
/**
* {@inheritDoc}
*/
@NotNull
@Override
public ItemMeta clone() {
return null;
}
FireworkMeta fireworkMeta = (FireworkMeta) super.clone();
fireworkMeta.effects = this.effects;
fireworkMeta.flightDuration = this.flightDuration;
public enum FireworkType {
SMALL_BALL, LARGE_BALL, STAR_SHAPED, CREEPER_SHAPED, BURST
return fireworkMeta;
}
}

View File

@ -44,6 +44,9 @@ public abstract class ItemMeta implements PublicCloneable<ItemMeta> {
*/
public abstract void write(@NotNull NBTCompound compound);
/**
* {@inheritDoc}
*/
@NotNull
@Override
public ItemMeta clone() {

View File

@ -1,19 +1,89 @@
package net.minestom.server.item.metadata;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Player;
import net.minestom.server.entity.PlayerSkin;
import net.minestom.server.utils.Utils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTList;
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
import java.util.UUID;
/**
* Represents a skull that can have an owner.
*/
public class PlayerHeadMeta extends ItemMeta {
private String playerName;
private UUID skullOwner;
private PlayerSkin playerSkin;
@Override
public boolean hasNbt() {
return playerSkin != null;
/**
* Sets the owner of the skull.
*
* @param player The new owner of the skull.
* @return {@code true} if the owner was successfully set, otherwise {@code false}.
*/
public boolean setOwningPlayer(@NotNull Player player) {
if (player.getSkin() != null) {
this.skullOwner = player.getUuid();
this.playerSkin = player.getSkin();
return true;
}
return false;
}
/**
* Retrieves the owner of the head.
*
* @return The head's owner.
*/
@Nullable
public UUID getSkullOwner() {
return skullOwner;
}
/**
* Changes the owner of the head.
*
* @param skullOwner The new head owner.
*/
public void setSkullOwner(@NotNull UUID skullOwner) {
this.skullOwner = skullOwner;
}
/**
* Retrieves the skin of the head.
*
* @return The head's skin.
*/
@Nullable
public PlayerSkin getPlayerSkin() {
return playerSkin;
}
/**
* Changes the skin of the head.
*
* @param playerSkin The new skin for the head.
*/
public void setPlayerSkin(@NotNull PlayerSkin playerSkin) {
this.playerSkin = playerSkin;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasNbt() {
return this.skullOwner != null || playerSkin != null;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
if (!(itemMeta instanceof PlayerHeadMeta))
@ -22,19 +92,67 @@ public class PlayerHeadMeta extends ItemMeta {
return playerHeadMeta.playerSkin == playerSkin;
}
/**
* {@inheritDoc}
*/
@Override
public void read(@NotNull NBTCompound compound) {
if (compound.containsKey("SkullOwner")) {
NBTCompound skullOwnerCompound = compound.getCompound("SkullOwner");
if (skullOwnerCompound.containsKey("Id")) {
this.skullOwner = Utils.intArrayToUuid(skullOwnerCompound.getIntArray("Id"));
}
if (skullOwnerCompound.containsKey("Properties")) {
NBTCompound propertyCompound = skullOwnerCompound.getCompound("Properties");
if (propertyCompound.containsKey("textures")) {
NBTList<NBTCompound> textures = propertyCompound.getList("textures");
if (textures != null) {
NBTCompound nbt = textures.get(0);
this.playerSkin = new PlayerSkin(nbt.getString("Value"), nbt.getString("Signature"));
}
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void write(@NotNull NBTCompound compound) {
NBTCompound skullOwnerCompound = new NBTCompound();
// Sets the identifier for the skull
skullOwnerCompound.setIntArray("Id", Utils.uuidToIntArray(this.skullOwner));
if (this.playerSkin == null) {
this.playerSkin = PlayerSkin.fromUuid(this.skullOwner.toString());
}
NBTList<NBTCompound> textures = new NBTList<>(NBTTypes.TAG_Compound);
textures.add(new NBTCompound().setString("Value", this.playerSkin.getTextures()).setString("Signature", this.playerSkin.getSignature()));
skullOwnerCompound.set("Properties", new NBTCompound().set("textures", textures));
compound.set("SkullOwner", skullOwnerCompound);
}
/**
* {@inheritDoc}
*/
@NotNull
@Override
public ItemMeta clone() {
return null;
PlayerHeadMeta playerHeadMeta = (PlayerHeadMeta) super.clone();
playerHeadMeta.skullOwner = this.skullOwner;
playerHeadMeta.playerSkin = this.playerSkin;
return playerHeadMeta;
}
}

View File

@ -9,6 +9,11 @@ import org.jetbrains.annotations.NotNull;
*/
public interface PublicCloneable<T> extends Cloneable {
/**
* Creates and returns a copy of this object.
*
* @return A clone of this instance.
*/
@NotNull
T clone();
}