Add SCOPENET developer API and plugin compatibility layer (LuckPerms, PlaceholderAPI, Vault, CoreProtect, WorldGuard, Spark)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DjMbLQujBHunCCu5GpsHaT
This commit is contained in:
Claude committed 2026-09-30 18:14:50 +00:00
1 parent c4a222451c
commit 9f3ed3fe24
38 files changed
+2185 -4

No files matched your search

@@ -0,0 +1,7 @@
package net.scopenet.api;
/** A player's guild, as seen from the server they are on. */
public record GuildSummary(String id, String name, String tag, String role, int claims) {
public boolean isLeader() { return "leader".equalsIgnoreCase(role); }
public boolean isOfficer() { return isLeader() || "officer".equalsIgnoreCase(role); }
}
@@ -0,0 +1,7 @@
package net.scopenet.api;
/** How many of a player's quests for the current period they have finished. */
public record QuestProgress(int total, int completed, int claimed) {
/** "3/5", handy for scoreboards. */
public String display() { return completed + "/" + total; }
}
@@ -0,0 +1,63 @@
package net.scopenet.api;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
/**
* The SCOPENET developer API. Everything that talks to the panel returns a
* {@link CompletableFuture} and never blocks the server thread; the
* {@code getCached…} methods answer instantly from data SCOPENET already holds.
* Futures complete on a worker thread, so hop back with the Bukkit scheduler
* before touching the world.
*
* <pre>{@code
* ScopenetApi api = ScopenetApiProvider.get();
* api.getPlayer(player.getUniqueId()).thenAccept(p -> getLogger().info(p.name() + " is level " + p.globalLevel()));
* api.addXP(player.getUniqueId(), XpScope.SERVER, 250, "boss kill");
* }</pre>
*
* Events ({@link net.scopenet.api.event.ScopenetLevelUpEvent},
* {@link net.scopenet.api.event.GuildJoinEvent},
* {@link net.scopenet.api.event.GuildLeaveEvent},
* {@link net.scopenet.api.event.AchievementUnlockEvent}) are ordinary Bukkit events.
*/
public interface ScopenetApi {
/** Bumped when the API changes incompatibly. */
int API_VERSION = 1;
/** Load a player's SCOPENET profile. Completes with empty if they have no SCOPENET account. */
CompletableFuture<Optional<ScopenetPlayer>> getPlayer(UUID uuid);
/** The last profile loaded for a player (kept fresh for online players), if any. Never blocks. */
Optional<ScopenetPlayer> getCachedPlayer(UUID uuid);
/** Find a guild on this server's instance by id, tag or name. */
CompletableFuture<Optional<ScopenetGuild>> getGuild(String idTagOrName);
/** The guild a player belongs to. */
CompletableFuture<Optional<ScopenetGuild>> getPlayerGuild(UUID uuid);
/** A player's balance on this server's economy. */
CompletableFuture<Double> getBalance(UUID uuid);
/**
* Give (positive) or take (negative) money. Completes with the new balance, or
* exceptionally if the player can't afford it.
*/
CompletableFuture<Double> addBalance(UUID uuid, String playerName, double amount, String reason);
/** Give (positive) or take (negative) XP. Level-ups fire {@link net.scopenet.api.event.ScopenetLevelUpEvent}. */
CompletableFuture<XpResult> addXP(UUID uuid, XpScope scope, long amount, String reason);
/**
* Move a quest forward. {@code objective} is a quest id (with {@code amount} 0 meaning "complete it")
* or an action like {@code block_broken:DIAMOND_ORE} that any matching quest counts.
* Completes with whether any quest advanced.
*/
CompletableFuture<Boolean> completeQuestObjective(UUID uuid, String objective, int amount);
/** A player's accepted friends. */
CompletableFuture<List<ScopenetFriend>> getFriends(UUID uuid);
}
@@ -0,0 +1,25 @@
package net.scopenet.api;
/** Where other plugins find the API. Also registered with Bukkit's ServicesManager. */
public final class ScopenetApiProvider {
private static volatile ScopenetApi api;
private ScopenetApiProvider() {}
/** @throws IllegalStateException if SCOPENET isn't loaded (add it to your plugin's depend/softdepend) */
public static ScopenetApi get() {
ScopenetApi current = api;
if (current == null) throw new IllegalStateException("SCOPENET is not enabled");
return current;
}
public static boolean isAvailable() { return api != null; }
/** Called by SCOPENET. */
public static void register(ScopenetApi implementation) { api = implementation; }
/** Called by SCOPENET when it shuts down. */
public static void unregister(ScopenetApi implementation) {
if (api == implementation) api = null;
}
}
@@ -0,0 +1,6 @@
package net.scopenet.api;
import java.util.UUID;
/** An accepted friend, and where they are playing right now (if anywhere). */
public record ScopenetFriend(UUID uuid, String name, boolean online, String playingOn) {}
@@ -0,0 +1,13 @@
package net.scopenet.api;
import java.util.List;
import java.util.UUID;
/** A guild and its members. Balances and claim counts are for the server the plugin runs on. */
public record ScopenetGuild(String id, String name, String tag, String description, UUID leader, int claims, double balance, List<Member> members) {
public record Member(UUID uuid, String name, String role) {}
public boolean isMember(UUID player) {
return members.stream().anyMatch(m -> m.uuid().equals(player));
}
}
@@ -0,0 +1,44 @@
package net.scopenet.api;
import java.util.UUID;
/**
* A snapshot of one SCOPENET account. It is a copy: fetch a new one (or use the
* cached copy, which is refreshed every few seconds for online players) to see changes.
*
* @param rankTitle the title earned from global level rewards, or {@code null}
* @param leaderboard the position on the global leaderboard, or 0 when unranked
* @param rankGroup the player's LuckPerms primary group as reported by the server, or {@code null}
* @param guild the player's guild on this server's instance, or {@code null}
*/
public record ScopenetPlayer(
UUID uuid,
String name,
int globalLevel,
long globalXp,
long globalLevelXp,
long globalNextLevelXp,
double globalProgress,
String rankTitle,
int leaderboard,
int serverLevel,
long serverXp,
String serverRank,
GuildSummary guild,
double balance,
long playtimeSeconds,
long serverPlaytimeSeconds,
int kills,
int deaths,
int friends,
int achievements,
QuestProgress dailyQuests,
QuestProgress weeklyQuests,
String joined,
String rankGroup,
String rankPrefix) {
public boolean inGuild() { return guild != null; }
public double killDeathRatio() { return deaths == 0 ? kills : (double) kills / deaths; }
}
@@ -0,0 +1,6 @@
package net.scopenet.api;
/** What a player's XP and level are after {@link ScopenetApi#addXP}. */
public record XpResult(XpScope scope, long xp, int level, int previousLevel) {
public boolean leveledUp() { return level > previousLevel; }
}
@@ -0,0 +1,9 @@
package net.scopenet.api;
/** Which level track an XP change applies to. */
public enum XpScope {
/** The account-wide SCOPENET level, shared by every server. */
GLOBAL,
/** The level on the server the plugin is running on. */
SERVER
}
@@ -0,0 +1,27 @@
package net.scopenet.api.event;
import org.bukkit.event.HandlerList;
import java.util.UUID;
/** A player unlocked a SCOPENET achievement. */
public final class AchievementUnlockEvent extends ScopenetEvent {
private static final HandlerList HANDLERS = new HandlerList();
private final String achievementId;
private final String title;
private final long xp;
public AchievementUnlockEvent(UUID playerId, String achievementId, String title, long xp) {
super(playerId);
this.achievementId = achievementId;
this.title = title;
this.xp = xp;
}
public String getAchievementId() { return achievementId; }
public String getTitle() { return title; }
/** XP the achievement was worth. */
public long getXp() { return xp; }
@Override public HandlerList getHandlers() { return HANDLERS; }
public static HandlerList getHandlerList() { return HANDLERS; }
}
@@ -0,0 +1,30 @@
package net.scopenet.api.event;
import org.bukkit.event.HandlerList;
import java.util.UUID;
/** A player founded or joined a guild. */
public final class GuildJoinEvent extends ScopenetEvent {
private static final HandlerList HANDLERS = new HandlerList();
private final String guildId;
private final String guildName;
private final String guildTag;
private final String role;
public GuildJoinEvent(UUID playerId, String guildId, String guildName, String guildTag, String role) {
super(playerId);
this.guildId = guildId;
this.guildName = guildName;
this.guildTag = guildTag;
this.role = role;
}
public String getGuildId() { return guildId; }
public String getGuildName() { return guildName; }
public String getGuildTag() { return guildTag; }
/** {@code leader}, {@code officer} or {@code member}. */
public String getRole() { return role; }
@Override public HandlerList getHandlers() { return HANDLERS; }
public static HandlerList getHandlerList() { return HANDLERS; }
}
@@ -0,0 +1,27 @@
package net.scopenet.api.event;
import org.bukkit.event.HandlerList;
import java.util.UUID;
/** A player left (or was removed from) a guild. */
public final class GuildLeaveEvent extends ScopenetEvent {
private static final HandlerList HANDLERS = new HandlerList();
private final String guildId;
private final String guildName;
private final String guildTag;
public GuildLeaveEvent(UUID playerId, String guildId, String guildName, String guildTag) {
super(playerId);
this.guildId = guildId;
this.guildName = guildName;
this.guildTag = guildTag;
}
public String getGuildId() { return guildId; }
/** May be {@code null} if the guild no longer exists. */
public String getGuildName() { return guildName; }
public String getGuildTag() { return guildTag; }
@Override public HandlerList getHandlers() { return HANDLERS; }
public static HandlerList getHandlerList() { return HANDLERS; }
}
@@ -0,0 +1,18 @@
package net.scopenet.api.event;
import org.bukkit.event.Event;
import java.util.UUID;
/**
* Base class for SCOPENET events. They are fired on the server thread when the
* panel reports a change for a player who is on this server.
*/
public abstract class ScopenetEvent extends Event {
private final UUID playerId;
protected ScopenetEvent(UUID playerId) {
this.playerId = playerId;
}
public UUID getPlayerId() { return playerId; }
}
@@ -0,0 +1,30 @@
package net.scopenet.api.event;
import net.scopenet.api.XpScope;
import org.bukkit.event.HandlerList;
import java.util.UUID;
/** A player's global or server level went up. */
public final class ScopenetLevelUpEvent extends ScopenetEvent {
private static final HandlerList HANDLERS = new HandlerList();
private final XpScope scope;
private final int level;
private final int previousLevel;
private final long xp;
public ScopenetLevelUpEvent(UUID playerId, XpScope scope, int level, int previousLevel, long xp) {
super(playerId);
this.scope = scope;
this.level = level;
this.previousLevel = previousLevel;
this.xp = xp;
}
public XpScope getScope() { return scope; }
public int getLevel() { return level; }
public int getPreviousLevel() { return previousLevel; }
public long getXp() { return xp; }
@Override public HandlerList getHandlers() { return HANDLERS; }
public static HandlerList getHandlerList() { return HANDLERS; }
}