mirror of
https://github.com/PlaceholderAPI/PlaceholderAPI
synced 2025-11-09 08:45:08 +01:00
Compare commits
36 Commits
feature/up
...
feature/co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a8bb3695c | ||
|
|
683dc6e8e3 | ||
|
|
f14b081f0b | ||
|
|
b51fbf4e13 | ||
|
|
e48b6fe702 | ||
|
|
984f944daf | ||
|
|
0575c8cf41 | ||
|
|
c181d18f5e | ||
|
|
2e0d9eb846 | ||
|
|
33d1009d6a | ||
|
|
b16d62fb75 | ||
|
|
e019d6370c | ||
|
|
581b73dbc7 | ||
|
|
25f7eafe32 | ||
|
|
ef22d564f3 | ||
|
|
787a053d98 | ||
|
|
f31dd2bea9 | ||
|
|
5c8086150a | ||
|
|
b838d1c52a | ||
|
|
0a712e6530 | ||
|
|
272e2e7904 | ||
|
|
98082398fc | ||
|
|
c66806ecf8 | ||
|
|
c97f5aa5a6 | ||
|
|
c1487898a9 | ||
|
|
907ced6d7d | ||
|
|
ac771207c3 | ||
|
|
8b031576aa | ||
|
|
2d1a0ee157 | ||
|
|
b9e2bf9429 | ||
|
|
a35923a117 | ||
|
|
d5e96bd6a6 | ||
|
|
7b230fc679 | ||
|
|
32f3d14682 | ||
|
|
152105017d | ||
|
|
be956f52b0 |
@@ -9,6 +9,7 @@
|
||||
[spigot]: https://www.spigotmc.org/resources/6245/
|
||||
[hangar]: https://hangar.papermc.io/HelpChat/PlaceholderAPI
|
||||
[bbb]: https://builtbybit.com/resources/placeholderapi.24306
|
||||
[modrinth]: https://modrinth.com/plugin/placeholderapi
|
||||
[Expansions cloud]: https://api.extendedclip.com/home
|
||||
[placeholder list]: https://helpch.at/placeholders
|
||||
[statistics]: https://bstats.org/plugin/bukkit/PlaceholderAPI
|
||||
@@ -16,7 +17,7 @@
|
||||
[ci]: http://ci.extendedclip.com/job/PlaceholderAPI/
|
||||
[ciImg]: http://ci.extendedclip.com/buildStatus/icon?job=PlaceholderAPI
|
||||
|
||||
[APIversionImg]: https://img.shields.io/nexus/placeholderapi/me.clip/placeholderapi?server=https%3A%2F%2Frepo.extendedclip.com&label=API%20Version
|
||||
[APIversionImg]: https://repo.extendedclip.com/api/badge/latest/releases/me/clip/placeholderapi?name=API%20Version
|
||||
[logo]: https://wiki.placeholderapi.com/assets/img/papi-logo.png
|
||||
|
||||
[contributing]: https://github.com/PlaceholderAPI/PlaceholderAPI/blob/master/.github/CONTRIBUTING.md
|
||||
@@ -32,7 +33,7 @@
|
||||
|
||||
Support for specific plugins are provided either by the plugin itself or through expansions. The expansions may be downloaded in-game through the PAPI Expansion Cloud. There are currently over 240+ expansions that support a wide variety of plugins, such as Essentials, Factions, LuckPerms, and Vault.
|
||||
|
||||
PlaceholderAPI has been downloaded over 1,000,000 times and has been used concurrently on over 40,000 servers, which makes it a must-have for a server of any type or scale.
|
||||
PlaceholderAPI has been downloaded over 1,700,000 times on Spigot and has been used concurrently on over 45,000 servers, which makes it a must-have for a server of any type or scale.
|
||||
|
||||
## Contribute
|
||||
If you would like to contribute towards PlaceholderAPI should you take a look at our [Contributing file][contributing] for the ins and outs on how you can do that and what you need to keep in mind.
|
||||
@@ -51,4 +52,5 @@ If you would like to create your own Placeholder Expansion for PlaceholderAPI, t
|
||||
- [Spigot Page][spigot]
|
||||
- [Hangar Page][hangar]
|
||||
- [BuiltByBit Page][bbb]
|
||||
- [Modrinth Page][modrinth]
|
||||
- [Plugin Statistics][statistics]
|
||||
|
||||
@@ -4,11 +4,11 @@ plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
id("com.github.hierynomus.license") version "0.16.1"
|
||||
id("com.github.johnrengelman.shadow") version "8.1.0"
|
||||
id("io.github.goooler.shadow") version "8.1.7"
|
||||
}
|
||||
|
||||
group = "me.clip"
|
||||
version = "2.11.6-DEV-${System.getProperty("BUILD_NUMBER")}"
|
||||
version = "2.11.7-DEV-${System.getProperty("BUILD_NUMBER")}"
|
||||
|
||||
description = "An awesome placeholder provider!"
|
||||
|
||||
@@ -20,13 +20,15 @@ repositories {
|
||||
|
||||
maven("https://repo.codemc.org/repository/maven-public/")
|
||||
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
|
||||
maven("https://repo.papermc.io/repository/maven-public/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.bstats:bstats-bukkit:3.0.1")
|
||||
implementation("net.kyori:adventure-platform-bukkit:4.3.2")
|
||||
implementation("net.kyori:adventure-platform-bukkit:4.4.1")
|
||||
|
||||
compileOnly("org.spigotmc:spigot-api:1.20-R0.1-SNAPSHOT")
|
||||
//compileOnly("org.spigotmc:spigot-api:1.21-R0.1-SNAPSHOT")
|
||||
compileOnly("dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT")
|
||||
compileOnlyApi("org.jetbrains:annotations:23.0.0")
|
||||
|
||||
testImplementation("org.openjdk.jmh:jmh-core:1.32")
|
||||
@@ -43,6 +45,8 @@ java {
|
||||
|
||||
withJavadocJar()
|
||||
withSourcesJar()
|
||||
|
||||
disableAutoTargetJvm()
|
||||
}
|
||||
|
||||
license {
|
||||
@@ -71,6 +75,7 @@ tasks {
|
||||
|
||||
withType<JavaCompile> {
|
||||
options.encoding = "UTF-8"
|
||||
options.release = 8
|
||||
}
|
||||
|
||||
withType<Javadoc> {
|
||||
@@ -88,6 +93,8 @@ tasks {
|
||||
|
||||
relocate("org.bstats", "me.clip.placeholderapi.metrics")
|
||||
relocate("net.kyori", "me.clip.placeholderapi.libs.kyori")
|
||||
|
||||
exclude("META-INF/versions/**")
|
||||
}
|
||||
|
||||
test {
|
||||
@@ -105,9 +112,9 @@ tasks {
|
||||
repositories {
|
||||
maven {
|
||||
if ("-DEV" in version.toString()) {
|
||||
url = uri("https://repo.extendedclip.com/content/repositories/dev/")
|
||||
url = uri("https://repo.extendedclip.com/snapshots")
|
||||
} else {
|
||||
url = uri("https://repo.extendedclip.com/content/repositories/placeholderapi/")
|
||||
url = uri("https://repo.extendedclip.com/releases")
|
||||
}
|
||||
|
||||
credentials {
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
28
gradlew
vendored
28
gradlew
vendored
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright <EFBFBD> 2015-2021 the original authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -32,10 +32,10 @@
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions <EFBFBD>$var<EFBFBD>, <EFBFBD>${var}<EFBFBD>, <EFBFBD>${var:-default}<EFBFBD>, <EFBFBD>${var+SET}<EFBFBD>,
|
||||
# <EFBFBD>${var#prefix}<EFBFBD>, <EFBFBD>${var%suffix}<EFBFBD>, and <EFBFBD>$( cmd )<EFBFBD>;
|
||||
# * compound commands having a testable exit status, especially <EFBFBD>case<EFBFBD>;
|
||||
# * various built-in commands including <EFBFBD>command<EFBFBD>, <EFBFBD>set<EFBFBD>, and <EFBFBD>ulimit<EFBFBD>.
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
@@ -55,7 +55,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -80,10 +80,10 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
@@ -143,12 +143,16 @@ fi
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -205,6 +209,12 @@ set -- \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
||||
15
gradlew.bat
vendored
15
gradlew.bat
vendored
@@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,7 +25,8 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
@@ -35,6 +35,8 @@ import me.clip.placeholderapi.replacer.CharsReplacer;
|
||||
import me.clip.placeholderapi.replacer.Replacer;
|
||||
import me.clip.placeholderapi.replacer.Replacer.Closure;
|
||||
import me.clip.placeholderapi.util.Msg;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextReplacementConfig;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
@@ -57,6 +59,12 @@ public final class PlaceholderAPI {
|
||||
|
||||
// === Current API ===
|
||||
|
||||
@NotNull
|
||||
public static Component setComponentPlaceholders(final OfflinePlayer player, @NotNull final Component component) {
|
||||
// change charsreplacer to custom that returns component instead of string - this is going to suck mega
|
||||
return component.replaceText(config -> config.match(PLACEHOLDER_PATTERN).replacement((result, builder) -> builder.content(REPLACER_PERCENT.apply(builder.content(), player, PlaceholderAPIPlugin.getInstance().getLocalExpansionManager()::getExpansion))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates all placeholders into their corresponding values.
|
||||
* <br>The pattern of a valid placeholder is {@literal %<identifier>_<params>%}.
|
||||
@@ -136,8 +144,8 @@ public final class PlaceholderAPI {
|
||||
* @return String containing all translated placeholders
|
||||
*/
|
||||
@NotNull
|
||||
public static List<String> setBracketPlaceholders(final OfflinePlayer player,
|
||||
@NotNull final List<String> text) {
|
||||
public static List<@NotNull String> setBracketPlaceholders(final OfflinePlayer player,
|
||||
@NotNull final List<@NotNull String> text) {
|
||||
return text.stream().map(line -> setBracketPlaceholders(player, line))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
@@ -150,7 +158,8 @@ public final class PlaceholderAPI {
|
||||
* @param text Text to set the placeholder values in
|
||||
* @return String containing all translated placeholders
|
||||
*/
|
||||
public static String setBracketPlaceholders(Player player, String text) {
|
||||
@NotNull
|
||||
public static String setBracketPlaceholders(Player player, @NotNull String text) {
|
||||
return setBracketPlaceholders((OfflinePlayer) player, text);
|
||||
}
|
||||
|
||||
@@ -162,7 +171,8 @@ public final class PlaceholderAPI {
|
||||
* @param text List of Strings to set the placeholder values in
|
||||
* @return String containing all translated placeholders
|
||||
*/
|
||||
public static List<String> setBracketPlaceholders(Player player, List<String> text) {
|
||||
@NotNull
|
||||
public static List<String> setBracketPlaceholders(Player player, @NotNull List<String> text) {
|
||||
return setBracketPlaceholders((OfflinePlayer) player, text);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ import me.clip.placeholderapi.expansion.Version;
|
||||
import me.clip.placeholderapi.expansion.manager.CloudExpansionManager;
|
||||
import me.clip.placeholderapi.expansion.manager.LocalExpansionManager;
|
||||
import me.clip.placeholderapi.listeners.ServerLoadEventListener;
|
||||
import me.clip.placeholderapi.scheduler.UniversalScheduler;
|
||||
import me.clip.placeholderapi.scheduler.scheduling.schedulers.TaskScheduler;
|
||||
import me.clip.placeholderapi.updatechecker.UpdateChecker;
|
||||
import me.clip.placeholderapi.util.Msg;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
@@ -68,7 +70,6 @@ public final class PlaceholderAPIPlugin extends JavaPlugin {
|
||||
version = 'v' + version.replace('.', '_').replace("_" + minor, "") + '_' + "R" + (minor - 1);
|
||||
}
|
||||
|
||||
|
||||
boolean isSpigot;
|
||||
try {
|
||||
Class.forName("org.spigotmc.SpigotConfig");
|
||||
@@ -87,9 +88,12 @@ public final class PlaceholderAPIPlugin extends JavaPlugin {
|
||||
private final LocalExpansionManager localExpansionManager = new LocalExpansionManager(this);
|
||||
@NotNull
|
||||
private final CloudExpansionManager cloudExpansionManager = new CloudExpansionManager(this);
|
||||
@NotNull
|
||||
private final TaskScheduler scheduler = UniversalScheduler.getScheduler(this);
|
||||
|
||||
private BukkitAudiences adventure;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the static instance of the main class for PlaceholderAPI. This class is not the actual API
|
||||
* class, this is the main class that extends JavaPlugin. For most API methods, use static methods
|
||||
@@ -175,7 +179,7 @@ public final class PlaceholderAPIPlugin extends JavaPlugin {
|
||||
|
||||
HandlerList.unregisterAll(this);
|
||||
|
||||
Bukkit.getScheduler().cancelTasks(this);
|
||||
scheduler.cancelTasks(this);
|
||||
|
||||
adventure.close();
|
||||
adventure = null;
|
||||
@@ -216,6 +220,11 @@ public final class PlaceholderAPIPlugin extends JavaPlugin {
|
||||
return adventure;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public TaskScheduler getScheduler() {
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the configuration class for PlaceholderAPI.
|
||||
*
|
||||
@@ -263,8 +272,8 @@ public final class PlaceholderAPIPlugin extends JavaPlugin {
|
||||
Class.forName("org.bukkit.event.server.ServerLoadEvent");
|
||||
new ServerLoadEventListener(this);
|
||||
} catch (final ClassNotFoundException ignored) {
|
||||
Bukkit.getScheduler()
|
||||
.runTaskLater(this, () -> getLocalExpansionManager().load(Bukkit.getConsoleSender()), 1);
|
||||
scheduler
|
||||
.runTaskLater(() -> getLocalExpansionManager().load(Bukkit.getConsoleSender()), 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,12 +20,16 @@
|
||||
|
||||
package me.clip.placeholderapi;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class PlaceholderHook {
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public String onRequest(final OfflinePlayer player, @NotNull final String params) {
|
||||
if (player != null && player.isOnline()) {
|
||||
@@ -35,8 +39,16 @@ public abstract class PlaceholderHook {
|
||||
return onPlaceholderRequest(null, params);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public String onPlaceholderRequest(final Player player, @NotNull final String params) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Component onPlaceholderComponentRequest(final OfflinePlayer player, @NotNull final String params) {
|
||||
final String result = onRequest(player, params);
|
||||
|
||||
return result == null ? null : Component.text(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,8 @@ public final class CommandParse extends PlaceholderCommand {
|
||||
final boolean command) {
|
||||
if (params.size() < 2) {
|
||||
Msg.msg(sender,
|
||||
"&cYou must supply a target, and a message: &b/papi " + (broadcast ? "bcparse" : "parse")
|
||||
"&cYou must provide a target and message: &b/papi "
|
||||
+ (command ? "cmdparse" : (broadcast ? "bcparse" : "parse"))
|
||||
+ " &7{target} &a{message}");
|
||||
return;
|
||||
}
|
||||
@@ -132,24 +133,49 @@ public final class CommandParse extends PlaceholderCommand {
|
||||
@NotNull @Unmodifiable final List<String> params) {
|
||||
if (params.size() < 3) {
|
||||
Msg.msg(sender,
|
||||
"&cYou must supply two targets, and a message: &b/papi parserel &7{target one} {target two} &a{message}");
|
||||
"&cYou must supply two targets, and a message: &b/papi parserel &7{target one} "
|
||||
+ "{target two} &a{message}");
|
||||
return;
|
||||
}
|
||||
|
||||
final OfflinePlayer targetOne = resolvePlayer(params.get(0));
|
||||
if (targetOne == null || !targetOne.isOnline()) {
|
||||
|
||||
OfflinePlayer playerOne;
|
||||
|
||||
if ("me".equalsIgnoreCase(params.get(0))) {
|
||||
if (!(sender instanceof Player)) {
|
||||
Msg.msg(sender, "&cYou must be a player to use &7me&c as a target!");
|
||||
return;
|
||||
}
|
||||
|
||||
playerOne = ((Player) sender);
|
||||
} else {
|
||||
playerOne = resolvePlayer(params.get(0));
|
||||
}
|
||||
|
||||
if (playerOne == null || !playerOne.isOnline()) {
|
||||
Msg.msg(sender, "&cFailed to find player: &f" + params.get(0));
|
||||
return;
|
||||
}
|
||||
|
||||
final OfflinePlayer targetTwo = resolvePlayer(params.get(1));
|
||||
if (targetTwo == null || !targetTwo.isOnline()) {
|
||||
|
||||
OfflinePlayer playerTwo;
|
||||
|
||||
if ("me".equalsIgnoreCase(params.get(1))) {
|
||||
if (!(sender instanceof Player)) {
|
||||
Msg.msg(sender, "&cYou must be a player to use &7me&c as a target!");
|
||||
return;
|
||||
}
|
||||
|
||||
playerTwo = ((Player) sender);
|
||||
} else {
|
||||
playerTwo = resolvePlayer(params.get(1));
|
||||
}
|
||||
|
||||
if (playerTwo == null || !playerTwo.isOnline()) {
|
||||
Msg.msg(sender, "&cFailed to find player: &f" + params.get(1));
|
||||
return;
|
||||
}
|
||||
|
||||
final String message = PlaceholderAPI
|
||||
.setRelationalPlaceholders(((Player) targetOne), ((Player) targetTwo),
|
||||
.setRelationalPlaceholders((Player) playerOne, (Player) playerTwo,
|
||||
String.join(" ", params.subList(2, params.size())));
|
||||
|
||||
sender.sendMessage(message);
|
||||
|
||||
@@ -50,7 +50,11 @@ public enum NMSVersion {
|
||||
SPIGOT_1_20_R1("v1_20_R1"),
|
||||
SPIGOT_1_20_R2("v1_20_R2"),
|
||||
SPIGOT_1_20_R3("v1_20_R3"),
|
||||
SPIGOT_1_20_R4("v1_20_R4");
|
||||
SPIGOT_1_20_R4("v1_20_R4"),
|
||||
SPIGOT_1_21_R1("v1_21_R1"),
|
||||
SPIGOT_1_21_R2("V1_21_R2"),
|
||||
SPIGOT_1_21_R3("V1_21_R3"),
|
||||
SPIGOT_1_21_R4("V1_21_R4");
|
||||
|
||||
private final String version;
|
||||
|
||||
|
||||
@@ -202,10 +202,8 @@ public final class CloudExpansionManager {
|
||||
|
||||
// loop through what's left on the main thread
|
||||
plugin
|
||||
.getServer()
|
||||
.getScheduler()
|
||||
.runTask(
|
||||
plugin,
|
||||
() -> {
|
||||
try {
|
||||
for (Map.Entry<String, CloudExpansion> entry : values.entrySet()) {
|
||||
|
||||
@@ -34,9 +34,9 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import me.clip.placeholderapi.PlaceholderAPIPlugin;
|
||||
import me.clip.placeholderapi.events.ExpansionRegisterEvent;
|
||||
@@ -440,7 +440,8 @@ public final class LocalExpansionManager implements Listener {
|
||||
Msg.severe("Failed to load expansion %s (is a dependency missing?)", e, file.getName());
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
throw new CompletionException(e.getMessage() + " (expansion file: " + file.getAbsolutePath() + ")", e);
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to load expansion file: " + file.getAbsolutePath(), e);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* This file is part of adventure, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) 2017-2025 KyoriPowered
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.clip.placeholderapi.replacer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import net.kyori.adventure.text.*;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.kyori.adventure.text.renderer.ComponentRenderer;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A renderer performing a replacement on every {@link TextComponent} element of a component tree.
|
||||
*/
|
||||
final class ComponentCharsReplacer implements ComponentRenderer<ComponentCharsReplacer.State> {
|
||||
//static final TextReplacementRenderer INSTANCE = new TextReplacementRenderer();
|
||||
private final OfflinePlayer player;
|
||||
private final Function<String, @Nullable PlaceholderExpansion> lookup;
|
||||
private static final Closure CLOSURE = Closure.PERCENT;
|
||||
|
||||
enum Closure {
|
||||
BRACKET('{', '}'),
|
||||
PERCENT('%', '%');
|
||||
|
||||
|
||||
public final char head, tail;
|
||||
|
||||
Closure(final char head, final char tail) {
|
||||
this.head = head;
|
||||
this.tail = tail;
|
||||
}
|
||||
}
|
||||
|
||||
public ComponentCharsReplacer(@Nullable final OfflinePlayer player,
|
||||
@NotNull final Function<String, @Nullable PlaceholderExpansion> lookup) {
|
||||
this.player = player;
|
||||
this.lookup = lookup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component render(final Component component, final State state) {
|
||||
if (!state.running) return component;
|
||||
final boolean prevFirstMatch = state.firstMatch;
|
||||
state.firstMatch = true;
|
||||
|
||||
final List<Component> oldChildren = component.children();
|
||||
final int oldChildrenSize = oldChildren.size();
|
||||
Style oldStyle = component.style();
|
||||
List<Component> children = null;
|
||||
Component modified = component;
|
||||
// replace the component itself
|
||||
if (component instanceof TextComponent) {
|
||||
TextComponent tc = (TextComponent) component;
|
||||
final String content = tc.content();
|
||||
|
||||
final char[] chars = content.toCharArray();
|
||||
final StringBuilder identifier = new StringBuilder();
|
||||
final StringBuilder parameters = new StringBuilder();
|
||||
|
||||
final Map<List<Integer>, Component> replacements = new HashMap<>();
|
||||
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
final char l = chars[i];
|
||||
|
||||
if (l != CLOSURE.head || i + 1 >= chars.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean identified = false;
|
||||
boolean invalid = true;
|
||||
boolean hadSpace = false;
|
||||
|
||||
while (++i < chars.length) {
|
||||
final char p = chars[i];
|
||||
|
||||
if (p == ' ' && !identified) {
|
||||
hadSpace = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (p == CLOSURE.tail) {
|
||||
invalid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (p == '_' && !identified) {
|
||||
identified = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (identified) {
|
||||
parameters.append(p);
|
||||
} else {
|
||||
identifier.append(p);
|
||||
}
|
||||
}
|
||||
|
||||
final String identifierString = identifier.toString();
|
||||
final String lowerIdentifiedString = identifierString.toLowerCase();
|
||||
final String parametersString = parameters.toString();
|
||||
|
||||
identifier.setLength(0);
|
||||
parameters.setLength(0);
|
||||
|
||||
if (invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
replacements.put(Lists.newArrayList(i, i + identifierString.length() + parametersString.length()), lookup.apply(lowerIdentifiedString).onPlaceholderComponentRequest(player, parametersString));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// do something with our replacements
|
||||
|
||||
|
||||
|
||||
|
||||
final Matcher matcher = state.pattern.matcher(content);
|
||||
int replacedUntil = 0; // last index handled
|
||||
while (matcher.find()) {
|
||||
final PatternReplacementResult result = state.continuer.shouldReplace(matcher, ++state.matchCount, state.replaceCount);
|
||||
|
||||
if (matcher.start() == 0) {
|
||||
// if we're a full match, modify the component directly
|
||||
if (matcher.end() == content.length()) {
|
||||
final ComponentLike replacement = state.replacement.apply(matcher, Component.text().content(matcher.group())
|
||||
.style(component.style()));
|
||||
|
||||
modified = replacement == null ? Component.empty() : replacement.asComponent();
|
||||
|
||||
if (modified.style().hoverEvent() != null) {
|
||||
oldStyle = oldStyle.hoverEvent(null); // Remove original hover if it has been replaced completely
|
||||
}
|
||||
|
||||
// merge style of the match into this component to prevent unexpected loss of style
|
||||
modified = modified.style(modified.style().merge(component.style(), Style.Merge.Strategy.IF_ABSENT_ON_TARGET));
|
||||
|
||||
if (children == null) { // Prepare children
|
||||
children = new ArrayList<>(oldChildrenSize + modified.children().size());
|
||||
children.addAll(modified.children());
|
||||
}
|
||||
} else {
|
||||
// otherwise, work on a child of the root node
|
||||
modified = Component.text("", component.style());
|
||||
final ComponentLike child = state.replacement.apply(matcher, Component.text().content(matcher.group()));
|
||||
if (child != null) {
|
||||
if (children == null) {
|
||||
children = new ArrayList<>(oldChildrenSize + 1);
|
||||
}
|
||||
children.add(child.asComponent());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (children == null) {
|
||||
children = new ArrayList<>(oldChildrenSize + 2);
|
||||
}
|
||||
if (state.firstMatch) {
|
||||
// truncate parent to content before match
|
||||
modified = ((TextComponent) component).content(content.substring(0, matcher.start()));
|
||||
} else if (replacedUntil < matcher.start()) {
|
||||
children.add(Component.text(content.substring(replacedUntil, matcher.start())));
|
||||
}
|
||||
final ComponentLike builder = state.replacement.apply(matcher, Component.text().content(matcher.group()));
|
||||
if (builder != null) {
|
||||
children.add(builder.asComponent());
|
||||
}
|
||||
}
|
||||
state.replaceCount++;
|
||||
state.firstMatch = false;
|
||||
replacedUntil = matcher.end();
|
||||
}
|
||||
if (replacedUntil < content.length()) {
|
||||
// append trailing content
|
||||
if (replacedUntil > 0) {
|
||||
if (children == null) {
|
||||
children = new ArrayList<>(oldChildrenSize);
|
||||
}
|
||||
children.add(Component.text(content.substring(replacedUntil)));
|
||||
}
|
||||
// otherwise, we haven't modified the component, so nothing to change
|
||||
}
|
||||
} else if (modified instanceof TranslatableComponent) { // get TranslatableComponent with() args
|
||||
final List<TranslationArgument> args = ((TranslatableComponent) modified).arguments();
|
||||
List<TranslationArgument> newArgs = null;
|
||||
for (int i = 0, size = args.size(); i < size; i++) {
|
||||
final TranslationArgument original = args.get(i);
|
||||
final TranslationArgument replaced = original.value() instanceof Component ? TranslationArgument.component(this.render((Component) original.value(), state)) : original;
|
||||
if (replaced != original) {
|
||||
if (newArgs == null) {
|
||||
newArgs = new ArrayList<>(size);
|
||||
if (i > 0) {
|
||||
newArgs.addAll(args.subList(0, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newArgs != null) {
|
||||
newArgs.add(replaced);
|
||||
}
|
||||
}
|
||||
if (newArgs != null) {
|
||||
modified = ((TranslatableComponent) modified).arguments(newArgs);
|
||||
}
|
||||
}
|
||||
// Only visit children if we're running
|
||||
if (state.running) {
|
||||
// hover event
|
||||
if (state.replaceInsideHoverEvents) {
|
||||
final HoverEvent<?> event = oldStyle.hoverEvent();
|
||||
if (event != null) {
|
||||
final HoverEvent<?> rendered = event.withRenderedValue(this, state);
|
||||
if (event != rendered) {
|
||||
modified = modified.style(s -> s.hoverEvent(rendered));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Children
|
||||
boolean first = true;
|
||||
for (int i = 0; i < oldChildrenSize; i++) {
|
||||
final Component child = oldChildren.get(i);
|
||||
final Component replaced = this.render(child, state);
|
||||
if (replaced != child) {
|
||||
if (children == null) {
|
||||
children = new ArrayList<>(oldChildrenSize);
|
||||
}
|
||||
if (first) {
|
||||
children.addAll(oldChildren.subList(0, i));
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
if (children != null) {
|
||||
children.add(replaced);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// we're not visiting children, re-add original children if necessary
|
||||
if (children != null) {
|
||||
children.addAll(oldChildren);
|
||||
}
|
||||
}
|
||||
|
||||
state.firstMatch = prevFirstMatch;
|
||||
// Update the modified component with new children
|
||||
if (children != null) {
|
||||
return modified.children(children);
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
static final class State {
|
||||
final Pattern pattern;
|
||||
final BiFunction<MatchResult, TextComponent.Builder, @Nullable ComponentLike> replacement;
|
||||
final TextReplacementConfig.Condition continuer;
|
||||
final boolean replaceInsideHoverEvents;
|
||||
boolean running = true;
|
||||
int matchCount = 0;
|
||||
int replaceCount = 0;
|
||||
boolean firstMatch = true;
|
||||
|
||||
State(final Pattern pattern, final BiFunction<MatchResult, TextComponent.Builder, @Nullable ComponentLike> replacement, final TextReplacementConfig.Condition continuer, final boolean replaceInsideHoverEvents) {
|
||||
this.pattern = pattern;
|
||||
this.replacement = replacement;
|
||||
this.continuer = continuer;
|
||||
this.replaceInsideHoverEvents = replaceInsideHoverEvents;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Sevastjan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.clip.placeholderapi.scheduler;
|
||||
|
||||
import me.clip.placeholderapi.scheduler.scheduling.schedulers.TaskScheduler;
|
||||
import me.clip.placeholderapi.scheduler.scheduling.tasks.MyScheduledTask;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/** Just modified BukkitRunnable */
|
||||
public abstract class UniversalRunnable implements Runnable {
|
||||
MyScheduledTask task;
|
||||
|
||||
public synchronized void cancel() throws IllegalStateException {
|
||||
checkScheduled();
|
||||
task.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this task has been cancelled.
|
||||
*
|
||||
* @return true if the task has been cancelled
|
||||
* @throws IllegalStateException if task was not scheduled yet
|
||||
*/
|
||||
public synchronized boolean isCancelled() throws IllegalStateException {
|
||||
checkScheduled();
|
||||
return task.isCancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules this in the Bukkit scheduler to run on next tick.
|
||||
*
|
||||
* @param plugin the reference to the plugin scheduling task
|
||||
* @return {@link MyScheduledTask}
|
||||
* @throws IllegalArgumentException if plugin is null
|
||||
* @throws IllegalStateException if this was already scheduled
|
||||
* @see TaskScheduler#runTask(Runnable)
|
||||
*/
|
||||
|
||||
public synchronized MyScheduledTask runTask(Plugin plugin) throws IllegalArgumentException, IllegalStateException {
|
||||
checkNotYetScheduled();
|
||||
return setupTask(UniversalScheduler.getScheduler(plugin).runTask(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>Asynchronous tasks should never access any API in Bukkit. Great care
|
||||
* should be taken to assure the thread-safety of asynchronous tasks.</b>
|
||||
* <p>
|
||||
* Schedules this in the Bukkit scheduler to run asynchronously.
|
||||
*
|
||||
* @param plugin the reference to the plugin scheduling task
|
||||
* @return {@link MyScheduledTask}
|
||||
* @throws IllegalArgumentException if plugin is null
|
||||
* @throws IllegalStateException if this was already scheduled
|
||||
* @see TaskScheduler#runTaskAsynchronously(Runnable)
|
||||
*/
|
||||
|
||||
public synchronized MyScheduledTask runTaskAsynchronously(Plugin plugin) throws IllegalArgumentException, IllegalStateException {
|
||||
checkNotYetScheduled();
|
||||
return setupTask(UniversalScheduler.getScheduler(plugin).runTaskAsynchronously(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules this to run after the specified number of server ticks.
|
||||
*
|
||||
* @param plugin the reference to the plugin scheduling task
|
||||
* @param delay the ticks to wait before running the task
|
||||
* @return {@link MyScheduledTask}
|
||||
* @throws IllegalArgumentException if plugin is null
|
||||
* @throws IllegalStateException if this was already scheduled
|
||||
* @see TaskScheduler#runTaskLater(Runnable, long)
|
||||
*/
|
||||
|
||||
public synchronized MyScheduledTask runTaskLater(Plugin plugin, long delay) throws IllegalArgumentException, IllegalStateException {
|
||||
checkNotYetScheduled();
|
||||
return setupTask(UniversalScheduler.getScheduler(plugin).runTaskLater(this, delay));
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>Asynchronous tasks should never access any API in Bukkit. Great care
|
||||
* should be taken to assure the thread-safety of asynchronous tasks.</b>
|
||||
* <p>
|
||||
* Schedules this to run asynchronously after the specified number of
|
||||
* server ticks.
|
||||
*
|
||||
* @param plugin the reference to the plugin scheduling task
|
||||
* @param delay the ticks to wait before running the task
|
||||
* @return {@link MyScheduledTask}
|
||||
* @throws IllegalArgumentException if plugin is null
|
||||
* @throws IllegalStateException if this was already scheduled
|
||||
* @see TaskScheduler#runTaskLaterAsynchronously(Runnable, long)
|
||||
*/
|
||||
|
||||
public synchronized MyScheduledTask runTaskLaterAsynchronously(Plugin plugin, long delay) throws IllegalArgumentException, IllegalStateException {
|
||||
checkNotYetScheduled();
|
||||
return setupTask(UniversalScheduler.getScheduler(plugin).runTaskLaterAsynchronously(this, delay));
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules this to repeatedly run until cancelled, starting after the
|
||||
* specified number of server ticks.
|
||||
*
|
||||
* @param plugin the reference to the plugin scheduling task
|
||||
* @param delay the ticks to wait before running the task
|
||||
* @param period the ticks to wait between runs
|
||||
* @return {@link MyScheduledTask}
|
||||
* @throws IllegalArgumentException if plugin is null
|
||||
* @throws IllegalStateException if this was already scheduled
|
||||
* @see TaskScheduler#runTaskTimer(Runnable, long, long)
|
||||
*/
|
||||
|
||||
public synchronized MyScheduledTask runTaskTimer(Plugin plugin, long delay, long period) throws IllegalArgumentException, IllegalStateException {
|
||||
checkNotYetScheduled();
|
||||
return setupTask(UniversalScheduler.getScheduler(plugin).runTaskTimer(this, delay, period));
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>Asynchronous tasks should never access any API in Bukkit. Great care
|
||||
* should be taken to assure the thread-safety of asynchronous tasks.</b>
|
||||
* <p>
|
||||
* Schedules this to repeatedly run asynchronously until cancelled,
|
||||
* starting after the specified number of server ticks.
|
||||
*
|
||||
* @param plugin the reference to the plugin scheduling task
|
||||
* @param delay the ticks to wait before running the task for the first
|
||||
* time
|
||||
* @param period the ticks to wait between runs
|
||||
* @return {@link MyScheduledTask}
|
||||
* @throws IllegalArgumentException if plugin is null
|
||||
* @throws IllegalStateException if this was already scheduled
|
||||
* @see TaskScheduler#runTaskTimerAsynchronously(Runnable, long, long)
|
||||
*/
|
||||
|
||||
public synchronized MyScheduledTask runTaskTimerAsynchronously(Plugin plugin, long delay, long period) throws IllegalArgumentException, IllegalStateException {
|
||||
checkNotYetScheduled();
|
||||
return setupTask(UniversalScheduler.getScheduler(plugin).runTaskTimerAsynchronously(this, delay, period));
|
||||
}
|
||||
|
||||
private void checkScheduled() {
|
||||
if (task == null) {
|
||||
throw new IllegalStateException("Not scheduled yet");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkNotYetScheduled() {
|
||||
if (task != null) {
|
||||
throw new IllegalStateException("Already scheduled");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private MyScheduledTask setupTask(final MyScheduledTask task) {
|
||||
this.task = task;
|
||||
return task;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Sevastjan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.clip.placeholderapi.scheduler;
|
||||
|
||||
import me.clip.placeholderapi.scheduler.bukkit.BukkitScheduler;
|
||||
import me.clip.placeholderapi.scheduler.folia.FoliaScheduler;
|
||||
import me.clip.placeholderapi.scheduler.paper.PaperScheduler;
|
||||
import me.clip.placeholderapi.scheduler.scheduling.schedulers.TaskScheduler;
|
||||
import me.clip.placeholderapi.scheduler.utils.JavaUtil;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class UniversalScheduler {
|
||||
private static final boolean IS_FOLIA = JavaUtil.classExists("io.papermc.paper.threadedregions.RegionizedServer");
|
||||
private static final boolean IS_CANVAS = JavaUtil.classExists("io.canvasmc.canvas.server.ThreadedServer");
|
||||
private static final boolean IS_EXPANDED_SCHEDULING_AVAILABLE = JavaUtil.classExists("io.papermc.paper.threadedregions.scheduler.ScheduledTask");
|
||||
|
||||
public static TaskScheduler getScheduler(Plugin plugin) {
|
||||
return IS_FOLIA || IS_CANVAS ? new FoliaScheduler(plugin) : (IS_EXPANDED_SCHEDULING_AVAILABLE ? new PaperScheduler(plugin) : new BukkitScheduler(plugin));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Sevastjan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.clip.placeholderapi.scheduler.bukkit;
|
||||
|
||||
import me.clip.placeholderapi.scheduler.scheduling.tasks.MyScheduledTask;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
public class BukkitScheduledTask implements MyScheduledTask {
|
||||
|
||||
BukkitTask task;
|
||||
|
||||
boolean isRepeating;
|
||||
|
||||
public BukkitScheduledTask(final BukkitTask task) {
|
||||
this.task = task;
|
||||
this.isRepeating = false;
|
||||
}
|
||||
|
||||
public BukkitScheduledTask(final BukkitTask task, boolean isRepeating) {
|
||||
this.task = task;
|
||||
this.isRepeating = isRepeating;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
task.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return task.isCancelled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin getOwningPlugin() {
|
||||
return task.getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCurrentlyRunning() {
|
||||
return Bukkit.getServer().getScheduler().isCurrentlyRunning(this.task.getTaskId()); //There's no other way. Fuck bukkit
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRepeatingTask() {
|
||||
return isRepeating;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Sevastjan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.clip.placeholderapi.scheduler.bukkit;
|
||||
|
||||
import me.clip.placeholderapi.scheduler.scheduling.schedulers.TaskScheduler;
|
||||
import me.clip.placeholderapi.scheduler.scheduling.tasks.MyScheduledTask;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class BukkitScheduler implements TaskScheduler {
|
||||
final Plugin plugin;
|
||||
|
||||
public BukkitScheduler(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGlobalThread() {
|
||||
return Bukkit.getServer().isPrimaryThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEntityThread(Entity entity) {
|
||||
return Bukkit.getServer().isPrimaryThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegionThread(Location location) {
|
||||
return Bukkit.getServer().isPrimaryThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTask(Runnable runnable) {
|
||||
return new BukkitScheduledTask(Bukkit.getScheduler().runTask(plugin, runnable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskLater(Runnable runnable, long delay) {
|
||||
return new BukkitScheduledTask(Bukkit.getScheduler().runTaskLater(plugin, runnable, delay));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskTimer(Runnable runnable, long delay, long period) {
|
||||
return new BukkitScheduledTask(Bukkit.getScheduler().runTaskTimer(plugin, runnable, delay, period));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskAsynchronously(Runnable runnable) {
|
||||
return new BukkitScheduledTask(Bukkit.getScheduler().runTaskAsynchronously(plugin, runnable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskLaterAsynchronously(Runnable runnable, long delay) {
|
||||
return new BukkitScheduledTask(Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, runnable, delay));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskTimerAsynchronously(Runnable runnable, long delay, long period) {
|
||||
return new BukkitScheduledTask(Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, runnable, delay, period));
|
||||
}
|
||||
|
||||
//Useless? Or...
|
||||
public MyScheduledTask runTask(Plugin plugin, Runnable runnable) {
|
||||
return new BukkitScheduledTask(Bukkit.getScheduler().runTask(plugin, runnable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskLater(Plugin plugin, Runnable runnable, long delay) {
|
||||
return new BukkitScheduledTask(Bukkit.getScheduler().runTaskLater(plugin, runnable, delay));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskTimer(Plugin plugin, Runnable runnable, long delay, long period) {
|
||||
return new BukkitScheduledTask(Bukkit.getScheduler().runTaskTimer(plugin, runnable, delay, period));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskAsynchronously(Plugin plugin, Runnable runnable) {
|
||||
return new BukkitScheduledTask(Bukkit.getScheduler().runTaskAsynchronously(plugin, runnable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskLaterAsynchronously(Plugin plugin, Runnable runnable, long delay) {
|
||||
return new BukkitScheduledTask(Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, runnable, delay));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskTimerAsynchronously(Plugin plugin, Runnable runnable, long delay, long period) {
|
||||
return new BukkitScheduledTask(Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, runnable, delay, period));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable runnable) {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelTasks() {
|
||||
Bukkit.getScheduler().cancelTasks(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelTasks(Plugin plugin) {
|
||||
Bukkit.getScheduler().cancelTasks(plugin);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Sevastjan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.clip.placeholderapi.scheduler.folia;
|
||||
|
||||
import me.clip.placeholderapi.scheduler.scheduling.tasks.MyScheduledTask;
|
||||
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class FoliaScheduledTask implements MyScheduledTask {
|
||||
private final ScheduledTask task;
|
||||
|
||||
public FoliaScheduledTask(final ScheduledTask task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
this.task.cancel();
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return this.task.isCancelled();
|
||||
}
|
||||
|
||||
public Plugin getOwningPlugin() {
|
||||
return this.task.getOwningPlugin();
|
||||
}
|
||||
|
||||
public boolean isCurrentlyRunning() {
|
||||
final ScheduledTask.ExecutionState state = this.task.getExecutionState();
|
||||
return state == ScheduledTask.ExecutionState.RUNNING || state == ScheduledTask.ExecutionState.CANCELLED_RUNNING;
|
||||
}
|
||||
|
||||
public boolean isRepeatingTask() {
|
||||
return this.task.isRepeatingTask();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Sevastjan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.clip.placeholderapi.scheduler.folia;
|
||||
|
||||
import me.clip.placeholderapi.scheduler.scheduling.schedulers.TaskScheduler;
|
||||
import me.clip.placeholderapi.scheduler.scheduling.tasks.MyScheduledTask;
|
||||
import io.papermc.paper.threadedregions.scheduler.AsyncScheduler;
|
||||
import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler;
|
||||
import io.papermc.paper.threadedregions.scheduler.RegionScheduler;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class FoliaScheduler implements TaskScheduler {
|
||||
|
||||
final Plugin plugin;
|
||||
|
||||
public FoliaScheduler(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
private final RegionScheduler regionScheduler = Bukkit.getServer().getRegionScheduler();
|
||||
private final GlobalRegionScheduler globalRegionScheduler = Bukkit.getServer().getGlobalRegionScheduler();
|
||||
private final AsyncScheduler asyncScheduler = Bukkit.getServer().getAsyncScheduler();
|
||||
|
||||
@Override
|
||||
public boolean isGlobalThread() {
|
||||
return Bukkit.getServer().isGlobalTickThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTickThread() {
|
||||
return Bukkit.getServer().isPrimaryThread(); // The Paper implementation checks whether this is a tick thread, this method exists to avoid confusion.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEntityThread(Entity entity) {
|
||||
return Bukkit.getServer().isOwnedByCurrentRegion(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegionThread(Location location) {
|
||||
return Bukkit.getServer().isOwnedByCurrentRegion(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTask(Runnable runnable) {
|
||||
return new FoliaScheduledTask(globalRegionScheduler.run(plugin, task -> runnable.run()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskLater(Runnable runnable, long delay) {
|
||||
//Folia exception: Delay ticks may not be <= 0
|
||||
if (delay <= 0) {
|
||||
return runTask(runnable);
|
||||
}
|
||||
return new FoliaScheduledTask(globalRegionScheduler.runDelayed(plugin, task -> runnable.run(), delay));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskTimer(Runnable runnable, long delay, long period) {
|
||||
//Folia exception: Delay ticks may not be <= 0
|
||||
delay = getOneIfNotPositive(delay);
|
||||
return new FoliaScheduledTask(globalRegionScheduler.runAtFixedRate(plugin, task -> runnable.run(), delay, period));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTask(Plugin plugin, Runnable runnable) {
|
||||
return new FoliaScheduledTask(globalRegionScheduler.run(plugin, task -> runnable.run()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskLater(Plugin plugin, Runnable runnable, long delay) {
|
||||
//Folia exception: Delay ticks may not be <= 0
|
||||
if (delay <= 0) {
|
||||
return runTask(plugin, runnable);
|
||||
}
|
||||
return new FoliaScheduledTask(globalRegionScheduler.runDelayed(plugin, task -> runnable.run(), delay));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskTimer(Plugin plugin, Runnable runnable, long delay, long period) {
|
||||
//Folia exception: Delay ticks may not be <= 0
|
||||
delay = getOneIfNotPositive(delay);
|
||||
return new FoliaScheduledTask(globalRegionScheduler.runAtFixedRate(plugin, task -> runnable.run(), delay, period));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTask(Location location, Runnable runnable) {
|
||||
return new FoliaScheduledTask(regionScheduler.run(plugin, location, task -> runnable.run()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskLater(Location location, Runnable runnable, long delay) {
|
||||
//Folia exception: Delay ticks may not be <= 0
|
||||
if (delay <= 0) {
|
||||
return runTask(runnable);
|
||||
}
|
||||
return new FoliaScheduledTask(regionScheduler.runDelayed(plugin, location, task -> runnable.run(), delay));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskTimer(Location location, Runnable runnable, long delay, long period) {
|
||||
//Folia exception: Delay ticks may not be <= 0
|
||||
delay = getOneIfNotPositive(delay);
|
||||
return new FoliaScheduledTask(regionScheduler.runAtFixedRate(plugin, location, task -> runnable.run(), delay, period));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTask(Entity entity, Runnable runnable) {
|
||||
return new FoliaScheduledTask(entity.getScheduler().run(plugin, task -> runnable.run(), null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskLater(Entity entity, Runnable runnable, long delay) {
|
||||
//Folia exception: Delay ticks may not be <= 0
|
||||
if (delay <= 0) {
|
||||
return runTask(entity, runnable);
|
||||
}
|
||||
return new FoliaScheduledTask(entity.getScheduler().runDelayed(plugin, task -> runnable.run(), null, delay));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskTimer(Entity entity, Runnable runnable, long delay, long period) {
|
||||
//Folia exception: Delay ticks may not be <= 0
|
||||
delay = getOneIfNotPositive(delay);
|
||||
return new FoliaScheduledTask(entity.getScheduler().runAtFixedRate(plugin, task -> runnable.run(), null, delay, period));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskAsynchronously(Runnable runnable) {
|
||||
return new FoliaScheduledTask(asyncScheduler.runNow(plugin, task -> runnable.run()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskLaterAsynchronously(Runnable runnable, long delay) {
|
||||
//Folia exception: Delay ticks may not be <= 0
|
||||
delay = getOneIfNotPositive(delay);
|
||||
return new FoliaScheduledTask(asyncScheduler.runDelayed(plugin, task -> runnable.run(), delay * 50L, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskTimerAsynchronously(Runnable runnable, long delay, long period) {
|
||||
return new FoliaScheduledTask(asyncScheduler.runAtFixedRate(plugin, task -> runnable.run(), delay * 50, period * 50, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskAsynchronously(Plugin plugin, Runnable runnable) {
|
||||
return new FoliaScheduledTask(asyncScheduler.runNow(plugin, task -> runnable.run()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskLaterAsynchronously(Plugin plugin, Runnable runnable, long delay) {
|
||||
//Folia exception: Delay ticks may not be <= 0
|
||||
delay = getOneIfNotPositive(delay);
|
||||
return new FoliaScheduledTask(asyncScheduler.runDelayed(plugin, task -> runnable.run(), delay * 50L, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyScheduledTask runTaskTimerAsynchronously(Plugin plugin, Runnable runnable, long delay, long period) {
|
||||
//Folia exception: Delay ticks may not be <= 0
|
||||
delay = getOneIfNotPositive(delay);
|
||||
return new FoliaScheduledTask(asyncScheduler.runAtFixedRate(plugin, task -> runnable.run(), delay * 50, period * 50, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable runnable) {
|
||||
globalRegionScheduler.execute(plugin, runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Location location, Runnable runnable) {
|
||||
regionScheduler.execute(plugin, location, runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Entity entity, Runnable runnable) {
|
||||
entity.getScheduler().execute(plugin, runnable, null, 1L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelTasks() {
|
||||
globalRegionScheduler.cancelTasks(plugin);
|
||||
asyncScheduler.cancelTasks(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelTasks(Plugin plugin) {
|
||||
globalRegionScheduler.cancelTasks(plugin);
|
||||
asyncScheduler.cancelTasks(plugin);
|
||||
}
|
||||
|
||||
private long getOneIfNotPositive(long x) {
|
||||
return x <= 0 ? 1L : x;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Sevastjan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.clip.placeholderapi.scheduler.paper;
|
||||
|
||||
import me.clip.placeholderapi.scheduler.folia.FoliaScheduler;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
//Thanks to Towny
|
||||
public class PaperScheduler extends FoliaScheduler {
|
||||
public PaperScheduler(Plugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGlobalThread() {
|
||||
// isGlobalThread does not exist on paper, match the bukkit task scheduler's behaviour.
|
||||
return Bukkit.getServer().isPrimaryThread();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Sevastjan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.clip.placeholderapi.scheduler.scheduling.schedulers;
|
||||
|
||||
import me.clip.placeholderapi.scheduler.scheduling.tasks.MyScheduledTask;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public interface TaskScheduler {
|
||||
|
||||
/**
|
||||
* <b>Folia</b>: Returns whether the current thread is ticking the global region <br>
|
||||
* <b>Paper & Bukkit</b>: Returns {@link org.bukkit.Server#isPrimaryThread}
|
||||
*/
|
||||
boolean isGlobalThread();
|
||||
|
||||
/**
|
||||
* @return {@link org.bukkit.Server#isPrimaryThread}
|
||||
*/
|
||||
default boolean isTickThread() {
|
||||
return Bukkit.getServer().isPrimaryThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>Folia & Paper</b>: Returns whether the current thread is ticking a region and that the region
|
||||
* being ticked owns the specified entity. Note that this function is the only appropriate method of
|
||||
* checking for ownership of an entity, as retrieving the entity's location is undefined unless the
|
||||
* entity is owned by the current region
|
||||
* <p>
|
||||
* <b>Bukkit</b>: returns {@link org.bukkit.Server#isPrimaryThread}
|
||||
*
|
||||
* @param entity Specified entity
|
||||
*/
|
||||
boolean isEntityThread(Entity entity);
|
||||
|
||||
/**
|
||||
* <b>Folia & Paper</b>: Returns whether the current thread is ticking a region and that the region
|
||||
* being ticked owns the chunk at the specified world and block position as included in the specified location
|
||||
* <p>
|
||||
* <b>Bukkit</b>: returns {@link org.bukkit.Server#isPrimaryThread}
|
||||
*
|
||||
* @param location Specified location, must have a non-null world.
|
||||
*/
|
||||
boolean isRegionThread(Location location);
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed on the next tick <br>
|
||||
* <b>Folia & Paper</b>: ...on the global region <br>
|
||||
* <b>Bukkit</b>: ...on the main thread
|
||||
*
|
||||
* @param runnable The task to execute
|
||||
*/
|
||||
MyScheduledTask runTask(Runnable runnable);
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the specified delay in ticks <br>
|
||||
* <b>Folia & Paper</b>: ...on the global region <br>
|
||||
* <b>Bukkit</b>: ...on the main thread
|
||||
*
|
||||
* @param runnable The task to execute
|
||||
* @param delay The delay, in ticks
|
||||
*/
|
||||
MyScheduledTask runTaskLater(Runnable runnable, long delay);
|
||||
|
||||
/**
|
||||
* Schedules a repeating task to be executed after the initial delay with the specified period <br>
|
||||
* <b>Folia & Paper</b>: ...on the global region <br>
|
||||
* <b>Bukkit</b>: ...on the main thread
|
||||
*
|
||||
* @param runnable The task to execute
|
||||
* @param delay The initial delay, in ticks.
|
||||
* @param period The period, in ticks.
|
||||
*/
|
||||
MyScheduledTask runTaskTimer(Runnable runnable, long delay, long period);
|
||||
|
||||
/**
|
||||
* Deprecated: use {@link #runTask(Runnable)}
|
||||
*/
|
||||
@Deprecated
|
||||
default MyScheduledTask runTask(Plugin plugin, Runnable runnable) {
|
||||
return runTask(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated: use {@link #runTaskLater(Runnable, long)}
|
||||
*/
|
||||
@Deprecated
|
||||
default MyScheduledTask runTaskLater(Plugin plugin, Runnable runnable, long delay) {
|
||||
return runTaskLater(runnable, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated: use {@link #runTaskTimer(Runnable, long, long)}
|
||||
*/
|
||||
@Deprecated
|
||||
default MyScheduledTask runTaskTimer(Plugin plugin, Runnable runnable, long delay, long period) {
|
||||
return runTaskTimer(runnable, delay, period);
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>Folia & Paper</b>: Schedules a task to be executed on the region which owns the location on the next tick
|
||||
* <p>
|
||||
* <b>Bukkit</b>: same as {@link #runTask(Runnable)}
|
||||
*
|
||||
* @param location The location which the region executing should own
|
||||
* @param runnable The task to execute
|
||||
*/
|
||||
default MyScheduledTask runTask(Location location, Runnable runnable) {
|
||||
return runTask(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>Folia & Paper</b>: Schedules a task to be executed on the region which owns the location after the
|
||||
* specified delay in ticks
|
||||
* <p>
|
||||
* <b>Bukkit</b>: same as {@link #runTaskLater(Runnable, long)}
|
||||
*
|
||||
* @param location The location which the region executing should own
|
||||
* @param runnable The task to execute
|
||||
* @param delay The delay, in ticks.
|
||||
*/
|
||||
default MyScheduledTask runTaskLater(Location location, Runnable runnable, long delay) {
|
||||
return runTaskLater(runnable, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>Folia & Paper</b>: Schedules a repeating task to be executed on the region which owns the location
|
||||
* after the initial delay with the specified period
|
||||
* <p>
|
||||
* <b>Bukkit</b>: same as {@link #runTaskTimer(Runnable, long, long)}
|
||||
*
|
||||
* @param location The location which the region executing should own
|
||||
* @param runnable The task to execute
|
||||
* @param delay The initial delay, in ticks.
|
||||
* @param period The period, in ticks.
|
||||
*/
|
||||
default MyScheduledTask runTaskTimer(Location location, Runnable runnable, long delay, long period) {
|
||||
return runTaskTimer(runnable, delay, period);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated: use {@link #runTaskLater(Runnable, long)}
|
||||
*/
|
||||
@Deprecated
|
||||
default MyScheduledTask scheduleSyncDelayedTask(Runnable runnable, long delay) {
|
||||
return runTaskLater(runnable, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated: use {@link #execute(Runnable)} or {@link #runTask(Runnable)}
|
||||
*/
|
||||
@Deprecated
|
||||
default MyScheduledTask scheduleSyncDelayedTask(Runnable runnable) {
|
||||
return runTask(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated: use {@link #runTaskTimer(Runnable, long, long)}
|
||||
*/
|
||||
@Deprecated
|
||||
default MyScheduledTask scheduleSyncRepeatingTask(Runnable runnable, long delay, long period) {
|
||||
return runTaskTimer(runnable, delay, period);
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>Folia & Paper</b>: Schedules a task to be executed on the region which owns the location
|
||||
* of given entity on the next tick
|
||||
* <p>
|
||||
* <b>Bukkit</b>: same as {@link #runTask(Runnable)}
|
||||
*
|
||||
* @param entity The entity whose location the region executing should own
|
||||
* @param runnable The task to execute
|
||||
*/
|
||||
default MyScheduledTask runTask(Entity entity, Runnable runnable) {
|
||||
return runTask(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>Folia & Paper</b>: Schedules a task to be executed on the region which owns the location
|
||||
* of given entity after the specified delay in ticks
|
||||
* <p>
|
||||
* <b>Bukkit</b>: same as {@link #runTaskLater(Runnable, long)}
|
||||
*
|
||||
* @param entity The entity whose location the region executing should own
|
||||
* @param runnable The task to execute
|
||||
* @param delay The delay, in ticks.
|
||||
*/
|
||||
default MyScheduledTask runTaskLater(Entity entity, Runnable runnable, long delay) {
|
||||
return runTaskLater(runnable, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>Folia & Paper</b>: Schedules a repeating task to be executed on the region which owns the
|
||||
* location of given entity after the initial delay with the specified period
|
||||
* <p>
|
||||
* <b>Bukkit</b>: same as {@link #runTaskTimer(Runnable, long, long)}
|
||||
*
|
||||
* @param entity The entity whose location the region executing should own
|
||||
* @param runnable The task to execute
|
||||
* @param delay The initial delay, in ticks.
|
||||
* @param period The period, in ticks.
|
||||
*/
|
||||
default MyScheduledTask runTaskTimer(Entity entity, Runnable runnable, long delay, long period) {
|
||||
return runTaskTimer(runnable, delay, period);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules the specified task to be executed asynchronously immediately
|
||||
*
|
||||
* @param runnable The task to execute
|
||||
* @return The {@link MyScheduledTask} that represents the scheduled task
|
||||
*/
|
||||
MyScheduledTask runTaskAsynchronously(Runnable runnable);
|
||||
|
||||
/**
|
||||
* Schedules the specified task to be executed asynchronously after the time delay has passed
|
||||
*
|
||||
* @param runnable The task to execute
|
||||
* @param delay The time delay to pass before the task should be executed
|
||||
* @return The {@link MyScheduledTask} that represents the scheduled task
|
||||
*/
|
||||
MyScheduledTask runTaskLaterAsynchronously(Runnable runnable, long delay);
|
||||
|
||||
/**
|
||||
* Schedules the specified task to be executed asynchronously after the initial delay has passed,
|
||||
* and then periodically executed with the specified period
|
||||
*
|
||||
* @param runnable The task to execute
|
||||
* @param delay The time delay to pass before the first execution of the task, in ticks
|
||||
* @param period The time between task executions after the first execution of the task, in ticks
|
||||
* @return The {@link MyScheduledTask} that represents the scheduled task
|
||||
*/
|
||||
MyScheduledTask runTaskTimerAsynchronously(Runnable runnable, long delay, long period);
|
||||
|
||||
/**
|
||||
* Deprecated: use {@link #runTaskAsynchronously(Runnable)}
|
||||
*/
|
||||
@Deprecated
|
||||
default MyScheduledTask runTaskAsynchronously(Plugin plugin, Runnable runnable) {
|
||||
return runTaskAsynchronously(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated: use {@link #runTaskLaterAsynchronously(Runnable, long)}
|
||||
*/
|
||||
@Deprecated
|
||||
default MyScheduledTask runTaskLaterAsynchronously(Plugin plugin, Runnable runnable, long delay) {
|
||||
return runTaskLaterAsynchronously(runnable, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated: use {@link #runTaskTimerAsynchronously(Runnable, long, long)}
|
||||
*/
|
||||
@Deprecated
|
||||
default MyScheduledTask runTaskTimerAsynchronously(Plugin plugin, Runnable runnable, long delay, long period) {
|
||||
return runTaskTimerAsynchronously(runnable, delay, period);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a method on the main thread and returns a Future object. This task will be executed
|
||||
* by the main(Bukkit)/global(Folia&Paper) server thread.
|
||||
* <p>
|
||||
* Note: The Future.get() methods must NOT be called from the main thread.
|
||||
* <p>
|
||||
* Note2: There is at least an average of 10ms latency until the isDone() method returns true.
|
||||
*
|
||||
* @param task Task to be executed
|
||||
*/
|
||||
default <T> Future<T> callSyncMethod(final Callable<T> task) {
|
||||
CompletableFuture<T> completableFuture = new CompletableFuture<>();
|
||||
execute(() -> {
|
||||
try {
|
||||
completableFuture.complete(task.call());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
return completableFuture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed on the global region
|
||||
*
|
||||
* @param runnable The task to execute
|
||||
*/
|
||||
void execute(Runnable runnable);
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed on the region which owns the location
|
||||
*
|
||||
* @param location The location which the region executing should own
|
||||
* @param runnable The task to execute
|
||||
*/
|
||||
default void execute(Location location, Runnable runnable) {
|
||||
execute(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed on the region which owns the location of given entity
|
||||
*
|
||||
* @param entity The entity which location the region executing should own
|
||||
* @param runnable The task to execute
|
||||
*/
|
||||
default void execute(Entity entity, Runnable runnable) {
|
||||
execute(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to cancel all tasks scheduled by this plugin
|
||||
*/
|
||||
void cancelTasks();
|
||||
|
||||
/**
|
||||
* Attempts to cancel all tasks scheduled by the specified plugin
|
||||
*
|
||||
* @param plugin specified plugin
|
||||
*/
|
||||
void cancelTasks(Plugin plugin);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Sevastjan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.clip.placeholderapi.scheduler.scheduling.tasks;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public interface MyScheduledTask {
|
||||
/**
|
||||
* Cancels executing task
|
||||
*/
|
||||
void cancel();
|
||||
|
||||
/**
|
||||
* @return true if task is cancelled, false otherwise
|
||||
*/
|
||||
boolean isCancelled();
|
||||
|
||||
/**
|
||||
* @return The plugin under which the task was scheduled.
|
||||
*/
|
||||
Plugin getOwningPlugin();
|
||||
|
||||
/**
|
||||
* @return true if task is currently executing, false otherwise
|
||||
*/
|
||||
boolean isCurrentlyRunning();
|
||||
|
||||
/**
|
||||
* @return true if task is repeating, false otherwise
|
||||
*/
|
||||
boolean isRepeatingTask();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package me.clip.placeholderapi.scheduler.utils;
|
||||
|
||||
public class JavaUtil {
|
||||
public static boolean classExists(String className) {
|
||||
try {
|
||||
Class.forName(className);
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import me.clip.placeholderapi.PlaceholderAPIPlugin;
|
||||
import me.clip.placeholderapi.scheduler.scheduling.schedulers.TaskScheduler;
|
||||
import me.clip.placeholderapi.util.Msg;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -35,15 +36,17 @@ import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
||||
public class UpdateChecker implements Listener {
|
||||
|
||||
private final int RESOURCE_ID = 6245;
|
||||
private static final int RESOURCE_ID = 6245;
|
||||
private final PlaceholderAPIPlugin plugin;
|
||||
private final TaskScheduler scheduler;
|
||||
private final String pluginVersion;
|
||||
private String spigotVersion;
|
||||
private boolean updateAvailable;
|
||||
|
||||
public UpdateChecker(PlaceholderAPIPlugin i) {
|
||||
plugin = i;
|
||||
pluginVersion = i.getDescription().getVersion();
|
||||
public UpdateChecker(PlaceholderAPIPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
scheduler = plugin.getScheduler();
|
||||
pluginVersion = plugin.getDescription().getVersion();
|
||||
}
|
||||
|
||||
public boolean hasUpdateAvailable() {
|
||||
@@ -55,7 +58,7 @@ public class UpdateChecker implements Listener {
|
||||
}
|
||||
|
||||
public void fetch() {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
scheduler.runTaskAsynchronously(() -> {
|
||||
try {
|
||||
HttpsURLConnection con = (HttpsURLConnection) new URL(
|
||||
"https://api.spigotmc.org/legacy/update.php?resource=" + RESOURCE_ID).openConnection();
|
||||
@@ -76,7 +79,7 @@ public class UpdateChecker implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
scheduler.runTask(() -> {
|
||||
plugin.getLogger()
|
||||
.info("An update for PlaceholderAPI (v" + getSpigotVersion() + ") is available at:");
|
||||
plugin.getLogger()
|
||||
|
||||
@@ -27,6 +27,8 @@ import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderAPIPlugin;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -36,14 +38,14 @@ public final class Futures {
|
||||
private Futures() {}
|
||||
|
||||
|
||||
public static <T> void onMainThread(@NotNull final Plugin plugin,
|
||||
public static <T> void onMainThread(@NotNull final PlaceholderAPIPlugin plugin,
|
||||
@NotNull final CompletableFuture<T> future,
|
||||
@NotNull final BiConsumer<T, Throwable> consumer) {
|
||||
future.whenComplete((value, exception) -> {
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
consumer.accept(value, exception);
|
||||
} else {
|
||||
Bukkit.getScheduler().runTask(plugin, () -> consumer.accept(value, exception));
|
||||
plugin.getScheduler().runTask(() -> consumer.accept(value, exception));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
name: PlaceholderAPI
|
||||
main: "me.clip.placeholderapi.PlaceholderAPIPlugin"
|
||||
folia-supported: true
|
||||
|
||||
version: ${version}
|
||||
author: HelpChat
|
||||
|
||||
Reference in New Issue
Block a user