diff --git a/folia/src/main/java/io/tebex/plugin/TebexFoliaPlugin.java b/folia/src/main/java/io/tebex/plugin/TebexFoliaPlugin.java index 04c821e..5277c80 100644 --- a/folia/src/main/java/io/tebex/plugin/TebexFoliaPlugin.java +++ b/folia/src/main/java/io/tebex/plugin/TebexFoliaPlugin.java @@ -94,8 +94,9 @@ public void onEnable() { }); }, 1, 60 * 20); - // start the initial check, which is rescheduled according to the next_check from remote - platform.checkCommandQueue(true); + // Initial check is started by initStore() when getServerInformation() completes (see BasePluginPlatform). + // Do not call checkCommandQueue here to avoid running it twice on Folia (once from here and once from + // initStore's callback), which would process online players' commands twice. } /** diff --git a/sdk/src/main/java/io/tebex/sdk/platform/BasePluginPlatform.java b/sdk/src/main/java/io/tebex/sdk/platform/BasePluginPlatform.java index 2b41557..6f2b337 100644 --- a/sdk/src/main/java/io/tebex/sdk/platform/BasePluginPlatform.java +++ b/sdk/src/main/java/io/tebex/sdk/platform/BasePluginPlatform.java @@ -20,6 +20,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import static io.tebex.sdk.util.ResourceUtil.getBundledFile; @@ -43,6 +44,7 @@ public abstract class BasePluginPlatform implements PluginPlatform { protected List serverEvents = Collections.synchronizedList(new ArrayList<>()); private final ArrayList PLUGIN_EVENTS = new ArrayList<>(); + private final AtomicBoolean commandCheckInProgress = new AtomicBoolean(false); /** * Checks if the configured store is Geyser/Offline @@ -113,10 +115,19 @@ public final CompletableFuture checkCommandQueue(boolean useRemoteNext return forceCheckOutput; } + if (!commandCheckInProgress.compareAndSet(false, true)) { + debug("Command queue check already in progress. Skipping to avoid duplicate execution."); + if (useRemoteNextCheck) { + executeAsyncLater(this::performCheck, 60, TimeUnit.SECONDS); + } + return forceCheckOutput; + } + debug("Checking for due players..."); getQueuedPlayers().clear(); getSDK().getDuePlayers().whenComplete((duePlayersResponse, ex) -> { + try { ArrayList output = new ArrayList<>(); if (ex != null) { if (ex.getMessage().contains("429")) { // handling for rate limits @@ -156,6 +167,9 @@ public final CompletableFuture checkCommandQueue(boolean useRemoteNext if(! duePlayersResponse.isExecuteOffline()) return; handleOfflineCommands(); + } finally { + commandCheckInProgress.set(false); + } }); return forceCheckOutput;