Initial commit ProxyDiscordLink

This commit is contained in:
Teriuihi 2021-09-10 19:31:36 +02:00
commit 7a7d797401
15 changed files with 1221 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.idea/
*.iml
*.bat
*.xml
target/
src/test/
src/main/resources/

View File

@ -0,0 +1,92 @@
package com.alttd.proxydiscordlink;
import com.alttd.proxydiscordlink.commands.DiscordCommand;
import com.alttd.proxydiscordlink.config.Config;
import com.alttd.proxydiscordlink.database.Database;
import com.alttd.proxydiscordlink.database.DatabaseConnection;
import com.alttd.proxydiscordlink.util.ALogger;
import com.alttd.proxydiscordlink.util.Cache;
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 java.io.File;
import java.nio.file.Path;
import java.sql.SQLException;
import java.util.logging.Logger;
@Plugin(id = "proxydiscordlink", name = "ProxyDiscordLink", version = "1.0.0",
description = "A plugin that links Discord accounts with uuid's",
authors = {"Teri"}
)
public class DiscordLink {
private static DiscordLink plugin;
private final ProxyServer server;
private final Logger logger;
private final Path dataDirectory;
private final Database database;
private final Cache cache;
@Inject
public DiscordLink(ProxyServer proxyServer, Logger proxyLogger, @DataDirectory Path proxydataDirectory)
{
plugin = this;
server = proxyServer;
logger = proxyLogger;
dataDirectory = proxydataDirectory;
database = new Database();
cache = new Cache();
}
@Subscribe
public void onProxyInitialization(ProxyInitializeEvent event) {
ALogger.init(logger);
ReloadConfig();
try {
DatabaseConnection.initialize();
} catch (SQLException exception) {
exception.printStackTrace();
getLogger().severe("*** Could not connect to the database. ***");
getLogger().severe("*** This plugin will be disabled. ***");
//TODO shutdown plugin
}
loadCommands();
}
public void ReloadConfig() {
Config.init();
ALogger.info("Reloaded DiscordLink config.");
}
public void loadCommands() {// all (proxy)commands go here
server.getCommandManager().register("discord", new DiscordCommand(), "discordlink");
}
public File getDataDirectory() {
return dataDirectory.toFile();
}
public static DiscordLink getPlugin() {
return plugin;
}
public Logger getLogger() {
return logger;
}
public ProxyServer getProxy() {
return server;
}
public Database getDatabase() {
return database;
}
public Cache getCache() {
return cache;
}
}

View File

@ -0,0 +1,92 @@
package com.alttd.proxydiscordlink.commands;
import com.alttd.proxydiscordlink.commands.subcommands.CheckLinked;
import com.alttd.proxydiscordlink.commands.subcommands.Link;
import com.alttd.proxydiscordlink.commands.subcommands.Unlink;
import com.alttd.proxydiscordlink.config.Config;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.Template;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DiscordCommand implements SimpleCommand {
private final List<SubCommand> subCommands;
private final MiniMessage miniMessage;
public DiscordCommand() {
subCommands = Arrays.asList(new CheckLinked(), new Link(), new Unlink());
miniMessage = MiniMessage.get();
}
@Override
public void execute(Invocation invocation) {
String[] args = invocation.arguments();
CommandSource source = invocation.source();
if (args.length < 1) {
if (!source.hasPermission("discordlink.link"))
source.sendMessage(miniMessage.parse(Config.NO_PERMISSION));
else
source.sendMessage(miniMessage.parse(Config.DISCORD_LINK));
return;
}
subCommands.stream()
.filter(subCommand -> subCommand.getName().equalsIgnoreCase(args[0]))
.findFirst()
.ifPresentOrElse(subCommand -> subCommand.execute(args, source)
, () -> source.sendMessage(getHelpMessage(source)));
}
@Override
public List<String> suggest(Invocation invocation) {
String[] args = invocation.arguments();
List<String> suggest = new ArrayList<>();
if (args.length == 0) {
subCommands.forEach(subCommand -> suggest.add(subCommand.getName()));
} else if (args.length <= 1) {
subCommands.stream().filter(subCommand -> subCommand.getName().startsWith(args[0].toLowerCase()))
.forEach(subCommand -> suggest.add(subCommand.getName()));
} else {
subCommands.stream().filter(subCommand -> subCommand.getName().equalsIgnoreCase(args[0])).findFirst()
.ifPresent(subCommand -> suggest.addAll(subCommand.suggest(args)));
}
if (args.length == 0)
return suggest;
else
return finalizeSuggest(suggest, args[args.length - 1]);
}
public List<String> finalizeSuggest(List<String> possibleValues, String remaining) {
List<String> finalValues = new ArrayList<>();
for (String str : possibleValues) {
if (str.toLowerCase().startsWith(remaining)) {
finalValues.add(StringArgumentType.escapeIfRequired(str));
}
}
return finalValues;
}
private Component getHelpMessage(CommandSource source) {
StringBuilder stringBuilder = new StringBuilder();
subCommands.stream()
.filter(subCommand -> source.hasPermission(subCommand.getPermission()))
.forEach(subCommand -> stringBuilder.append(subCommand.getHelpMessage()).append("\n"));
if (stringBuilder.length() != 0)
stringBuilder.replace(stringBuilder.length() - 1, stringBuilder.length(), "");
return miniMessage.parse(Config.HELP_MESSAGE, Template.of("commands", stringBuilder.toString()));
}
}

View File

@ -0,0 +1,19 @@
package com.alttd.proxydiscordlink.commands;
import com.velocitypowered.api.command.CommandSource;
import java.util.List;
public interface SubCommand {
String getName();
String getPermission();
void execute(String[] args, CommandSource source);
List<String> suggest(String[] args);
String getHelpMessage();
}

View File

@ -0,0 +1,79 @@
package com.alttd.proxydiscordlink.commands.subcommands;
import com.alttd.proxydiscordlink.DiscordLink;
import com.alttd.proxydiscordlink.commands.SubCommand;
import com.alttd.proxydiscordlink.config.Config;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.Template;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public class CheckLinked implements SubCommand {
private final String name;
private final String permission;
private final MiniMessage miniMessage;
public CheckLinked() {
name = "checklinked";
permission = "discordlink.checklinked";
miniMessage = MiniMessage.get();
}
public String getName() {
return name;
}
public String getPermission() {
return permission;
}
@Override
public void execute(String[] args, CommandSource source) {
if (!source.hasPermission(getPermission())) {
source.sendMessage(miniMessage.parse(Config.NO_PERMISSION));
return;
}
if (args.length != 2 || !args[1].matches("\\w{3,16}")) {
source.sendMessage(miniMessage.parse(getHelpMessage()));
return;
}
Optional<Player> optionalPlayer = DiscordLink.getPlugin().getProxy().getPlayer(args[1]);
if (optionalPlayer.isEmpty())
{
optionalPlayer = DiscordLink.getPlugin().getProxy()
.getPlayer(UUID.fromString(DiscordLink.getPlugin().getDatabase().uuidFromName(args[1])));
if (optionalPlayer.isEmpty())
{
source.sendMessage(miniMessage.parse(Config.INVALID_PLAYER));
return;
}
}
isLinked(source, optionalPlayer.get());
}
private void isLinked(CommandSource source, Player player) {
List<Template> templates = List.of(
Template.of("linked_status", DiscordLink.getPlugin().getDatabase()
.playerIsLinked(player) ? "linked" : "not linked"),
Template.of("player", player.getUsername()));
source.sendMessage(miniMessage.parse(Config.IS_LINKED, templates));
}
@Override
public List<String> suggest(String[] args) {
return null;
}
@Override
public String getHelpMessage() {
return Config.HELP_CHECK_LINKED;
}
}

View File

@ -0,0 +1,77 @@
package com.alttd.proxydiscordlink.commands.subcommands;
import com.alttd.proxydiscordlink.DiscordLink;
import com.alttd.proxydiscordlink.commands.SubCommand;
import com.alttd.proxydiscordlink.config.Config;
import com.alttd.proxydiscordlink.database.Database;
import com.alttd.proxydiscordlink.util.Utilities;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.Template;
import java.util.List;
public class Link implements SubCommand {
private final String name;
private final String permission;
private final MiniMessage miniMessage;
public Link() {
name = "link";
permission = "discordlink.link";
miniMessage = MiniMessage.get();
}
public String getName() {
return name;
}
public String getPermission() {
return permission;
}
@Override
public void execute(String[] args, CommandSource source) {
if (!(source instanceof Player player)) {
source.sendMessage(miniMessage.parse(Config.CONSOLE));
return;
}
if (!player.hasPermission(getPermission())) {
source.sendMessage(miniMessage.parse(Config.NO_PERMISSION));
return;
}
linkAccounts(player);
}
private void linkAccounts(Player player) {
Database database = DiscordLink.getPlugin().getDatabase();
if (database.playerIsLinked(player)) {
player.sendMessage(miniMessage.parse(Config.ALREADY_LINKED_ACCOUNTS));
return;
}
if (database.isInCache(player)) {
player.sendMessage(miniMessage.parse(Config.ALREADY_GOT_CODE));
}
String authCode = Utilities.getAuthKey();
player.sendMessage(miniMessage.parse(Config.GIVE_CODE, Template.of("code", authCode)));
DiscordLink.getPlugin().getCache()
.cacheCode(player.getUniqueId(), authCode);
database.storeDataInCache(player, authCode, Utilities.getRankName(player), Utilities.isDonor(player));
}
@Override
public List<String> suggest(String[] args) {
return null;
}
@Override
public String getHelpMessage() {
return Config.HELP_LINK;
}
}

View File

@ -0,0 +1,70 @@
package com.alttd.proxydiscordlink.commands.subcommands;
import com.alttd.proxydiscordlink.DiscordLink;
import com.alttd.proxydiscordlink.commands.SubCommand;
import com.alttd.proxydiscordlink.config.Config;
import com.alttd.proxydiscordlink.database.Database;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.minimessage.MiniMessage;
import java.util.List;
public class Unlink implements SubCommand {
private final String name;
private final String permission;
private final MiniMessage miniMessage;
public Unlink() {
name = "unlink";
permission = "discordlink.unlink";
miniMessage = MiniMessage.get();
}
@Override
public String getName() {
return name;
}
@Override
public String getPermission() {
return permission;
}
@Override
public void execute(String[] args, CommandSource source) {
if (!(source instanceof Player player)) {
source.sendMessage(miniMessage.parse(Config.CONSOLE));
return;
}
if (!player.hasPermission(getPermission())) {
source.sendMessage(miniMessage.parse(Config.NO_PERMISSION));
return;
}
unlinkAccounts(player);
}
private void unlinkAccounts(Player player) {
Database database = DiscordLink.getPlugin().getDatabase();
if (!database.playerIsLinked(player)) {
player.sendMessage(miniMessage.parse(Config.ACCOUNTS_NOT_LINKED));
return;
}
database.removeLinkedAccount(player);
player.sendMessage(miniMessage.parse(Config.UNLINKED_ACCOUNTS));
}
@Override
public List<String> suggest(String[] args) {
return null;
}
@Override
public String getHelpMessage() {
return Config.HELP_UNLINK;
}
}

View File

@ -0,0 +1,220 @@
package com.alttd.proxydiscordlink.config;
import com.google.common.base.Throwables;
import com.google.common.reflect.TypeToken;
import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.ConfigurationOptions;
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 File CONFIGPATH;
public static void init() { // todo setup share for the config
CONFIGPATH = new File(System.getProperty("user.home") + File.separator + "share" + File.separator + "configs" + File.separator + "DiscordLink");
CONFIG_FILE = new File(CONFIGPATH, "config.yml");
configLoader = YAMLConfigurationLoader.builder()
.setFile(CONFIG_FILE)
.setFlowStyle(DumperOptions.FlowStyle.BLOCK)
.build();
if (!CONFIG_FILE.getParentFile().exists()) {
if (!CONFIG_FILE.getParentFile().mkdirs()) {
return;
}
}
if (!CONFIG_FILE.exists()) {
try {
if (!CONFIG_FILE.createNewFile()) {
return;
}
} catch (IOException error) {
error.printStackTrace();
}
}
try {
config = configLoader.load(ConfigurationOptions.defaults().setHeader(HEADER));
} catch (IOException e) {
e.printStackTrace();
}
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 | IllegalAccessException ex) {
throw Throwables.propagate(ex.getCause());
}
}
}
}
try {
configLoader.save(config);
} catch (IOException ex) {
throw Throwables.propagate(ex.getCause());
}
}
public static void saveConfig() {
try {
configLoader.save(config);
} catch (IOException ex) {
throw Throwables.propagate(ex.getCause());
}
}
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 void setString(String path, String def) {
try {
if (config.getNode(splitPath(path)).isVirtual())
config.getNode(splitPath(path)).setValue(TypeToken.of(String.class), def);
} catch (ObjectMappingException ex) {
}
}
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) {
setString(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 <T> List<String> 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<>();
}
private static ConfigurationNode getNode(String path) {
if (config.getNode(splitPath(path)).isVirtual()) {
//new RegexConfig("Dummy");
}
config.getChildrenMap();
return config.getNode(splitPath(path));
}
/**
* ONLY EDIT ANYTHING BELOW THIS LINE
**/
public static String DRIVERS = "mysql";
public static String IP = "localhost";
public static String PORT = "3306";
public static String DATABASE_NAME = "discordlink";
public static String USERNAME = "root";
public static String PASSWORD = "root";
private static void database() {
DRIVERS = getString("database.drivers", DRIVERS);
IP = getString("database.ip", IP);
PORT = getString("database.port", PORT);
DATABASE_NAME = getString("database.database_name", DATABASE_NAME);
USERNAME = getString("database.username", USERNAME);
PASSWORD = getString("database.password", PASSWORD);
}
public static List<String> DONOR_GROUPS = new ArrayList<>(List.of("donor"));
public static List<String> DISCORD_GROUPS = new ArrayList<>(List.of("nitro"));
private static void loadGroups() {
DONOR_GROUPS = getList("settings.donor-groups", DONOR_GROUPS);
DISCORD_GROUPS = getList("settings.discord-groups", DISCORD_GROUPS);
}
public static List<String> DISCORD_MESSAGE = new ArrayList<>(List.of("Invite code here."));
public static String DISCORD_LINK = "<click:run:command:discord link:><yellow>Your Minecraft and Discord accounts aren't linked yet, to link them click this message!</yellow></click>";
public static String GIVE_CODE = "<yellow>Your code is <gold><code></gold>, To link your accounts do <gold>&link <code></gold> in the Discord #link channel.</yellow>";
public static String ALREADY_LINKED_ACCOUNTS = "<yellow>Your accounts are already linked. You can unlink your accounts by doing <gold>/discord unlink</gold>.</yellow>";
public static String ALREADY_GOT_CODE = "<yellow>You have already got your code. Your code is <gold><code><gold></yellow>";
public static String ACCOUNTS_NOT_LINKED = "<yellow>Your Minecraft and Discord accounts aren't linked</yellow>";
public static String UNLINKED_ACCOUNTS = "<yellow>You have successfully unlinked your accounts.</yellow>";
public static String IS_LINKED = "<yellow><player> is <linked_status>.</yellow>";
public static String INVALID_PLAYER = "<red><player> is not online or is not a valid player.</red>";
public static String NO_PERMISSION = "<red>You do not have permission to do that.</red>";
public static String CONSOLE = "<red>This command can not be executed from console.</red>";
public static String HELP_MESSAGE = "<yellow>DiscordLink commands:\n<commands></yellow>";
public static String HELP_LINK = "<yellow><gold>/discord link</gold>: Get a code which can be used to link your Minecraft and Discord accounts.</yellow>";
public static String HELP_UNLINK = "<yellow><gold>/discord unlink</gold>: Unlink your Minecraft and Discord accounts.</yellow>";
public static String HELP_CHECK_LINKED = "<yellow><gold>/discord checklinked <user></gold>: Check if the specified user has their Minecraft and Discord accounts linked.</yellow>";
private static void loadMessages() {
DISCORD_MESSAGE = getList("messages.discord-message", DISCORD_MESSAGE);
GIVE_CODE = getString("messages.give-code", GIVE_CODE);
ALREADY_LINKED_ACCOUNTS = getString("messages.already-linked-accounts", ALREADY_LINKED_ACCOUNTS);
ALREADY_GOT_CODE = getString("messages.already-got-code", ALREADY_GOT_CODE);
ACCOUNTS_NOT_LINKED = getString("messages.accounts-not-linked", ACCOUNTS_NOT_LINKED);
UNLINKED_ACCOUNTS = getString("messages.unlinked-accounts", UNLINKED_ACCOUNTS);
IS_LINKED = getString("messages.is-linked", IS_LINKED);
INVALID_PLAYER = getString("messages.invalid-player", INVALID_PLAYER);
NO_PERMISSION = getString("messages.no-permission", NO_PERMISSION);
CONSOLE = getString("messages.console", CONSOLE);
HELP_MESSAGE = getString("message.help-message", HELP_MESSAGE);
HELP_LINK = getString("message.help-link", HELP_LINK);
HELP_UNLINK = getString("message.help-unlink", HELP_UNLINK);
HELP_CHECK_LINKED = getString("message.help-check-linked", HELP_CHECK_LINKED);
}
}

View File

@ -0,0 +1,295 @@
package com.alttd.proxydiscordlink.database;
import com.alttd.proxydiscordlink.util.ALogger;
import com.alttd.proxydiscordlink.util.Utilities;
import com.velocitypowered.api.proxy.Player;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Objects;
import java.util.UUID;
public class Database {
public void createTables() {
String linked_accounts = "CREATE TABLE IF NOT EXISTS linked_accounts (" +
"player_uuid VARCHAR(36) NOT NULL, " +
"player_name VARCHAR(16) NOT NULL, " +
"player_nickname VARCHAR(16), " +
"player_rank VARCHAR(256), " +
"player_isdonor BIT NOT NULL, " +
"player_isnitro BIT NOT NULL, " +
"discord_username VARCHAR(256) NOT NULL, " +
"discord_id VARCHAR(256) NOT NULL, " +
"PRIMARY KEY(player_uuid)" +
");";
String cache = "CREATE TABLE IF NOT EXISTS cache (" +
"player_uuid VARCHAR(36) NOT NULL, " +
"player_name VARCHAR(16) NOT NULL, " +
"player_nickname VARCHAR(16), " +
"player_rank VARCHAR(256), " +
"player_isdonor BIT NOT NULL, " +
"code VARCHAR(6) NOT NULL, " +
"PRIMARY KEY(player_uuid)" +
");";
String updates = "CREATE TABLE IF NOT EXISTS `updates` (" +
"`player_uuid` varchar(36) NOT NULL, " +
"`player_name` varchar(16) NOT NULL, " +
"`player_nickname` varchar(16) DEFAULT NULL, " +
"`player_rank` varchar(256) DEFAULT NULL, " +
"`player_isdonor` bit(1) DEFAULT b'0', " +
"`player_isnitro` bit(1) DEFAULT b'0', " +
"`discord_username` varchar(256) DEFAULT NULL, " +
"`discord_id` varchar(256) DEFAULT NULL, " +
"`discord_update` bit(2) NOT NULL DEFAULT b'0', " +
"`minecraft_update` bit(2) NOT NULL DEFAULT b'0', " +
"PRIMARY KEY (`player_uuid`)" +
")";
try {
Statement statement = DatabaseConnection.getConnection().createStatement();
statement.execute(linked_accounts);
statement.execute(cache);
statement.execute(updates);
} catch (SQLException var3) {
var3.printStackTrace();
}
}
public void storeDataInCache(Player player, String code, String rank, boolean isDonor) {
String sql = "INSERT INTO cache (player_uuid, player_name, player_nickname, player_rank, player_isdonor, code) VALUES (?, ?, ?, ?, ?, ?)";
try {
PreparedStatement statement = DatabaseConnection.getConnection().prepareStatement(sql);
statement.setString(1, player.getUniqueId().toString());
statement.setString(2, player.getUsername());
statement.setString(3, getNick(player.getUniqueId()));
statement.setString(4, rank);
statement.setBoolean(5, isDonor);
statement.setString(6, code);
statement.execute();
} catch (SQLException exception) {
exception.printStackTrace();
}
}
public void syncPlayerData(Player player) {
ResultSet resultSet = getPlayerData(player.getUniqueId());
try {
if (!resultSet.next()) return;
String discordNickname = resultSet.getString("player_nickname");
String playerNickname = getNick(player.getUniqueId());
boolean correctName = resultSet.getString("player_name").equals(player.getUsername());
boolean correctNick = Objects.equals(discordNickname, playerNickname);
boolean correctRankName = resultSet.getString("player_rank").equals(Utilities.getRankName(player));
boolean correctRank = resultSet.getBoolean("player_isdonor") == (Utilities.isDonor(player));
if (correctName && correctNick && correctRankName && correctRank) {
return;
}
syncPlayer(resultSet, player, playerNickname);
} catch (SQLException exception) {
exception.printStackTrace();
}
}
public ResultSet getPlayerData(UUID uuid) {
try {
return getStringResult("SELECT * FROM linked_accounts WHERE player_uuid = ?", uuid.toString());
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
private void syncPlayer(ResultSet resultSet, Player player, String playerNickname) {
try {
String sql = "INSERT INTO updates " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, 1) " +
"ON DUPLICATE KEY UPDATE player_uuid = ?";
PreparedStatement statement = DatabaseConnection.getConnection().prepareStatement(sql);
int donor = Utilities.isDonor(player) ? 1 : 0;
String uuid = player.getUniqueId().toString();
statement.setString(1, player.getUniqueId().toString());
statement.setString(2, player.getUsername());
statement.setString(3, playerNickname);
statement.setString(4, Utilities.getRankName(player));
statement.setInt(5, donor);
statement.setInt(6, resultSet.getInt("player_isnitro"));
statement.setString(7, resultSet.getString("discord_username"));
statement.setString(8, resultSet.getString("discord_id"));
statement.setString(9, uuid);
statement.execute();
} catch (SQLException exception) {
exception.printStackTrace();
}
}
public boolean isInCache(Player player) { //TODO maybe this can be a map instead
try {
ResultSet resultSet = getStringResult("SELECT * FROM cache WHERE player_uuid = ?", player.getUniqueId().toString());
if (resultSet.next()) {
return true;
}
} catch (SQLException exception) {
exception.printStackTrace();
}
return false;
}
public boolean playerIsLinked(Player player) { //TODO maybe this can be using the discord api instead? (or a cache idk)
try {
PreparedStatement statement = DatabaseConnection.getConnection()
.prepareStatement("SELECT * FROM linked_accounts WHERE player_uuid = '" + player.getUniqueId().toString() + "'");
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
return true;
}
} catch (SQLException exception) {
exception.printStackTrace();
}
return false;
}
public void removeLinkedAccount(Player player) {
String discordId = "0";
try {
PreparedStatement statementSelect = DatabaseConnection.getConnection()
.prepareStatement("SELECT * FROM linked_accounts WHERE player_uuid = '" + player.getUniqueId().toString() + "'");
ResultSet resultSet = statementSelect.executeQuery();
if (!resultSet.next()) {
ALogger.error("Unable to remove linked account for: " + player.getUsername() + " : " + player.getUniqueId());
return;
}
PreparedStatement statementInsert = DatabaseConnection.getConnection()
.prepareStatement("INSERT INTO `updates` (`player_uuid`, `player_name`, `player_nickname`, `player_rank`, " +
"`player_isdonor`, `player_isnitro`, `discord_username`, `discord_id`, " +
"`discord_update`, `minecraft_update`) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE player_uuid = ?");
discordId = resultSet.getString("discord_id");
statementInsert.setString(1, resultSet.getString("player_uuid"));
statementInsert.setString(2, resultSet.getString("player_name"));
statementInsert.setString(3, resultSet.getString("player_nickname"));
statementInsert.setString(4, resultSet.getString("player_rank"));
statementInsert.setInt(5, resultSet.getInt("player_isdonor"));
statementInsert.setInt(6, resultSet.getInt("player_isnitro"));
statementInsert.setString(7, resultSet.getString("discord_username"));
statementInsert.setString(8, discordId);
statementInsert.setInt(9, 0);
statementInsert.setInt(10, 2);
statementInsert.setString(11, resultSet.getString("player_uuid"));
statementInsert.execute();
statementInsert.close();
} catch (SQLException exception) {
exception.printStackTrace();
}
try {
PreparedStatement statement = DatabaseConnection.getConnection()
.prepareStatement("DELETE FROM linked_accounts WHERE player_uuid = ?");
statement.setString(1, player.getUniqueId().toString());
statement.execute();
statement = DatabaseConnection.getConnection()
.prepareStatement("DELETE FROM name_type WHERE discord_id = ?");
statement.setString(1, discordId);
statement.execute();
statement.close();
} catch (SQLException var2) {
var2.printStackTrace();
}
}
public void removePlayerFromCache(Player player) {
try {
PreparedStatement statement = DatabaseConnection.getConnection()
.prepareStatement("DELETE FROM cache WHERE player_uuid = '" + player.getUniqueId().toString() + "'");
statement.executeUpdate();
statement.close();
} catch (SQLException exception) {
exception.printStackTrace();
}
}
public boolean hasDiscordNitro(Player player) {
try {
PreparedStatement statement = DatabaseConnection.getConnection()
.prepareStatement("SELECT * FROM linked_accounts WHERE player_uuid = '" + player.getUniqueId().toString() + "'");
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
return resultSet.getInt("player_isnitro") == 1;
}
} catch (SQLException exception) {
exception.printStackTrace();
}
return false;
}
public String uuidFromName(String playerName) {
try {
PreparedStatement statement = DatabaseConnection.getConnection()
.prepareStatement("SELECT * FROM linked_accounts WHERE player_name = '" + playerName + "'");
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
return resultSet.getString("player_name");
}
} catch (SQLException exception) {
exception.printStackTrace();
}
return null;
}
public String getNick(UUID uuid) {
try {
PreparedStatement statement = DatabaseConnection.getConnection()
.prepareStatement("SELECT * FROM nicknames WHERE uuid = ?");
statement.setString(1, uuid.toString());
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
return resultSet.getString("player_name");
}
} catch (SQLException exception) {
exception.printStackTrace();
}
return null;
}
private ResultSet getStringResult(String query, String... parameters) throws SQLException {
PreparedStatement statement = DatabaseConnection.getConnection().prepareStatement(query);
for (int i = 1; i < parameters.length + 1; ++i) {
statement.setString(i, parameters[i - 1]);
}
return statement.executeQuery();
}
}

View File

@ -0,0 +1,49 @@
package com.alttd.proxydiscordlink.database;
import com.alttd.proxydiscordlink.config.Config;
import com.alttd.proxydiscordlink.DiscordLink;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DatabaseConnection {
private static DatabaseConnection instance;
private Connection connection;
public DatabaseConnection() throws SQLException {
instance = this;
instance.openConnection();
DiscordLink.getPlugin().getDatabase().createTables();
}
public void openConnection() throws SQLException {
if (this.connection == null || this.connection.isClosed()) {
synchronized(this) {
if (this.connection == null || this.connection.isClosed()) {
this.connection = DriverManager.getConnection("jdbc:"
+ Config.DRIVERS + "://"
+ Config.IP + ":"
+ Config.PORT + "/"
+ Config.DATABASE_NAME
+ "?autoReconnect=true&useSSL=false", Config.USERNAME, Config.PASSWORD);
}
}
}
}
public static Connection getConnection() {
try {
instance.openConnection();
} catch (SQLException var1) {
var1.printStackTrace();
}
return instance.connection;
}
public static void initialize() throws SQLException {
instance = new DatabaseConnection();
}
}

View File

@ -0,0 +1,52 @@
package com.alttd.proxydiscordlink.events;
import com.alttd.proxydiscordlink.database.Database;
import com.alttd.proxydiscordlink.DiscordLink;
import com.alttd.proxydiscordlink.util.ALogger;
import com.alttd.proxydiscordlink.util.Utilities;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.player.ServerConnectedEvent;
import com.velocitypowered.api.proxy.Player;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.model.group.Group;
import net.luckperms.api.model.user.User;
import net.luckperms.api.node.Node;
import net.luckperms.api.node.types.InheritanceNode;
public class playerJoin {
@Subscribe(order = PostOrder.LATE)
public void onPlayerJoin(ServerConnectedEvent event) {
Player player = event.getPlayer();
Database database = DiscordLink.getPlugin().getDatabase();
if (database.playerIsLinked(player)) {
database.syncPlayerData(player);
boolean isNitro = database.hasDiscordNitro(player);
boolean hasNitro = Utilities.hasMinecraftNitro(player);
LuckPerms luckPermsAPI = Utilities.getLuckPerms();
Group discord = luckPermsAPI.getGroupManager().getGroup("discord");
if (discord == null) {
ALogger.error("Unable to find discord group in DiscordLink");
return;
}
if (isNitro && !hasNitro) {
luckPermsAPI.getUserManager().modifyUser(player.getUniqueId(), (User user) -> {
Node node = InheritanceNode.builder(discord).build();
user.data().add(node);
});
} else if (!isNitro && hasNitro) {
luckPermsAPI.getUserManager().modifyUser(player.getUniqueId(), (User user) -> {
Node node = InheritanceNode.builder(discord).build();
user.data().remove(node);
});
}
}
}
}

View File

@ -0,0 +1,23 @@
package com.alttd.proxydiscordlink.events;
import com.alttd.proxydiscordlink.database.Database;
import com.alttd.proxydiscordlink.DiscordLink;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.DisconnectEvent;
import com.velocitypowered.api.proxy.Player;
public class playerLeave {
@Subscribe(order = PostOrder.LATE)
public void onPlayerLeave(DisconnectEvent event) {
Player player = event.getPlayer();
Database database = DiscordLink.getPlugin().getDatabase();
if (database.isInCache(player)) //TODO async?
database.removePlayerFromCache(player);
DiscordLink.getPlugin().getCache().removeCachedPlayer(player.getUniqueId());
}
}

View File

@ -0,0 +1,29 @@
package com.alttd.proxydiscordlink.util;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ALogger {
private static Logger logger;
public static void init(Logger log) {
logger = log;
}
private void log(String message) {
logger.info(message);
}
public static void warn(String message) {
logger.warning(message);
}
public static void info(String message) {
logger.info(message);
}
public static void error(String message) {
logger.log(Level.SEVERE, message);
}
}

View File

@ -0,0 +1,28 @@
package com.alttd.proxydiscordlink.util;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class Cache {
private final Map<UUID, String> playerCacheCode;
public Cache() {
playerCacheCode = new HashMap<>();
}
public void cacheCode(UUID uuid, String code) {
playerCacheCode.put(uuid, code);
}
public String getCode(UUID uuid)
{
return playerCacheCode.get(uuid);
}
public void removeCachedPlayer(UUID uuid)
{
playerCacheCode.remove(uuid);
}
}

View File

@ -0,0 +1,89 @@
package com.alttd.proxydiscordlink.util;
import com.alttd.proxydiscordlink.config.Config;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.Template;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider;
import net.luckperms.api.model.user.User;
import net.luckperms.api.node.NodeType;
import net.luckperms.api.node.types.InheritanceNode;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
public class Utilities {
private static LuckPerms luckPerms;
private static MiniMessage miniMessage = MiniMessage.get();
public static LuckPerms getLuckPerms() {
if (luckPerms == null)
luckPerms = LuckPermsProvider.get();
return luckPerms;
}
public static boolean isDonor(Player player) {
User user = getLuckPerms().getUserManager().getUser(player.getUniqueId());
if (user == null) {
ALogger.error("Unable to find user in isDonor!");
return false;
}
Set<String> groups = user
.getNodes().stream()
.filter(NodeType.INHERITANCE::matches)
.map(NodeType.INHERITANCE::cast)
.map(InheritanceNode::getGroupName)
.collect(Collectors.toSet());
for (String group : Config.DONOR_GROUPS) {
if (groups.contains(group)) {
return true;
}
}
return false;
}
public static boolean hasMinecraftNitro(Player player) {
User user = getLuckPerms().getUserManager().getUser(player.getUniqueId());
if (user == null) {
ALogger.error("Unable to find user in isNitro!");
return false;
}
Set<String> groups = user
.getNodes().stream()
.filter(NodeType.INHERITANCE::matches)
.map(NodeType.INHERITANCE::cast)
.map(InheritanceNode::getGroupName)
.collect(Collectors.toSet());
for (String group : Config.DISCORD_GROUPS) {
if (groups.contains(group)) {
return true;
}
}
return false;
}
public static String getAuthKey() {
String randChars = "1234567890";
StringBuilder salt = new StringBuilder();
Random rnd = new Random();
while (salt.length() < 6) {
int index = (int) (rnd.nextFloat() * (float) randChars.length());
salt.append(randChars.charAt(index));
}
return salt.toString();
}
public static String getRankName(Player player) {
return getLuckPerms().getUserManager().getUser(player.getUniqueId()).getPrimaryGroup();
}
}