Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
import com.bencodez.advancedcore.api.item.ItemBuilder;
import com.bencodez.advancedcore.api.messages.PlaceholderUtils;
import com.bencodez.advancedcore.api.user.AdvancedCoreUser;
import com.bencodez.advancedcore.bukkit.platform.BukkitPlatformServices;
import com.bencodez.advancedcore.core.platform.ConsoleCommandDispatcher;
import com.bencodez.simpleapi.messages.MessageAPI;
import com.bencodez.simpleapi.player.PlayerUtils;

Expand All @@ -60,6 +62,9 @@ public static MiscUtils getInstance() {
/** The plugin. */
AdvancedCorePlugin plugin = AdvancedCorePlugin.getInstance();

private final ConsoleCommandDispatcher consoleCommands = new ConsoleCommandDispatcher(
new BukkitPlatformServices(() -> plugin));

private MiscUtils() {
}

Expand Down Expand Up @@ -458,25 +463,7 @@ public int getYearFromMili(long time) {
}

private void runConsoleCommand(String command, int delay, boolean hasDelay) {
final String commandToRun = stripLeadingSlash(command);
if (hasDelay && delay > 0) {
plugin.getBukkitScheduler().runTaskLater(plugin, new Runnable() {

@Override
public void run() {
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), commandToRun);
}
}, delay);

} else {
plugin.getBukkitScheduler().runTask(plugin, new Runnable() {

@Override
public void run() {
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), commandToRun);
}
});
}
consoleCommands.dispatch(command, delay, hasDelay);
}

private String stripLeadingSlash(String command) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package com.bencodez.advancedcore.bukkit.platform;

import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.bukkit.Bukkit;
import org.bukkit.entity.Player;

import com.bencodez.advancedcore.AdvancedCorePlugin;
import com.bencodez.advancedcore.core.platform.PlatformPlayer;
import com.bencodez.advancedcore.core.platform.PlatformScheduler;
import com.bencodez.advancedcore.core.platform.PlatformServices;

/**
* Uses the existing plugin-owned SimpleAPI Bukkit/Folia scheduler. Creates no
* executor, listener, task registry, persistent user, or additional player cache.
* The supplier is lazy, including during construction, for legacy facade owners.
*/
public final class BukkitPlatformServices implements PlatformServices {
private final Supplier<AdvancedCorePlugin> plugin;
private final PlatformScheduler scheduler = new Scheduler();

public BukkitPlatformServices(Supplier<AdvancedCorePlugin> plugin) {
this.plugin = Objects.requireNonNull(plugin, "plugin");
}

@Override
public Optional<PlatformPlayer> findOnlinePlayer(UUID playerId) {
Objects.requireNonNull(playerId, "playerId");
Player player = Bukkit.getPlayer(playerId);
if (player == null || !player.isOnline()) {
return Optional.empty();
}
return Optional.of(new OnlinePlayer(playerId, player));
}

@Override
public PlatformScheduler scheduler() {
return scheduler;
}

@Override
public boolean dispatchConsoleCommand(String command) {
return Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), command);
}

@Override
public void sendConsoleMessage(String message) {
Bukkit.getConsoleSender().sendMessage(Objects.requireNonNull(message, "message"));
}

private AdvancedCorePlugin owner() {
return Objects.requireNonNull(plugin.get(), "plugin is not initialized");
}

private final class Scheduler implements PlatformScheduler {
@Override
public void runServer(Runnable task) {
Objects.requireNonNull(task, "task");
AdvancedCorePlugin owner = owner();
owner.getBukkitScheduler().runTask(owner, task);
}

@Override
public void runServerLater(Runnable task, long delaySeconds) {
Objects.requireNonNull(task, "task");
if (delaySeconds < 0) {
throw new IllegalArgumentException("delaySeconds must not be negative");
}
AdvancedCorePlugin owner = owner();
// This existing overload explicitly uses TimeUnit.SECONDS, not ticks.
owner.getBukkitScheduler().runTaskLater(owner, task, delaySeconds);
}

@Override
public boolean runPlayer(UUID playerId, Consumer<PlatformPlayer> task) {
Objects.requireNonNull(playerId, "playerId");
Objects.requireNonNull(task, "task");
// Async callers may locate the entity, but must not read player
// state until the entity scheduler transfers us to its owning thread.
Player player = Bukkit.getPlayer(playerId);
if (player == null) {
return false;
}
AdvancedCorePlugin owner = owner();
OnlinePlayer session = new OnlinePlayer(playerId, player);
// Never pass null: SimpleAPI's entity overload otherwise falls back
// to global scheduling. Capture this login, not a future UUID lookup.
owner.getBukkitScheduler().runTask(owner, () -> {
if (owner.isEnabled() && session.isOnline()) {
task.accept(session);
}
}, player);
return true;
}
}

private static final class OnlinePlayer implements PlatformPlayer {
private final UUID playerId;
private final Player player;

private OnlinePlayer(UUID playerId, Player player) {
this.playerId = playerId;
this.player = player;
}

@Override
public UUID getUniqueId() {
return playerId;
}

@Override
public String getName() {
return player.getName();
}

@Override
public boolean isOnline() {
return player.isOnline() && Bukkit.getPlayer(playerId) == player;
}

@Override
public boolean hasPermission(String permission) {
Objects.requireNonNull(permission, "permission");
return isOnline() && player.hasPermission(permission);
}

@Override
public void sendMessage(String message) {
Objects.requireNonNull(message, "message");
requireOnline();
player.sendMessage(message);
}

@Override
public boolean performCommand(String command) {
Objects.requireNonNull(command, "command");
requireOnline();
return player.performCommand(command);
}

private void requireOnline() {
if (!isOnline()) {
throw new IllegalStateException("Player session is no longer online");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.bencodez.advancedcore.core.platform;

import java.util.Objects;

/** The legacy console-list handoff policy, independent of Bukkit and reward storage. */
public final class ConsoleCommandDispatcher {
private final PlatformServices platform;

public ConsoleCommandDispatcher(PlatformServices platform) {
this.platform = Objects.requireNonNull(platform, "platform");
}

/**
* Removes exactly one leading slash, then preserves the legacy stagger rule:
* only a positive delay with staggering enabled uses the delayed scheduler.
* Input has already been rendered by the caller. Null/empty lines are not
* filtered here; native dispatch retains responsibility for rejecting them.
* Returning from this method does not mean the command has executed.
*/
public void dispatch(String command, long delaySeconds, boolean stagger) {
String line = command != null && command.startsWith("/") ? command.substring(1) : command;
Runnable dispatch = () -> platform.dispatchConsoleCommand(line);
if (stagger && delaySeconds > 0) {
platform.scheduler().runServerLater(dispatch, delaySeconds);
} else {
platform.scheduler().runServer(dispatch);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.bencodez.advancedcore.core.platform;

import java.util.UUID;

/**
* A live online-player session, not a persistent user or an offline identity.
* Implementations must not redirect a retained handle to a replacement session.
* Except for the UUID, use this handle only on its owning game thread (normally
* inside {@link PlatformScheduler#runPlayer}). Do not retain it across callbacks.
*/
public interface PlatformPlayer {
UUID getUniqueId();

String getName();

boolean isOnline();

/** Checks one literal native permission node; no offline/Vault fallback. */
boolean hasPermission(String permission);

/** Sends already-rendered text; no placeholders, scripts, or color parsing. */
void sendMessage(String message);

/**
* Runs an already-rendered command as this player, without granting operator
* privileges. The caller supplies the native command line without a slash.
* Returns the native dispatch result, not a durable reward acknowledgement.
*/
boolean performCommand(String command);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.bencodez.advancedcore.core.platform;

import java.util.UUID;
import java.util.function.Consumer;

/**
* Game-thread handoff only; no executor, persistent queue, or retry ownership.
* The platform/plugin owns cancellation on disable. Scheduling is not completion:
* accepted work can be retired on shutdown or disconnect. These void/boolean
* methods must not be used as durable reward-completion acknowledgements.
*/
public interface PlatformScheduler {
/** Queues server-wide work. This does not authorize access to player/world state. */
void runServer(Runnable task);

/**
* Queues server-wide work using the existing scheduler's seconds-based delay.
* This is not a tick count or a hard wall-clock deadline. Negative delays fail.
*/
void runServerLater(Runnable task, long delaySeconds);

/**
* Queues work on the current online session's owning entity/game thread.
* Async callers may locate the entity for scheduling, but must defer player
* state reads (including online checks) to the owning-thread callback.
* Returns false when UUID lookup finds no player; true means only submitted,
* not that the captured session is still online. A disconnected/replaced
* session must never fall back to a global callback or transfer the action
* to a new login. The callback may therefore not run. Rejection exceptions
* propagate; task failures belong to the native scheduler. Do not block on,
* retry, or acknowledge rewards from this boolean.
*/
boolean runPlayer(UUID playerId, Consumer<PlatformPlayer> task);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.bencodez.advancedcore.core.platform;

import java.util.Optional;
import java.util.UUID;

/** Minimal game services used by shared code; no Bukkit or mod-loader types. */
public interface PlatformServices {
/**
* Read-only UUID lookup; never creates a persistent/offline user or cache.
* Use the returned session only on its owning game thread. For asynchronous
* callers, prefer {@link PlatformScheduler#runPlayer} for lookup plus handoff.
*/
Optional<PlatformPlayer> findOnlinePlayer(UUID playerId);

PlatformScheduler scheduler();

/** Native synchronous dispatch; call from server-wide game context, not async. */
boolean dispatchConsoleCommand(String command);

/** Sends already-rendered text from server-wide game context. */
void sendConsoleMessage(String message);
}
Loading
Loading