Updated to new server location, and 1.16.3
This commit is contained in:
parent
4b9c5d3a59
commit
833b55d6ca
12 changed files with 2699 additions and 465 deletions
3
.idea/artifacts/MCTPAudio_jar.xml
generated
3
.idea/artifacts/MCTPAudio_jar.xml
generated
|
@ -3,8 +3,9 @@
|
||||||
<output-path>$PROJECT_DIR$/out/artifacts/MCTPAudio_jar</output-path>
|
<output-path>$PROJECT_DIR$/out/artifacts/MCTPAudio_jar</output-path>
|
||||||
<root id="archive" name="MCTPAudio.jar">
|
<root id="archive" name="MCTPAudio.jar">
|
||||||
<element id="module-output" name="MCTPAudio" />
|
<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/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>
|
</root>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</component>
|
11
.idea/libraries/spigot_1_16_3.xml
generated
Normal file
11
.idea/libraries/spigot_1_16_3.xml
generated
Normal 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>
|
|
@ -16,8 +16,7 @@
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="spigot-1.14.4" level="project" />
|
<orderEntry type="library" name="Java-WebSocket-1.3.8" level="project" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="Java-WebSocket-1.3.8" level="project" />
|
|
||||||
<orderEntry type="module-library" scope="PROVIDED">
|
<orderEntry type="module-library" scope="PROVIDED">
|
||||||
<library>
|
<library>
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
|
@ -54,5 +53,15 @@
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
</library>
|
</library>
|
||||||
</orderEntry>
|
</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>
|
</component>
|
||||||
</module>
|
</module>
|
|
@ -1,12 +1,12 @@
|
||||||
package me.mctp;
|
package me.mctp;
|
||||||
|
|
||||||
import me.mctp.commands.MCTPAudioCMD;
|
import me.mctp.commands.MCTPAudioCMD;
|
||||||
import me.mctp.commands.MCTPShowCMD;
|
|
||||||
import me.mctp.listener.LogoutListener;
|
import me.mctp.listener.LogoutListener;
|
||||||
import me.mctp.listener.WGListener;
|
import me.mctp.listener.WGListener;
|
||||||
import me.mctp.managers.WGManager;
|
import me.mctp.managers.WGManager;
|
||||||
import me.mctp.radio.Playlist;
|
import me.mctp.radio.Playlist;
|
||||||
import me.mctp.socket.Client;
|
import me.mctp.socket.Client;
|
||||||
|
import nl.iobyte.menuapi.MenuAPI;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
|
@ -39,18 +39,19 @@ public class Main extends JavaPlugin implements Listener {
|
||||||
|
|
||||||
pl = this;
|
pl = this;
|
||||||
|
|
||||||
client = new Client("ws://144.91.100.169:30217");
|
client = new Client("ws://bots.d-group.nl:8166");
|
||||||
client.connect();
|
client.connect();
|
||||||
|
|
||||||
getCommand("audio").setExecutor(new MCTPAudioCMD());
|
getCommand("audio").setExecutor(new MCTPAudioCMD());
|
||||||
getCommand("mctpaudio").setExecutor(new MCTPAudioCMD());
|
getCommand("mctpaudio").setExecutor(new MCTPAudioCMD());
|
||||||
getCommand("mctpshow").setExecutor(new MCTPShowCMD());
|
|
||||||
|
|
||||||
Bukkit.getPluginManager().registerEvents(new WGListener(), this);
|
Bukkit.getPluginManager().registerEvents(new WGListener(), this);
|
||||||
Bukkit.getPluginManager().registerEvents(new LogoutListener(), this);
|
Bukkit.getPluginManager().registerEvents(new LogoutListener(), this);
|
||||||
|
|
||||||
playlist = new Playlist();
|
playlist = new Playlist();
|
||||||
|
|
||||||
|
MenuAPI.register(this);
|
||||||
|
|
||||||
Bukkit.getLogger().info(prefix + " __ __ ____ _____ ____ _ _ _ ");
|
Bukkit.getLogger().info(prefix + " __ __ ____ _____ ____ _ _ _ ");
|
||||||
Bukkit.getLogger().info(prefix + " | \\/ |/ ___|_ _| _ \\ / \\ _ _ __| (_) ___ ");
|
Bukkit.getLogger().info(prefix + " | \\/ |/ ___|_ _| _ \\ / \\ _ _ __| (_) ___ ");
|
||||||
Bukkit.getLogger().info(prefix + " | |\\/| | | | | | |_) / _ \\| | | |/ _` | |/ _ \\ ");
|
Bukkit.getLogger().info(prefix + " | |\\/| | | | | | |_) / _ \\| | | |/ _` | |/ _ \\ ");
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
57
src/me/mctp/utils/MultiPageGUI.java
Normal file
57
src/me/mctp/utils/MultiPageGUI.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
144
src/me/mctp/xutils/ReflectionUtils.java
Normal file
144
src/me/mctp/xutils/ReflectionUtils.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
123
src/me/mctp/xutils/SkullCacheListener.java
Normal file
123
src/me/mctp/xutils/SkullCacheListener.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
191
src/me/mctp/xutils/SkullUtils.java
Normal file
191
src/me/mctp/xutils/SkullUtils.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
2156
src/me/mctp/xutils/XMaterial.java
Normal file
2156
src/me/mctp/xutils/XMaterial.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -9,8 +9,6 @@ depend: ['WorldGuard']
|
||||||
commands:
|
commands:
|
||||||
mctpaudio:
|
mctpaudio:
|
||||||
description: Main command
|
description: Main command
|
||||||
mctpshow:
|
|
||||||
description: Show command
|
|
||||||
audio:
|
audio:
|
||||||
description: Connect command
|
description: Connect command
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue