diff --git a/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java b/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java index 2aa8a363d..b2876e289 100644 --- a/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java +++ b/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java @@ -15,7 +15,11 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.java.JavaPlugin; import us.myles.ViaVersion.api.ViaVersion; import us.myles.ViaVersion.api.ViaVersionAPI; +import us.myles.ViaVersion.api.boss.BossBar; +import us.myles.ViaVersion.api.boss.BossColor; +import us.myles.ViaVersion.api.boss.BossStyle; import us.myles.ViaVersion.armor.ArmorListener; +import us.myles.ViaVersion.boss.ViaBossBar; import us.myles.ViaVersion.commands.ViaVersionCommand; import us.myles.ViaVersion.handlers.ViaVersionInitializer; import us.myles.ViaVersion.listeners.CommandBlockListener; @@ -110,7 +114,12 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI { @Override public boolean isPorted(Player player) { - return portedPlayers.containsKey(player.getUniqueId()); + return isPorted(player.getUniqueId()); + } + + @Override + public boolean isPorted(UUID playerUUID) { + return portedPlayers.containsKey(playerUUID); } @Override @@ -119,11 +128,26 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI { } public void sendRawPacket(Player player, ByteBuf packet) throws IllegalArgumentException { - if (!isPorted(player)) throw new IllegalArgumentException("This player is not on 1.9"); - ConnectionInfo ci = portedPlayers.get(player.getUniqueId()); + sendRawPacket(player.getUniqueId(), packet); + } + + @Override + public void sendRawPacket(UUID uuid, ByteBuf packet) throws IllegalArgumentException { + if (!isPorted(uuid)) throw new IllegalArgumentException("This player is not on 1.9"); + ConnectionInfo ci = portedPlayers.get(uuid); ci.sendRawPacket(packet); } + @Override + public BossBar createBossBar(String title, BossColor color, BossStyle style) { + return new ViaBossBar(title, 1F, color, style); + } + + @Override + public BossBar createBossBar(String title, float health, BossColor color, BossStyle style) { + return new ViaBossBar(title, health, color, style); + } + @Override public boolean isDebug() { return this.debug; diff --git a/src/main/java/us/myles/ViaVersion/api/Boss/BossColor.java b/src/main/java/us/myles/ViaVersion/api/Boss/BossColor.java new file mode 100644 index 000000000..802f93b3a --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/api/Boss/BossColor.java @@ -0,0 +1,21 @@ +package us.myles.ViaVersion.api.boss; + +public enum BossColor { + PINK(0), + BLUE(1), + RED(2), + GREEN(3), + YELLOW(4), + PURPLE(5), + WHITE(6); + + private final int id; + + BossColor(int id) { + this.id = id; + } + + public int getId() { + return id; + } +} diff --git a/src/main/java/us/myles/ViaVersion/api/Boss/BossFlag.java b/src/main/java/us/myles/ViaVersion/api/Boss/BossFlag.java new file mode 100644 index 000000000..5b72fd69a --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/api/Boss/BossFlag.java @@ -0,0 +1,16 @@ +package us.myles.ViaVersion.api.boss; + +public enum BossFlag { + DARKEN_SKY(1), + PLAY_BOSS_MUSIC(2); + + private final int id; + + BossFlag(int id) { + this.id = id; + } + + public int getId() { + return id; + } +} diff --git a/src/main/java/us/myles/ViaVersion/api/Boss/BossStyle.java b/src/main/java/us/myles/ViaVersion/api/Boss/BossStyle.java new file mode 100644 index 000000000..60dd209ea --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/api/Boss/BossStyle.java @@ -0,0 +1,19 @@ +package us.myles.ViaVersion.api.boss; + +public enum BossStyle { + SOLID(0), + SEGMENTED_6(1), + SEGMENTED_10(2), + SEGMENTED_12(3), + SEGMENTED_20(4); + + private final int id; + + BossStyle(int id) { + this.id = id; + } + + public int getId() { + return id; + } +} diff --git a/src/main/java/us/myles/ViaVersion/api/ViaVersionAPI.java b/src/main/java/us/myles/ViaVersion/api/ViaVersionAPI.java index f80186838..8bd548666 100644 --- a/src/main/java/us/myles/ViaVersion/api/ViaVersionAPI.java +++ b/src/main/java/us/myles/ViaVersion/api/ViaVersionAPI.java @@ -2,37 +2,85 @@ package us.myles.ViaVersion.api; import io.netty.buffer.ByteBuf; import org.bukkit.entity.Player; +import us.myles.ViaVersion.api.boss.BossBar; +import us.myles.ViaVersion.api.boss.BossColor; +import us.myles.ViaVersion.api.boss.BossStyle; + +import java.util.UUID; public interface ViaVersionAPI { /** * Is player using 1.9? + * * @param player * @return True if the client is on 1.9 */ boolean isPorted(Player player); + /** + * Is player using 1.9? + * + * @param playerUUID + * @return True if the client is on 1.9 + */ + boolean isPorted(UUID playerUUID); + /** * Get the version of the plugin + * * @return Plugin version */ String getVersion(); /** * Send a raw packet to the player (Use new IDs) + * * @param player The player to send packet * @param packet The packet, you need a VarInt ID then the packet contents. * @throws IllegalArgumentException If not on 1.9 throws IllegalArg */ void sendRawPacket(Player player, ByteBuf packet) throws IllegalArgumentException; + /** + * Send a raw packet to the player (Use new IDs) + * + * @param uuid The uuid from the player to send packet + * @param packet The packet, you need a VarInt ID then the packet contents. + * @throws IllegalArgumentException If not on 1.9 throws IllegalArg + */ + void sendRawPacket(UUID uuid, ByteBuf packet) throws IllegalArgumentException; + + /** + * Create a new bossbar instance + * + * @param title The title + * @param color The color + * @param style The style + * @return Bossbar instance + */ + BossBar createBossBar(String title, BossColor color, BossStyle style); + + /** + * Create a new bossbar instance + * + * @param title The title + * @param health Number between 0 and 1 + * @param color The color + * @param style The style + * @return Bossbar instance + */ + BossBar createBossBar(String title, float health, BossColor color, BossStyle style); + /** * Obtain if global debug is enabled + * * @return true if debug is enabled */ boolean isDebug(); /** * Obtains if syncing chunks is on + * * @return true if it is */ boolean isSyncedChunks(); diff --git a/src/main/java/us/myles/ViaVersion/boss/ViaBossBar.java b/src/main/java/us/myles/ViaVersion/boss/ViaBossBar.java new file mode 100644 index 000000000..94cd6fb56 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/boss/ViaBossBar.java @@ -0,0 +1,231 @@ +package us.myles.ViaVersion.boss; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.apache.commons.lang.Validate; +import org.bukkit.entity.Player; +import us.myles.ViaVersion.api.ViaVersion; +import us.myles.ViaVersion.api.boss.BossBar; +import us.myles.ViaVersion.api.boss.BossColor; +import us.myles.ViaVersion.api.boss.BossFlag; +import us.myles.ViaVersion.api.boss.BossStyle; +import us.myles.ViaVersion.packets.PacketType; +import us.myles.ViaVersion.transformers.OutgoingTransformer; +import us.myles.ViaVersion.util.PacketUtil; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +public class ViaBossBar implements BossBar { + private UUID uuid; + private String title; + private float health; + private BossColor color; + private BossStyle style; + private Set players; + private boolean visible; + private Set flags; + + public ViaBossBar(String title, float health, BossColor color, BossStyle style) { + Validate.notNull(title, "Title cannot be null"); + Validate.isTrue((health >= 0 && health <= 1), "Health must be between 0 and 1"); + this.uuid = UUID.randomUUID(); + this.title = title; + this.health = health; + this.color = color == null ? BossColor.PURPLE : color; + this.style = style == null ? BossStyle.SOLID : style; + this.players = new HashSet<>(); + this.flags = new HashSet<>(); + visible = true; + } + + @Override + public void setTitle(String title) { + Validate.notNull(title, "Title cannot be null"); + this.title = title; + sendPacket(UpdateAction.UPDATE_TITLE); + } + + @Override + public String getTitle() { + return title; + } + + @Override + public void setHealth(float health) { + Validate.isTrue((health >= 0 && health <= 1), "Health must be between 0 and 1"); + this.health = health; + sendPacket(UpdateAction.UPDATE_HEALTH); + } + + @Override + public float getHealth() { + return health; + } + + @Override + public void setColor(BossColor color) { + Validate.notNull(color, "Color cannot be null"); + this.color = color; + sendPacket(UpdateAction.UPDATE_STYLE); + } + + @Override + public BossColor getColor() { + return color; + } + + @Override + public void setStyle(BossStyle style) { + Validate.notNull(style, "Style cannot be null"); + this.style = style; + sendPacket(UpdateAction.UPDATE_STYLE); + } + + @Override + public BossStyle getStyle() { + return style; + } + + @Override + public void addPlayer(Player player) { + if (player != null && !players.contains(player.getUniqueId())) { + players.add(player.getUniqueId()); + if (visible) + sendPacket(player.getUniqueId(), getPacket(UpdateAction.ADD)); + } + } + + @Override + public void removePlayer(Player player) { + if (player != null && players.contains(player.getUniqueId())) { + players.remove(player.getUniqueId()); + sendPacket(player.getUniqueId(), getPacket(UpdateAction.REMOVE)); + } + } + + @Override + public void addFlag(BossFlag flag) { + if (!hasFlag(flag)) + flags.add(flag); + sendPacket(UpdateAction.UPDATE_FLAGS); + } + + @Override + public void removeFlag(BossFlag flag) { + if (hasFlag(flag)) + flags.remove(flag); + sendPacket(UpdateAction.UPDATE_FLAGS); + } + + @Override + public boolean hasFlag(BossFlag flag) { + return flags.contains(flag); + } + + @Override + public Set getPlayers() { + return Collections.unmodifiableSet(players); + } + + @Override + public void show() { + setVisible(true); + } + + @Override + public void hide() { + setVisible(false); + } + + @Override + public boolean isVisible() { + return visible; + } + + private void setVisible(boolean value) { + if (visible != value) { + visible = value; + sendPacket(value ? UpdateAction.ADD : UpdateAction.REMOVE); + } + } + + private void sendPacket(UpdateAction action) { + ByteBuf buf = getPacket(action); + for (UUID uuid : players) + sendPacket(uuid, buf); + } + + private void sendPacket(UUID uuid, ByteBuf buf) { + if (!ViaVersion.getInstance().isPorted(uuid)) { + players.remove(uuid); + return; + } + ViaVersion.getInstance().sendRawPacket(uuid, buf); + } + + private ByteBuf getPacket(UpdateAction action) { + ByteBuf buf = Unpooled.buffer(); + PacketUtil.writeVarInt(PacketType.PLAY_BOSS_BAR.getNewPacketID(), buf); + PacketUtil.writeUUID(uuid, buf); + PacketUtil.writeVarInt(action.getId(), buf); + switch (action) { + case ADD: + PacketUtil.writeString(fixJson(title), buf); + buf.writeFloat(health); + PacketUtil.writeVarInt(color.getId(), buf); + PacketUtil.writeVarInt(style.getId(), buf); + buf.writeByte(flagToBytes()); + break; + case REMOVE: + break; + case UPDATE_HEALTH: + buf.writeFloat(health); + break; + case UPDATE_TITLE: + PacketUtil.writeString(fixJson(title), buf); + break; + case UPDATE_STYLE: + PacketUtil.writeVarInt(color.getId(), buf); + PacketUtil.writeVarInt(style.getId(), buf); + break; + case UPDATE_FLAGS: + buf.writeByte(flagToBytes()); + break; + } + + return buf; + } + + private int flagToBytes() { + int bitmask = 0; + for (BossFlag flag : flags) + bitmask |= flag.getId(); + return bitmask; + } + + private String fixJson(String text) { + return OutgoingTransformer.fixJson(text); + } + + private enum UpdateAction { + ADD(0), + REMOVE(1), + UPDATE_HEALTH(2), + UPDATE_TITLE(3), + UPDATE_STYLE(4), + UPDATE_FLAGS(5); + + private final int id; + + UpdateAction(int id) { + this.id = id; + } + + public int getId() { + return id; + } + } +}