Updated to new server location, and 1.16.3

This commit is contained in:
stijnb1234 2020-09-17 16:44:17 +02:00
parent 4b9c5d3a59
commit 833b55d6ca
12 changed files with 2699 additions and 465 deletions

View file

@ -3,8 +3,9 @@
<output-path>$PROJECT_DIR$/out/artifacts/MCTPAudio_jar</output-path>
<root id="archive" name="MCTPAudio.jar">
<element id="module-output" name="MCTPAudio" />
<element id="extracted-dir" path="$USER_HOME$/Downloads/Java-WebSocket-1.3.8.jar" path-in-jar="/" />
<element id="extracted-dir" path="$USER_HOME$/Downloads/menuapi-1.0.jar" path-in-jar="/" />
<element id="extracted-dir" path="$USER_HOME$/Downloads/mp3agic-0.9.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$USER_HOME$/Downloads/Java-WebSocket-1.3.8.jar" path-in-jar="/" />
</root>
</artifact>
</component>

11
.idea/libraries/spigot_1_16_3.xml generated Normal file
View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="spigot-1.16.3">
<CLASSES>
<root url="jar://$USER_HOME$/Downloads/spigot-1.16.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/Downloads/spigot-1.16.3.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -16,8 +16,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="PROVIDED" name="spigot-1.14.4" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Java-WebSocket-1.3.8" level="project" />
<orderEntry type="library" name="Java-WebSocket-1.3.8" level="project" />
<orderEntry type="module-library" scope="PROVIDED">
<library>
<CLASSES>
@ -54,5 +53,15 @@
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$USER_HOME$/Downloads/menuapi-1.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="library" scope="PROVIDED" name="spigot-1.16.3" level="project" />
</component>
</module>

View file

@ -1,12 +1,12 @@
package me.mctp;
import me.mctp.commands.MCTPAudioCMD;
import me.mctp.commands.MCTPShowCMD;
import me.mctp.listener.LogoutListener;
import me.mctp.listener.WGListener;
import me.mctp.managers.WGManager;
import me.mctp.radio.Playlist;
import me.mctp.socket.Client;
import nl.iobyte.menuapi.MenuAPI;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.event.Listener;
@ -39,18 +39,19 @@ public class Main extends JavaPlugin implements Listener {
pl = this;
client = new Client("ws://144.91.100.169:30217");
client = new Client("ws://bots.d-group.nl:8166");
client.connect();
getCommand("audio").setExecutor(new MCTPAudioCMD());
getCommand("mctpaudio").setExecutor(new MCTPAudioCMD());
getCommand("mctpshow").setExecutor(new MCTPShowCMD());
Bukkit.getPluginManager().registerEvents(new WGListener(), this);
Bukkit.getPluginManager().registerEvents(new LogoutListener(), this);
playlist = new Playlist();
MenuAPI.register(this);
Bukkit.getLogger().info(prefix + " __ __ ____ _____ ____ _ _ _ ");
Bukkit.getLogger().info(prefix + " | \\/ |/ ___|_ _| _ \\ / \\ _ _ __| (_) ___ ");
Bukkit.getLogger().info(prefix + " | |\\/| | | | | | |_) / _ \\| | | |/ _` | |/ _ \\ ");

View file

@ -1,81 +0,0 @@
package me.mctp.commands;
import me.mctp.Main;
import me.mctp.utils.Laser;
import org.bukkit.ChatColor;
import org.bukkit.Sound;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.HashMap;
import java.util.Map;
public class MCTPShowCMD implements CommandExecutor {
public static String prefix = (ChatColor.GOLD + "[" + ChatColor.YELLOW + "MCTP" + ChatColor.GOLD + "] " + ChatColor.GRAY);
private Map<Player, LaserRunnable> lasers =new HashMap<>();
@Override
public boolean onCommand(CommandSender sender, Command cmd, String commandlabel, String[] args) {
if (cmd.getName().equalsIgnoreCase("mctpshow")) {
if (!sender.hasPermission("mctp.show")) {
sender.sendMessage(prefix + "You don't have the permission to do this.");
return true;
}
if (args.length == 1 && args[0].equalsIgnoreCase("demo")) {
Player p = (Player) sender;
if (lasers.containsKey(p)){
lasers.get(p).cancel();
}else {
try{
lasers.put(p, new LaserRunnable(p));
lasers.get(p).runTaskTimer(Main.getPlugin(), 5, 1);
}catch (ReflectiveOperationException e){
e.printStackTrace();
}
}
}
}
return true;
}
public class LaserRunnable extends BukkitRunnable {
public static final byte LOADING_TIME = 30;
public static final byte RANGE = 10;
private final Laser laser;
private final Player p;
public byte loading = 0;
public LaserRunnable(Player p) throws ReflectiveOperationException{
this.p = p;
this.laser = new Laser(p.getLocation(), p.getLocation().add(0, 1, 0), -1, 50);
this.laser.start(Main.getPlugin());
}
public void run(){
if (loading != LOADING_TIME){
loading++;
p.getWorld().playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 0.7f, loading == LOADING_TIME ? 1.5f : 0.2f);
}
try{
laser.moveStart(p.getLocation().add(0, 0.8, 0));
laser.moveEnd(p.getLocation().add(0, 1.2, 0).add(p.getLocation().getDirection().multiply(loading == LOADING_TIME ? RANGE : loading / (LOADING_TIME / RANGE * 1.3))));
}catch (ReflectiveOperationException e){
e.printStackTrace();
}
}
public synchronized void cancel() throws IllegalStateException{
laser.stop();
lasers.remove(p);
super.cancel();
}
}
}

View file

@ -1,376 +0,0 @@
package me.mctp.utils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashSet;
import java.util.UUID;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable;
/**
* A whole class to create Guardian Beams by reflection </br>
* Inspired by the API <a href="https://www.spigotmc.org/resources/guardianbeamapi.18329">GuardianBeamAPI</a></br>
* <b>1.9 -> 1.15</b>
*
* @see <a href="https://github.com/SkytAsul/GuardianBeam">GitHub page</a>
* @author SkytAsul
*/
public class Laser {
private final int duration;
private final int distanceSquared;
private Location start;
private Location end;
private final Object createGuardianPacket;
private final Object createSquidPacket;
private final Object teamAddPacket;
private final Object destroyPacket;
private final Object metadataPacketGuardian;
private final Object metadataPacketSquid;
private final Object fakeGuardianDataWatcher;
private final int squid;
private final UUID squidUUID;
private final int guardian;
private final UUID guardianUUID;
private BukkitRunnable run;
private HashSet<Player> show = new HashSet<>();
/**
* Create a Laser instance
* @param start Location where laser will starts
* @param end Location where laser will ends
* @param duration Duration of laser in seconds (<i>-1 if infinite</i>)
* @param distance Distance where laser will be visible
*/
public Laser(Location start, Location end, int duration, int distance) throws ReflectiveOperationException {
this.start = start;
this.end = end;
this.duration = duration;
distanceSquared = distance * distance;
createSquidPacket = Packets.createPacketSquidSpawn(end);
squid = (int) Packets.getField(Packets.packetSpawn, "a", createSquidPacket);
squidUUID = (UUID) Packets.getField(Packets.packetSpawn, "b", createSquidPacket);
metadataPacketSquid = Packets.createPacketMetadata(squid, Packets.fakeSquidWatcher);
Packets.setDirtyWatcher(Packets.fakeSquidWatcher);
fakeGuardianDataWatcher = Packets.createFakeDataWatcher();
createGuardianPacket = Packets.createPacketGuardianSpawn(start, fakeGuardianDataWatcher, squid);
guardian = (int) Packets.getField(Packets.packetSpawn, "a", createGuardianPacket);
guardianUUID = (UUID) Packets.getField(Packets.packetSpawn, "b", createGuardianPacket);
metadataPacketGuardian = Packets.createPacketMetadata(guardian, fakeGuardianDataWatcher);
teamAddPacket = Packets.createPacketTeamAddEntities(squidUUID, guardianUUID);
destroyPacket = Packets.createPacketRemoveEntities(squid, guardian);
}
public void start(Plugin plugin) {
Validate.isTrue(run == null, "Task already started");
run = new BukkitRunnable() {
int time = duration;
@Override
public void run() {
try {
if (time == 0) {
cancel();
return;
}
for (Player p : start.getWorld().getPlayers()) {
if (isCloseEnough(p.getLocation())) {
if (!show.contains(p)) {
sendStartPackets(p);
show.add(p);
}
}else if (show.contains(p)) {
Packets.sendPacket(p, destroyPacket);
show.remove(p);
}
}
if (time != -1) time--;
}catch (ReflectiveOperationException e) {
e.printStackTrace();
}
}
@Override
public synchronized void cancel() throws IllegalStateException {
super.cancel();
try {
for (Player p : show) {
Packets.sendPacket(p, destroyPacket);
}
}catch (ReflectiveOperationException e) {
e.printStackTrace();
}
run = null;
}
};
run.runTaskTimerAsynchronously(plugin, 0L, 20L);
}
public void stop() {
Validate.isTrue(run != null, "Task not started");
run.cancel();
}
public void moveStart(Location location) throws ReflectiveOperationException {
this.start = location;
Object packet = Packets.createPacketMoveEntity(start, guardian);
for (Player p : show) {
Packets.sendPacket(p, packet);
}
}
public Location getStart() {
return start;
}
public void moveEnd(Location location) throws ReflectiveOperationException {
this.end = location;
Object packet = Packets.createPacketMoveEntity(end, squid);
for (Player p : show) {
Packets.sendPacket(p, packet);
}
}
public Location getEnd() {
return end;
}
public void callColorChange() throws ReflectiveOperationException{
for (Player p : show) {
Packets.sendPacket(p, metadataPacketGuardian);
}
}
public boolean isStarted() {
return run != null;
}
private void sendStartPackets(Player p) throws ReflectiveOperationException {
Packets.sendPacket(p, createSquidPacket);
Packets.sendPacket(p, createGuardianPacket);
if (Packets.version > 14) {
Packets.sendPacket(p, metadataPacketSquid);
Packets.sendPacket(p, metadataPacketGuardian);
}
Packets.sendPacket(p, Packets.packetTeamCreate);
Packets.sendPacket(p, teamAddPacket);
}
private boolean isCloseEnough(Location location) {
return start.distanceSquared(location) <= distanceSquared ||
end.distanceSquared(location) <= distanceSquared;
}
private static class Packets {
private static int lastIssuedEID = 2000000000;
static int generateEID() {
return lastIssuedEID++;
}
private static int version = Integer.parseInt(Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3].substring(1).split("_")[1]);
private static String npack = "net.minecraft.server." + Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3] + ".";
private static String cpack = Bukkit.getServer().getClass().getPackage().getName() + ".";
private static Object packetTeamCreate;
private static Constructor<?> watcherConstructor;
private static Method watcherSet;
private static Method watcherRegister;
private static Method watcherDirty;
private static Class<?> packetSpawn;
private static Class<?> packetRemove;
private static Class<?> packetTeleport;
private static Class<?> packetTeam;
private static Class<?> packetMetadata;
private static Object watcherObject1; // invisilibity
private static Object watcherObject2; // spikes
private static Object watcherObject3; // attack id
private static int squidID;
private static int guardianID;
private static Object fakeSquid;
private static Object fakeSquidWatcher;
static {
try {
String watcherName1 = null, watcherName2 = null, watcherName3 = null;
if (version < 13) {
watcherName1 = "Z";
watcherName2 = "bA";
watcherName3 = "bB";
squidID = 94;
guardianID = 68;
}else if (version == 13) {
watcherName1 = "ac";
watcherName2 = "bF";
watcherName3 = "bG";
squidID = 70;
guardianID = 28;
}else if (version == 14) {
watcherName1 = "W";
watcherName2 = "b";
watcherName3 = "bD";
squidID = 73;
guardianID = 30;
}else if (version > 14) {
watcherName1 = "T";
watcherName2 = "b";
watcherName3 = "bA";
squidID = 74;
guardianID = 31;
}
watcherObject1 = getField(Class.forName(npack + "Entity"), watcherName1, null);
watcherObject2 = getField(Class.forName(npack + "EntityGuardian"), watcherName2, null);
watcherObject3 = getField(Class.forName(npack + "EntityGuardian"), watcherName3, null);
watcherConstructor = Class.forName(npack + "DataWatcher").getDeclaredConstructor(Class.forName(npack + "Entity"));
watcherSet = getMethod(Class.forName(npack + "DataWatcher"), "set");
watcherRegister = getMethod(Class.forName(npack + "DataWatcher"), "register");
if (version >= 15) watcherDirty = getMethod(Class.forName(npack + "DataWatcher"), "markDirty");
packetSpawn = Class.forName(npack + "PacketPlayOutSpawnEntityLiving");
packetRemove = Class.forName(npack + "PacketPlayOutEntityDestroy");
packetTeleport = Class.forName(npack + "PacketPlayOutEntityTeleport");
packetTeam = Class.forName(npack + "PacketPlayOutScoreboardTeam");
packetMetadata = Class.forName(npack + "PacketPlayOutEntityMetadata");
packetTeamCreate = packetTeam.newInstance();
setField(packetTeamCreate, "a", "noclip");
setField(packetTeamCreate, "i", 0);
setField(packetTeamCreate, "f", "never");
Object world = Class.forName(cpack + "CraftWorld").getDeclaredMethod("getHandle").invoke(Bukkit.getWorlds().get(0));
Object[] entityConstructorParams = version < 14 ? new Object[] { world } : new Object[] { Class.forName(npack + "EntityTypes").getDeclaredField("SQUID").get(null), world };
fakeSquid = getMethod(Class.forName(cpack + "entity.CraftSquid"), "getHandle").invoke(Class.forName(cpack + "entity.CraftSquid").getDeclaredConstructors()[0].newInstance(
null, Class.forName(npack + "EntitySquid").getDeclaredConstructors()[0].newInstance(
entityConstructorParams)));
fakeSquidWatcher = createFakeDataWatcher();
tryWatcherSet(fakeSquidWatcher, watcherObject1, (byte) 32);
}catch (ReflectiveOperationException e) {
e.printStackTrace();
}
}
public static void sendPacket(Player p, Object packet) throws ReflectiveOperationException {
Object entityPlayer = Class.forName(cpack + "entity.CraftPlayer").getDeclaredMethod("getHandle").invoke(p);
Object playerConnection = entityPlayer.getClass().getDeclaredField("playerConnection").get(entityPlayer);
playerConnection.getClass().getDeclaredMethod("sendPacket", Class.forName(npack + "Packet")).invoke(playerConnection, packet);
}
public static Object createFakeDataWatcher() throws ReflectiveOperationException {
Object watcher = watcherConstructor.newInstance(fakeSquid);
if (version > 13) setField(watcher, "registrationLocked", false);
return watcher;
}
public static void setDirtyWatcher(Object watcher) throws ReflectiveOperationException {
if (version >= 15) watcherDirty.invoke(watcher, watcherObject1);
}
public static Object createPacketSquidSpawn(Location location) throws ReflectiveOperationException {
Object packet = packetSpawn.newInstance();
setField(packet, "a", generateEID());
setField(packet, "b", UUID.randomUUID());
setField(packet, "c", squidID);
setField(packet, "d", location.getX());
setField(packet, "e", location.getY());
setField(packet, "f", location.getZ());
setField(packet, "j", (byte) (location.getYaw() * 256.0F / 360.0F));
setField(packet, "k", (byte) (location.getPitch() * 256.0F / 360.0F));
if (version <= 14) setField(packet, "m", fakeSquidWatcher);
return packet;
}
public static Object createPacketGuardianSpawn(Location location, Object watcher, int squidId) throws ReflectiveOperationException {
Object packet = packetSpawn.newInstance();
setField(packet, "a", generateEID());
setField(packet, "b", UUID.randomUUID());
setField(packet, "c", guardianID);
setField(packet, "d", location.getX());
setField(packet, "e", location.getY());
setField(packet, "f", location.getZ());
setField(packet, "j", (byte) (location.getYaw() * 256.0F / 360.0F));
setField(packet, "k", (byte) (location.getPitch() * 256.0F / 360.0F));
tryWatcherSet(watcher, watcherObject1, (byte) 32);
tryWatcherSet(watcher, watcherObject2, false);
tryWatcherSet(watcher, watcherObject3, squidId);
if (version <= 14) setField(packet, "m", watcher);
return packet;
}
public static Object createPacketRemoveEntities(int squidId, int guardianId) throws ReflectiveOperationException {
Object packet = packetRemove.newInstance();
setField(packet, "a", new int[] { squidId, guardianId });
return packet;
}
public static Object createPacketMoveEntity(Location location, int entityId) throws ReflectiveOperationException {
Object packet = packetTeleport.newInstance();
setField(packet, "a", entityId);
setField(packet, "b", location.getX());
setField(packet, "c", location.getY());
setField(packet, "d", location.getZ());
setField(packet, "e", (byte) (location.getYaw() * 256.0F / 360.0F));
setField(packet, "f", (byte) (location.getPitch() * 256.0F / 360.0F));
setField(packet, "g", true);
return packet;
}
public static Object createPacketTeamAddEntities(UUID squidUUID, UUID guardianUUID) throws ReflectiveOperationException {
Object packet = packetTeam.newInstance();
setField(packet, "a", "noclip");
setField(packet, "i", 3);
Collection<String> players = (Collection<String>) getField(packetTeam, "h", packet);
players.add(squidUUID.toString());
players.add(guardianUUID.toString());
return packet;
}
private static Object createPacketMetadata(int entityId, Object watcher) throws ReflectiveOperationException {
return packetMetadata.getConstructor(int.class, watcher.getClass(), boolean.class).newInstance(entityId, watcher, false);
}
private static void tryWatcherSet(Object watcher, Object watcherObject, Object watcherData) throws ReflectiveOperationException {
try {
watcherSet.invoke(watcher, watcherObject, watcherData);
}catch (InvocationTargetException ex) {
watcherRegister.invoke(watcher, watcherObject, watcherData);
if (version >= 15) watcherDirty.invoke(watcher, watcherObject);
}
}
/* Reflection utils */
private static Method getMethod(Class<?> clazz, String name) {
for (Method m : clazz.getDeclaredMethods()) {
if (m.getName().equals(name)) return m;
}
return null;
}
private static void setField(Object instance, String name, Object value) throws ReflectiveOperationException {
Validate.notNull(instance);
Field field = instance.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(instance, value);
}
private static Object getField(Class<?> clazz, String name, Object instance) throws ReflectiveOperationException {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(instance);
}
}
}

View file

@ -0,0 +1,57 @@
package me.mctp.utils;
import me.mctp.xutils.SkullUtils;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class MultiPageGUI {
private List<Inventory> inventories = new ArrayList<>();
public MultiPageGUI(String title, int itemsPerPage, List<ItemStack> items) {
if (!title.contains("%page%")) {
throw new IllegalArgumentException("Title must contain %page% for the page.");
}
int invSize = itemsPerPage + 9;
if (itemsPerPage%9 != 0) {
throw new IllegalArgumentException("ItemsPerPage must be divisible by 9.");
}
int neededInvs = ((int) Math.ceil(items.size() / invSize));
for (int i = 0; i < neededInvs; i++) {
//Create inv
Inventory inv = Bukkit.createInventory(null, invSize - 1, title.replace("%page%", String.valueOf(i + 1)));
inventories.add(inv);
//Add navigation
ItemStack previous = SkullUtils.getSkull(UUID.fromString("a68f0b64-8d14-4000-a95f-4b9ba14f8df9"));
ItemMeta previousMeta = previous.getItemMeta();
previousMeta.setDisplayName(ChatColor.GOLD + "<< Vorige Pagina");
previous.setItemMeta(previousMeta);
ItemStack close = new ItemStack(Material.BARRIER, 1);
ItemMeta closeMeta = close.getItemMeta();
closeMeta.setDisplayName(ChatColor.RED + "Sluit Menu");
close.setItemMeta(closeMeta);
ItemStack next = SkullUtils.getSkull(UUID.fromString("50c8510b-5ea0-4d60-be9a-7d542d6cd156"));
ItemMeta nextMeta = next.getItemMeta();
nextMeta.setDisplayName(ChatColor.GOLD + "Volgende Pagina >>");
next.setItemMeta(nextMeta);
inv.setItem(itemsPerPage, previous);
inv.setItem(itemsPerPage + 4, close);
inv.setItem(itemsPerPage + 8, next);
}
}
}

View file

@ -0,0 +1,144 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Crypto Morin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package me.mctp.xutils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.concurrent.CompletableFuture;
/**
* <b>ReflectionUtils</b> - Reflection handler for NMS and CraftBukkit.<br>
* Caches the packet related methods and is asynchronous.
* <p>
* This class does not handle null checks as most of the requests are from the
* other utility classes that already handle null checks.
* <p>
* <a href="https://wiki.vg/Protocol">Clientbound Packets</a> are considered fake
* updates to the client without changing the actual data. Since all the data is handled
* by the server.
*
* @author Crypto Morin
* @version 1.0.2
*/
public class ReflectionUtils {
/**
* We use reflection mainly to avoid writing a new class for version barrier.
* The version barrier is for NMS that uses the Minecraft version as the main package name.
* <p>
* E.g. EntityPlayer in 1.15 is in the class {@code net.minecraft.server.v1_15_R1}
* but in 1.14 it's in {@code net.minecraft.server.v1_14_R1}
* In order to maintain cross-version compatibility we cannot import these classes.
*/
public static final String VERSION = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
public static final String CRAFTBUKKIT = "org.bukkit.craftbukkit." + VERSION + '.';
public static final String NMS = "net.minecraft.server." + VERSION + '.';
private static final MethodHandle PLAYER_CONNECTION;
private static final MethodHandle GET_HANDLE;
private static final MethodHandle SEND_PACKET;
static {
Class<?> entityPlayer = getNMSClass("EntityPlayer");
Class<?> craftPlayer = getCraftClass("entity.CraftPlayer");
Class<?> playerConnection = getNMSClass("PlayerConnection");
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle sendPacket = null;
MethodHandle getHandle = null;
MethodHandle connection = null;
try {
connection = lookup.findGetter(entityPlayer, "playerConnection", playerConnection);
getHandle = lookup.findVirtual(craftPlayer, "getHandle", MethodType.methodType(entityPlayer));
sendPacket = lookup.findVirtual(playerConnection, "sendPacket", MethodType.methodType(void.class, getNMSClass("Packet")));
} catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException ex) {
ex.printStackTrace();
}
PLAYER_CONNECTION = connection;
SEND_PACKET = sendPacket;
GET_HANDLE = getHandle;
}
/**
* Get a NMS (net.minecraft.server) class.
*
* @param name the name of the class.
* @return the class.
* @since 1.0.0
*/
@Nullable
public static Class<?> getNMSClass(@Nonnull String name) {
try {
return Class.forName(NMS + name);
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
return null;
}
}
/**
* Sends a packet to the player asynchronously.
* Packets are thread-safe.
*
* @param player the player to send the packet to.
* @param packets the packets to send.
* @return the async thread handling the packet.
* @since 1.0.0
*/
@Nonnull
public static CompletableFuture<Void> sendPacket(@Nonnull Player player, @Nonnull Object... packets) {
return CompletableFuture.runAsync(() -> {
try {
Object handle = GET_HANDLE.invoke(player);
Object connection = PLAYER_CONNECTION.invoke(handle);
if (player.isOnline()) {
for (Object packet : packets) SEND_PACKET.invoke(connection, packet);
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
});
}
/**
* Get a CraftBukkit class.
*
* @param name the name of the class.
* @return a class.
* @since 1.0.0
*/
@Nullable
public static Class<?> getCraftClass(@Nonnull String name) {
try {
return Class.forName(CRAFTBUKKIT + name);
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
return null;
}
}
}

View file

@ -0,0 +1,123 @@
package me.mctp.xutils;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.mojang.authlib.GameProfile;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
/**
* This class is currently unused until I find a solution.
*/
@SuppressWarnings("unused")
final class SkullCacheListener {
protected static final Map<UUID, String> CACHE = new HashMap<>();
private static final String SESSION = "https://sessionserver.mojang.com/session/minecraft/profile/";
/**
* https://api.mojang.com/users/profiles/minecraft/Username gives the ID
* https://api.mojang.com/user/profiles/ID without dashes/names gives the names used for the unique ID.
* https://sessionserver.mojang.com/session/minecraft/profile/ID example data:
* <p>
* <pre>
* {
* "id": "Without dashes -",
* "name": "",
* "properties": [
* {
* "name": "textures",
* "value": ""
* }
* ]
* }
* </pre>
*/
@Nullable
public static String getSkinValue(@Nonnull String id) {
Objects.requireNonNull(id, "Player UUID cannot be null");
try {
JsonParser parser = new JsonParser();
URL properties = new URL(SESSION + id); // + "?unsigned=false"
try (InputStreamReader readProperties = new InputStreamReader(properties.openStream())) {
JsonObject jObjectP = parser.parse(readProperties).getAsJsonObject();
if (mojangError(jObjectP)) return null;
JsonObject textureProperty = jObjectP.get("properties").getAsJsonArray().get(0).getAsJsonObject();
//String signature = textureProperty.get("signature").getAsString();
return textureProperty.get("value").getAsString();
}
} catch (IOException | IllegalStateException e) {
System.err.println("Could not get skin data from session servers! " + e.getMessage());
e.printStackTrace();
return null;
}
}
@Nullable
public static String getIdFromUsername(@Nonnull String username) {
Validate.notEmpty(username, "Cannot get UUID of a null or empty username");
int len = username.length();
if (len < 3 || len > 16) throw new IllegalArgumentException("Username cannot be less than 3 and longer than 16 characters: " + username);
try {
URL convertName = new URL("https://api.mojang.com/users/profiles/minecraft/" + username);
JsonParser parser = new JsonParser();
try (InputStreamReader idReader = new InputStreamReader(convertName.openStream())) {
JsonElement jElement = parser.parse(idReader);
if (!jElement.isJsonObject()) return null;
JsonObject jObject = jElement.getAsJsonObject();
if (mojangError(jObject)) return null;
return jObject.get("id").getAsString();
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private static boolean mojangError(@Nonnull JsonObject jsonObject) {
if (!jsonObject.has("error")) return false;
String err = jsonObject.get("error").getAsString();
String msg = jsonObject.get("errorMessage").getAsString();
System.err.println("Mojang Error " + err + ": " + msg);
return true;
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
GameProfile profile = new GameProfile(player.getUniqueId(), player.getName());
ItemStack head = XMaterial.PLAYER_HEAD.parseItem();
SkullMeta meta = (SkullMeta) head.getItemMeta();
try {
SkullUtils.GAME_PROFILE.invoke(meta, profile);
} catch (Throwable ex) {
ex.printStackTrace();
}
head.setItemMeta(meta);
// If you don't add it to the players inventory, it won't be cached. That's the problem.
// Or is the inventory cached? I tested this with multiple inventories and other inventories load immediately after an inventory with
// the skull in it is opened once.
player.getInventory().addItem(head);
}
}

View file

@ -0,0 +1,191 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Crypto Morin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package me.mctp.xutils;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.Objects;
import java.util.UUID;
import java.util.regex.Pattern;
/**
* <b>SkullUtils</b> - Apply skull texture from different sources.<br>
* Skull Meta: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/inventory/meta/SkullMeta.html
* Mojang API: https://wiki.vg/Mojang_API
*
* @author Crypto Morin
* @version 3.0.1
* @see XMaterial
*/
public class SkullUtils {
protected static final MethodHandle GAME_PROFILE;
private static final String VALUE_PROPERTY = "{\"textures\":{\"SKIN\":{\"url\":\"";
private static final boolean SUPPORTS_UUID = XMaterial.supports(12);
private static final String TEXTURES = "https://textures.minecraft.net/texture/";
private static final Pattern BASE64 = Pattern.compile("(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?");
static {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle gameProfile = null;
try {
Class<?> craftSkull = ReflectionUtils.getCraftClass("inventory.CraftMetaSkull");
Field profileField = craftSkull.getDeclaredField("profile");
profileField.setAccessible(true);
gameProfile = lookup.unreflectSetter(profileField);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
GAME_PROFILE = gameProfile;
}
@SuppressWarnings("deprecation")
@Nonnull
public static ItemStack getSkull(@Nonnull UUID id) {
ItemStack head = XMaterial.PLAYER_HEAD.parseItem();
SkullMeta meta = (SkullMeta) head.getItemMeta();
if (SUPPORTS_UUID) meta.setOwningPlayer(Bukkit.getOfflinePlayer(id));
else meta.setOwner(id.toString());
head.setItemMeta(meta);
return head;
}
@Nonnull
public static SkullMeta applyCachedSkin(@Nonnull ItemMeta head, @Nonnull UUID identifier) {
String base64 = SkullCacheListener.CACHE.get(identifier);
SkullMeta meta = (SkullMeta) head;
return getSkullByValue(meta, base64);
}
@SuppressWarnings("deprecation")
@Nonnull
public static SkullMeta applySkin(@Nonnull ItemMeta head, @Nonnull OfflinePlayer identifier) {
SkullMeta meta = (SkullMeta) head;
if (SUPPORTS_UUID) {
meta.setOwningPlayer(identifier);
} else {
meta.setOwner(identifier.getName());
}
return meta;
}
@Nonnull
public static SkullMeta applySkin(@Nonnull ItemMeta head, @Nonnull UUID identifier) {
return applySkin(head, Bukkit.getOfflinePlayer(identifier));
}
@SuppressWarnings("deprecation")
@Nonnull
public static SkullMeta applySkin(@Nonnull ItemMeta head, @Nonnull String identifier) {
SkullMeta meta = (SkullMeta) head;
if (isUsername(identifier)) return applySkin(head, Bukkit.getOfflinePlayer(identifier));
if (identifier.contains("textures.minecraft.net")) return getValueFromTextures(meta, identifier);
if (identifier.length() > 100 && isBase64(identifier)) return getSkullByValue(meta, identifier);
return getTexturesFromUrlValue(meta, identifier);
}
@Nonnull
private static SkullMeta getSkullByValue(@Nonnull SkullMeta head, @Nonnull String value) {
Validate.notEmpty(value, "Skull value cannot be null or empty");
GameProfile profile = new GameProfile(UUID.randomUUID(), null);
profile.getProperties().put("textures", new Property("textures", value));
try {
GAME_PROFILE.invoke(head, profile);
} catch (Throwable ex) {
ex.printStackTrace();
}
return head;
}
@Nonnull
private static SkullMeta getValueFromTextures(@Nonnull SkullMeta head, @Nonnull String url) {
return getSkullByValue(head, encodeBase64(VALUE_PROPERTY + url + "\"}}}"));
}
@Nonnull
private static SkullMeta getTexturesFromUrlValue(@Nonnull SkullMeta head, @Nonnull String urlValue) {
return getValueFromTextures(head, TEXTURES + urlValue);
}
@Nonnull
private static String encodeBase64(@Nonnull String str) {
return Base64.getEncoder().encodeToString(str.getBytes());
}
private static boolean isBase64(@Nonnull String base64) {
return BASE64.matcher(base64).matches();
}
@Nullable
public static String getSkinValue(@Nonnull ItemStack skull) {
Objects.requireNonNull(skull, "Skull ItemStack cannot be null");
SkullMeta meta = (SkullMeta) skull.getItemMeta();
GameProfile profile = null;
try {
Field profileField = meta.getClass().getDeclaredField("profile");
profileField.setAccessible(true);
profile = (GameProfile) profileField.get(meta);
} catch (SecurityException | NoSuchFieldException | IllegalAccessException ex) {
ex.printStackTrace();
}
if (profile != null && !profile.getProperties().get("textures").isEmpty())
for (Property property : profile.getProperties().get("textures"))
if (!property.getValue().isEmpty())
return property.getValue();
return null;
}
private static boolean isUsername(@Nullable String name) {
if (Strings.isNullOrEmpty(name)) return false;
int len = name.length();
if (len < 3 || len > 16) return false;
// For some reasons Apache's Lists.charactersOf is faster than character indexing for small strings.
for (char ch : Lists.charactersOf(name)) {
if (ch != '_' && !(ch >= 'A' && ch <= 'Z') && !(ch >= 'a' && ch <= 'z') && !(ch >= '0' && ch <= '9')) return false;
}
return true;
}
}

File diff suppressed because it is too large Load diff

View file

@ -9,8 +9,6 @@ depend: ['WorldGuard']
commands:
mctpaudio:
description: Main command
mctpshow:
description: Show command
audio:
description: Connect command