This commit is contained in:
ajgeiss0702
2021-07-21 17:17:58 -05:00
parent cfdadd17ca
commit 5bd770da9c
18 changed files with 725 additions and 30 deletions
+16
View File
@@ -8,6 +8,10 @@ group = "us.ajg0702.queue.spigot"
repositories {
mavenCentral()
maven { url = uri("https://repo.extendedclip.com/content/repositories/placeholderapi/") }
maven { url = uri("https://repo.codemc.io/repository/nms/") }
maven { url = uri("https://repo.ajg0702.us") }
}
@@ -16,6 +20,18 @@ dependencies {
compileOnly("com.google.guava:guava:30.1.1-jre")
compileOnly("us.ajg0702:ajUtils:1.1.6")
compileOnly(group = "org.spigotmc", name = "spigot", version = "1.16.5-R0.1-SNAPSHOT")
compileOnly("me.clip:placeholderapi:2.10.4")
}
tasks.withType<ProcessResources> {
include("**/*.yml")
filter<org.apache.tools.ant.filters.ReplaceTokens>(
"tokens" to mapOf(
"VERSION" to project.version.toString()
)
)
}
publishing {
@@ -0,0 +1,64 @@
package us.ajg0702.queue.spigot;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class Commands implements CommandExecutor {
Main pl;
public Commands(Main pl) {
this.pl = pl;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
Player player = null;
if(sender instanceof Player) {
player = (Player) sender;
}
if(command.getName().equals("leavequeue")) {
if(player == null) return true;
StringBuilder arg = new StringBuilder();
for(String a : args) {
arg.append(" ");
arg.append(a);
}
pl.sendMessage(player, "leavequeue", arg.toString());
return true;
}
if(args.length < 1) return false;
String srvname = args[0];
if(args.length > 1) {
pl.getLogger().info("Sending "+args[0]+" to queue");
if(!sender.hasPermission("ajqueue.send")) {
sender.sendMessage(color("&cYou do not have permission to do this!"));
return true;
}
Player tply = Bukkit.getPlayer(args[0]);
if(tply == null) {
sender.sendMessage(color("&cCannot find that player!"));
return true;
}
player = tply;
srvname = args[1];
}
if(pl.config.getBoolean("send-queue-commands-in-batches")) {
pl.queuebatch.put(player, srvname);
} else {
pl.sendMessage(player, "queue", srvname);
}
return true;
}
public String color(String txt) {
return ChatColor.translateAlternateColorCodes('&', txt);
}
}
@@ -0,0 +1,59 @@
package us.ajg0702.queue.spigot;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
public class Config {
File f;
YamlConfiguration yml;
JavaPlugin pl;
public boolean getBoolean(String key) {
return yml.getBoolean(key);
}
public Config(JavaPlugin pl) {
this.pl = pl;
f = new File(pl.getDataFolder(), "config.yml");
if(!f.exists()) {
if(!Files.exists(pl.getDataFolder().toPath())) {
try {
Files.createDirectory(pl.getDataFolder().toPath());
} catch (IOException e) {
e.printStackTrace();
}
}
try {
PrintWriter writer = new PrintWriter(pl.getDataFolder()+File.separator+"config.yml", "UTF-8");
String[] lines = getDefaultConfig().split("\n");
for(String line : lines) {
writer.println(line);
}
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
yml = YamlConfiguration.loadConfiguration(f);
}
public String getDefaultConfig() {
return "# This is the config for the spigot side.\n"
+ "# You can find more settings in the config of bungee.\n"
+ "\n\n"
+ "# Should we send queue requests from commands in batches?\n"
+ "# Enable this if you have issues with players sometimes not executing commands correctly\n"
+ "# Note though that it could delay queue commands by up to 1 second!\n"
+ "send-queue-commands-in-batches: false";
}
}
@@ -0,0 +1,181 @@
package us.ajg0702.queue.spigot;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.messaging.PluginMessageListener;
import us.ajg0702.queue.spigot.utils.VersionSupport;
import java.util.HashMap;
public class Main extends JavaPlugin implements PluginMessageListener,Listener {
boolean papi = false;
Placeholders placeholders;
Config config;
public void onEnable() {
getServer().getMessenger().registerIncomingPluginChannel(this, "ajqueue:tospigot", this);
getServer().getMessenger().registerOutgoingPluginChannel(this, "ajqueue:tobungee");
this.getCommand("move").setExecutor(new Commands(this));
this.getCommand("leavequeue").setExecutor(new Commands(this));
Bukkit.getPluginManager().registerEvents(this, this);
papi = Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null;
if(papi) {
placeholders = new Placeholders(this);
placeholders.register();
getLogger().info("Registered PlaceholderAPI placeholders");
}
Bukkit.getScheduler().runTaskTimer(this, new Runnable() {
public void run() {
if(Bukkit.getOnlinePlayers().size() <= 0 || queuebatch.size() <= 0) return;
String msg = "";
for(Player p : queuebatch.keySet()) {
if(p == null || !p.isOnline()) continue;
msg += p.getName()+":"+queuebatch.get(p)+",";
}
if(msg.length() > 1) {
msg = msg.substring(0, msg.length()-1);
}
queuebatch.clear();
sendMessage("massqueue", msg);
}
}, 2*20, 20);
config = new Config(this);
getLogger().info("Spigot side enabled! v"+getDescription().getVersion());
}
HashMap<Player, String> queuebatch = new HashMap<>();
@Override
public void onPluginMessageReceived(String channel, Player player, byte[] message) {
if (!channel.equals("ajqueue:tospigot")) return;
ByteArrayDataInput in = ByteStreams.newDataInput(message);
String subchannel = in.readUTF();
if(subchannel.equals("actionbar")) {
String playername = in.readUTF();
Player p = Bukkit.getPlayer(playername);
if(p == null) return;
String data = in.readUTF();
final String text = data.split(";time=")[0];
//getLogger().info("recieved actionbar for "+player.getName()+": "+text);
VersionSupport.sendActionBar(p, text);
/*QueueActionbarUpdateEvent e = new QueueActionbarUpdateEvent(p);
Bukkit.getPluginManager().callEvent(e);*/
return;
}
if(subchannel.equals("inqueueevent")) {
String playername = in.readUTF();
Player p = Bukkit.getPlayer(playername);
if(p == null) return;
QueueScoreboardActivator e = new QueueScoreboardActivator(p);
Bukkit.getPluginManager().callEvent(e);
}
if(subchannel.equals("queuename") && papi) {
String playername = in.readUTF();
Player p = Bukkit.getPlayer(playername);
if(p == null) return;
if(!p.isOnline()) return;
String data = in.readUTF();
HashMap<String, String> phs = placeholders.responseCache.get(p);
if(phs == null) phs = new HashMap<>();
phs.put("queued", data);
placeholders.responseCache.put(p, phs);
}
if(subchannel.equals("position") && papi) {
String playername = in.readUTF();
Player p = Bukkit.getPlayer(playername);
if(p == null) return;
if(!p.isOnline()) return;
String data = in.readUTF();
HashMap<String, String> phs = placeholders.responseCache.get(p);
if(phs == null) phs = new HashMap<>();
phs.put("position", data);
placeholders.responseCache.put(p, phs);
}
if(subchannel.equals("positionof") && papi) {
String playername = in.readUTF();
Player p = Bukkit.getPlayer(playername);
if(p == null) return;
if(!p.isOnline()) return;
String data = in.readUTF();
HashMap<String, String> phs = placeholders.responseCache.get(p);
if(phs == null) phs = new HashMap<>();
phs.put("of", data);
placeholders.responseCache.put(p, phs);
}
if(subchannel.equals("inqueue") && papi) {
String playername = in.readUTF();
Player p = Bukkit.getPlayer(playername);
if(p == null) return;
if(!p.isOnline()) return;
String data = in.readUTF();
HashMap<String, String> phs = placeholders.responseCache.get(p);
if(phs == null) phs = new HashMap<>();
phs.put("inqueue", data);
placeholders.responseCache.put(p, phs);
}
if(subchannel.equals("queuedfor")) {
String playername = in.readUTF();
String queuename = in.readUTF();
Player p = Bukkit.getPlayer(playername);
if(p == null) return;
if(!p.isOnline()) return;
int number = Integer.valueOf(in.readUTF());
HashMap<String, String> phs = placeholders.responseCache.get(p);
if(phs == null) phs = new HashMap<>();
phs.put("queuedfor_"+queuename, number+"");
placeholders.responseCache.put(p, phs);
}
}
public void sendMessage(Player player, String subchannel, String data) {
//getLogger().info("Sending message. "+subchannel+" "+data);
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF(subchannel);
out.writeUTF(data);
player.sendPluginMessage(this, "ajqueue:tobungee", out.toByteArray());
}
public void sendMessage(String subchannel, String data) {
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF(subchannel);
out.writeUTF(data);
Bukkit.getOnlinePlayers().iterator().next()
.sendPluginMessage(this, "ajqueue:tobungee", out.toByteArray());
}
@EventHandler
public void onLeave(PlayerQuitEvent e) {
if(!papi) return;
placeholders.cleanCache();
}
}
@@ -0,0 +1,214 @@
package us.ajg0702.queue.spigot;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
/**
* This class will be registered through the register-method in the
* plugins onEnable-method.
*/
public class Placeholders extends PlaceholderExpansion {
private Main plugin;
/**
* Since we register the expansion inside our own plugin, we
* can simply use this method here to get an instance of our
* plugin.
*
* @param plugin
* The instance of our plugin.
*/
public Placeholders(Main plugin){
this.plugin = plugin;
}
/**
* Because this is an internal class,
* you must override this method to let PlaceholderAPI know to not unregister your expansion class when
* PlaceholderAPI is reloaded
*
* @return true to persist through reloads
*/
@Override
public boolean persist(){
return true;
}
/**
* Because this is a internal class, this check is not needed
* and we can simply return {@code true}
*
* @return Always true since it's an internal class.
*/
@Override
public boolean canRegister(){
return true;
}
/**
* The name of the person who created this expansion should go here.
* <br>For convienience do we return the author from the plugin.yml
*
* @return The name of the author as a String.
*/
@Override
public String getAuthor(){
return plugin.getDescription().getAuthors().toString();
}
/**
* The placeholder identifier should go here.
* <br>This is what tells PlaceholderAPI to call our onRequest
* method to obtain a value if a placeholder starts with our
* identifier.
* <br>This must be unique and can not contain % or _
*
* @return The identifier in {@code %<identifier>_<value>%} as String.
*/
@Override
public String getIdentifier(){
return "ajqueue";
}
/**
* This is the version of the expansion.
* <br>You don't have to use numbers, since it is set as a String.
*
* For convienience do we return the version from the plugin.yml
*
* @return The version as a String.
*/
@Override
public String getVersion(){
return plugin.getDescription().getVersion();
}
HashMap<Player, HashMap<String, String>> responseCache = new HashMap<>();
public void cleanCache() {
Iterator<Player> it = responseCache.keySet().iterator();
while(it.hasNext()) {
Player p = it.next();
if(p == null) {
it.remove();
continue;
}
if(!p.isOnline()) {
it.remove();
}
}
}
/**
* This is the method called when a placeholder with our identifier
* is found and needs a value.
* <br>We specify the value identifier in this method.
* <br>Since version 2.9.1 can you use OfflinePlayers in your requests.
*
* @param player
* A org.bukkit.PkPlayer Player.
* @param identifier
* A String containing the identifier/value.
*
* @return possibly-null String of the requested identifier.
*/
@Override
public String onPlaceholderRequest(Player player, final String identifier){
//Bukkit.getLogger().info("itentifier: "+identifier);
if(player == null) return "No player";
String noc = "_nocache";
if(identifier.length() > noc.length()) {
int olen = identifier.length()-noc.length();
if(identifier.indexOf(noc) == olen) {
String idfr = identifier.substring(0, olen);
return this.parsePlaceholder(player, idfr);
}
}
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
public void run() {
HashMap<String, String> playerCache;
if(responseCache.containsKey(player)) {
playerCache = responseCache.get(player);
} else {
playerCache = new HashMap<String, String>();
}
if(playerCache.size() > 75) {
try {
playerCache.remove(playerCache.keySet().toArray()[0]);
} catch(ConcurrentModificationException e) {
Bukkit.getScheduler().runTask(plugin, new Runnable() {
public void run() {
playerCache.remove(playerCache.keySet().toArray()[0]);
}
});
}
}
String resp = parsePlaceholder(player, identifier);
if(resp == null) return;
playerCache.put(identifier, resp);
responseCache.put(player, playerCache);
}
});
if(responseCache.containsKey(player)) {
HashMap<String, String> playerCache = responseCache.get(player);
if(playerCache.containsKey(identifier)) {
return playerCache.get(identifier);
}
} else {
if(identifier.equalsIgnoreCase("queued")) {
return "None";
}
if(identifier.equalsIgnoreCase("position") || identifier.equalsIgnoreCase("of")) {
return "None";
}
if(identifier.equalsIgnoreCase("inqueue")) {
return "false";
}
if(identifier.matches("queuedfor_*.*")) {
return "0";
}
}
return null;
}
private String parsePlaceholder(Player player, String identifier) {
if(identifier.equalsIgnoreCase("queued")) {
plugin.sendMessage(player, "queuename", "");
return null;
}
if(identifier.equalsIgnoreCase("position")) {
plugin.sendMessage(player, "position", "");
return null;
}
if(identifier.equalsIgnoreCase("of")) {
plugin.sendMessage(player, "positionof", "");
return null;
}
if(identifier.equalsIgnoreCase("inqueue")) {
plugin.sendMessage(player, "inqueue", "");
return null;
}
if(identifier.matches("queuedfor_*.*")) {
plugin.sendMessage(player, "queuedfor", identifier.split("_")[1]);
return null;
}
return null;
}
}
@@ -0,0 +1,28 @@
package us.ajg0702.queue.spigot;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class QueueScoreboardActivator extends Event {
private static final HandlerList HANDLERS = new HandlerList();
public HandlerList getHandlers() {
return HANDLERS;
}
public static HandlerList getHandlerList() {
return HANDLERS;
}
Player ply;
public QueueScoreboardActivator(Player p) {
this.ply = p;
}
public Player getPlayer() {
return ply.getPlayer();
}
}
@@ -0,0 +1,71 @@
package us.ajg0702.queue.spigot.utils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* A cross-version actionbar class
* @author Based on ActionBarAPI from https://github.com/ConnorLinfoot
*
*/
public class ActionBar {
private static String version;
private static boolean old;
public static void send(Player player, String message) {
if(player == null) return;
if(!player.isOnline()) return;
version = Bukkit.getServer().getClass().getPackage().getName();
version = version.substring(version.lastIndexOf(".") + 1);
old = version.equalsIgnoreCase("v1_8_R1") || version.startsWith("v1_7_");
try {
Class<?> craftPlayerClass = Class.forName("org.bukkit.craftbukkit." + version + ".entity.CraftPlayer");
Object craftPlayer = craftPlayerClass.cast(player);
Object packet;
Class<?> packetPlayOutChatClass = Class.forName("net.minecraft.server." + version + ".PacketPlayOutChat");
Class<?> packetClass = Class.forName("net.minecraft.server." + version + ".Packet");
if (old) {
Class<?> chatSerializerClass = Class.forName("net.minecraft.server." + version + ".ChatSerializer");
Class<?> iChatBaseComponentClass = Class.forName("net.minecraft.server." + version + ".IChatBaseComponent");
Method m3 = chatSerializerClass.getDeclaredMethod("a", String.class);
Object cbc = iChatBaseComponentClass.cast(m3.invoke(chatSerializerClass, "{\"text\": \"" + message + "\"}"));
packet = packetPlayOutChatClass.getConstructor(new Class<?>[]{iChatBaseComponentClass, byte.class}).newInstance(cbc, (byte) 2);
} else {
Class<?> chatComponentTextClass = Class.forName("net.minecraft.server." + version + ".ChatComponentText");
Class<?> iChatBaseComponentClass = Class.forName("net.minecraft.server." + version + ".IChatBaseComponent");
try {
Class<?> chatMessageTypeClass = Class.forName("net.minecraft.server." + version + ".ChatMessageType");
Object[] chatMessageTypes = chatMessageTypeClass.getEnumConstants();
Object chatMessageType = null;
for (Object o : chatMessageTypes) {
if (o.toString().equals("GAME_INFO")) {
chatMessageType = o;
}
}
Object chatCompontentText = chatComponentTextClass.getConstructor(new Class<?>[]{String.class}).newInstance(message);
packet = packetPlayOutChatClass.getConstructor(new Class<?>[]{iChatBaseComponentClass, chatMessageTypeClass}).newInstance(chatCompontentText, chatMessageType);
} catch (ClassNotFoundException e) {
Object chatCompontentText = chatComponentTextClass.getConstructor(new Class<?>[]{String.class}).newInstance(message);
packet = packetPlayOutChatClass.getConstructor(new Class<?>[]{iChatBaseComponentClass, byte.class}).newInstance(chatCompontentText, (byte) 2);
}
}
Method craftPlayerHandleMethod = craftPlayerClass.getDeclaredMethod("getHandle");
Object craftPlayerHandle = craftPlayerHandleMethod.invoke(craftPlayer);
Field playerConnectionField = craftPlayerHandle.getClass().getDeclaredField("playerConnection");
Object playerConnection = playerConnectionField.get(craftPlayerHandle);
Method sendPacketMethod = playerConnection.getClass().getDeclaredMethod("sendPacket", packetClass);
sendPacketMethod.invoke(playerConnection, packet);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,31 @@
package us.ajg0702.queue.spigot.utils;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class VersionSupport {
public static String getVersion() {
return Bukkit.getVersion().split("\\(MC: ")[1].split("\\)")[0];
}
public static int getMinorVersion() {
return Integer.parseInt(getVersion().split("\\.")[1]);
}
/**
* Send the player an actionbar message
* @param ply The {@link org.bukkit.entity.Player Player} to send the actionbar to
* @param message The message to send in the actionbar.
*/
public static void sendActionBar(Player ply, String message) {
// Use spigot version if available, otherwise use packets.
if(getMinorVersion() >= 11) {
ply.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message));
} else if(getMinorVersion() >= 8) {
ActionBar.send(ply, message);
}
}
}
+13
View File
@@ -0,0 +1,13 @@
main: us.ajg0702.queue.spigot.Main
version: "@VERSION@"
api-version: 1.13
author: ajgeiss0702
name: ajQueue
softdepend: [PlaceholderAPI]
commands:
move:
aliases: [server, queue, joinq, joinqueue]
description: Queue for a server
leavequeue:
aliases: [leaveq]
description: Leaves a queue