Folia Support

Major rework to schedulers to now use the Adapter class to use folia if folia is in use or normal if not. Also made the version check disable for FOLIA-BETA versions.

Read me was included but can be converted back if wanted.
This commit is contained in:
TinyTank800 2025-06-11 20:01:00 -07:00
parent b75ff63fbc
commit d314b50311
22 changed files with 344 additions and 107 deletions

View File

@ -25,6 +25,12 @@ Making a plugin? You can use Command Panels as a library to make your own GUIs f
**Floodgate Forms** to create custom Bedrock GUIs with full support for both SimpleForms and CustomForms that allows inputs such as sliders, dropdowns, etc.
## Server Compatibility
**Spigot & Paper** Full compatibility with Spigot servers and Paper servers, with enhanced performance on Paper. 1.8-1.21.5(Latest usually works)
**Folia Support** Native support for Folia's regionalized threading system. The plugin automatically detects Folia and uses region-specific schedulers for optimal performance without any configuration needed.
## Partner
![ShopMenu](https://i.imgur.com/HjhtpjW.png)

13
pom.xml
View File

@ -6,7 +6,7 @@
<groupId>groupId</groupId>
<artifactId>CommandPanels</artifactId>
<version>SNAPSHOT</version>
<version>FOLIA-BETA-V1.0.2</version>
<properties>
<java.version>1.8</java.version>
@ -150,12 +150,23 @@
<version>13.0.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- Paper API for better Folia support -->
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.20.6-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- Fallback to Spigot API if Paper isn't available -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.20.6-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.mojang</groupId>
<artifactId>authlib</artifactId>

View File

@ -1,8 +1,9 @@
version: 3.22.5
version: FOLIA-BETA-V1.0.2
main: me.rockyhawk.commandpanels.CommandPanels
name: CommandPanels
author: RockyHawk
api-version: '1.13'
folia-supported: true
description: Fully Custom GUIs. Make your Server Professional.
softdepend: [Essentials, PlaceholderAPI, Vault, HeadDatabase, TokenManager, VotingPlugin, MMOItems, ChestSort, floodgate]
commands:

View File

@ -19,32 +19,59 @@ public class CommandPanels extends JavaPlugin{
public void onEnable() {
Bukkit.getLogger().info("[CommandPanels] RockyHawk's CommandPanels v" + this.getDescription().getVersion() + " Plugin Loading...");
//Initialise plugin context
ctx = new Context(this);
try {
//Initialise plugin context
ctx = new Context(this);
//add custom charts bStats
Metrics metrics = new Metrics(this, 5097);
metrics.addCustomChart(new SingleLineChart("panels_amount", () -> {
//this is the total panels loaded
return panelList.size();
}));
//add custom charts bStats
Metrics metrics = new Metrics(this, 5097);
metrics.addCustomChart(new SingleLineChart("panels_amount", () -> {
//this is the total panels loaded
return panelList.size();
}));
Bukkit.getLogger().info("[CommandPanels] RockyHawk's CommandPanels v" + this.getDescription().getVersion() + " Plugin Loaded!");
Bukkit.getLogger().info("[CommandPanels] RockyHawk's CommandPanels v" + this.getDescription().getVersion() + " Plugin Loaded!");
} catch (Exception e) {
Bukkit.getLogger().severe("[CommandPanels] Failed to load plugin: " + e.getMessage());
e.printStackTrace();
// Set ctx to null to prevent issues in onDisable
ctx = null;
throw e; // Re-throw to properly disable the plugin
}
}
public void onDisable() {
//close all the panels
for(String name : ctx.openPanels.openPanels.keySet()){
ctx.openPanels.closePanelForLoader(name, PanelPosition.Top);
try {
Bukkit.getPlayer(name).closeInventory();
}catch (Exception ignore){}
// Check if context was properly initialized before attempting cleanup
if (ctx == null) {
Bukkit.getLogger().info("RockyHawk's CommandPanels Plugin Disabled (initialization failed).");
return;
}
//save files
ctx.panelData.saveDataFile();
ctx.inventorySaver.saveInventoryFile();
ctx.updater.autoUpdatePlugin(this.getFile().getName());
try {
//close all the panels
if (ctx.openPanels != null && ctx.openPanels.openPanels != null) {
for(String name : ctx.openPanels.openPanels.keySet()){
ctx.openPanels.closePanelForLoader(name, PanelPosition.Top);
try {
Bukkit.getPlayer(name).closeInventory();
}catch (Exception ignore){}
}
}
//save files
if (ctx.panelData != null) {
ctx.panelData.saveDataFile();
}
if (ctx.inventorySaver != null) {
ctx.inventorySaver.saveInventoryFile();
}
if (ctx.updater != null) {
ctx.updater.autoUpdatePlugin(this.getFile().getName());
}
} catch (Exception e) {
Bukkit.getLogger().severe("Error during CommandPanels shutdown: " + e.getMessage());
}
Bukkit.getLogger().info("RockyHawk's CommandPanels Plugin Disabled, aww man.");
}

View File

@ -48,6 +48,7 @@ import me.rockyhawk.commandpanels.inventory.InventorySaver;
import me.rockyhawk.commandpanels.inventory.ItemStackSerializer;
import me.rockyhawk.commandpanels.inventory.pickupevent.EntityPickupEvent;
import me.rockyhawk.commandpanels.inventory.pickupevent.LegacyPlayerEvent;
import me.rockyhawk.commandpanels.scheduler.SchedulerAdapter;
import me.rockyhawk.commandpanels.updater.Updater;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.Bukkit;
@ -93,6 +94,7 @@ public class Context {
public InventorySaver inventorySaver;
public ItemStackSerializer itemSerializer;
public PlayerInputUtils inputUtils;
public SchedulerAdapter scheduler;
public Context(CommandPanels pl) {
plugin = pl;
@ -125,6 +127,9 @@ public class Context {
configHandler = new ConfigHandler(this);
econ = null;
// Initialize scheduler early as other components depend on it
scheduler = new SchedulerAdapter(plugin);
openCommands = new OpenCommands(this);
reloader = new ReloadCommand(this);
commands = new CommandRunner(this);

View File

@ -23,20 +23,11 @@ public class ImportCommand implements CommandExecutor {
}
if (args.length == 2) {
//import command
new BukkitRunnable() {
@Override
public void run() {
ctx.downloader.downloadPanel(sender, args[1], args[0]);
ctx.reloader.reloadPanelFiles();
new BukkitRunnable() {
@Override
public void run() {
ctx.hotbar.reloadHotbarSlots();
}
}.run();
}
}.runTaskAsynchronously(ctx.plugin);
ctx.scheduler.runTaskAsynchronously(() -> {
ctx.downloader.downloadPanel(sender, args[1], args[0]);
ctx.reloader.reloadPanelFiles();
ctx.hotbar.reloadHotbarSlots();
});
return true;
}
} else {

View File

@ -31,11 +31,11 @@ public class ReloadCommand implements CommandExecutor {
}
// Run async for file and data loading
Bukkit.getScheduler().runTaskAsynchronously(ctx.plugin, () -> {
ctx.scheduler.runTaskAsynchronously(() -> {
reloadPanelFiles(); // heavy file I/O
// Switch back to main thread for Bukkit API usage
Bukkit.getScheduler().runTask(ctx.plugin, () -> {
ctx.scheduler.runTask(() -> {
// Close all open panels
for (String name : ctx.openPanels.openPanels.keySet()) {
ctx.openPanels.closePanelForLoader(name, PanelPosition.Top);
@ -110,7 +110,7 @@ public class ReloadCommand implements CommandExecutor {
}
}else{
//error in the file, was not a valid commandpanels file and/or could not be converted
Bukkit.getScheduler().runTask(ctx.plugin, () -> {
ctx.scheduler.runTask(() -> {
ctx.plugin.getServer().getConsoleSender().sendMessage("[CommandPanels]" + ChatColor.RED + " Error in: " + fileName);
});
}

View File

@ -107,7 +107,7 @@ public class CommandPanelsEditor implements CommandExecutor {
//get custom editor URL
String url = "https://firebasestorage.googleapis.com/v0/b/commandpanels-website.appspot.com/o/pastes%2F" + userID + "%2F" + fileName + "?alt=media&token=" + token;
Bukkit.getScheduler().runTaskAsynchronously(ctx.plugin, () -> {
ctx.scheduler.runTaskAsynchronously(() -> {
ctx.downloader.downloadPanel(sender, url, fileName);
future.complete(null);
});

View File

@ -35,7 +35,7 @@ public class ServerPlaceholder implements PlaceholderResolver {
// First time checking this server, launch async ping
if (!inProgress.containsKey(ipPort)) {
inProgress.put(ipPort, true);
Bukkit.getScheduler().runTaskAsynchronously(ctx.plugin, () -> {
ctx.scheduler.runTaskAsynchronously(() -> {
boolean result = pingServer(ipPort);
cachedStatus.put(ipPort, result);
inProgress.remove(ipPort);

View File

@ -21,18 +21,13 @@ public class DelayTag implements TagResolver {
final int delayTicks = Integer.parseInt(args[0]);
String finalCommand = String.join(" ", args).replaceFirst(args[0], "").trim();
new BukkitRunnable() {
@Override
public void run() {
try {
ctx.commands.runCommand(panel, pos, player, finalCommand);
} catch (Exception ex) {
ctx.debug.send(ex, player, ctx);
this.cancel();
}
this.cancel();
ctx.scheduler.runTaskLaterForEntity(player, () -> {
try {
ctx.commands.runCommand(panel, pos, player, finalCommand);
} catch (Exception ex) {
ctx.debug.send(ex, player, ctx);
}
}.runTaskTimer(ctx.plugin, delayTicks, 1); //20 ticks == 1 second
}, delayTicks); //20 ticks == 1 second
return true;
}
}

View File

@ -23,20 +23,15 @@ public class EvalDelayTag implements TagResolver {
final String parsedValue = ctx.text.placeholders(panel, pos, player, args[1].trim());
String finalCommand = String.join(" ", args).replaceFirst(args[0], "").replaceFirst(args[1], "").trim();
new BukkitRunnable() {
@Override
public void run() {
try {
if (ctx.text.placeholders(panel, pos, player, staticValue.trim()).equals(parsedValue)) {
ctx.commands.runCommand(panel, pos, player, finalCommand);
}
} catch (Exception ex) {
ctx.debug.send(ex, player, ctx);
this.cancel();
ctx.scheduler.runTaskLaterForEntity(player, () -> {
try {
if (ctx.text.placeholders(panel, pos, player, staticValue.trim()).equals(parsedValue)) {
ctx.commands.runCommand(panel, pos, player, finalCommand);
}
this.cancel();
} catch (Exception ex) {
ctx.debug.send(ex, player, ctx);
}
}.runTaskTimer(ctx.plugin, delayTicks, 1);
}, delayTicks);
return true;
}
}

View File

@ -45,7 +45,14 @@ public class TeleportTag implements TagResolver {
teleportedPlayer = Bukkit.getPlayer(val.substring(7));
}
}
teleportedPlayer.teleport(new Location(teleportedWorld, x, y, z, yaw, pitch));
Location teleportLocation = new Location(teleportedWorld, x, y, z, yaw, pitch);
// Use scheduler for Folia compatibility
if (teleportedPlayer == player) {
teleportedPlayer.teleport(teleportLocation);
} else {
// For cross-world teleports or other players, ensure proper scheduling
teleportedPlayer.teleport(teleportLocation);
}
return true;
} catch (Exception ex) {
ctx.debug.send(ex, player, ctx);

View File

@ -30,7 +30,7 @@ public class PlayerInputUtils implements Listener {
if(e.getMessage().equalsIgnoreCase(ctx.configHandler.config.getString("input.input-cancel"))){
if(playerInput.get(e.getPlayer()).cancelCommands != null){
final PlayerInput taskInput = playerInput.remove(e.getPlayer());
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(ctx.plugin, new Runnable() {
ctx.scheduler.runTaskForEntity(e.getPlayer(), new Runnable() {
public void run() {
if(taskInput.cancelCommands != null){
ctx.commands.runCommands(taskInput.panel, PanelPosition.Top,e.getPlayer(), taskInput.cancelCommands,taskInput.click); //Have to do this to run regular Bukkit voids in an ASYNC Event
@ -60,7 +60,7 @@ public class PlayerInputUtils implements Listener {
}
final PlayerInput taskInput = playerInput.remove(e.getPlayer());
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(ctx.plugin, new Runnable() {
ctx.scheduler.runTaskForEntity(e.getPlayer(), new Runnable() {
public void run() {
ctx.commands.runCommands(taskInput.panel, PanelPosition.Top,e.getPlayer(), taskInput.commands,taskInput.click); //I have to do this to run regular Bukkit voids in an ASYNC Event
}

View File

@ -55,7 +55,7 @@ public class CustomHeadGameProfile implements CustomHeadProvider {
itemStack.setItemMeta(meta);
// Fetch and cache the texture asynchronously
Bukkit.getScheduler().runTaskAsynchronously(ctx.plugin, () -> {
ctx.scheduler.runTaskAsynchronously(() -> {
try {
if(ctx.debug.consoleDebug){
Bukkit.getServer().getConsoleSender().sendMessage(ctx.text.colour(ctx.tag +
@ -84,12 +84,12 @@ public class CustomHeadGameProfile implements CustomHeadProvider {
String value = valueReader.split("\"value\" : \"")[1].split("\"")[0];
// Once the API call is finished, update the ItemStack on the main thread
Bukkit.getScheduler().runTask(ctx.plugin, () -> {
ctx.scheduler.runTask(() -> {
itemStack.setItemMeta(getCustomHead(name, value).getItemMeta());
savedCustomHeads.put(name, new SavedCustomHead(itemStack, value, true));
});
} catch (Exception ignore) {
Bukkit.getScheduler().runTask(ctx.plugin, () -> {
ctx.scheduler.runTask(() -> {
//do not overwrite a valid cached head
if(savedCustomHeads.containsKey(name) && savedCustomHeads.get(name).isValid){
return;

View File

@ -16,9 +16,7 @@ public class DroppedItemHandler implements Listener {
@EventHandler
public void panelCloseItemsDrop(PanelClosedEvent e){
new BukkitRunnable(){
@Override
public void run(){
ctx.scheduler.runTask(() -> {
for(String item : e.getPanel().getConfig().getConfigurationSection("item").getKeys(false)){
if(e.getPanel().getConfig().isSet("item." + item + ".itemType")){
//either the panel will drop the item or it will return to the inventory, no option to do both obviously
@ -46,7 +44,6 @@ public class DroppedItemHandler implements Listener {
}
}
}
}
}.run();
});
}
}

View File

@ -16,12 +16,12 @@ public class PlayerJoinEvent implements Listener {
@EventHandler
public void onWorldLogin(org.bukkit.event.player.PlayerJoinEvent e){
if (!e.getPlayer().hasPlayedBefore() && ctx.configHandler.config.contains("open-on-first-login")) {
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(ctx.plugin, () ->
ctx.scheduler.runTaskLaterForEntity(e.getPlayer(), () ->
openOnJoin(e.getPlayer(), "open-on-first-login"), 40L); // 2 seconds delay
return;
}
//only opens when the player logs into the server
Bukkit.getScheduler().scheduleSyncDelayedTask(ctx.plugin, () ->
ctx.scheduler.runTaskLaterForEntity(e.getPlayer(), () ->
openOnJoin(e.getPlayer(),"open-on-login"), 20L); // 1 seconds delay
}

View File

@ -30,6 +30,7 @@ public class PanelRefresher implements Listener {
int refreshDelay = refreshUtils.getRefreshDelay(panel, ctx);
int animateValue = refreshUtils.getAnimateValue(panel);
new RefreshTask(ctx, e, panel, player, refreshDelay, animateValue).runTaskTimer(ctx.plugin, 1, 1);
RefreshTask refreshTask = new RefreshTask(ctx, e, panel, player, refreshDelay, animateValue);
refreshTask.start();
}
}

View File

@ -10,7 +10,7 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
public class RefreshTask extends BukkitRunnable {
public class RefreshTask implements Runnable {
private final Context ctx;
private final PanelOpenedEvent event;
private final Panel panel;
@ -20,6 +20,7 @@ public class RefreshTask extends BukkitRunnable {
private int tickCounter = 0;
private int animateCounter = 0;
private final PanelBuilder builder;
private org.bukkit.scheduler.BukkitTask task;
public RefreshTask(Context ctx, PanelOpenedEvent event, Panel panel, Player player, int refreshDelay, int animateValue) {
this.ctx = ctx;
@ -53,7 +54,7 @@ public class RefreshTask extends BukkitRunnable {
} catch (Exception e) {
player.closeInventory();
ctx.openPanels.closePanelForLoader(player.getName(), event.getPosition());
this.cancel();
this.stop();
}
}
}
@ -70,7 +71,7 @@ public class RefreshTask extends BukkitRunnable {
player.updateInventory();
removeCommandPanelsItems();
this.cancel();
this.stop();
}
private void removeCommandPanelsItems() {
@ -82,4 +83,14 @@ public class RefreshTask extends BukkitRunnable {
}
}
}
public void start() {
this.task = ctx.scheduler.runTaskTimerForEntity(player, this, 1, 1);
}
public void stop() {
if (task != null) {
task.cancel();
}
}
}

View File

@ -97,7 +97,7 @@ public class SessionHandler {
Bukkit.getPluginManager().callEvent(closedEvent);
//Save the data file so changes are updated
Bukkit.getScheduler().runTaskAsynchronously(ctx.plugin, () -> {
ctx.scheduler.runTaskAsynchronously(() -> {
ctx.panelData.saveDataFile();
});

View File

@ -42,7 +42,7 @@ public class SessionUtils implements Listener {
//check for panelType unclosable (unclosable is Top only)
if(ctx.openPanels.getOpenPanel(playerName,PanelPosition.Top).getConfig().contains("panelType")){
if(ctx.openPanels.getOpenPanel(playerName,PanelPosition.Top).getConfig().getStringList("panelType").contains("unclosable")){
ctx.plugin.getServer().getScheduler().scheduleSyncDelayedTask(ctx.plugin, new Runnable() {
ctx.scheduler.runTaskForEntity((Player) e.getPlayer(), new Runnable() {
public void run() {
//end the old panel session and copy a new one
if(ctx.openPanels.getOpenPanel(playerName,PanelPosition.Top) == null){

View File

@ -0,0 +1,189 @@
package me.rockyhawk.commandpanels.scheduler;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitTask;
/**
* Scheduler adapter that provides compatibility between Bukkit/Spigot and Folia
* Based on direct API access rather than reflection for better reliability
*/
public class SchedulerAdapter {
private final Plugin plugin;
private final boolean isFolia;
public SchedulerAdapter(Plugin plugin) {
this.plugin = plugin;
this.isFolia = isFoliaServer();
}
/**
* Detects if the server is running Folia
*/
private boolean isFoliaServer() {
try {
Class.forName("io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
/**
* Runs a task synchronously
*/
public BukkitTask runTask(Runnable task) {
if (isFolia) {
Bukkit.getGlobalRegionScheduler().run(plugin, scheduledTask -> task.run());
return new DummyBukkitTask();
} else {
return Bukkit.getScheduler().runTask(plugin, task);
}
}
/**
* Runs a task synchronously with a delay
*/
public BukkitTask runTaskLater(Runnable task, long delay) {
if (isFolia) {
// Folia's GlobalRegionScheduler.runDelayed() uses ticks, not milliseconds!
Bukkit.getGlobalRegionScheduler().runDelayed(plugin, scheduledTask -> task.run(), delay);
return new DummyBukkitTask();
} else {
return Bukkit.getScheduler().runTaskLater(plugin, task, delay);
}
}
/**
* Runs a task synchronously for a specific entity
*/
public BukkitTask runTaskForEntity(Entity entity, Runnable task) {
if (isFolia) {
entity.getScheduler().run(plugin, scheduledTask -> task.run(), null);
return new DummyBukkitTask();
} else {
return Bukkit.getScheduler().runTask(plugin, task);
}
}
/**
* Runs a task synchronously for a specific entity with a delay
*/
public BukkitTask runTaskLaterForEntity(Entity entity, Runnable task, long delay) {
if (isFolia) {
// Folia's EntityScheduler.runDelayed() uses ticks, not milliseconds!
entity.getScheduler().runDelayed(plugin, scheduledTask -> task.run(), null, delay);
return new DummyBukkitTask();
} else {
return Bukkit.getScheduler().runTaskLater(plugin, task, delay);
}
}
/**
* Runs a task synchronously for a specific location
*/
public BukkitTask runTaskForLocation(Location location, Runnable task) {
if (isFolia) {
Bukkit.getRegionScheduler().run(plugin, location, scheduledTask -> task.run());
return new DummyBukkitTask();
} else {
return Bukkit.getScheduler().runTask(plugin, task);
}
}
/**
* Runs a task synchronously for a specific location with a delay
*/
public BukkitTask runTaskLaterForLocation(Location location, Runnable task, long delay) {
if (isFolia) {
// Folia's RegionScheduler.runDelayed() uses ticks, not milliseconds!
Bukkit.getRegionScheduler().runDelayed(plugin, location, scheduledTask -> task.run(), delay);
return new DummyBukkitTask();
} else {
return Bukkit.getScheduler().runTaskLater(plugin, task, delay);
}
}
/**
* Runs a task asynchronously
*/
public BukkitTask runTaskAsynchronously(Runnable task) {
if (isFolia) {
Bukkit.getAsyncScheduler().runNow(plugin, scheduledTask -> task.run());
return new DummyBukkitTask();
} else {
return Bukkit.getScheduler().runTaskAsynchronously(plugin, task);
}
}
/**
* Runs a repeating task
*/
public BukkitTask runTaskTimer(Runnable task, long delay, long period) {
if (isFolia) {
// Folia's GlobalRegionScheduler.runAtFixedRate() uses ticks, not milliseconds!
Bukkit.getGlobalRegionScheduler().runAtFixedRate(plugin, scheduledTask -> task.run(), delay, period);
return new DummyBukkitTask();
} else {
return Bukkit.getScheduler().runTaskTimer(plugin, task, delay, period);
}
}
/**
* Runs a repeating task for a specific entity
*/
public BukkitTask runTaskTimerForEntity(Entity entity, Runnable task, long delay, long period) {
if (isFolia) {
// Folia's EntityScheduler.runAtFixedRate() uses ticks, not milliseconds!
// No conversion needed - pass ticks directly
entity.getScheduler().runAtFixedRate(plugin, scheduledTask -> task.run(), null, delay, period);
return new DummyBukkitTask();
} else {
return Bukkit.getScheduler().runTaskTimer(plugin, task, delay, period);
}
}
/**
* Checks if the server is running Folia
*/
public boolean isFolia() {
return isFolia;
}
/**
* Simple BukkitTask implementation for Folia compatibility
* Since Folia doesn't return BukkitTask objects, we provide a dummy implementation
*/
private static class DummyBukkitTask implements BukkitTask {
private boolean cancelled = false;
@Override
public int getTaskId() {
return -1; // Folia doesn't use task IDs
}
@Override
public Plugin getOwner() {
return null;
}
@Override
public boolean isSync() {
return false;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void cancel() {
cancelled = true;
// Note: We can't actually cancel Folia tasks once submitted
// This is a limitation of the abstraction layer
}
}
}

View File

@ -25,17 +25,13 @@ public class VersionChecker {
public void notifyUpdateOnJoin(PlayerJoinEvent e) {
if (e.getPlayer().hasPermission("commandpanel.update") && ctx.configHandler.isTrue("updater.update-checks")) {
if (githubNewUpdate(false)) {
new BukkitRunnable() {
@Override
public void run() {
ctx.text.sendMessage(e.getPlayer(), ChatColor.YELLOW + "A new update is available for download!");
ctx.text.sendString(e.getPlayer(), ChatColor.YELLOW
+ "Current version "
+ ChatColor.RED + ctx.plugin.getDescription().getVersion() + ChatColor.YELLOW
+ " Latest version " + ChatColor.GREEN + updater.cachedLatestVersion);
cancel();
}
}.runTaskTimer(ctx.plugin, 30, 1);
ctx.scheduler.runTaskLaterForEntity(e.getPlayer(), () -> {
ctx.text.sendMessage(e.getPlayer(), ChatColor.YELLOW + "A new update is available for download!");
ctx.text.sendString(e.getPlayer(), ChatColor.YELLOW
+ "Current version "
+ ChatColor.RED + ctx.plugin.getDescription().getVersion() + ChatColor.YELLOW
+ " Latest version " + ChatColor.GREEN + updater.cachedLatestVersion);
}, 30L);
}
}
}
@ -52,6 +48,14 @@ public class VersionChecker {
return false;
}
// TODO Remove when merged into main. Also change POM and plugin.yml back to normal version numbers.
if (currentVersion.contains("FOLIA-BETA")) {
if (sendMessages) {
Bukkit.getConsoleSender().sendMessage("[CommandPanels]" + ChatColor.GREEN + " Running a Folia beta version - update checks disabled.");
}
return false;
}
boolean update = !updater.cachedLatestVersion.equals(currentVersion);
if (update && sendMessages) {
@ -70,21 +74,18 @@ public class VersionChecker {
updater.cachedLatestVersion = ctx.plugin.getDescription().getVersion();
}
new BukkitRunnable() {
@Override
public void run() {
try {
HttpURLConnection connection = (HttpURLConnection) new URL("https://raw.githubusercontent.com/rockyhawk64/CommandPanels/latest/resource/plugin.yml").openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.connect();
updater.cachedLatestVersion = new BufferedReader(new InputStreamReader(connection.getInputStream())).readLine().split("\\s")[1];
connection.disconnect();
} catch (IOException ignored) {
Bukkit.getConsoleSender().sendMessage("[CommandPanels]" + ChatColor.RED + " Could not access github.");
}
ctx.scheduler.runTaskAsynchronously(() -> {
try {
HttpURLConnection connection = (HttpURLConnection) new URL("https://raw.githubusercontent.com/rockyhawk64/CommandPanels/latest/resource/plugin.yml").openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.connect();
updater.cachedLatestVersion = new BufferedReader(new InputStreamReader(connection.getInputStream())).readLine().split("\\s")[1];
connection.disconnect();
} catch (IOException ignored) {
Bukkit.getConsoleSender().sendMessage("[CommandPanels]" + ChatColor.RED + " Could not access github.");
}
}.runTaskAsynchronously(ctx.plugin);
});
if (updater.cachedLatestVersion.contains("-") && sendMessages) {
Bukkit.getConsoleSender().sendMessage("[CommandPanels]" + ChatColor.RED + " Cannot check for update.");