diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9103db8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +# ProxyUtils +.idea +testserver +run + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Maven +target/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f7e7fc1 --- /dev/null +++ b/pom.xml @@ -0,0 +1,118 @@ + + + 4.0.0 + + com.alttd.chat + Chat + 1.0 + jar + + Chat + + + UTF-8 + 11 + 11 + + + + clean package + ${project.name} + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + 11 + 11 + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + + + net.kyori:* + + META-INF/*.MF + + + + + + net.kyori:adventure-text-minimessage + + + META-INF/*.MF + + + + + + + + + + src/main + true + + + + + + + velocitypowered-repo + https://repo.velocitypowered.com/releases/ + + + minecraft-libraries + https://libraries.minecraft.net/ + + + jcenter + jcenter-bintray + https://jcenter.bintray.com + + + sonatype-oss-snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + + + + + + com.velocitypowered + velocity-api + 1.1.4 + provided + + + net.luckperms + api + 5.3 + provided + + + net.dv8tion + JDA + 4.2.0_168 + provided + + + net.kyori + adventure-text-minimessage + 4.1.0-SNAPSHOT + + + diff --git a/src/main/java/com/alttd/chat/ChatPlugin.java b/src/main/java/com/alttd/chat/ChatPlugin.java new file mode 100644 index 0000000..d805f3b --- /dev/null +++ b/src/main/java/com/alttd/chat/ChatPlugin.java @@ -0,0 +1,72 @@ +package com.alttd.chat; + +import com.alttd.chat.config.Config; +import com.alttd.chat.listeners.ChatListener; +import com.google.inject.Inject; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; +import com.velocitypowered.api.plugin.Plugin; +import com.velocitypowered.api.plugin.annotation.DataDirectory; +import com.velocitypowered.api.proxy.ProxyServer; +import net.luckperms.api.LuckPerms; +import net.luckperms.api.LuckPermsProvider; +import org.slf4j.Logger; + +import java.io.File; +import java.nio.file.Path; + +@Plugin(id = "chatplugin", name = "ChatPlugin", version = "1.0.0", + description = "A chat plugin for Altitude Minecraft Server", + authors = {"destro174", "teri"} + ) +public class ChatPlugin { + + private static ChatPlugin plugin; + private final ProxyServer server; + private final Logger logger; + private final Path dataDirectory; + private LuckPerms luckPerms; + + @Inject + public ChatPlugin(ProxyServer proxyServer, Logger proxylogger, @DataDirectory Path proxydataDirectory) { + plugin = this; + server = proxyServer; + logger = proxylogger; + dataDirectory = proxydataDirectory; + } + + @Subscribe + public void onProxyInitialization(ProxyInitializeEvent event) { + Config.init(getDataDirectory()); + loadCommands(); + server.getEventManager().register(this, new ChatListener()); + //statusTask = new StatusTask(); + //statusTask.init(); + } + + public File getDataDirectory() { + return dataDirectory.toFile(); + } + + public static ChatPlugin getInstance() { + return plugin; + } + + public LuckPerms getLuckPerms() { + if(luckPerms == null) + luckPerms = LuckPermsProvider.get(); + return luckPerms; + } + + public Logger getLogger() { + return logger; + } + + public ProxyServer getProxy() { + return server; + } + + public void loadCommands() { + // all commands go here + } +} diff --git a/src/main/java/com/alttd/chat/api/MessageEvent.java b/src/main/java/com/alttd/chat/api/MessageEvent.java new file mode 100644 index 0000000..d5e161c --- /dev/null +++ b/src/main/java/com/alttd/chat/api/MessageEvent.java @@ -0,0 +1,29 @@ +package com.alttd.chat.api; + + +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.proxy.Player; + +public class MessageEvent { + private final CommandSource sender; + private final Player recipient; + private final String message; + + public MessageEvent(CommandSource sender, Player recipient, String message) { + this.sender = sender; + this.recipient = recipient; + this.message = message; + } + + public CommandSource getSender() { + return sender; + } + + public Player getRecipient() { + return recipient; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/com/alttd/chat/commands/Message.java b/src/main/java/com/alttd/chat/commands/Message.java new file mode 100644 index 0000000..8183dde --- /dev/null +++ b/src/main/java/com/alttd/chat/commands/Message.java @@ -0,0 +1,68 @@ +package com.alttd.chat.commands; + +import com.alttd.chat.api.MessageEvent; +import com.alttd.chat.config.Config; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.tree.LiteralCommandNode; +import com.velocitypowered.api.command.BrigadierCommand; +import com.velocitypowered.api.command.CommandMeta; +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ProxyServer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class Message { + + public Message(ProxyServer proxyServer) { + LiteralCommandNode command = LiteralArgumentBuilder + .literal("message") + .requires(ctx -> ctx.hasPermission("command.proxy.message"))// TODO permission system? load permissions from config? + .then(RequiredArgumentBuilder + .argument("player", StringArgumentType.word()) + .suggests((context, builder) -> { + for (Player player : proxyServer.getAllPlayers()) { + builder.suggest(player.getGameProfile().getName()); + } + return builder.buildFuture(); + }) + .then(RequiredArgumentBuilder + .argument("message", StringArgumentType.greedyString()) + .executes(context -> { + Optional playerOptional = proxyServer.getPlayer(context.getArgument("player", String.class)); + + if (playerOptional.isPresent()) { + Player receiver = playerOptional.get(); + proxyServer.getEventManager().fire(new MessageEvent(context.getSource(), receiver, context.getArgument("message", String.class))); + + return 1; + } + return 0; + }) + ) + .executes(context -> 0) + ) + .executes(context -> 0) + .build(); + + BrigadierCommand brigadierCommand = new BrigadierCommand(command); + + CommandMeta.Builder metaBuilder = proxyServer.getCommandManager().metaBuilder(brigadierCommand); + + for (String alias : Config.MESSAGECOMMANDALIASES) { + metaBuilder.aliases(alias); + } + + CommandMeta meta = metaBuilder.build(); + + proxyServer.getCommandManager().register(meta, brigadierCommand); + } + +} diff --git a/src/main/java/com/alttd/chat/config/Config.java b/src/main/java/com/alttd/chat/config/Config.java new file mode 100644 index 0000000..5fc4069 --- /dev/null +++ b/src/main/java/com/alttd/chat/config/Config.java @@ -0,0 +1,156 @@ +package com.alttd.chat.config; + +import com.google.common.base.Throwables; +import com.google.common.reflect.TypeToken; +import ninja.leaping.configurate.ConfigurationNode; +import ninja.leaping.configurate.objectmapping.ObjectMappingException; +import ninja.leaping.configurate.yaml.YAMLConfigurationLoader; +import org.yaml.snakeyaml.DumperOptions; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +public final class Config { + private static final Pattern PATH_PATTERN = Pattern.compile("\\."); + private static final String HEADER = ""; + + private static File CONFIG_FILE; + public static ConfigurationNode config; + public static YAMLConfigurationLoader configLoader; + + static int version; + static boolean verbose; + + public static void init(File path) { + CONFIG_FILE = new File(path, "config.yml");; + configLoader = YAMLConfigurationLoader.builder() + .setFile(CONFIG_FILE) + .setFlowStyle(DumperOptions.FlowStyle.BLOCK) + .build(); + if (!CONFIG_FILE.getParentFile().exists()) { + CONFIG_FILE.getParentFile().mkdirs(); + } + if (!CONFIG_FILE.exists()) { + try { + CONFIG_FILE.createNewFile(); + } catch (IOException error) { + error.printStackTrace(); + } + } + + try { + config = configLoader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + configLoader.getDefaultOptions().setHeader(HEADER); + configLoader.getDefaultOptions().withShouldCopyDefaults(true); + + verbose = getBoolean("verbose", true); + version = getInt("config-version", 1); + + readConfig(Config.class, null); + try { + configLoader.save(config); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void readConfig(Class clazz, Object instance) { + for (Method method : clazz.getDeclaredMethods()) { + if (Modifier.isPrivate(method.getModifiers())) { + if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) { + try { + method.setAccessible(true); + method.invoke(instance); + } catch (InvocationTargetException ex) { + throw Throwables.propagate(ex.getCause()); + } catch (Exception ex) { + } + } + } + } + try { + configLoader.save(config); + } catch (IOException ex) { + } + } + + public static boolean saveConfig() { + try { + configLoader.save(config); + return true; + } catch (IOException ex) { + return false; + } + } + + private static Object[] splitPath(String key) { + return PATH_PATTERN.split(key); + } + + private static void set(String path, Object def) { + if(config.getNode(splitPath(path)).isVirtual()) + config.getNode(splitPath(path)).setValue(def); + } + + private static boolean getBoolean(String path, boolean def) { + set(path, def); + return config.getNode(splitPath(path)).getBoolean(def); + } + + private static double getDouble(String path, double def) { + set(path, def); + return config.getNode(splitPath(path)).getDouble(def); + } + + private static int getInt(String path, int def) { + set(path, def); + return config.getNode(splitPath(path)).getInt(def); + } + + private static String getString(String path, String def) { + set(path, def); + return config.getNode(splitPath(path)).getString(def); + } + + private static Long getLong(String path, Long def) { + set(path, def); + return config.getNode(splitPath(path)).getLong(def); + } + + private static List getList(String path, T def) { + try { + set(path, def); + return config.getNode(splitPath(path)).getList(TypeToken.of(String.class)); + } catch(ObjectMappingException ex) { + } + return new ArrayList<>(); + } + + /** ONLY EDIT ANYTHING BELOW THIS LINE **/ + + public static List MESSAGECOMMANDALIASES = new ArrayList<>(); + public static String MESSAGESENDER = " '>(Me -> ) "; + public static String MESSAGERECIEVER = " '>( on -> Me) "; + private static void messageCommand() { + MESSAGECOMMANDALIASES.clear(); + getList("commands.message.aliases", new ArrayList(){{ + add("msg"); + add("whisper"); + add("tell"); + }}).forEach(key -> { + MESSAGECOMMANDALIASES.add(key.toString()); + }); + MESSAGESENDER = getString("commands.message.sender-message", MESSAGESENDER); + MESSAGERECIEVER = getString("commands.message.reciever-message", MESSAGERECIEVER); + } +} diff --git a/src/main/java/com/alttd/chat/config/ServerConfig.java b/src/main/java/com/alttd/chat/config/ServerConfig.java new file mode 100644 index 0000000..eaf8a6e --- /dev/null +++ b/src/main/java/com/alttd/chat/config/ServerConfig.java @@ -0,0 +1,60 @@ +package com.alttd.chat.config; + +import java.util.regex.Pattern; + +public final class ServerConfig { + private static final Pattern PATH_PATTERN = Pattern.compile("\\."); + + private final String serverName; + private final String configPath; + private final String defaultPath; + + public ServerConfig(String serverName) { + this.serverName = serverName; + this.configPath = "server-settings." + this.serverName + "."; + this.defaultPath = "server-settings.default."; + init(); + } + + public void init() { + Config.readConfig(ServerConfig.class, this); + Config.saveConfig(); + } + + public static Object[] splitPath(String key) { + return PATH_PATTERN.split(key); + } + + private static void set(String path, Object def) { + if(Config.config.getNode(splitPath(path)).isVirtual()) { + Config.config.getNode(splitPath(path)).setValue(def); + } + } + + private boolean getBoolean(String path, boolean def) { + set(defaultPath +path, def); + return Config.config.getNode(splitPath(configPath+path)).getBoolean( + Config.config.getNode(splitPath(defaultPath +path)).getBoolean(def)); + } + + private double getDouble(String path, double def) { + set(defaultPath +path, def); + return Config.config.getNode(splitPath(configPath+path)).getDouble( + Config.config.getNode(splitPath(defaultPath +path)).getDouble(def)); + } + + private int getInt(String path, int def) { + set(defaultPath +path, def); + return Config.config.getNode(splitPath(configPath+path)).getInt( + Config.config.getNode(splitPath(defaultPath +path)).getInt(def)); + } + + private String getString(String path, String def) { + set(defaultPath +path, def); + return Config.config.getNode(splitPath(configPath+path)).getString( + Config.config.getNode(splitPath(defaultPath +path)).getString(def)); + } + + /** DO NOT EDIT ANYTHING ABOVE **/ + +} diff --git a/src/main/java/com/alttd/chat/listeners/ChatListener.java b/src/main/java/com/alttd/chat/listeners/ChatListener.java new file mode 100644 index 0000000..2c32d66 --- /dev/null +++ b/src/main/java/com/alttd/chat/listeners/ChatListener.java @@ -0,0 +1,43 @@ +package com.alttd.chat.listeners; + +import com.alttd.chat.api.MessageEvent; +import com.alttd.chat.config.Config; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.proxy.Player; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; + +import java.util.HashMap; +import java.util.Map; + +public class ChatListener { + + @Subscribe + public void onMessage(MessageEvent event) { + String senderName; + String receiverName; + + if (event.getSender() instanceof Player) { + Player sender = (Player) event.getSender(); + senderName = sender.getUsername(); + } else { + senderName = "UNKNOWN"; + } + receiverName = event.getRecipient().getUsername(); + + MiniMessage miniMessage = MiniMessage.get(); + + Map map = new HashMap<>(); + + map.put("sender", senderName); + map.put("receiver", receiverName); + map.put("message", event.getMessage()); + map.put("server", event.getRecipient().getCurrentServer().isPresent() ? event.getRecipient().getCurrentServer().get().getServerInfo().getName() : "Altitude"); + + Component senderMessage = miniMessage.parse(Config.MESSAGESENDER, map); + Component receiverMessage = miniMessage.parse(Config.MESSAGERECIEVER, map); + + event.getSender().sendMessage(senderMessage); + event.getRecipient().sendMessage(receiverMessage); + } +}