diff --git a/build.gradle b/build.gradle
index 16e9d74..25542a8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -32,6 +32,7 @@ dependencies {
implementation("net.dv8tion:JDA:5.0.0-alpha.3") {
exclude module: 'opus-java'
}
+ implementation('com.zaxxer:HikariCP:5.0.1')
implementation 'club.minnced:discord-webhooks:0.7.4'
implementation 'com.fasterxml.jackson.core:jackson-core:2.13.1'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.1'
@@ -50,7 +51,7 @@ shadowJar {
group = 'work.novablog.mcplugin'
version = '3.1'
description = 'DiscordConnect'
-archivesBaseName = 'DiscordConnect-spigot'
+archivesBaseName = 'DiscordConnect-old-spigot'
java.sourceCompatibility = JavaVersion.VERSION_1_8
tasks.withType(JavaCompile) {
diff --git a/src/main/java/work/novablog/mcplugin/discordconnect/DiscordConnect.java b/src/main/java/work/novablog/mcplugin/discordconnect/DiscordConnect.java
index 2891126..6ee00bc 100644
--- a/src/main/java/work/novablog/mcplugin/discordconnect/DiscordConnect.java
+++ b/src/main/java/work/novablog/mcplugin/discordconnect/DiscordConnect.java
@@ -7,12 +7,13 @@
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
+import work.novablog.mcplugin.discordconnect.account.AccountManager;
+import work.novablog.mcplugin.discordconnect.account.db.DatabaseConfig;
import work.novablog.mcplugin.discordconnect.command.BukkitCommand;
import work.novablog.mcplugin.discordconnect.command.DiscordCommandExecutor;
import work.novablog.mcplugin.discordconnect.command.DiscordStandardCommand;
import work.novablog.mcplugin.discordconnect.listener.BukkitListener;
import work.novablog.mcplugin.discordconnect.listener.LunaChatListener;
-import work.novablog.mcplugin.discordconnect.util.AccountManager;
import work.novablog.mcplugin.discordconnect.util.ConfigManager;
import work.novablog.mcplugin.discordconnect.util.GithubAPI;
import work.novablog.mcplugin.discordconnect.util.discord.BotManager;
@@ -20,7 +21,6 @@
import javax.annotation.Nullable;
import javax.security.auth.login.LoginException;
-import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Optional;
@@ -38,7 +38,7 @@ public final class DiscordConnect extends JavaPlugin {
private UUIDCacheData uuidCacheData;
private LunaChatListener lunaChatListener;
- private final AccountManager accountManager = new AccountManager(new File(getDataFolder(), "accounts.yml"));
+ private @Nullable AccountManager accountManager;
/**
* インスタンスを返します
@@ -56,7 +56,7 @@ public static DiscordConnect getInstance() {
return botManager;
}
- public AccountManager getAccountManager() {
+ public @Nullable AccountManager getAccountManager() {
return accountManager;
}
@@ -105,7 +105,12 @@ public void onEnable() {
discordCommandExecutor = new DiscordCommandExecutor();
discordCommandExecutor.registerCommand(new DiscordStandardCommand());
- init();
+ try {
+ init();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ setEnabled(false);
+ }
}
/**
@@ -115,20 +120,17 @@ public void onEnable() {
* 複数回呼び出した場合、新しいconfigデータが読み出されます
*
*/
- public void init() {
+ public void init() throws IOException {
if(botManager != null) botManager.botShutdown(true);
if(discordWebhookSenders != null) discordWebhookSenders.forEach(DiscordWebhookSender::shutdown);
if(bukkitListener != null) HandlerList.unregisterAll(bukkitListener);
if(lunaChatListener != null) HandlerList.unregisterAll(lunaChatListener);
- ConfigManager configManager;
- try {
- configManager = new ConfigManager(this);
- } catch (IOException e) {
- e.printStackTrace();
- return;
- }
- accountManager.loadFile();
+ ConfigManager configManager = new ConfigManager(this);
+
+ DatabaseConfig dbConfig = configManager.getAccountsDatabaseConfig();
+ accountManager = AccountManager.createManager(dbConfig, this);
+ accountManager.connect();
discordCommandExecutor.setAdminRole(configManager.adminRole);
@@ -160,7 +162,7 @@ public void init() {
}
//BungeecordイベントのListenerを登録
- bukkitListener = new BukkitListener(configManager.fromMinecraftToDiscordName);
+ bukkitListener = new BukkitListener(configManager);
getServer().getPluginManager().registerEvents(bukkitListener, this);
if(lunaChatAPI != null) {
lunaChatListener = new LunaChatListener(
@@ -203,5 +205,13 @@ public void init() {
public void onDisable() {
if(botManager != null) botManager.botShutdown(false);
if(discordWebhookSenders != null) discordWebhookSenders.forEach(DiscordWebhookSender::shutdown);
+
+ if (accountManager != null) {
+ try {
+ accountManager.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
}
}
diff --git a/src/main/java/work/novablog/mcplugin/discordconnect/account/AccountManager.java b/src/main/java/work/novablog/mcplugin/discordconnect/account/AccountManager.java
new file mode 100644
index 0000000..c7186fc
--- /dev/null
+++ b/src/main/java/work/novablog/mcplugin/discordconnect/account/AccountManager.java
@@ -0,0 +1,97 @@
+package work.novablog.mcplugin.discordconnect.account;
+
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import work.novablog.mcplugin.discordconnect.account.db.DatabaseConfig;
+import work.novablog.mcplugin.discordconnect.account.db.MySQLAccountManager;
+import work.novablog.mcplugin.discordconnect.account.db.SQLiteAccountManager;
+import work.novablog.mcplugin.discordconnect.account.db.YamlAccountManager;
+
+import java.io.IOException;
+import java.util.Locale;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ThreadLocalRandom;
+
+public abstract class AccountManager {
+ private final Map linkingCodes = new ConcurrentHashMap<>();
+ private final Map names = new ConcurrentHashMap<>();
+
+
+ public abstract void connect() throws IOException;
+
+ public abstract void close() throws IOException;
+
+
+ public final String generateCode(UUID playerUuid, String playerName) {
+ names.put(playerUuid, playerName);
+ String codeString;
+ do {
+ int code = ThreadLocalRandom.current().nextInt(10000);
+ codeString = String.format("%04d", code);
+
+ } while (linkingCodes.putIfAbsent(codeString, playerUuid) != null);
+ return codeString;
+ }
+
+ public @Nullable String getLinkingPlayerName(UUID playerId) {
+ return names.get(playerId);
+ }
+
+ public final @Nullable UUID removeMinecraftIdByLinkCode(@NotNull String code) {
+ return linkingCodes.remove(code);
+ }
+
+ public final Map linkingCodes() {
+ return linkingCodes;
+ }
+
+
+ public abstract CompletableFuture<@NotNull Boolean> isLinkedDiscord(@NotNull UUID minecraftId);
+
+ public abstract CompletableFuture<@Nullable Long> getLinkedDiscordId(@NotNull UUID minecraftId);
+
+ public abstract CompletableFuture linkDiscordId(@NotNull UUID minecraftId, long discordId);
+
+ public abstract CompletableFuture<@NotNull Boolean> isLinkedMinecraft(long discordId);
+
+ public abstract CompletableFuture<@Nullable UUID> getLinkedMinecraftId(long discordId);
+
+ public abstract CompletableFuture unlinkByMinecraftId(@NotNull UUID minecraftId);
+
+ public abstract CompletableFuture unlinkByDiscordId(long discordId);
+
+
+ public abstract CompletableFuture getLinkedAccountAll();
+
+ public abstract CompletableFuture getLinkedAccountCount();
+
+
+ public static DatabaseConfig createDatabaseConfig(String dbType, ConfigurationSection config) {
+ switch (dbType.toLowerCase(Locale.ROOT)) {
+ case "yaml":
+ return new YamlAccountManager.DatabaseConfig(config);
+ case "sqlite":
+ return new SQLiteAccountManager.DatabaseConfig(config);
+ case "mysql":
+ return new MySQLAccountManager.DatabaseConfig(config);
+ }
+ throw new IllegalArgumentException("Unknown database type: " + dbType);
+ }
+
+ public static AccountManager createManager(DatabaseConfig config, Plugin plugin) {
+ if (config instanceof YamlAccountManager.DatabaseConfig) {
+ return new YamlAccountManager(plugin.getDataFolder(), ((YamlAccountManager.DatabaseConfig) config));
+ } else if (config instanceof SQLiteAccountManager.DatabaseConfig) {
+ return new SQLiteAccountManager(plugin.getDataFolder(), ((SQLiteAccountManager.DatabaseConfig) config));
+ } else if (config instanceof MySQLAccountManager.DatabaseConfig) {
+ return new MySQLAccountManager(((MySQLAccountManager.DatabaseConfig) config));
+ }
+ throw new IllegalArgumentException("Unknown database config: " + config.getClass().getSimpleName());
+ }
+
+}
diff --git a/src/main/java/work/novablog/mcplugin/discordconnect/account/LinkedAccount.java b/src/main/java/work/novablog/mcplugin/discordconnect/account/LinkedAccount.java
new file mode 100644
index 0000000..defbd42
--- /dev/null
+++ b/src/main/java/work/novablog/mcplugin/discordconnect/account/LinkedAccount.java
@@ -0,0 +1,25 @@
+package work.novablog.mcplugin.discordconnect.account;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.UUID;
+
+public class LinkedAccount {
+
+ private final @NotNull UUID minecraftId;
+ private final long discordId;
+
+ public LinkedAccount(@NotNull UUID minecraftId, long discordId) {
+ this.minecraftId = minecraftId;
+ this.discordId = discordId;
+ }
+
+ public @NotNull UUID getMinecraftId() {
+ return minecraftId;
+ }
+
+ public long getDiscordId() {
+ return discordId;
+ }
+
+}
diff --git a/src/main/java/work/novablog/mcplugin/discordconnect/account/db/DatabaseConfig.java b/src/main/java/work/novablog/mcplugin/discordconnect/account/db/DatabaseConfig.java
new file mode 100644
index 0000000..ab99a1f
--- /dev/null
+++ b/src/main/java/work/novablog/mcplugin/discordconnect/account/db/DatabaseConfig.java
@@ -0,0 +1,19 @@
+package work.novablog.mcplugin.discordconnect.account.db;
+
+import org.bukkit.configuration.ConfigurationSection;
+
+public abstract class DatabaseConfig {
+
+ protected final ConfigurationSection config;
+
+ public DatabaseConfig(ConfigurationSection config) {
+ this.config = config;
+ }
+
+ public abstract String getType();
+
+ public ConfigurationSection getConfig() {
+ return config;
+ }
+
+}
diff --git a/src/main/java/work/novablog/mcplugin/discordconnect/account/db/MySQLAccountManager.java b/src/main/java/work/novablog/mcplugin/discordconnect/account/db/MySQLAccountManager.java
new file mode 100644
index 0000000..c73479d
--- /dev/null
+++ b/src/main/java/work/novablog/mcplugin/discordconnect/account/db/MySQLAccountManager.java
@@ -0,0 +1,291 @@
+package work.novablog.mcplugin.discordconnect.account.db;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import org.bukkit.configuration.ConfigurationSection;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import work.novablog.mcplugin.discordconnect.account.AccountManager;
+import work.novablog.mcplugin.discordconnect.account.LinkedAccount;
+
+import java.io.IOException;
+import java.sql.*;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+
+public class MySQLAccountManager extends AccountManager {
+
+ private final DatabaseConfig config;
+ private @Nullable HikariDataSource hikari;
+
+ public MySQLAccountManager(DatabaseConfig config) {
+ this.config = config;
+ }
+
+ public DatabaseConfig getConfig() {
+ return config;
+ }
+
+ @Override
+ public void connect() throws IOException {
+ HikariConfig config = new HikariConfig();
+ config.setDriverClassName("com.mysql.jdbc.Driver");
+ config.setJdbcUrl("jdbc:mysql://" + this.config.getAddress() + "/" + this.config.getDatabase());
+ config.setConnectionInitSql("SELECT 1");
+ config.setAutoCommit(true);
+ this.config.getProperties().forEach(config::addDataSourceProperty);
+ hikari = new HikariDataSource(config);
+ initDatabase();
+ }
+
+ @Override
+ public void close() {
+ if (hikari != null) {
+ hikari.close();
+ }
+ hikari = null;
+ }
+
+ public void initDatabase() throws IOException {
+ if (hikari == null)
+ throw new IOException("database closed");
+
+ try (Connection conn = hikari.getConnection();
+ Statement stmt = conn.createStatement()) {
+ String sql = "CREATE TABLE IF NOT EXISTS linked (mc_uuid VARCHAR(36) UNIQUE, discord_id BIGINT, link_time BIGINT)";
+ stmt.execute(sql);
+ } catch (SQLException e) {
+ throw new IOException(e);
+ }
+ }
+
+
+ @Override
+ public CompletableFuture<@NotNull Boolean> isLinkedDiscord(@NotNull UUID minecraftId) {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ String sql = "SELECT 1 FROM `linked` WHERE `mc_uuid` = ? LIMIT 1";
+ try (Connection conn = hikari.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+ stmt.setString(1, minecraftId.toString().toLowerCase(Locale.ROOT));
+ ResultSet rs = stmt.executeQuery();
+ return rs.next();
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ @Override
+ public CompletableFuture<@Nullable Long> getLinkedDiscordId(@NotNull UUID minecraftId) {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ String sql = "SELECT `discord_id` FROM `linked` WHERE `mc_uuid` = ? LIMIT 1";
+ try (Connection conn = hikari.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+ stmt.setString(1, minecraftId.toString().toLowerCase(Locale.ROOT));
+ ResultSet rs = stmt.executeQuery();
+
+ if (rs.next())
+ return rs.getLong("discord_id");
+ return null;
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ @Override
+ public CompletableFuture linkDiscordId(@NotNull UUID minecraftId, long discordId) {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ try (Connection conn = hikari.getConnection()) {
+ try (PreparedStatement stmt = conn.prepareStatement("DELETE FROM `linked` WHERE `discord_id` = ?")) {
+ stmt.setLong(1, discordId);
+ stmt.execute();
+ }
+
+ try (PreparedStatement stmt = conn.prepareStatement("REPLACE INTO `linked` VALUES (?, ?, ?)")) {
+ stmt.setString(1, minecraftId.toString().toLowerCase(Locale.ROOT));
+ stmt.setLong(2, discordId);
+ stmt.setLong(3, System.currentTimeMillis());
+ stmt.executeUpdate();
+ }
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ return null;
+ });
+ }
+
+ @Override
+ public CompletableFuture<@NotNull Boolean> isLinkedMinecraft(long discordId) {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ String sql = "SELECT 1 FROM `linked` WHERE `discord_id` = ? LIMIT 1";
+ try (Connection conn = hikari.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+ stmt.setLong(1, discordId);
+ ResultSet rs = stmt.executeQuery();
+ return rs.next();
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ @Override
+ public CompletableFuture<@Nullable UUID> getLinkedMinecraftId(long discordId) {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ String sql = "SELECT `mc_uuid` FROM `linked` WHERE `discord_id` = ? LIMIT 1";
+ try (Connection conn = hikari.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+ stmt.setLong(1, discordId);
+ ResultSet rs = stmt.executeQuery();
+
+ if (rs.next())
+ return UUID.fromString(rs.getString("mc_uuid"));
+ return null;
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ @Override
+ public CompletableFuture unlinkByMinecraftId(@NotNull UUID minecraftId) {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ String sql = "DELETE FROM `linked` WHERE `mc_uuid` = ?";
+ try (Connection conn = hikari.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+ stmt.setString(1, minecraftId.toString().toLowerCase(Locale.ROOT));
+ stmt.executeUpdate();
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ return null;
+ });
+ }
+
+ @Override
+ public CompletableFuture unlinkByDiscordId(long discordId) {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ String sql = "DELETE FROM `linked` WHERE `discord_id` = ?";
+ try (Connection conn = hikari.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+ stmt.setLong(1, discordId);
+ stmt.executeUpdate();
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ return null;
+ });
+ }
+
+ @Override
+ public CompletableFuture getLinkedAccountAll() {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ String sql = "SELECT `mc_uuid`, `discord_id` FROM `linked`";
+ try (Connection conn = hikari.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql);
+ ResultSet rs = stmt.executeQuery()) {
+
+ List data = Lists.newArrayList();
+ while (rs.next())
+ data.add(new LinkedAccount(UUID.fromString(rs.getString(1)), rs.getLong(2)));
+
+ return data.toArray(new LinkedAccount[0]);
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ @Override
+ public CompletableFuture getLinkedAccountCount() {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ String sql = "SELECT COUNT(*) AS `total` FROM `linked`";
+ try (Connection conn = hikari.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql);
+ ResultSet rs = stmt.executeQuery()) {
+
+ if (rs.next())
+ return rs.getInt("total");
+ return 0;
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+
+ public static class DatabaseConfig extends work.novablog.mcplugin.discordconnect.account.db.DatabaseConfig {
+
+ public DatabaseConfig(ConfigurationSection config) {
+ super(config);
+ }
+
+ @Override
+ public String getType() {
+ return "mysql";
+ }
+
+ public String getAddress() {
+ return config.getString("address", "localhost:3306");
+ }
+
+ public String getDatabase() {
+ return config.getString("database", "discordconnect_accounts");
+ }
+
+ public Map getProperties() {
+ ConfigurationSection props = config.getConfigurationSection("properties");
+ if (props == null)
+ return Collections.emptyMap();
+
+ Map values = Maps.newHashMap();
+ for (String key : props.getKeys(false)) {
+ values.put(key, props.get(key));
+ }
+
+ return values;
+ }
+
+ }
+
+}
diff --git a/src/main/java/work/novablog/mcplugin/discordconnect/account/db/SQLiteAccountManager.java b/src/main/java/work/novablog/mcplugin/discordconnect/account/db/SQLiteAccountManager.java
new file mode 100644
index 0000000..f28c472
--- /dev/null
+++ b/src/main/java/work/novablog/mcplugin/discordconnect/account/db/SQLiteAccountManager.java
@@ -0,0 +1,290 @@
+package work.novablog.mcplugin.discordconnect.account.db;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import org.bukkit.configuration.ConfigurationSection;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import work.novablog.mcplugin.discordconnect.account.AccountManager;
+import work.novablog.mcplugin.discordconnect.account.LinkedAccount;
+
+import java.io.File;
+import java.io.IOException;
+import java.sql.*;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+
+public class SQLiteAccountManager extends AccountManager {
+
+ private final DatabaseConfig config;
+ private final File dataDir;
+ private @Nullable HikariDataSource hikari;
+
+ public SQLiteAccountManager(File dataDir, DatabaseConfig config) {
+ this.dataDir = dataDir;
+ this.config = config;
+ }
+
+ public DatabaseConfig getConfig() {
+ return config;
+ }
+
+ @Override
+ public void connect() throws IOException {
+ HikariConfig config = new HikariConfig();
+ config.setDriverClassName("org.sqlite.JDBC");
+ config.setJdbcUrl("jdbc:sqlite:" + new File(dataDir, this.config.getFile()));
+ config.setConnectionInitSql("SELECT 1");
+ config.setAutoCommit(true);
+ this.config.getProperties().forEach(config::addDataSourceProperty);
+ hikari = new HikariDataSource(config);
+ initDatabase();
+ }
+
+ @Override
+ public void close() {
+ if (hikari != null) {
+ hikari.close();
+ }
+ hikari = null;
+ }
+
+ public void initDatabase() throws IOException {
+ if (hikari == null)
+ throw new IOException("database closed");
+
+ try (Connection conn = hikari.getConnection();
+ Statement stmt = conn.createStatement()) {
+ String sql = "CREATE TABLE IF NOT EXISTS linked (mc_uuid VARCHAR(36) UNIQUE, discord_id BIGINT, link_time BIGINT)";
+ stmt.execute(sql);
+ } catch (SQLException e) {
+ throw new IOException(e);
+ }
+ }
+
+
+ @Override
+ public CompletableFuture<@NotNull Boolean> isLinkedDiscord(@NotNull UUID minecraftId) {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ String sql = "SELECT 1 FROM `linked` WHERE `mc_uuid` = ? LIMIT 1";
+ try (Connection conn = hikari.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+ stmt.setString(1, minecraftId.toString().toLowerCase(Locale.ROOT));
+ ResultSet rs = stmt.executeQuery();
+ return rs.next();
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ @Override
+ public CompletableFuture<@Nullable Long> getLinkedDiscordId(@NotNull UUID minecraftId) {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ String sql = "SELECT `discord_id` FROM `linked` WHERE `mc_uuid` = ? LIMIT 1";
+ try (Connection conn = hikari.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+ stmt.setString(1, minecraftId.toString().toLowerCase(Locale.ROOT));
+ ResultSet rs = stmt.executeQuery();
+
+ if (rs.next())
+ return rs.getLong("discord_id");
+ return null;
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ @Override
+ public CompletableFuture linkDiscordId(@NotNull UUID minecraftId, long discordId) {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ try (Connection conn = hikari.getConnection()) {
+ try (PreparedStatement stmt = conn.prepareStatement("DELETE FROM `linked` WHERE `discord_id` = ?")) {
+ stmt.setLong(1, discordId);
+ stmt.execute();
+ }
+
+ try (PreparedStatement stmt = conn.prepareStatement("REPLACE INTO `linked` VALUES (?, ?, ?)")) {
+ stmt.setString(1, minecraftId.toString().toLowerCase(Locale.ROOT));
+ stmt.setLong(2, discordId);
+ stmt.setLong(3, System.currentTimeMillis());
+ stmt.executeUpdate();
+ }
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ return null;
+ });
+ }
+
+ @Override
+ public CompletableFuture<@NotNull Boolean> isLinkedMinecraft(long discordId) {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ String sql = "SELECT 1 FROM `linked` WHERE `discord_id` = ? LIMIT 1";
+ try (Connection conn = hikari.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+ stmt.setLong(1, discordId);
+ ResultSet rs = stmt.executeQuery();
+ return rs.next();
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ @Override
+ public CompletableFuture<@Nullable UUID> getLinkedMinecraftId(long discordId) {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ String sql = "SELECT `mc_uuid` FROM `linked` WHERE `discord_id` = ? LIMIT 1";
+ try (Connection conn = hikari.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+ stmt.setLong(1, discordId);
+ ResultSet rs = stmt.executeQuery();
+
+ if (rs.next())
+ return UUID.fromString(rs.getString("mc_uuid"));
+ return null;
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ @Override
+ public CompletableFuture unlinkByMinecraftId(@NotNull UUID minecraftId) {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ String sql = "DELETE FROM `linked` WHERE `mc_uuid` = ?";
+ try (Connection conn = hikari.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+ stmt.setString(1, minecraftId.toString().toLowerCase(Locale.ROOT));
+ stmt.executeUpdate();
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ return null;
+ });
+ }
+
+ @Override
+ public CompletableFuture unlinkByDiscordId(long discordId) {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ String sql = "DELETE FROM `linked` WHERE `discord_id` = ?";
+ try (Connection conn = hikari.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+ stmt.setLong(1, discordId);
+ stmt.executeUpdate();
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ return null;
+ });
+ }
+
+ @Override
+ public CompletableFuture getLinkedAccountAll() {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ String sql = "SELECT `mc_uuid`, `discord_id` FROM `linked`";
+ try (Connection conn = hikari.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql);
+ ResultSet rs = stmt.executeQuery()) {
+
+ List data = Lists.newArrayList();
+ while (rs.next())
+ data.add(new LinkedAccount(UUID.fromString(rs.getString(1)), rs.getLong(2)));
+
+ return data.toArray(new LinkedAccount[0]);
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ @Override
+ public CompletableFuture getLinkedAccountCount() {
+ return CompletableFuture.supplyAsync(() -> {
+ if (hikari == null)
+ throw new CompletionException(new IOException("database closed"));
+
+ String sql = "SELECT COUNT(*) AS `total` FROM `linked`";
+ try (Connection conn = hikari.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql);
+ ResultSet rs = stmt.executeQuery()) {
+
+ if (rs.next())
+ return rs.getInt("total");
+ return 0;
+
+ } catch (SQLException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+
+ public static class DatabaseConfig extends work.novablog.mcplugin.discordconnect.account.db.DatabaseConfig {
+
+ public DatabaseConfig(ConfigurationSection config) {
+ super(config);
+ }
+
+ @Override
+ public String getType() {
+ return "sqlite";
+ }
+
+ public String getFile() {
+ return config.getString("file", "./accounts.yml");
+ }
+
+ public Map getProperties() {
+ ConfigurationSection props = config.getConfigurationSection("properties");
+ if (props == null)
+ return Collections.emptyMap();
+
+ Map values = Maps.newHashMap();
+ for (String key : props.getKeys(false)) {
+ values.put(key, props.get(key));
+ }
+
+ return values;
+ }
+
+ }
+
+}
diff --git a/src/main/java/work/novablog/mcplugin/discordconnect/account/db/YamlAccountManager.java b/src/main/java/work/novablog/mcplugin/discordconnect/account/db/YamlAccountManager.java
new file mode 100644
index 0000000..efe4696
--- /dev/null
+++ b/src/main/java/work/novablog/mcplugin/discordconnect/account/db/YamlAccountManager.java
@@ -0,0 +1,184 @@
+package work.novablog.mcplugin.discordconnect.account.db;
+
+import com.google.common.collect.Maps;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import work.novablog.mcplugin.discordconnect.account.AccountManager;
+import work.novablog.mcplugin.discordconnect.account.LinkedAccount;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Supplier;
+
+public class YamlAccountManager extends AccountManager {
+ private final Map discordAccounts = Maps.newConcurrentMap();
+ private final File dataFilePath;
+ private final DatabaseConfig config;
+
+ public YamlAccountManager(File dataDir, DatabaseConfig config) {
+ this.config = config;
+ this.dataFilePath = new File(dataDir, config.getFile());
+ }
+
+
+ public DatabaseConfig getConfig() {
+ return config;
+ }
+
+ public void loadFile() {
+ discordAccounts.clear();
+ if (dataFilePath.isFile()) {
+ ConfigurationSection ids = YamlConfiguration.loadConfiguration(dataFilePath).getConfigurationSection("ids");
+ if (ids != null) {
+ for (String key : ids.getKeys(false)) {
+ UUID uuid;
+ try {
+ uuid = UUID.fromString(key);
+ } catch (IllegalArgumentException e) {
+ continue;
+ }
+ long discordId = ids.getLong(key);
+ discordAccounts.put(uuid, discordId);
+ }
+ }
+ }
+ }
+
+ public void saveFile() throws IOException {
+ YamlConfiguration config;
+ if (dataFilePath.isFile()) {
+ config = YamlConfiguration.loadConfiguration(dataFilePath);
+ } else {
+ config = new YamlConfiguration();
+ }
+
+ config.set("ids", null);
+ discordAccounts.forEach((uuid, accountId) ->
+ config.set("ids." + uuid.toString(), accountId));
+
+ config.save(dataFilePath);
+ }
+
+
+ @Override
+ public void connect() {
+ loadFile();
+ }
+
+ @Override
+ public void close() throws IOException {
+ saveFile();
+ discordAccounts.clear();
+ }
+
+
+ @Override
+ public CompletableFuture<@NotNull Boolean> isLinkedDiscord(@NotNull UUID minecraftId) {
+ return runCurrent(() -> discordAccounts.containsKey(minecraftId));
+ }
+
+ @Override
+ public CompletableFuture<@Nullable Long> getLinkedDiscordId(@NotNull UUID minecraftId) {
+ return runCurrent(() -> discordAccounts.get(minecraftId));
+ }
+
+ @Override
+ public CompletableFuture linkDiscordId(@NotNull UUID minecraftId, long discordId) {
+ return runCurrent(() -> {
+ discordAccounts.put(minecraftId, discordId);
+ saveFile();
+ });
+ }
+
+ @Override
+ public CompletableFuture<@NotNull Boolean> isLinkedMinecraft(long discordId) {
+ return runCurrent(() -> discordAccounts.containsValue(discordId));
+ }
+
+ @Override
+ public CompletableFuture<@Nullable UUID> getLinkedMinecraftId(long discordId) {
+ return runCurrent(() -> discordAccounts.entrySet().stream()
+ .filter(e -> e.getValue() == discordId)
+ .map(Map.Entry::getKey)
+ .findFirst()
+ .orElse(null));
+ }
+
+ @Override
+ public CompletableFuture unlinkByMinecraftId(@NotNull UUID minecraftId) {
+ return runCurrent(() -> {
+ if (discordAccounts.remove(minecraftId) != null)
+ saveFile();
+ });
+ }
+
+ @Override
+ public CompletableFuture unlinkByDiscordId(long discordId) {
+ return runCurrent(() -> {
+ if (discordAccounts.values().removeIf(dId -> dId == discordId))
+ saveFile();
+ });
+ }
+
+
+ @Override
+ public CompletableFuture getLinkedAccountAll() {
+ return runCurrent(() -> discordAccounts.entrySet().stream()
+ .map(e -> new LinkedAccount(e.getKey(), e.getValue()))
+ .toArray(LinkedAccount[]::new));
+ }
+
+ @Override
+ public CompletableFuture getLinkedAccountCount() {
+ return CompletableFuture.completedFuture(discordAccounts.size());
+ }
+
+
+ private CompletableFuture runCurrent(Supplier runnable) {
+ try {
+ return CompletableFuture.completedFuture(runnable.get());
+ } catch (Throwable e) {
+ CompletableFuture f = new CompletableFuture<>();
+ f.completeExceptionally(e);
+ return f;
+ }
+ }
+
+ private CompletableFuture runCurrent(ThrowRunnable runnable) {
+ try {
+ runnable.run();
+ return CompletableFuture.completedFuture(null);
+ } catch (Throwable e) {
+ CompletableFuture f = new CompletableFuture<>();
+ f.completeExceptionally(e);
+ return f;
+ }
+ }
+
+ private interface ThrowRunnable {
+ void run() throws Throwable;
+ }
+
+
+ public static class DatabaseConfig extends work.novablog.mcplugin.discordconnect.account.db.DatabaseConfig {
+ public DatabaseConfig(ConfigurationSection config) {
+ super(config);
+ }
+
+ @Override
+ public String getType() {
+ return "yaml";
+ }
+
+ public String getFile() {
+ return config.getString("file", "./accounts.yml");
+ }
+
+ }
+
+}
diff --git a/src/main/java/work/novablog/mcplugin/discordconnect/command/BukkitCommand.java b/src/main/java/work/novablog/mcplugin/discordconnect/command/BukkitCommand.java
index f2d61b0..1a52bac 100644
--- a/src/main/java/work/novablog/mcplugin/discordconnect/command/BukkitCommand.java
+++ b/src/main/java/work/novablog/mcplugin/discordconnect/command/BukkitCommand.java
@@ -38,7 +38,7 @@ public void linkCmd(CommandSender sender, String[] args) {
}
Player player = (Player) sender;
String botName = instance.getBotManager().getBotUser().getName();
- String code = instance.getAccountManager().generateCode(player.getUniqueId());
+ String code = instance.getAccountManager().generateCode(player.getUniqueId(), player.getName());
sender.sendMessage(ConfigManager.Message.accountLinkShowCode.toString()
.replaceAll("\\{bot}", botName)
@@ -46,7 +46,13 @@ public void linkCmd(CommandSender sender, String[] args) {
}
public void reloadCmd(CommandSender sender, String[] args) {
- DiscordConnect.getInstance().init();
+ try {
+ DiscordConnect.getInstance().init();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ sender.sendMessage(ConfigManager.Message.configReloadFailed.toString());
+ return;
+ }
sender.sendMessage(ConfigManager.Message.configReloaded.toString());
}
diff --git a/src/main/java/work/novablog/mcplugin/discordconnect/command/DiscordStandardCommand.java b/src/main/java/work/novablog/mcplugin/discordconnect/command/DiscordStandardCommand.java
index 72b76c6..0601f91 100644
--- a/src/main/java/work/novablog/mcplugin/discordconnect/command/DiscordStandardCommand.java
+++ b/src/main/java/work/novablog/mcplugin/discordconnect/command/DiscordStandardCommand.java
@@ -103,6 +103,10 @@ public void reloadCmd(Member member, DiscordBotSender channel, String[] args) {
eb.setDescription(ConfigManager.Message.discordCommandReloading.toString());
channel.addQueue(eb.build());
- DiscordConnect.getInstance().init();
+ try {
+ DiscordConnect.getInstance().init();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
}
}
diff --git a/src/main/java/work/novablog/mcplugin/discordconnect/listener/BukkitListener.java b/src/main/java/work/novablog/mcplugin/discordconnect/listener/BukkitListener.java
index 7f89406..be716a5 100644
--- a/src/main/java/work/novablog/mcplugin/discordconnect/listener/BukkitListener.java
+++ b/src/main/java/work/novablog/mcplugin/discordconnect/listener/BukkitListener.java
@@ -8,10 +8,12 @@
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
+import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.jetbrains.annotations.NotNull;
import work.novablog.mcplugin.discordconnect.DiscordConnect;
+import work.novablog.mcplugin.discordconnect.account.AccountManager;
import work.novablog.mcplugin.discordconnect.util.ConfigManager;
import work.novablog.mcplugin.discordconnect.util.ConvertUtil;
import work.novablog.mcplugin.discordconnect.util.discord.BotManager;
@@ -19,16 +21,21 @@
import java.awt.*;
import java.util.ArrayList;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
public class BukkitListener implements Listener {
+ private final ConfigManager config;
private final String fromMinecraftToDiscordName;
/**
* bungeecordのイベントを受け取るインスタンスを生成します
- * @param fromMinecraftToDiscordName マイクラからDiscordへ転送するときの名前欄のフォーマット
+ * @param config プラグイン設定
*/
- public BukkitListener(@NotNull String fromMinecraftToDiscordName) {
- this.fromMinecraftToDiscordName = fromMinecraftToDiscordName;
+ public BukkitListener(@NotNull ConfigManager config) {
+ this.config = config;
+ this.fromMinecraftToDiscordName = config.fromMinecraftToDiscordName;
}
/**
@@ -60,6 +67,45 @@ public void onChat(AsyncPlayerChatEvent event) {
}
}
+ @EventHandler
+ public void onPreLogin(AsyncPlayerPreLoginEvent event) {
+ if (!config.isAccountLinkRequired() || !AsyncPlayerPreLoginEvent.Result.ALLOWED.equals(event.getLoginResult()))
+ return;
+
+ DiscordConnect plugin = DiscordConnect.getInstance();
+ UUID playerId = event.getUniqueId();
+ String playerName = event.getName();
+ AccountManager accountManager = plugin.getAccountManager();
+
+ try {
+ Objects.requireNonNull(accountManager, "Account Manager not loaded");
+ Boolean linked = accountManager.isLinkedDiscord(playerId).get();
+
+ if (Boolean.TRUE.equals(linked)) {
+ return; // linked
+ }
+
+ // create code
+ BotManager botManager = Objects.requireNonNull(plugin.getBotManager(), "Bot Manager not loaded");
+ String botName = botManager.getBotUser().getName();
+
+ String code = accountManager.linkingCodes().entrySet().stream()
+ .filter(e -> playerId.equals(e.getValue()))
+ .map(Map.Entry::getKey)
+ .findAny()
+ .orElseGet(() -> accountManager.generateCode(playerId, playerName));
+
+ event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, ConfigManager.Message.accountLinkRequired.toString()
+ .replaceAll("\\{bot}", botName)
+ .replaceAll("\\{code}", code));
+
+
+ } catch (Throwable e) {
+ e.printStackTrace();
+ event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, ConfigManager.Message.accountLinkProcessError.toString());
+ }
+ }
+
/**
* プレイヤーがログインしたら実行されます
* @param e ログイン情報
diff --git a/src/main/java/work/novablog/mcplugin/discordconnect/listener/DiscordListener.java b/src/main/java/work/novablog/mcplugin/discordconnect/listener/DiscordListener.java
index e04fde5..382f7ce 100644
--- a/src/main/java/work/novablog/mcplugin/discordconnect/listener/DiscordListener.java
+++ b/src/main/java/work/novablog/mcplugin/discordconnect/listener/DiscordListener.java
@@ -3,6 +3,7 @@
import com.gmail.necnionch.myapp.markdownconverter.MarkComponent;
import com.gmail.necnionch.myapp.markdownconverter.MarkdownConverter;
import net.dv8tion.jda.api.entities.ChannelType;
+import net.dv8tion.jda.api.entities.MessageChannel;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
@@ -11,12 +12,11 @@
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit;
-import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import work.novablog.mcplugin.discordconnect.DiscordConnect;
+import work.novablog.mcplugin.discordconnect.account.AccountManager;
import work.novablog.mcplugin.discordconnect.command.DiscordCommandExecutor;
-import work.novablog.mcplugin.discordconnect.util.AccountManager;
import work.novablog.mcplugin.discordconnect.util.ConfigManager;
import work.novablog.mcplugin.discordconnect.util.discord.BotManager;
@@ -171,6 +171,8 @@ public void onMessageReceived(@NotNull MessageReceivedEvent receivedMessage) {
private boolean processLinkCodeMessage(MessageReceivedEvent event, long userId) {
String content = event.getMessage().getContentStripped();
AccountManager mgr = DiscordConnect.getInstance().getAccountManager();
+ if (mgr == null)
+ return false;
Matcher matcher = Pattern.compile("(\\d+)").matcher(content);
while (matcher.find()) {
@@ -179,46 +181,51 @@ private boolean processLinkCodeMessage(MessageReceivedEvent event, long userId)
if (uuid == null)
continue;
- Player player = Optional.ofNullable(Bukkit.getOfflinePlayer(uuid).getPlayer()).orElse(null);
- if (player == null)
- return true;
+ mgr.linkDiscordId(uuid, userId).whenComplete((v, th) -> {
+ if (th != null) {
+ th.printStackTrace();
+ } else {
+ Bukkit.getScheduler().callSyncMethod(DiscordConnect.getInstance(), () -> {
+ String playerName = Objects.requireNonNull(mgr.getLinkingPlayerName(uuid));
+ processLinkedPlayer(uuid, playerName, event.getAuthor(), event.getChannel());
+ return null;
+ });
+ }
+ });
+ return true;
+ }
+ return false;
+ }
- mgr.setLinkedDiscordId(uuid, userId);
- mgr.saveFile();
+ private void processLinkedPlayer(UUID playerId, String playerName, User user, MessageChannel channel) {
+ try {
+ Optional.ofNullable(Bukkit.getPlayer(playerId)).ifPresent(p ->
+ p.sendMessage(ConfigManager.Message.accountLinkLinked.toString()
+ .replaceAll("\\{user}", user.getAsTag()))
+ );
- String mcid = player.getName();
+ channel.sendMessage(ConfigManager.Message.accountLinkLinkedToDiscord.toString()
+ .replaceAll("\\{mcid}", playerName))
+ .queue();
- User author = event.getAuthor();
- try {
- player.sendMessage(ConfigManager.Message.accountLinkLinked.toString()
- .replaceAll("\\{user}", author.getAsTag()));
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
- event.getChannel().sendMessage(ConfigManager.Message.accountLinkLinkedToDiscord.toString()
- .replaceAll("\\{mcid}", mcid))
- .queue();
+ String linkedCommand = linkedToConsoleCommand;
+ if (linkedCommand != null && !linkedCommand.isEmpty()) {
+ try {
+ Bukkit.dispatchCommand(Bukkit.getConsoleSender(), linkedCommand
+ .replaceAll("\\{playerId}", playerId.toString())
+ .replaceAll("\\{discordId}", user.getId())
+ .replaceAll("\\{player}", playerName)
+ .replaceAll("\\{discord}", user.getAsTag())
+ );
} catch (Throwable e) {
e.printStackTrace();
}
-
- String linkedCommand = linkedToConsoleCommand;
- if (linkedCommand != null && !linkedCommand.isEmpty()) {
- Bukkit.getScheduler().callSyncMethod(DiscordConnect.getInstance(), () -> {
- try {
- Bukkit.dispatchCommand(Bukkit.getConsoleSender(), linkedCommand
- .replaceAll("\\{playerId}", player.getUniqueId().toString())
- .replaceAll("\\{discordId}", author.getId())
- .replaceAll("\\{player}", player.getName())
- .replaceAll("\\{discord}", author.getAsTag())
- );
- } catch (Throwable e) {
- e.printStackTrace();
- }
- return null;
- });
- }
- return true;
}
- return false;
}
+
}
diff --git a/src/main/java/work/novablog/mcplugin/discordconnect/util/AccountManager.java b/src/main/java/work/novablog/mcplugin/discordconnect/util/AccountManager.java
deleted file mode 100644
index 4f066c5..0000000
--- a/src/main/java/work/novablog/mcplugin/discordconnect/util/AccountManager.java
+++ /dev/null
@@ -1,127 +0,0 @@
-package work.novablog.mcplugin.discordconnect.util;
-
-import com.google.common.collect.Maps;
-import org.bukkit.configuration.ConfigurationSection;
-import org.bukkit.configuration.file.YamlConfiguration;
-import org.jetbrains.annotations.Nullable;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ThreadLocalRandom;
-
-public class AccountManager {
- private final Map discordAccounts = Maps.newHashMap();
- private final Map linkingCodes = new ConcurrentHashMap<>();
- private final File dataFilePath;
-
- public AccountManager(File dataFilePath) {
- this.dataFilePath = dataFilePath;
- }
-
-
- public String generateCode(UUID playerUuid) {
- String codeString;
- do {
- int code = ThreadLocalRandom.current().nextInt(10000);
- codeString = String.format("%04d", code);
-
- } while (linkingCodes.putIfAbsent(codeString, playerUuid) != null);
- return codeString;
- }
-
- public @Nullable UUID removeMinecraftIdByLinkCode(String code) {
- return linkingCodes.remove(code);
- }
-
-
- public File getFilePath() {
- return dataFilePath;
- }
-
- public boolean saveFile() {
- YamlConfiguration config;
- if (dataFilePath.isFile()) {
- config = YamlConfiguration.loadConfiguration(dataFilePath);
- } else {
- config = new YamlConfiguration();
- }
-
- config.set("ids", null);
- discordAccounts.forEach((uuid, accountId) ->
- config.set("ids." + uuid.toString(), accountId));
-
- try {
- config.save(dataFilePath);
- } catch (IOException e) {
- e.printStackTrace();
- return false;
- }
- return true;
- }
-
- public void loadFile() {
- discordAccounts.clear();
- if (dataFilePath.isFile()) {
- ConfigurationSection ids = YamlConfiguration.loadConfiguration(dataFilePath).getConfigurationSection("ids");
- if (ids != null) {
- for (String key : ids.getKeys(false)) {
- UUID uuid;
- try {
- uuid = UUID.fromString(key);
- } catch (IllegalArgumentException e) {
- continue;
- }
- long discordId = ids.getLong(key);
- discordAccounts.put(uuid, discordId);
- }
- }
- }
- }
-
-
- public boolean isLinkedDiscord(UUID minecraftId) {
- return discordAccounts.containsKey(minecraftId);
- }
-
- public @Nullable Long getLinkedDiscordId(UUID minecraftId) {
- return discordAccounts.get(minecraftId);
- }
-
- public void setLinkedDiscordId(UUID minecraftId, long discordId) {
- discordAccounts.put(minecraftId, discordId);
- }
-
- public boolean isLinkedMinecraft(long discordId) {
- return discordAccounts.containsValue(discordId);
- }
-
- public @Nullable UUID getLinkedMinecraftId(long discordId) {
- return discordAccounts.entrySet().stream()
- .filter(e -> e.getValue() == discordId)
- .map(Map.Entry::getKey)
- .findFirst()
- .orElse(null);
- }
-
- public void unlinkByMinecraftId(UUID minecraftId) {
- discordAccounts.remove(minecraftId);
- }
-
- public void unlinkByDiscordId(long discordId) {
- discordAccounts.values().removeIf(dId -> dId == discordId);
- }
-
-
- public Map discordAccounts() {
- return discordAccounts;
- }
-
- public Map getDiscordAccounts() {
- return Collections.unmodifiableMap(discordAccounts);
- }
-
-}
diff --git a/src/main/java/work/novablog/mcplugin/discordconnect/util/ConfigManager.java b/src/main/java/work/novablog/mcplugin/discordconnect/util/ConfigManager.java
index 4fd7735..62d2f5f 100644
--- a/src/main/java/work/novablog/mcplugin/discordconnect/util/ConfigManager.java
+++ b/src/main/java/work/novablog/mcplugin/discordconnect/util/ConfigManager.java
@@ -1,8 +1,11 @@
package work.novablog.mcplugin.discordconnect.util;
+import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
+import work.novablog.mcplugin.discordconnect.account.AccountManager;
+import work.novablog.mcplugin.discordconnect.account.db.DatabaseConfig;
import java.io.File;
import java.io.IOException;
@@ -10,11 +13,14 @@
import java.nio.file.Files;
import java.util.List;
import java.util.Locale;
+import java.util.Optional;
public class ConfigManager {
private static final int CONFIG_LATEST = 3;
private static YamlConfiguration langData;
+ private final DatabaseConfig accountsDatabaseConfig;
+ private final boolean accountLinkRequired;
public String botToken;
public List botWebhookURLs;
@@ -83,6 +89,14 @@ public ConfigManager(@NotNull Plugin plugin) throws IOException {
lunaChatJapanizeFormat = pluginConfig.getString("japanizeFormat");
linkedToConsoleCommand = pluginConfig.getString("linkedToConsoleCommand");
+
+ String dbType = Optional.ofNullable(pluginConfig.getString("accounts.dbType")).orElse("yaml");
+ ConfigurationSection dbSection = pluginConfig.getConfigurationSection("accounts.database." + dbType);
+ if (dbSection == null)
+ dbSection = new YamlConfiguration();
+ accountLinkRequired = pluginConfig.getBoolean("accounts.requireLink", false);
+
+ accountsDatabaseConfig = AccountManager.createDatabaseConfig(dbType, dbSection);
}
private YamlConfiguration getConfigData(Plugin plugin) throws IOException {
@@ -108,6 +122,14 @@ private YamlConfiguration getLangData(Plugin plugin) throws IOException {
return YamlConfiguration.loadConfiguration(langFile);
}
+ public DatabaseConfig getAccountsDatabaseConfig() {
+ return accountsDatabaseConfig;
+ }
+
+ public boolean isAccountLinkRequired() {
+ return accountLinkRequired;
+ }
+
private void backupOldFile(Plugin plugin, String targetFileName) throws IOException {
File oldFile = new File(plugin.getDataFolder(), targetFileName + "_old");
Files.deleteIfExists(oldFile.toPath());
@@ -127,6 +149,7 @@ public enum Message {
botIsReady,
botRestarted,
configReloaded,
+ configReloadFailed,
configPropertyIsNull,
dispatchedCommand,
bungeeCommandPlayerOnly,
@@ -139,6 +162,8 @@ public enum Message {
accountLinkLinkedToDiscord,
accountLinkLinked,
accountLinkShowCode,
+ accountLinkRequired,
+ accountLinkProcessError,
bungeeCommandDenied,
bungeeCommandNotFound,
diff --git a/src/main/java/work/novablog/mcplugin/discordconnect/util/ConvertUtil.java b/src/main/java/work/novablog/mcplugin/discordconnect/util/ConvertUtil.java
index b30b1be..de902d8 100644
--- a/src/main/java/work/novablog/mcplugin/discordconnect/util/ConvertUtil.java
+++ b/src/main/java/work/novablog/mcplugin/discordconnect/util/ConvertUtil.java
@@ -5,7 +5,7 @@
import java.util.UUID;
public class ConvertUtil {
- private static final String AVATAR_IMG_URL = "https://crafatar.com/avatars/{uuid}?size=512&default=MHF_Steve&overlay";
+ private static final String AVATAR_IMG_URL = "https://mineskin.eu/helm/{uuid}/100.png";
/**
* MinecraftプレイヤーのUUIDからアバターのURLを取得します
@@ -14,7 +14,7 @@ public class ConvertUtil {
*
* @param uuid プレイヤーのUUID
* @return プレイヤーのアバターURL
- * @see crafatarを利用させていただいています
+ * @see Mineskinを利用させていただいています
*/
public static String getMinecraftAvatarURL(@NotNull UUID uuid) {
String uuidText = uuid.toString();
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 06063be..91b0d3b 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -30,5 +30,23 @@ japanizeFormat: "__JP__: {japanized}"
# {discordId} --- DiscordユーザーID
linkedToConsoleCommand: ""
+# アカウントに関するデータベース設定
+accounts:
+ # 参加するためにリンクが必要
+ requireLink: false
+ dbType: yaml
+ database:
+ yaml:
+ file: ./accounts.yml
+ sqlite:
+ file: ./accounts.db
+ properties: {}
+ mysql:
+ address: localhost:3306
+ database: "discordconnect_accounts"
+ properties:
+ user: root
+ password: password
+
# この値は変更しないでください! (Don't change the value!)
configVersion: 4
\ No newline at end of file
diff --git a/src/main/resources/ja_JP.yml b/src/main/resources/ja_JP.yml
index ae16e02..58bbb0b 100644
--- a/src/main/resources/ja_JP.yml
+++ b/src/main/resources/ja_JP.yml
@@ -7,6 +7,7 @@ normalShutdown: "botのシャットダウンを行います"
botIsReady: "botのログインが完了しました"
botRestarted: "botの再起動が完了しました"
configReloaded: "§2configの再読込が完了しました"
+configReloadFailed: "§cconfigの再読込中にエラーが発生しました"
configPropertyIsNull: "コンフィグの {property} プロパティが定義されていません。"
dispatchedCommand: "ユーザーID {authorId} がコマンド {commandLine} を実行しました。"
@@ -18,6 +19,8 @@ pluginIsLatest: "プラグインは最新バージョンです({current})"
accountLinkLinkedToDiscord: "Minecraftアカウント {mcid} と連携しました!"
accountLinkLinked: "§6Discordアカウント §f{user}§6 と連携しました!"
accountLinkShowCode: "§eDiscordボット §f{bot}§e に §6{code}§e を送信してください"
+accountLinkRequired: "§7このサーバーに接続するには、§fDiscord §7とのリンクが必要です\n§eDiscordボット §f{bot} §eに §6{code} §eを送信してください"
+accountLinkProcessError: "§cアカウント処理に失敗しました。管理者にお問い合わせください。"
bungeeCommandDenied: "§cあなたはこのコマンドを実行する権限を持っていません!"
bungeeCommandNotFound: "§cコマンドが見つかりません!"