commit f88146f7b1de63e99adcd78c6338bcc526edd844 Author: stijnb1234 Date: Wed Jul 22 19:54:24 2020 +0200 First commit! diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e6ebc35 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +target/ +*.iml \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..96a8517 --- /dev/null +++ b/pom.xml @@ -0,0 +1,132 @@ + + + 4.0.0 + + nl.SBDeveloper + ShowAPI + 1.0 + jar + ShowAPI + https://sbdplugins.nl + + Make perfect shows with this API! + + 1.8 + UTF-8 + + + + + iobyte-nexus + https://nexus.iobyte.nl/repository/maven-releases + + + + + clean package + ${project.name} v${project.version} + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.1.0 + + + package + + shade + + + false + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.8 + + + default-deploy + deploy + + deploy + + + + + nexus + https://nexus.iobyte.nl/ + true + + + + + + src/main/resources + true + + + + + + + spigotmc-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + sonatype + https://oss.sonatype.org/content/groups/public/ + + + inventive-repo + https://repo.inventivetalent.org/content/groups/public/ + + + + + + org.bukkit + craftbukkit + 1.15.2-R0.1-SNAPSHOT + provided + + + org.spigotmc + spigot-api + 1.15.2-R0.1-SNAPSHOT + provided + + + org.inventivetalent + apimanager + 1.0.3-SNAPSHOT + + + org.jetbrains + annotations + 18.0.0 + compile + + + diff --git a/src/main/java/nl/sbdeveloper/showapi/ShowAPI.java b/src/main/java/nl/sbdeveloper/showapi/ShowAPI.java new file mode 100644 index 0000000..e11074a --- /dev/null +++ b/src/main/java/nl/sbdeveloper/showapi/ShowAPI.java @@ -0,0 +1,99 @@ +package nl.sbdeveloper.showapi; + +import nl.sbdeveloper.showapi.utils.Laser; +import nl.sbdeveloper.showapi.utils.VersionUtil; +import org.bukkit.Location; +import org.bukkit.event.Listener; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; +import org.inventivetalent.apihelper.API; +import org.inventivetalent.apihelper.APIManager; + +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +public class ShowAPI implements API, Listener { + Logger logger = Logger.getLogger("ShowAPI"); + + //This gets called either by #registerAPI above, or by the API manager if another plugin requires this API + @Override + public void load() { + + } + + //This gets called either by #initAPI above or #initAPI in one of the requiring plugins + @Override + public void init(Plugin plugin) { + if (VersionUtil.getVersion() < 9 || VersionUtil.getVersion() > 16) { + logger.severe("This API only works from 1.9 to 1.16.1."); + disable(plugin); + return; + } + + APIManager.registerEvents(this, this); + } + + //This gets called either by #disableAPI above or #disableAPI in one of the requiring plugins + @Override + public void disable(Plugin plugin) { + + } + + //LASERS + public static class Lasers { + private static Map lasers = new HashMap<>(); + + public static boolean startLaser(String name, Location baseLoc) { + try { + lasers.put(name, new LaserRunnable(name, baseLoc)); + lasers.get(name).runTaskTimer(ShowAPIPlugin.getInstance(), 0, 1); + return true; + } catch (ReflectiveOperationException e) { + e.printStackTrace(); + } + return false; + } + + public static boolean moveLaser(String name, Location posLoc) { + if (!lasers.containsKey(name)) return false; + + lasers.get(name).changePositionLocation(posLoc); + return true; + } + + private static class LaserRunnable extends BukkitRunnable { + private final Laser laser; + private final String name; + private final Location baseLoc; + + private Location posLoc; + + public LaserRunnable(String name, Location baseLoc) throws ReflectiveOperationException { + this.name = name; + this.baseLoc = baseLoc; + this.laser = new Laser(baseLoc, baseLoc.add(0, 5, 0), -1, 50); + this.laser.start(ShowAPIPlugin.getInstance()); + } + + public void run() { + try { + laser.moveStart(baseLoc); + laser.moveEnd(posLoc); + } catch (ReflectiveOperationException e) { + e.printStackTrace(); + } + } + + public void changePositionLocation(Location posLoc) { + this.posLoc = posLoc; + } + + public synchronized void cancel() throws IllegalStateException { + laser.stop(); + lasers.remove(name); + super.cancel(); + } + } + } +} diff --git a/src/main/java/nl/sbdeveloper/showapi/ShowAPIPlugin.java b/src/main/java/nl/sbdeveloper/showapi/ShowAPIPlugin.java new file mode 100644 index 0000000..807179d --- /dev/null +++ b/src/main/java/nl/sbdeveloper/showapi/ShowAPIPlugin.java @@ -0,0 +1,31 @@ +package nl.sbdeveloper.showapi; + +import org.bukkit.plugin.java.JavaPlugin; +import org.inventivetalent.apihelper.APIManager; + +public final class ShowAPIPlugin extends JavaPlugin { + + private static ShowAPIPlugin instance; + private ShowAPI showAPI = new ShowAPI(); + + @Override + public void onLoad() { + APIManager.registerAPI(showAPI, this); + } + + @Override + public void onEnable() { + instance = this; + APIManager.initAPI(ShowAPI.class); + } + + @Override + public void onDisable() { + instance = null; + APIManager.disableAPI(ShowAPI.class); + } + + public static ShowAPIPlugin getInstance() { + return instance; + } +} diff --git a/src/main/java/nl/sbdeveloper/showapi/utils/Laser.java b/src/main/java/nl/sbdeveloper/showapi/utils/Laser.java new file mode 100644 index 0000000..021eda7 --- /dev/null +++ b/src/main/java/nl/sbdeveloper/showapi/utils/Laser.java @@ -0,0 +1,339 @@ +package nl.sbdeveloper.showapi.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
+ * Inspired by the API GuardianBeamAPI
+ * 1.9 -> 1.16 + * + * @see GitHub page + * @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 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 (-1 if infinite) + * @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 == 15) { + watcherName1 = "T"; + watcherName2 = "b"; + watcherName3 = "bA"; + squidID = 74; + guardianID = 31; + }else if (version >= 16) { + watcherName1 = "T"; + watcherName2 = "b"; + watcherName3 = "d"; + 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 players = (Collection) 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); + } + } +} \ No newline at end of file diff --git a/src/main/java/nl/sbdeveloper/showapi/utils/VersionUtil.java b/src/main/java/nl/sbdeveloper/showapi/utils/VersionUtil.java new file mode 100644 index 0000000..02993ba --- /dev/null +++ b/src/main/java/nl/sbdeveloper/showapi/utils/VersionUtil.java @@ -0,0 +1,34 @@ +package nl.sbdeveloper.showapi.utils; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; + +public class VersionUtil { + private static final int VERSION = Integer.parseInt(getMajorVersion(Bukkit.getVersion()).substring(2)); + + @NotNull + private static String getMajorVersion(String version) { + Validate.notEmpty(version, "Cannot get major Minecraft version from null or empty string"); + + // getVersion() + int index = version.lastIndexOf("MC:"); + if (index != -1) { + version = version.substring(index + 4, version.length() - 1); + } else if (version.endsWith("SNAPSHOT")) { + // getBukkitVersion() + index = version.indexOf('-'); + version = version.substring(0, index); + } + + // 1.13.2, 1.14.4, etc... + int lastDot = version.lastIndexOf('.'); + if (version.indexOf('.') != lastDot) version = version.substring(0, lastDot); + + return version; + } + + public static int getVersion() { + return VERSION; + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..df9d989 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,7 @@ +name: ShowAPI +version: ${project.version} +main: nl.sbdeveloper.showapi.ShowAPIPlugin +api-version: 1.13 +authors: [SBDeveloper] +description: Make perfect shows with this API! +website: https://sbdplugins.nl