diff --git a/src/main/java/fi/flexplex/connect/FlexConnect.java b/src/main/java/fi/flexplex/connect/FlexConnect.java index f57a461..9c0632e 100644 --- a/src/main/java/fi/flexplex/connect/FlexConnect.java +++ b/src/main/java/fi/flexplex/connect/FlexConnect.java @@ -8,14 +8,21 @@ import java.util.Properties; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.java.JavaPlugin; +import fi.flexplex.connect.util.FileChangeListener; + public final class FlexConnect extends JavaPlugin { private String token = ""; + private FlexPlexGraphQLApi flexPlexGraphQLApi; public String getToken() { return this.token; } + public FlexPlexGraphQLApi getFlexPlexGraphQLApi() { + return this.flexPlexGraphQLApi; + } + @Override public void onEnable() { @@ -26,6 +33,9 @@ public final class FlexConnect extends JavaPlugin { this.getLogger().warning("Token for FlexPlex server network has not been set into plugins config file. Please request token from FlexPlex staff members if you don't already have one!"); } + // GraphQL Api + this.flexPlexGraphQLApi = new FlexPlexGraphQLApi(this, "https://api.flexplex.fi/graphql"); + boolean configsModified = false; // Check online mode @@ -81,6 +91,9 @@ public final class FlexConnect extends JavaPlugin { // Register listener this.getServer().getPluginManager().registerEvents(new FlexConnectListener(this), this); + + // Register file change listener + new FileChangeListener(this, "ops.json", "whitelist.json"); } } diff --git a/src/main/java/fi/flexplex/connect/FlexConnectListener.java b/src/main/java/fi/flexplex/connect/FlexConnectListener.java index 3331dc3..989ca04 100644 --- a/src/main/java/fi/flexplex/connect/FlexConnectListener.java +++ b/src/main/java/fi/flexplex/connect/FlexConnectListener.java @@ -1,11 +1,16 @@ package fi.flexplex.connect; +import com.destroystokyo.paper.event.server.WhitelistToggleEvent; import com.destroystokyo.paper.profile.ProfileProperty; +import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.AsyncPlayerPreLoginEvent; +import fi.flexplex.connect.event.AsyncWhitelistChangedEvent; + public final class FlexConnectListener implements Listener { private final FlexConnect flexConnect; @@ -27,4 +32,16 @@ public final class FlexConnectListener implements Listener { event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, "Access denied"); } + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onWhitelistToggle(final WhitelistToggleEvent event) { + Bukkit.getScheduler().runTaskAsynchronously(this.flexConnect, () -> + this.flexConnect.getFlexPlexGraphQLApi().updateWhitelist(event.isEnabled()) + ); + } + + @EventHandler + public void onAsyncWhitelistChanged(final AsyncWhitelistChangedEvent event) { + this.flexConnect.getFlexPlexGraphQLApi().updateWhitelist(); + } + } diff --git a/src/main/java/fi/flexplex/connect/FlexPlexGraphQLApi.java b/src/main/java/fi/flexplex/connect/FlexPlexGraphQLApi.java new file mode 100644 index 0000000..23cf569 --- /dev/null +++ b/src/main/java/fi/flexplex/connect/FlexPlexGraphQLApi.java @@ -0,0 +1,77 @@ +package fi.flexplex.connect; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import org.bukkit.OfflinePlayer; + +public final class FlexPlexGraphQLApi { + + private final String graphqlEndpoint; + private final FlexConnect flexConnect; + + public FlexPlexGraphQLApi(final FlexConnect flexConnect, final String graphQLEndpoint) { + this.flexConnect = flexConnect; + this.graphqlEndpoint = graphQLEndpoint; + } + + public void updateWhitelist() { + this.updateWhitelist(this.flexConnect.getServer().hasWhitelist()); + } + + public void updateWhitelist(final boolean whitelistEnabled) { + final Set whitelist = new HashSet<>(); + for (final OfflinePlayer player : this.flexConnect.getServer().getWhitelistedPlayers()) { + whitelist.add(player.getUniqueId()); + } + for (final OfflinePlayer player : this.flexConnect.getServer().getOperators()) { + whitelist.add(player.getUniqueId()); + } + this.updateWhitelist(whitelistEnabled, whitelist); + } + + private void updateWhitelist(final boolean whitelistEnabled, final Set whitelist) { + final StringBuilder whitelistString = new StringBuilder(" "); + for (final UUID uuid : whitelist) { + whitelistString.append("\\\"" + uuid.toString() + "\\\" "); + } + + try { + final URL url = new URL(graphqlEndpoint); + final HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setDoOutput(true); + conn.setDoInput(true); + conn.setConnectTimeout(10000); + conn.setReadTimeout(10000); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json"); + + final DataOutputStream wr = new DataOutputStream(conn.getOutputStream()); + + wr.writeBytes("{\"query\":\"mutation { updateCommunityServerWhitelist ( token: \\\"" + this.flexConnect.getToken() + "\\\" enabled: " + whitelistEnabled + " whitelist: [" + whitelistString + "] )}\"}"); + wr.flush(); + wr.close(); + + final JsonObject response = (JsonObject) new JsonParser().parse(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + + if (!response.get("data").getAsJsonObject().get("updateCommunityServerWhitelist").getAsBoolean()) { + this.flexConnect.getLogger().warning("Updating whitelist data failed, propably invalid token."); + } + + conn.disconnect(); + } catch (final IOException e) { + this.flexConnect.getLogger().warning("Updating whitelist data failed, propably connection to FlexPlex API server is down."); + } + } + +} diff --git a/src/main/java/fi/flexplex/connect/event/AsyncWhitelistChangedEvent.java b/src/main/java/fi/flexplex/connect/event/AsyncWhitelistChangedEvent.java new file mode 100644 index 0000000..d3ae108 --- /dev/null +++ b/src/main/java/fi/flexplex/connect/event/AsyncWhitelistChangedEvent.java @@ -0,0 +1,23 @@ +package fi.flexplex.connect.event; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public final class AsyncWhitelistChangedEvent extends Event { + + private static final HandlerList HANDLERS = new HandlerList(); + + public static HandlerList getHandlerList() { + return HANDLERS; + } + + public AsyncWhitelistChangedEvent() { + super(true); + } + + @Override + public HandlerList getHandlers() { + return HANDLERS; + } + +} diff --git a/src/main/java/fi/flexplex/connect/util/FileChangeListener.java b/src/main/java/fi/flexplex/connect/util/FileChangeListener.java new file mode 100644 index 0000000..f958cda --- /dev/null +++ b/src/main/java/fi/flexplex/connect/util/FileChangeListener.java @@ -0,0 +1,68 @@ +package fi.flexplex.connect.util; + +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Paths; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; + +import org.bukkit.Bukkit; + +import fi.flexplex.connect.FlexConnect; +import fi.flexplex.connect.event.AsyncWhitelistChangedEvent; + +public final class FileChangeListener { + + final FlexConnect flexConnect; + final Thread thread; + boolean isRunning = true; + + // Bukkit API does not provide good way to detect whitelist changes. So we need this quite interesting solution... + public FileChangeListener(final FlexConnect flexConnect, final String...files) { + this.flexConnect = flexConnect; + this.thread = new Thread() { + @Override + public void run() { + try { + final WatchService service = FileSystems.getDefault().newWatchService(); + Paths.get(flexConnect.getServer().getWorldContainer().getPath()).register(service, StandardWatchEventKinds.ENTRY_MODIFY); + long lastRun = 0; + while (isRunning) { + final WatchKey key = service.take(); + for (final WatchEvent event : key.pollEvents()) { + final String fileName = String.valueOf(event.context()); + for (final String file : files) { + if (file.equals(fileName)) { + // Cooldown because file change events are called quite many times + if (lastRun + 100 < System.currentTimeMillis()) { + Bukkit.getScheduler().runTaskLaterAsynchronously( + flexConnect, + () -> Bukkit.getPluginManager().callEvent(new AsyncWhitelistChangedEvent()), + 20 + ); + lastRun = System.currentTimeMillis(); + } + break; + } + } + } + key.reset(); + } + } catch (final IOException e) { + e.printStackTrace(); + } catch (final InterruptedException e) { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + }; + this.thread.start(); + } + + public void stop() { + this.isRunning = false; + } + +}