diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b67afb27..a53a65eb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,47 +13,58 @@ jobs: - uses: actions/setup-java@v3 with: - java-version: 8 + java-version: 17 distribution: temurin - cache: gradle - name: Build uses: gradle/gradle-build-action@v2 with: arguments: build + cache-read-only: ${{ github.ref_name != 'master' && github.ref_name != 'development' }} - name: Publish to Maven Repository - if: ${{ job.status == 'success' && github.repository == 'GeyserMC/Floodgate' }} + if: ${{ github.repository == 'GeyserMC/Floodgate' }} uses: gradle/gradle-build-action@v2 env: ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }} ORG_GRADLE_PROJECT_geysermcPassword: ${{ secrets.DEPLOY_PASS }} with: arguments: publish + cache-read-only: ${{ github.ref_name != 'master' && github.ref_name != 'development' }} - name: Publish to Downloads API - if: ${{ github.ref_name == 'master' && job.status == 'success' && github.repository == 'GeyserMC/Floodgate' }} + if: ${{ github.ref_name == 'master' && github.repository == 'GeyserMC/Floodgate' }} shell: bash + env: + DOWNLOADS_USERNAME: ${{ vars.DOWNLOADS_USERNAME }} + DOWNLOADS_PRIVATE_KEY: ${{ secrets.DOWNLOADS_PRIVATE_KEY }} + DOWNLOADS_SERVER_IP: ${{ secrets.DOWNLOADS_SERVER_IP }} run: | # Save the private key to a file echo "$DOWNLOADS_PRIVATE_KEY" > id_ecdsa chmod 600 id_ecdsa + + # Set the project + project=floodgate # Get the version from gradle.properties version=$(cat gradle.properties | grep -o "version=[0-9\\.]*" | cut -d"=" -f2) + + # Create the build folder + ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP mkdir -p "~/uploads/$project/$GITHUB_RUN_NUMBER/" # Copy over artifacts - scp -B -o StrictHostKeyChecking=no -i id_ecdsa **/build/libs/floodgate-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/files/ + rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" **/build/libs/floodgate-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/ # Remove un-needed artifacts - ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP rm ~/files/floodgate-parent-*.jar ~/files/floodgate-api.jar ~/files/floodgate-core.jar + ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP rm ./uploads/$project/$GITHUB_RUN_NUMBER/floodgate-api.jar ./uploads/$project/$GITHUB_RUN_NUMBER/floodgate-core.jar - # Run the build script - ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP ./handleBuild.sh floodgate $version $GITHUB_RUN_ID $GITHUB_SHA + # Push the metadata + echo "{\"project\": \"$project\", \"version\": \"$version\", \"id\": $GITHUB_RUN_NUMBER, \"commit\": \"$GITHUB_SHA\"}" > metadata.json + rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" metadata.json $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/ - name: Notify Discord - if: ${{ github.repository == 'GeyserMC/Floodgate' }} + if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Floodgate' }} uses: Tim203/actions-git-discord-webhook@main with: webhook_url: ${{ secrets.DISCORD_WEBHOOK }} - status: ${{ job.status }} diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 4edc17e3..016ad98d 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -10,11 +10,11 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 17 uses: actions/setup-java@v2 with: distribution: 'temurin' - java-version: '8' + java-version: '17' cache: 'gradle' - name: Build with Maven diff --git a/Jenkinsfile b/Jenkinsfile index 58099d91..f9dbc474 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,7 +2,7 @@ pipeline { agent any tools { gradle 'Gradle 7' - jdk 'Java 8' + jdk 'Java 17' } options { buildDiscarder(logRotator(artifactNumToKeepStr: '5')) diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index ed15ef06..43cf80cd 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -33,7 +33,7 @@ object Versions { const val nettyVersion = "4.1.49.Final" const val snakeyamlVersion = "2.0" const val cloudVersion = "1.5.0" - const val bstatsVersion = "3.0.1" + const val bstatsVersion = "3.0.2" const val javaWebsocketVersion = "1.5.2" @@ -41,6 +41,5 @@ object Versions { // Platform versions const val velocityVersion = "3.1.1" - const val bungeeCommit = "ff5727c" - const val spigotVersion = "1.13-R0.1-SNAPSHOT" + const val spigotVersion = "1.19.4-R0.1-SNAPSHOT" } \ No newline at end of file diff --git a/build-logic/src/main/kotlin/floodgate.publish-conventions.gradle.kts b/build-logic/src/main/kotlin/floodgate.publish-conventions.gradle.kts index 696d05ac..85e35202 100644 --- a/build-logic/src/main/kotlin/floodgate.publish-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/floodgate.publish-conventions.gradle.kts @@ -10,6 +10,6 @@ indra { } } - publishSnapshotsTo("geysermc", "https://repo.opencollab.dev/artifactory/maven-snapshots") - publishReleasesTo("geysermc", "https://repo.opencollab.dev/artifactory/maven-releases") + publishSnapshotsTo("geysermc", "https://repo.opencollab.dev/maven-snapshots") + publishReleasesTo("geysermc", "https://repo.opencollab.dev/maven-releases") } \ No newline at end of file diff --git a/bungee/build.gradle.kts b/bungee/build.gradle.kts index b5183540..f0e93a8a 100644 --- a/bungee/build.gradle.kts +++ b/bungee/build.gradle.kts @@ -1,3 +1,4 @@ +var bungeeCommit = "dfd847f" var gsonVersion = "2.8.0" var guavaVersion = "21.0" @@ -13,7 +14,7 @@ relocate("cloud.commandframework") relocate("io.leangen.geantyref") // these dependencies are already present on the platform -provided("com.github.SpigotMC.BungeeCord", "bungeecord-proxy", Versions.bungeeCommit) +provided("com.github.SpigotMC.BungeeCord", "bungeecord-proxy", bungeeCommit) provided("com.google.code.gson", "gson", gsonVersion) provided("com.google.guava", "guava", guavaVersion) provided("org.yaml", "snakeyaml", Versions.snakeyamlVersion) diff --git a/bungee/src/main/java/org/geysermc/floodgate/bungee/inject/BungeeInjector.java b/bungee/src/main/java/org/geysermc/floodgate/bungee/inject/BungeeInjector.java index 9515299a..3f3de2fd 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/bungee/inject/BungeeInjector.java +++ b/bungee/src/main/java/org/geysermc/floodgate/bungee/inject/BungeeInjector.java @@ -36,6 +36,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import net.md_5.bungee.netty.PipelineUtils; import net.md_5.bungee.protocol.MinecraftEncoder; +import net.md_5.bungee.protocol.Varint21LengthFieldExtraBufPrepender; import net.md_5.bungee.protocol.Varint21LengthFieldPrepender; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.bungee.util.BungeeReflectionUtils; @@ -54,6 +55,16 @@ public final class BungeeInjector extends CommonPlatformInjector { public void inject() { // Can everyone just switch to Velocity please :) + // Newer Bungee versions have a separate prepender for backend and client connections + Field serverFramePrepender = + ReflectionUtils.getField(PipelineUtils.class, "serverFramePrepender"); + if (serverFramePrepender != null) { + BungeeCustomServerPrepender customServerPrepender = new BungeeCustomServerPrepender( + this, ReflectionUtils.castedStaticValue(serverFramePrepender) + ); + BungeeReflectionUtils.setFieldValue(null, serverFramePrepender, customServerPrepender); + } + Field framePrepender = ReflectionUtils.getField(PipelineUtils.class, "framePrepender"); // Required in order to inject into both Geyser <-> proxy AND proxy <-> server @@ -121,6 +132,22 @@ public final class BungeeInjector extends CommonPlatformInjector { } } + @RequiredArgsConstructor + private static final class BungeeCustomServerPrepender + extends Varint21LengthFieldExtraBufPrepender { + private final BungeeInjector injector; + private final Varint21LengthFieldExtraBufPrepender original; + + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception { + original.handlerAdded(ctx); + // The Minecraft encoder being in the pipeline isn't present until later + + // Proxy <-> Server + ctx.pipeline().addLast(BUNGEE_INIT, new BungeeProxyToServerInjectInitializer(injector)); + } + } + @RequiredArgsConstructor private static final class BungeeClientToProxyInjectInitializer extends ChannelInboundHandlerAdapter { diff --git a/core/src/main/java/org/geysermc/floodgate/core/pluginmessage/channel/FormChannel.java b/core/src/main/java/org/geysermc/floodgate/core/pluginmessage/channel/FormChannel.java index cebbd19f..86ca3a83 100644 --- a/core/src/main/java/org/geysermc/floodgate/core/pluginmessage/channel/FormChannel.java +++ b/core/src/main/java/org/geysermc/floodgate/core/pluginmessage/channel/FormChannel.java @@ -27,6 +27,7 @@ package org.geysermc.floodgate.core.pluginmessage.channel; import com.google.common.base.Charsets; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; +import it.unimi.dsi.fastutil.shorts.Short2ObjectMaps; import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; import jakarta.inject.Inject; import java.util.UUID; @@ -41,7 +42,8 @@ import org.geysermc.floodgate.core.pluginmessage.PluginMessageChannel; public class FormChannel implements PluginMessageChannel { private final FormDefinitions formDefinitions = FormDefinitions.instance(); - private final Short2ObjectMap
storedForms = new Short2ObjectOpenHashMap<>(); + private final Short2ObjectMap storedForms = + Short2ObjectMaps.synchronize(new Short2ObjectOpenHashMap<>()); private final AtomicInteger nextFormId = new AtomicInteger(0); @Inject PluginMessageUtils pluginMessageUtils; diff --git a/core/src/main/java/org/geysermc/floodgate/core/util/Metrics.java b/core/src/main/java/org/geysermc/floodgate/core/util/Metrics.java index a0f6c6f7..777a4758 100644 --- a/core/src/main/java/org/geysermc/floodgate/core/util/Metrics.java +++ b/core/src/main/java/org/geysermc/floodgate/core/util/Metrics.java @@ -25,6 +25,7 @@ package org.geysermc.floodgate.core.util; +import io.micronaut.runtime.event.annotation.EventListener; import jakarta.inject.Inject; import jakarta.inject.Named; import jakarta.inject.Singleton; @@ -43,6 +44,7 @@ import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.core.config.FloodgateConfig; import org.geysermc.floodgate.core.config.FloodgateConfig.MetricsConfig; +import org.geysermc.floodgate.core.event.lifecycle.ShutdownEvent; import org.geysermc.floodgate.core.platform.util.PlatformUtils; @Singleton @@ -150,4 +152,9 @@ public final class Metrics { builder.appendField("osVersion", System.getProperty("os.version")); builder.appendField("coreCount", Runtime.getRuntime().availableProcessors()); } + + @EventListener + public void onShutdown(ShutdownEvent ignored) { + metricsBase.shutdown(); + } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 7620744c..d27285cd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,13 +21,7 @@ dependencyResolutionManagement { // maven("https://repo.papermc.io/repository/maven-snapshots") { // mavenContent { snapshotsOnly() } // } - maven("https://repo.papermc.io/repository/maven-public") { - content { - includeGroupByRegex( - "(io\\.papermc\\..*|com\\.destroystokyo\\..*|com\\.velocitypowered)" - ) - } - } + maven("https://repo.papermc.io/repository/maven-public") // Spigot maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") { mavenContent { snapshotsOnly() } diff --git a/spigot/build.gradle.kts b/spigot/build.gradle.kts index 8bd8b9c0..2a819ac0 100644 --- a/spigot/build.gradle.kts +++ b/spigot/build.gradle.kts @@ -2,12 +2,26 @@ var authlibVersion = "1.5.21" var guavaVersion = "21.0" var gsonVersion = "2.8.5" +indra { + javaVersions { + // For Folia + target(8) + minimumToolchain(17) + } +} + dependencies { api(projects.core) implementation("cloud.commandframework", "cloud-bukkit", Versions.cloudVersion) // hack to make pre 1.12 work implementation("com.google.guava", "guava", guavaVersion) + + compileOnlyApi("dev.folia", "folia-api", Versions.spigotVersion) { + attributes { + attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17) + } + } } relocate("com.google.inject") @@ -21,7 +35,6 @@ relocate("com.google.guava") relocate("it.unimi") // these dependencies are already present on the platform -provided("com.destroystokyo.paper", "paper-api", Versions.spigotVersion) provided("com.mojang", "authlib", authlibVersion) provided("io.netty", "netty-transport", Versions.nettyVersion) provided("io.netty", "netty-codec", Versions.nettyVersion) diff --git a/spigot/src/main/java/org/geysermc/floodgate/spigot/module/SpigotPlatformModule.java b/spigot/src/main/java/org/geysermc/floodgate/spigot/module/SpigotPlatformModule.java index 88e0a917..38806003 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/spigot/module/SpigotPlatformModule.java +++ b/spigot/src/main/java/org/geysermc/floodgate/spigot/module/SpigotPlatformModule.java @@ -85,7 +85,7 @@ public final class SpigotPlatformModule extends AbstractModule { SpigotVersionSpecificMethods versionSpecificMethods, LanguageManager languageManager) { return new SpigotCommandUtil( - languageManager, plugin.getServer(), api, versionSpecificMethods, plugin); + languageManager, plugin.getServer(), api, versionSpecificMethods); } @Provides diff --git a/spigot/src/main/java/org/geysermc/floodgate/spigot/pluginmessage/SpigotSkinApplier.java b/spigot/src/main/java/org/geysermc/floodgate/spigot/pluginmessage/SpigotSkinApplier.java index 2185f932..fd95bc46 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/spigot/pluginmessage/SpigotSkinApplier.java +++ b/spigot/src/main/java/org/geysermc/floodgate/spigot/pluginmessage/SpigotSkinApplier.java @@ -32,7 +32,6 @@ import jakarta.inject.Inject; import jakarta.inject.Singleton; import org.bukkit.Bukkit; import org.bukkit.entity.Player; -import org.bukkit.plugin.java.JavaPlugin; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.floodgate.api.event.skin.SkinApplyEvent; import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData; @@ -48,7 +47,6 @@ import org.geysermc.floodgate.spigot.util.SpigotVersionSpecificMethods; @Singleton public final class SpigotSkinApplier implements SkinApplier { @Inject SpigotVersionSpecificMethods versionSpecificMethods; - @Inject JavaPlugin plugin; @Inject EventBus eventBus; @Override @@ -62,8 +60,7 @@ public final class SpigotSkinApplier implements SkinApplier { // player is probably not logged in yet if (player == null) { if (firstTry) { - Bukkit.getScheduler().runTaskLater( - plugin, + versionSpecificMethods.schedule( () -> applySkin0(floodgatePlayer, skinData, false), 10 * 20 ); @@ -94,12 +91,10 @@ public final class SpigotSkinApplier implements SkinApplier { replaceSkin(properties, event.newSkin()); - // By running as a task, we don't run into async issues - plugin.getServer().getScheduler().runTask(plugin, () -> { + versionSpecificMethods.maybeSchedule(() -> { for (Player p : Bukkit.getOnlinePlayers()) { if (!p.equals(player) && p.canSee(player)) { - versionSpecificMethods.hidePlayer(p, player); - versionSpecificMethods.showPlayer(p, player); + versionSpecificMethods.hideAndShowPlayer(p, player); } } }); diff --git a/spigot/src/main/java/org/geysermc/floodgate/spigot/util/ClassNames.java b/spigot/src/main/java/org/geysermc/floodgate/spigot/util/ClassNames.java index 0e7e981a..adc10c44 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/spigot/util/ClassNames.java +++ b/spigot/src/main/java/org/geysermc/floodgate/spigot/util/ClassNames.java @@ -79,6 +79,8 @@ public class ClassNames { public static final Field BUNGEE; + public static final boolean IS_FOLIA; + static { String version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; SPIGOT_MAPPING_PREFIX = "net.minecraft.server." + version; @@ -224,6 +226,10 @@ public class ClassNames { PAPER_VELOCITY_SUPPORT = null; } } + + IS_FOLIA = ReflectionUtils.getClassSilently( + "io.papermc.paper.threadedregions.scheduler.EntityScheduler" + ) != null; } private static T checkNotNull(@CheckForNull T toCheck, @CheckForNull String objectName) { diff --git a/spigot/src/main/java/org/geysermc/floodgate/spigot/util/SpigotCommandUtil.java b/spigot/src/main/java/org/geysermc/floodgate/spigot/util/SpigotCommandUtil.java index d8483494..fd836214 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/spigot/util/SpigotCommandUtil.java +++ b/spigot/src/main/java/org/geysermc/floodgate/spigot/util/SpigotCommandUtil.java @@ -27,11 +27,9 @@ package org.geysermc.floodgate.spigot.util; import java.util.Collection; import java.util.UUID; -import org.bukkit.Bukkit; import org.bukkit.Server; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.bukkit.plugin.java.JavaPlugin; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.core.platform.command.CommandUtil; @@ -43,19 +41,17 @@ import org.geysermc.floodgate.core.util.LanguageManager; public final class SpigotCommandUtil extends CommandUtil { private final Server server; private final SpigotVersionSpecificMethods versionSpecificMethods; - private final JavaPlugin plugin; private UserAudience console; public SpigotCommandUtil( LanguageManager manager, Server server, FloodgateApi api, - SpigotVersionSpecificMethods versionSpecificMethods, - JavaPlugin plugin) { + SpigotVersionSpecificMethods versionSpecificMethods + ) { super(manager, api); this.server = server; this.versionSpecificMethods = versionSpecificMethods; - this.plugin = plugin; } @Override @@ -121,7 +117,7 @@ public final class SpigotCommandUtil extends CommandUtil { public void kickPlayer(Object player, String message) { // can also be console if (player instanceof Player) { - Bukkit.getScheduler().runTask(plugin, () -> ((Player) player).kickPlayer(message)); + versionSpecificMethods.schedule(() -> ((Player) player).kickPlayer(message), 0); } } diff --git a/spigot/src/main/java/org/geysermc/floodgate/spigot/util/SpigotVersionSpecificMethods.java b/spigot/src/main/java/org/geysermc/floodgate/spigot/util/SpigotVersionSpecificMethods.java index f341b09c..be4a0a4b 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/spigot/util/SpigotVersionSpecificMethods.java +++ b/spigot/src/main/java/org/geysermc/floodgate/spigot/util/SpigotVersionSpecificMethods.java @@ -25,17 +25,22 @@ package org.geysermc.floodgate.spigot.util; +import java.lang.reflect.Method; +import java.util.concurrent.TimeUnit; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import org.geysermc.floodgate.core.util.ReflectionUtils; public final class SpigotVersionSpecificMethods { - private static final boolean NEW_GET_LOCALE; + private static final Method GET_SPIGOT; + private static final Method OLD_GET_LOCALE; private static final boolean NEW_VISIBILITY; static { - NEW_GET_LOCALE = ReflectionUtils.getMethod(Player.class, "getLocale") != null; + GET_SPIGOT = ReflectionUtils.getMethod(Player.class, "spigot"); + OLD_GET_LOCALE = ReflectionUtils.getMethod(Player.Spigot.class, "getLocale"); + NEW_VISIBILITY = null != ReflectionUtils.getMethod( Player.class, "hidePlayer", Plugin.class, Player.class @@ -49,27 +54,51 @@ public final class SpigotVersionSpecificMethods { } public String getLocale(Player player) { - if (NEW_GET_LOCALE) { + if (OLD_GET_LOCALE == null) { return player.getLocale(); } - return player.spigot().getLocale(); + Object spigot = ReflectionUtils.invoke(player, GET_SPIGOT); + return ReflectionUtils.castedInvoke(spigot, OLD_GET_LOCALE); + } + + public void hideAndShowPlayer(Player on, Player target) { + // In Folia we don't have to schedule this as there is no concept of a single main thread. + // Instead, we have to schedule the task per player. + if (ClassNames.IS_FOLIA) { + on.getScheduler().execute(plugin, () -> hideAndShowPlayer0(on, target), null, 0); + return; + } + hideAndShowPlayer0(on, target); + } + + public void schedule(Runnable runnable, long delay) { + if (ClassNames.IS_FOLIA) { + plugin.getServer().getAsyncScheduler().runDelayed( + plugin, $ -> runnable.run(), delay * 50, TimeUnit.MILLISECONDS + ); + return; + } + plugin.getServer().getScheduler().runTaskLater(plugin, runnable, delay); } @SuppressWarnings("deprecation") - public void hidePlayer(Player hideFor, Player playerToHide) { + private void hideAndShowPlayer0(Player source, Player target) { if (NEW_VISIBILITY) { - hideFor.hidePlayer(plugin, playerToHide); + source.hidePlayer(plugin, target); + source.showPlayer(plugin, target); return; } - hideFor.hidePlayer(playerToHide); + source.hidePlayer(target); + source.showPlayer(target); } - @SuppressWarnings("deprecation") - public void showPlayer(Player showFor, Player playerToShow) { - if (NEW_VISIBILITY) { - showFor.showPlayer(plugin, playerToShow); + public void maybeSchedule(Runnable runnable) { + // In Folia we don't have to schedule this as there is no concept of a single main thread. + // Instead, we have to schedule the task per player. + if (ClassNames.IS_FOLIA) { + runnable.run(); return; } - showFor.showPlayer(playerToShow); + plugin.getServer().getScheduler().runTask(plugin, runnable); } } diff --git a/spigot/src/main/resources/plugin.yml b/spigot/src/main/resources/plugin.yml index 41b612e8..0c6b9c6f 100644 --- a/spigot/src/main/resources/plugin.yml +++ b/spigot/src/main/resources/plugin.yml @@ -4,4 +4,5 @@ version: ${version} author: ${author} website: ${url} main: org.geysermc.floodgate.spigot.SpigotPlugin -api-version: 1.13 \ No newline at end of file +api-version: 1.13 +folia-supported: true \ No newline at end of file