diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8c3df84 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +### IntelliJ IDEA ### +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ +.idea/ +PAC_API.iml + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4498566 --- /dev/null +++ b/README.md @@ -0,0 +1,170 @@ +# AZ Launcher (aka Pactify Launcher) API + +--- + +Cette API a pour but d'implémenter d'une part, les fonctionnalités offertes par le [PLSP Protocol](https://github.com/PactifyLauncherExamples/maven-repository) permettant de communiquer avec le [AZ Launcher](https://www.az-launcher.nz/). Et d'autre part les autres fonctionnalités liées au Launcher (menus GUIs). + +Version de Minecraft testée : `CraftBukkit version git-Spigot-c6871e2-e1d3516 (MC: 1.9.4)` + +Ce repository n'est sans doute pas complet et peut être amélioré. Libre à vous d'y contribuer ! + +## Installation + +Téléchargez le .jar dans les releases (ou compillez-le) et ajoutez-le en dépendance de votre projet. Compillez-le avec votre plugin final. + +## Utilisation + +### PLSP + +Pour fonctionner, vous devez nécéssairement instancier le `PactifyManager` au lancement de votre plugin. + +```java +private PactifyManager pactifyManager; + +@Override +public void onEnable() { + pactifyManager = new PactifyManager(this); //Instanciation par défaut + //Instanciation avec des ConfFlags par défauts + //Ceux-ci seront envoyés lorsqu'un joueur rejoint. + pactifyManager = new PactifyManager(this, Arrays.asList(PLSPConfFlag.SEE_CHUNKS, PLSPConfFlag.SMOOTH_EXPERIENCE_BAR)); +} +``` + +Exemple de transformation + +```java +@EventHandler +public void onJoin(PlayerJoinEvent event) { + pactifyManager.getPlayer(event.getPlayer()).rescalePlayer(1.5f); + pactifyManager.getPlayer(event.getPlayer()).editOpacity(0.8f); + pactifyManager.getPlayer(event.getPlayer()).transformIntoMob(EntityType.ZOMBIE); + //Ou + PactifyTransformation trans = new PactifyTransformation(EntityType.ZOMBIE, 1.5f); + trans.setOpacity(0.8f); + pactifyManager.getPlayer(event.getPlayer()).setTransformation(trans); + pactifyManager.getPlayer(event.getPlayer()).applyTransformation(); +} +``` + +### NBT GUIs + +Exemple d'utilisation des NBT GUIs. + +```java +event.getPlayer().getInventory().setItem(4, new PactifyItem(new ItemStack(Material.QUARTZ_BLOCK)) + .addPacDisplay(new PacDisplay() + .setSprite(Sprite.EMOJI) + .setSpriteData("\uEEEE\uDBFF\uDF2D") + .setChilds(new PactifyItem.PacDisplayChild() + .setMaterial(Material.GRASS) + .setCount(1) + .setPactifyDisplay(new PacDisplay() + .setSprite(Sprite.EMOJI) + .setSpriteData("\uEEEE\uDBFF\uDEE6") + .setScale(1.2f) + .setZIndex(-1.0f) + ) + ) + ).addPacMenu(new PacMenu() + .setBackground(false) + .setState(PactifyItem.MenuState.DISABLED) + ).getItemStack()); +``` + +### GUIs Transparants +*À venir ...* + +## Remerciements +Merci beaucoup à [@nathan818fr](https://github.com/nathan818fr/) pour +- son travail sur le AZ Launcher, +- d'avoir rendu ces informations disponibles, +- ses quelques coups de main +- son autorisation pour ce repo. + +--- + +# AZ Launcher (aka Pactify Launcher) API + +This API has been made for +- Implement the functionalities included in the [PLSP Protocol](https://github.com/PactifyLauncherExamples/maven-repository) which allow to communicate with the [AZ Launcher](https://www.az-launcher.nz/). +- Integrate other functionalities related to the Launcher such as NBT GUIs. + +Tested minecraft version : `CraftBukkit version git-Spigot-c6871e2-e1d3516 (MC: 1.9.4)`. + +This repository might not be complete and can be improved. Feel free to contribute! + +## Installation + +Download the .jar file in the releases (or compile it) and add it as a dependency to your project. Compile it with your final plugin. + +## Usage + +### PLSP + +You need to instantiate the `PactifyManager` class at the start of your plugin. + +```java +private PactifyManager pactifyManager; + +@Override +public void onEnable() { + pactifyManager = new PactifyManager(this); //Instanciation par défaut + //Instanciation avec des ConfFlags par défauts + //Ceux-ci seront envoyés lorsqu'un joueur rejoint. + pactifyManager = new PactifyManager(this, Arrays.asList(PLSPConfFlag.SEE_CHUNKS, PLSPConfFlag.SMOOTH_EXPERIENCE_BAR)); +} +``` + +Transformation example + +```java +@EventHandler +public void onJoin(PlayerJoinEvent event) { + pactifyManager.getPlayer(event.getPlayer()).rescalePlayer(1.5f); + pactifyManager.getPlayer(event.getPlayer()).editOpacity(0.8f); + pactifyManager.getPlayer(event.getPlayer()).transformIntoMob(EntityType.ZOMBIE); + //Ou + PactifyTransformation trans = new PactifyTransformation(EntityType.ZOMBIE, 1.5f); + trans.setOpacity(0.8f); + pactifyManager.getPlayer(event.getPlayer()).setTransformation(trans); + pactifyManager.getPlayer(event.getPlayer()).applyTransformation(); +} +``` + +### NBT GUIs + +NBT GUIs usage example. + +```java +event.getPlayer().getInventory().setItem(4, new PactifyItem(new ItemStack(Material.QUARTZ_BLOCK)) + .addPacDisplay(new PacDisplay() + .setSprite(Sprite.EMOJI) + .setSpriteData("\uEEEE\uDBFF\uDF2D") + .setChilds(new PactifyItem.PacDisplayChild() + .setMaterial(Material.GRASS) + .setCount(1) + .setPactifyDisplay(new PacDisplay() + .setSprite(Sprite.EMOJI) + .setSpriteData("\uEEEE\uDBFF\uDEE6") + .setScale(1.2f) + .setZIndex(-1.0f) + ) + ) + ).addPacMenu(new PacMenu() + .setBackground(false) + .setState(PactifyItem.MenuState.DISABLED) + ).getItemStack()); +``` + +### Clear GUI + +*Coming soon...* + +## Many thanks +Thanks to [@nathan818fr](https://github.com/nathan818fr/) for +- his work on the AZ Launcher, +- making these informations available, +- his assistance, +- his permission for this repository. + +--- \ No newline at end of file diff --git a/src/fr/thebatteur/pacapi/items/PactifyItem.java b/src/fr/thebatteur/pacapi/items/PactifyItem.java new file mode 100644 index 0000000..54feea9 --- /dev/null +++ b/src/fr/thebatteur/pacapi/items/PactifyItem.java @@ -0,0 +1,276 @@ +package fr.thebatteur.pacapi.items; + +import fr.thebatteur.pacapi.utils.NBTUtil; +import net.minecraft.server.v1_9_R2.NBTTagCompound; +import org.bukkit.Material; +import org.bukkit.craftbukkit.v1_9_R2.inventory.CraftItemStack; +import org.bukkit.inventory.ItemStack; + +public class PactifyItem { + + private ItemStack itemStack; + + public PactifyItem(ItemStack itemStack) { + this.itemStack = itemStack; + } + + public PactifyItem addPacDisplay(PacDisplay display) { + System.out.println(display.getNbtUtil().toString()); + this.addNBTTags("PacDisplay", display.getNbtUtil().toNBT()); + return this; + } + + public PactifyItem addPacRender(PacRender render) { + this.addNBTTags("PacRender", render.getNbtUtil().toNBT()); + return this; + } + + public PactifyItem addPacMenu(PacMenu menu) { + this.addNBTTags("PacMenu", menu.getNbtUtil().toNBT()); + return this; + } + + private void addNBTTags(String name, NBTTagCompound compound) { + net.minecraft.server.v1_9_R2.ItemStack nmsItemStack = CraftItemStack.asNMSCopy(this.itemStack); + NBTTagCompound nmsCompound = nmsItemStack.getTag(); + if (nmsCompound == null) { + nmsCompound = new NBTTagCompound(); + } + nmsCompound.set(name, compound); + nmsItemStack.setTag(nmsCompound); + this.itemStack = CraftItemStack.asBukkitCopy(nmsItemStack); + } + + public ItemStack getItemStack() { + return itemStack; + } + + public static class PacDisplay { + private NBTUtil nbtUtil; + + public PacDisplay() { + this.nbtUtil = new NBTUtil(); + } + + /** + * Utiliser un sprite qui remplace la texture de l'item + * @param sprite + * @return + */ + public PacDisplay setSprite(Sprite sprite) { + this.nbtUtil.addTag("Sprite", sprite.name()); + return this; + } + + /** + * Si Sprite="EMOJI" il s'agit de l'emoji à afficher + * @param spriteData + * @return + */ + public PacDisplay setSpriteData(String spriteData) { + this.nbtUtil.addTag("SpriteData", spriteData); + return this; + } + + /** + * Re-colore la la texture (int au format 0xAARRGGBB, ex pour du vert pur c'est 0xFF00FF00, ce qui donne -16711936) + * @param color + * @return + */ + public PacDisplay setColor(int color) { + this.nbtUtil.addTag("Color", color); + return this; + } + + /** + * Déplace la texture sur l'axe X + * @param translatX + * @return + */ + public PacDisplay setTranslatX(float translatX) { + this.nbtUtil.addTag("TranslatX", translatX); + return this; + } + + /** + * Déplace la texture sur l'axe Y + * @param translatY + * @return + */ + public PacDisplay setTranslatY(float translatY) { + this.nbtUtil.addTag("TranslatY", translatY); + return this; + } + + /** + * Fait tourner la texture + * @param rotation + * @return + */ + public PacDisplay setRotation(float rotation) { + this.nbtUtil.addTag("Rotation", rotation); + return this; + } + + /** + * Change la taille de la texture (1 = taille normale) + * @param scale + * @return + */ + public PacDisplay setScale(float scale) { + this.nbtUtil.addTag("Scale", scale); + return this; + } + + /** + * Change la taille de la texture sur l'axe X (1 = taille normale, seulement si Scale n'est pas défini) + * @param scaleX + * @return + */ + public PacDisplay setScaleX(float scaleX) { + this.nbtUtil.addTag("ScaleX", scaleX); + return this; + } + + /** + * Change la taille de la texture sur l'axe Y (1 = taille normale, seulement si Scale n'est pas défini) + * @param scaleY + * @return + */ + public PacDisplay setScaleY(float scaleY) { + this.nbtUtil.addTag("ScaleY", scaleY); + return this; + } + + /** + * Change l'ordre de rendu de la texture (à n'utiliser que si la texture apparait derrière une autre, etc) + * @param zIndex + * @return + */ + public PacDisplay setZIndex(float zIndex) { + this.nbtUtil.addTag("ZIndex", zIndex); + return this; + } + + /** + * Permet d'afficher plusieurs items en un en les superposant + * @param childs + * @return + */ + public PacDisplay setChilds(PacDisplayChild childs) { + this.nbtUtil.addTag("Childs", new NBTUtil.NBTListUtil().addTag(childs.getNbtUtil().toNBT()).toList()); + return this; + } + + public NBTUtil getNbtUtil() { + return nbtUtil; + } + } + + public static class PacDisplayChild { + private NBTUtil nbtUtil; + + public PacDisplayChild() { + this.nbtUtil = new NBTUtil(); + } + + /** + * Type de l'item + * @param material + */ + public PacDisplayChild setMaterial(Material material) { + this.nbtUtil.addTag("id", "minecraft:" + material.name().toLowerCase()); + return this; + } + + /** + * Nombre d'items + * @param count + */ + public PacDisplayChild setCount(int count) { + this.nbtUtil.addTag("Count", count); + return this; + } + + /** + * Ajouter des caractéristiques PacDisplay à l'item + * @param pacDisplay + */ + public PacDisplayChild setPactifyDisplay(PacDisplay pacDisplay) { + this.nbtUtil.addTag("tag", new NBTUtil().addTag("PacDisplay", pacDisplay.getNbtUtil().toNBT()).toNBT()); + return this; + } + + public NBTUtil getNbtUtil() { + return nbtUtil; + } + } + + public static class PacRender { + private NBTUtil nbtUtil; + + public PacRender() { + this.nbtUtil = new NBTUtil(); + } + + /** + * Re-colore la texture (int au format 0xAARRGGBB) + * @param color + */ + public PacRender setColor(int color) { + this.nbtUtil.addTag("Color", color); + return this; + } + + /** + * Change la taille (1 = taille normale) + * @param scale + */ + public PacRender setScale(float scale) { + this.nbtUtil.addTag("Scale", scale); + return this; + } + + public NBTUtil getNbtUtil() { + return nbtUtil; + } + } + + public static class PacMenu { + private NBTUtil nbtUtil; + + public PacMenu() { + this.nbtUtil = new NBTUtil(); + } + + /** + * Afficher ou non le background (la texture de "bouton" cliquable) + * @param background + */ + public PacMenu setBackground(boolean background) { + this.nbtUtil.addTag("Background", background); + return this; + } + + /** + * Modifier l'état de la texture du background + * @param menuState + */ + public PacMenu setState(MenuState menuState) { + this.nbtUtil.addTag("State", menuState.name()); + return this; + } + + public NBTUtil getNbtUtil() { + return nbtUtil; + } + } + + public enum MenuState { + DISABLED, + NORMAL, + HOVER, + ACTIVE; + } + +} diff --git a/src/fr/thebatteur/pacapi/items/Sprite.java b/src/fr/thebatteur/pacapi/items/Sprite.java new file mode 100644 index 0000000..0447310 --- /dev/null +++ b/src/fr/thebatteur/pacapi/items/Sprite.java @@ -0,0 +1,51 @@ +package fr.thebatteur.pacapi.items; + +public enum Sprite { + + PLAY_RIGHT_ARROW, + PLAY_LEFT_ARROW, + SERVER_DOWN_ARROW, + SERVER_UP_ARROW, + BOOK_NEXT_PAGE, + BOOK_PREV_PAGE, + PADLOCK_CLOSE, + PADLOCK_OPEN, + WORLD_LENS, + MAIL, + MAIL_NEW, + SIGNAL_RED, + SIGNAL_GREEN, + SIGNAL_GRAY, + BEACON_CHECK, + BEACON_CROSS, + EFFECT_MOVESPEED, + EFFECT_MOVESLOWDOWN, + EFFECT_DIGSPEED, + EFFECT_DIGSLOWDOWN, + EFFECT_DAMAGEBOOST, + EFFECT_HEAL, + EFFECT_HARM, + EFFECT_JUMP, + EFFECT_CONFUSION, + EFFECT_REGENERATION, + EFFECT_RESISTANCE, + EFFECT_FIRERESISTANCE, + EFFECT_WATERBREATHING, + EFFECT_INVISIBILITY, + EFFECT_BLINDNESS, + EFFECT_NIGHTVISION, + EFFECT_HUNGER, + EFFECT_WEAKNESS, + EFFECT_POISON, + EFFECT_WITHER, + EFFECT_HEALTHBOOST, + EFFECT_ABSORPTION, + EFFECT_SATURATION, + EFFECT_GLOWING, + EFFECT_LEVITATION, + EFFECT_LUCK, + EFFECT_UNLUCK, + EMOJI; + + +} diff --git a/src/fr/thebatteur/pacapi/plsp/PLSPConfFlag.java b/src/fr/thebatteur/pacapi/plsp/PLSPConfFlag.java new file mode 100644 index 0000000..ee3fa15 --- /dev/null +++ b/src/fr/thebatteur/pacapi/plsp/PLSPConfFlag.java @@ -0,0 +1,31 @@ +package fr.thebatteur.pacapi.plsp; + +import pactify.client.api.plsp.packet.client.PLSPPacketConfFlag; + +public enum PLSPConfFlag { + + ATTACK_COOLDOWN, + PLAYER_PUSH, + LARGE_HITBOX, + SWORD_BLOCKING, + HIT_AND_BLOCK, + OLD_ENCHANTEMENTS, + PVP_HIT_PRIORITY, + SEE_CHUNKS, + SIDEBAR_SCORES, + SMOOTH_EXPERIENCE_BAR, + SORT_TAB_LIST_BY_NAMES; + + public PLSPPacketConfFlag toPacket() { + return this.toPacket(true); + } + + public PLSPPacketConfFlag toPacket(boolean activated) { + return new PLSPPacketConfFlag(this.toString(), activated); + } + + @Override + public String toString() { + return super.toString().toLowerCase(); + } +} diff --git a/src/fr/thebatteur/pacapi/plsp/PactifyManager.java b/src/fr/thebatteur/pacapi/plsp/PactifyManager.java new file mode 100644 index 0000000..4b4a0c0 --- /dev/null +++ b/src/fr/thebatteur/pacapi/plsp/PactifyManager.java @@ -0,0 +1,128 @@ +/* + * Class issue du repo https://github.com/PactifyLauncherExamples/PactifyPlugin + */ +package fr.thebatteur.pacapi.plsp; + +import fr.thebatteur.pacapi.plsp.PLSPConfFlag; +import fr.thebatteur.pacapi.plsp.PactifyPlayer; +import fr.thebatteur.pacapi.utils.BukkitUtil; +import fr.thebatteur.pacapi.utils.PacketOutBuffer; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.plugin.Plugin; +import pactify.client.api.mcprotocol.NotchianPacketBuffer; +import pactify.client.api.mcprotocol.util.NotchianPacketUtil; +import pactify.client.api.plsp.PLSPPacket; +import pactify.client.api.plsp.PLSPPacketHandler; +import pactify.client.api.plsp.PLSPProtocol; + +import java.io.Closeable; +import java.io.IOException; +import java.util.*; +import java.util.logging.Level; + +public class PactifyManager implements Listener, Closeable { + private Plugin plugin; + + private int serverVersion; + + public Plugin getPlugin() { + return this.plugin; + } + + public int getServerVersion() { + return this.serverVersion; + } + + private final Map players = new HashMap<>(); + private List defaultFlags; + + public PactifyManager(Plugin plugin) { + this.plugin = plugin; + this.serverVersion = BukkitUtil.findServerVersion(); + plugin.getServer().getPluginManager().registerEvents(this, plugin); + plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, "PLSP"); + Bukkit.getLogger().info(String.valueOf(getClass().getSimpleName()) + " class loaded"); + } + + public PactifyManager(Plugin plugin, List defaultFlags) { + this.plugin = plugin; + this.serverVersion = BukkitUtil.findServerVersion(); + this.defaultFlags = defaultFlags; + plugin.getServer().getPluginManager().registerEvents(this, plugin); + plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, "PLSP"); + Bukkit.getLogger().info(String.valueOf(getClass().getSimpleName()) + " class loaded"); + } + + public PactifyPlayer getPlayer(Player player) { + return this.players.get(player.getUniqueId()); + } + + private void playerQuit(Player player) { + PactifyPlayer pactifyPlayer = this.players.remove(player.getUniqueId()); + if (pactifyPlayer != null) + pactifyPlayer.free(true); + } + + public void sendPLSPMessage(Player player, PLSPPacket message) { + try { + PacketOutBuffer buf = new PacketOutBuffer(); + PLSPProtocol.PacketData packetData = PLSPProtocol.getClientPacketByClass(message.getClass()); + NotchianPacketUtil.writeString((NotchianPacketBuffer) buf, packetData.getId(), 32767); + message.write((NotchianPacketBuffer) buf); + player.sendPluginMessage(this.plugin, "PLSP", buf.toBytes()); + } catch (Exception e) { + this.plugin.getLogger().log(Level.WARNING, "Exception sending PLSP message to " + ((player != null) ? player.getName() : "null") + ":", e); + } + } + + public void close() throws IOException { + HandlerList.unregisterAll(this); + this.plugin.getServer().getMessenger().unregisterOutgoingPluginChannel(this.plugin, "PLSP"); + } + + //Listeners --------------------------- + + @EventHandler(priority = EventPriority.LOWEST) + private void onPlayerLogin(PlayerLoginEvent event) { + event.getPlayer().setMetadata("PACAPI:hostname", + (MetadataValue) new FixedMetadataValue(this.plugin, event.getHostname())); + PactifyPlayer pactifyPlayer; + this.players.put(event.getPlayer().getUniqueId(), pactifyPlayer = new PactifyPlayer(this, event.getPlayer())); + pactifyPlayer.init(); + } + + @EventHandler(priority = EventPriority.MONITOR) + private void onPlayerLoginMonitor(PlayerLoginEvent event) { + if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) + playerQuit(event.getPlayer()); + } + + @EventHandler + private void onPlayerJoin(PlayerJoinEvent event) { + PactifyPlayer pactifyPlayer = getPlayer(event.getPlayer()); + pactifyPlayer.join(defaultFlags); + this.players.forEach(((uuid, pactifyPlayer1) -> { + if (pactifyPlayer1.isTransformed()) + sendPLSPMessage(pactifyPlayer.getPlayer(), pactifyPlayer1.getTransformation().toMobPacket(uuid)); + if (pactifyPlayer1.isScaled() || pactifyPlayer1.isntDefaultOpacity()) + sendPLSPMessage(pactifyPlayer.getPlayer(), pactifyPlayer1.getTransformation().toScalePacket(pactifyPlayer1.getPlayer())); + })); + } + + + + @EventHandler(priority = EventPriority.HIGHEST) + private void onPlayerQuit(PlayerQuitEvent event) { + playerQuit(event.getPlayer()); + } +} diff --git a/src/fr/thebatteur/pacapi/plsp/PactifyPlayer.java b/src/fr/thebatteur/pacapi/plsp/PactifyPlayer.java new file mode 100644 index 0000000..c8708fc --- /dev/null +++ b/src/fr/thebatteur/pacapi/plsp/PactifyPlayer.java @@ -0,0 +1,175 @@ +/* + * Class issue du repo https://github.com/PactifyLauncherExamples/PactifyPlugin + */ +package fr.thebatteur.pacapi.plsp; + +import fr.thebatteur.pacapi.utils.BukkitUtil; +import org.bukkit.Bukkit; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.metadata.MetadataValue; +import pactify.client.api.plsp.PLSPPacket; +import pactify.client.api.plsp.PLSPPacketHandler; +import pactify.client.api.plsp.packet.client.PLSPPacketReset; +import pactify.client.api.plsp.packet.client.PLSPPacketVignette; + +import java.awt.*; +import java.util.*; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class PactifyPlayer { + + private static final Pattern PACTIFY_HOSTNAME_PATTERN = Pattern.compile("[\000\002]PAC([0-9A-F]{5})[\000\002]"); + + private final PactifyManager service; + + private final Player player; + private PactifyTransformation transformation; + + private boolean joined; + + private int launcherProtocolVersion; + + public PactifyPlayer(PactifyManager service, Player player) { + this.service = service; + this.player = player; + this.transformation = new PactifyTransformation(EntityType.PLAYER); + } + + public void init() { + List hostnameMeta = this.player.getMetadata("PACAPI:hostname"); + if (!hostnameMeta.isEmpty()) { + String hostname = ((MetadataValue)hostnameMeta.get(0)).asString(); + Matcher m = PACTIFY_HOSTNAME_PATTERN.matcher(hostname); + if (m.find()) + this.launcherProtocolVersion = Math.max(1, Integer.parseInt(m.group(1), 16)); + } else { + this.service.getPlugin().getLogger().warning("Unable to verify the launcher of " + this.player.getName() + + ": it probably logged when the plugin was disabled!"); + } + BukkitUtil.addChannel(this.player, "PLSP"); + } + + public void join(List activatedFlags) { + this.joined = true; + this.service.sendPLSPMessage(this.player, (PLSPPacket) new PLSPPacketReset()); + this.setFlags(activatedFlags, true); + } + + public void setFlag(PLSPConfFlag flag, boolean activated) { + this.service.sendPLSPMessage(this.player, flag.toPacket(activated)); + } + + public void setFlags(List flags, boolean activated) { + for (PLSPConfFlag flag : flags) { + this.service.sendPLSPMessage(player, flag.toPacket(activated)); + } + } + + public void setFlags(List flags, List activated) { + for (int i = 0; i < flags.size(); i++) { + Optional activatedFlag = Optional.ofNullable(activated.get(i)); + this.service.sendPLSPMessage(player, flags.get(i).toPacket(activatedFlag.orElseGet(() -> activated.get(activated.size() - 1)))); + } + } + + public void resetPLSPPackets() { + this.service.sendPLSPMessage(this.player, new PLSPPacketReset()); + } + + public void setVignette(Color color) { + this.service.sendPLSPMessage(this.player, new PLSPPacketVignette(true, color.getRed()/255.0F, color.getGreen()/255.0F, color.getBlue()/255.0F)); + } + + public void transformIntoMob(EntityType entityType) { + this.transformation.setEntityType(entityType); + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { + this.service.sendPLSPMessage(onlinePlayer, this.transformation.toMobPacket(player.getUniqueId())); + this.service.sendPLSPMessage(onlinePlayer, this.transformation.toScalePacket(player)); + } + } + + public void rescalePlayer(float scale) { + this.transformation.setScale(scale); + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { + this.service.sendPLSPMessage(onlinePlayer, transformation.toScalePacket(this.player)); + } + } + + public void editOpacity(float opacity) { + this.transformation.setOpacity(opacity); + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { + this.service.sendPLSPMessage(onlinePlayer, transformation.toScalePacket(this.player)); + } + } + + public PactifyTransformation getTransformation() { + return this.transformation; + } + + public boolean isTransformed() { + return this.transformation.getEntityType() != EntityType.PLAYER; + } + + public boolean isScaled() { + return Math.abs(this.transformation.getScale()-1.0) <= 0.001F; + } + + public boolean isntDefaultOpacity() { + return Math.abs(this.transformation.getOpacity()-1.0) <= 0.001F; + } + + public boolean hasSupPrefix() { + return this.transformation.getSupPrefix() != null && !this.transformation.getSupPrefix().isEmpty(); + } + + public void free(boolean onQuit) { + if (!onQuit) + this.service.sendPLSPMessage(this.player, (PLSPPacket)new PLSPPacketReset()); + } + + public void setTransformation(PactifyTransformation transformation) { + this.transformation = transformation; + } + + public void applyTransformation() { + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { + this.service.sendPLSPMessage(onlinePlayer, this.transformation.toMobPacket(this.player.getUniqueId())); + this.service.sendPLSPMessage(onlinePlayer, this.transformation.toScalePacket(this.player)); + } + } + + public PactifyManager getService() { + return this.service; + } + + public Player getPlayer() { + return this.player; + } + + public boolean isJoined() { + return this.joined; + } + + public void setJoined(boolean joined) { + this.joined = joined; + } + + public void setLauncherProtocolVersion(int launcherProtocolVersion) { + this.launcherProtocolVersion = launcherProtocolVersion; + } + + public boolean hasLauncher() { + return (this.launcherProtocolVersion > 0); + } + + public int getLauncherProtocolVersion() { + return this.launcherProtocolVersion; + } + + public String toString() { + return "PactifyPlayer(service=" + getService() + ", player=" + getPlayer() + ", joined=" + isJoined() + ", launcherProtocolVersion=" + getLauncherProtocolVersion() + ")"; + } +} diff --git a/src/fr/thebatteur/pacapi/plsp/PactifyTransformation.java b/src/fr/thebatteur/pacapi/plsp/PactifyTransformation.java new file mode 100644 index 0000000..cd141f0 --- /dev/null +++ b/src/fr/thebatteur/pacapi/plsp/PactifyTransformation.java @@ -0,0 +1,81 @@ +package fr.thebatteur.pacapi.plsp; + +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import pactify.client.api.plprotocol.metadata.ImmutablePactifyScaleMetadata; +import pactify.client.api.plprotocol.metadata.PactifyModelMetadata; +import pactify.client.api.plprotocol.metadata.PactifyTagMetadata; +import pactify.client.api.plsp.packet.client.PLSPPacketEntityMeta; +import pactify.client.api.plsp.packet.client.PLSPPacketPlayerModel; + +import java.util.UUID; + +public class PactifyTransformation { + + private EntityType entityType; + private float scale; + private float opacity; + private String supPrefix; + + public PactifyTransformation(EntityType entityType) { + this.entityType = entityType; + this.scale = 1.0F; + this.opacity = 1.0F; + } + + public PactifyTransformation(EntityType entityType, float scale) { + this.entityType = entityType; + this.scale = scale; + this.opacity = -1.0F; + } + + public EntityType getEntityType() { + return entityType; + } + + public float getScale() { + return scale; + } + + public float getOpacity() { + return this.opacity; + } + + public String getSupPrefix() { + return supPrefix; + } + + public void setEntityType(EntityType entityType) { + this.entityType = entityType; + } + + public void setScale(float scale) { + this.scale = scale; + } + + public void setOpacity(float opacity) { + this.opacity = opacity; + } + + public void setSupPrefix(String supPrefix) { + this.supPrefix = supPrefix; + } + + public PLSPPacketPlayerModel toMobPacket(UUID uuid) { + return new PLSPPacketPlayerModel(uuid, new PactifyModelMetadata(this.entityType.getTypeId())); + } + + public PLSPPacketEntityMeta toScalePacket(Player player) { + PLSPPacketEntityMeta packet = new PLSPPacketEntityMeta(); + ImmutablePactifyScaleMetadata ipsm = new ImmutablePactifyScaleMetadata(this.getScale(), this.getScale(), this.getScale(), this.getScale(), this.getScale(), true); + packet.setEntityId(player.getEntityId()); + packet.setScale(ipsm); + PactifyTagMetadata ptm = new PactifyTagMetadata(); + if (this.supPrefix != null && !this.supPrefix.isEmpty()) + ptm.setText(this.supPrefix); + ptm.setScale(this.scale); + packet.setOpacity(this.opacity); + packet.setSupTag(ptm); + return packet; + } +} diff --git a/src/fr/thebatteur/pacapi/utils/BukkitUtil.java b/src/fr/thebatteur/pacapi/utils/BukkitUtil.java new file mode 100644 index 0000000..7892aa0 --- /dev/null +++ b/src/fr/thebatteur/pacapi/utils/BukkitUtil.java @@ -0,0 +1,52 @@ +/* + * Class issue du repo https://github.com/PactifyLauncherExamples/PactifyPlugin + */ +package fr.thebatteur.pacapi.utils; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.lang.reflect.Method; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class BukkitUtil { + private static final Method PLAYER_ADDCHANNEL_METHOD; + + private BukkitUtil() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + private static final Pattern SERVER_VERSION = Pattern.compile("\\(MC: (?[0-9]{1,3})\\.(?[0-9]{1,3})(?:\\.(?[0-9]{1,3}))?\\)"); + + static { + String ocbPackage = Bukkit.getServer().getClass().getName().replaceAll("\\.[^.]+$", ""); + try { + Class playerClass = Class.forName(String.valueOf(String.valueOf(ocbPackage)) + ".entity.CraftPlayer"); + Method addChannelMethod = playerClass.getDeclaredMethod("addChannel", new Class[] { String.class }); + addChannelMethod.setAccessible(true); + PLAYER_ADDCHANNEL_METHOD = addChannelMethod; + } catch (ClassNotFoundException|NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + public static int findServerVersion() { + Matcher m = SERVER_VERSION.matcher(Bukkit.getVersion()); + if (m.find()) { + int major = Integer.parseInt(m.group("major")); + int minor = Integer.parseInt(m.group("minor")); + int patch = (m.group("patch") != null) ? Integer.parseInt(m.group("patch")) : 0; + return major * 1000000 + minor * 1000 + patch; + } + throw new RuntimeException("Unable to detect server version! Bukkit.getVersion()=" + Bukkit.getVersion()); + } + + public static void addChannel(Player player, String channel) { + try { + PLAYER_ADDCHANNEL_METHOD.invoke(player, channel); + } catch (IllegalAccessException|java.lang.reflect.InvocationTargetException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/fr/thebatteur/pacapi/utils/NBTUtil.java b/src/fr/thebatteur/pacapi/utils/NBTUtil.java new file mode 100644 index 0000000..ca00fb1 --- /dev/null +++ b/src/fr/thebatteur/pacapi/utils/NBTUtil.java @@ -0,0 +1,88 @@ +package fr.thebatteur.pacapi.utils; + +import net.minecraft.server.v1_9_R2.NBTBase; +import net.minecraft.server.v1_9_R2.NBTTagCompound; +import net.minecraft.server.v1_9_R2.NBTTagList; + +public class NBTUtil { + private NBTTagCompound compound; + + public NBTUtil(NBTTagCompound compound) { + this.compound = compound; + } + + public NBTUtil() { + this.compound = new NBTTagCompound(); + } + + public NBTUtil addTag(String name, int value) { + this.compound.setInt(name, value); + return this; + } + + public NBTUtil addTag(String name, String value) { + this.compound.setString(name, value); + return this; + } + + public NBTUtil addTag(String name, NBTBase value) { + this.compound.set(name, value); + return this; + } + + public NBTUtil addTag(String name, boolean value) { + this.compound.setBoolean(name, value); + return this; + } + + public NBTUtil addTag(String name, double value) { + this.compound.setDouble(name, value); + return this; + } + + public NBTUtil addTag(String name, float value) { + this.compound.setFloat(name, value); + return this; + } + + public NBTUtil addTag(String name, long value) { + this.compound.setLong(name, value); + return this; + } + + public NBTUtil addCompound(String name, NBTTagCompound compound) { + this.compound.set(name, compound); + return this; + } + + public NBTUtil addList(String name, NBTTagList list) { + this.compound.set(name, list); + return this; + } + + public NBTTagCompound toNBT() { + return this.compound; + } + + @Override + public String toString() { + return this.compound.toString(); + } + + public static class NBTListUtil { + private NBTTagList list; + + public NBTListUtil() { + list = new NBTTagList(); + } + + public NBTListUtil addTag(NBTBase value) { + list.add(value); + return this; + } + + public NBTTagList toList() { + return this.list; + } + } +} diff --git a/src/fr/thebatteur/pacapi/utils/PacketOutBuffer.java b/src/fr/thebatteur/pacapi/utils/PacketOutBuffer.java new file mode 100644 index 0000000..49edc00 --- /dev/null +++ b/src/fr/thebatteur/pacapi/utils/PacketOutBuffer.java @@ -0,0 +1,110 @@ +/* + * Class issue du repo https://github.com/PactifyLauncherExamples/PactifyPlugin + */ +package fr.thebatteur.pacapi.utils; + +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import pactify.client.api.mcprotocol.AbstractNotchianPacketBuffer; + +public class PacketOutBuffer extends AbstractNotchianPacketBuffer { + private final ByteArrayDataOutput handle = ByteStreams.newDataOutput(); + + public PacketOutBuffer writeBytes(byte[] src) { + this.handle.write(src); + return this; + } + + public PacketOutBuffer writeBytes(byte[] src, int offset, int len) { + this.handle.write(src, offset, len); + return this; + } + + public PacketOutBuffer writeBoolean(boolean value) { + this.handle.writeBoolean(value); + return this; + } + + public PacketOutBuffer writeByte(int value) { + this.handle.writeByte(value); + return this; + } + + public PacketOutBuffer writeShort(int value) { + this.handle.writeShort(value); + return this; + } + + public PacketOutBuffer writeInt(int value) { + this.handle.writeInt(value); + return this; + } + + public PacketOutBuffer writeLong(long value) { + this.handle.writeLong(value); + return this; + } + + public PacketOutBuffer writeFloat(float value) { + this.handle.writeFloat(value); + return this; + } + + public PacketOutBuffer writeDouble(double value) { + this.handle.writeDouble(value); + return this; + } + + public PacketOutBuffer writeVarInt(int value) { + while ((value & 0xFFFFFF80) != 0) { + this.handle.writeByte(value & 0x7F | 0x80); + value >>>= 7; + } + this.handle.writeByte(value); + return this; + } + + public PacketOutBuffer writeVarLong(long value) { + while ((value & 0xFFFFFFFFFFFFFF80L) != 0L) { + writeByte((int)(value & 0x7FL) | 0x80); + value >>>= 7L; + } + writeByte((int)value); + return this; + } + + public PacketOutBuffer writeByteArray(byte[] bytes) { + writeVarInt(bytes.length); + writeBytes(bytes); + return this; + } + + public PacketOutBuffer writeVarIntArray(int[] ints) { + writeVarInt(ints.length); + byte b; + int i, arrayOfInt[]; + for (i = (arrayOfInt = ints).length, b = 0; b < i; ) { + int value = arrayOfInt[b]; + writeVarInt(value); + b++; + } + return this; + } + + public PacketOutBuffer writeLongArray(long[] longs) { + writeVarInt(longs.length); + byte b; + int i; + long[] arrayOfLong; + for (i = (arrayOfLong = longs).length, b = 0; b < i; ) { + long value = arrayOfLong[b]; + writeLong(value); + b++; + } + return this; + } + + public byte[] toBytes() { + return this.handle.toByteArray(); + } +}