feat: add global & server leveling, quests, achievements, guilds land-claiming, social & toggleable configs

This commit is contained in:
SCOPEDD committed 2026-09-29 03:24:27 -04:00
1 parent 4b79f69c20
commit b4cef85b1f
40 files changed
+10261 -27

No files matched your search

@@ -0,0 +1,9 @@
package net.scopenet.integration;
public record ChunkCheckResult(
boolean claimed,
boolean allowed,
String guildName,
String guildTag
) {
}
@@ -9,6 +9,7 @@ import java.util.function.*;
public final class Integration implements AutoCloseable {
public static final String UNAVAILABLE = "SCOPENET sign-in is unavailable. Please try again shortly.";
public final Activity activity = new Activity();
private final Settings settings;
private final PanelClient client;
private final JsonObject hello;
private final Consumer<String> log;
@@ -27,6 +28,7 @@ public final class Integration implements AutoCloseable {
public Integration(Settings settings, String software, String mcVersion, String version,
boolean onlineMode, int maxPlayers, Consumer<String> log, BiConsumer<UUID, String> kick) {
if (!onlineMode) throw new IllegalArgumentException("SCOPENET requires online-mode=true and authlib-injector");
this.settings = settings;
this.client = new PanelClient(settings);
this.log = log;
this.kick = kick;
@@ -72,7 +74,10 @@ public final class Integration implements AutoCloseable {
try { sync(tps); }
catch (Exception e) {
if (e instanceof InterruptedException) Thread.currentThread().interrupt();
log.accept("SCOPENET sync failed; retained batch will be retried (" + e.getClass().getSimpleName() + ")");
String msg = e.getMessage() != null && !e.getMessage().isBlank()
? e.getClass().getSimpleName() + ": " + e.getMessage()
: e.getClass().getSimpleName();
log.accept("SCOPENET sync failed to connect to panel at " + settings.panel() + "; retained batch will be retried (" + msg + ")");
} finally { syncing.set(false); }
});
}
@@ -83,7 +88,19 @@ public final class Integration implements AutoCloseable {
if (!response.has("server_id")) throw new IllegalStateException("Invalid hello response");
registered = true;
}
if (pending == null) pending = activity.drain(tps);
if (pending == null) {
pending = activity.drain(tps);
JsonObject features = new JsonObject();
features.addProperty("leveling_enabled", settings.levelingEnabled());
features.addProperty("global_xp_multiplier", settings.globalXpMultiplier());
features.addProperty("server_xp_multiplier", settings.serverXpMultiplier());
features.addProperty("quests_enabled", settings.questsEnabled());
features.addProperty("achievements_enabled", settings.achievementsEnabled());
features.addProperty("guilds_enabled", settings.guildsEnabled());
features.addProperty("land_claiming_enabled", settings.landClaimingEnabled());
features.addProperty("social_enabled", settings.socialEnabled());
pending.add("features", features);
}
JsonObject response = client.post("sync", pending);
if (!response.has("ok") || !response.get("ok").getAsBoolean() || !response.has("kick")
|| !response.get("kick").isJsonArray()) throw new IllegalStateException("Invalid sync response");
@@ -98,6 +115,14 @@ public final class Integration implements AutoCloseable {
}
}
public Settings settings() {
return settings;
}
public ChunkCheckResult checkChunk(String dimension, int chunkX, int chunkZ, UUID uuid) {
return client.checkChunk(dimension, chunkX, chunkZ, uuid);
}
@Override public void close() {
closed = true;
loginWorker.shutdownNow();
@@ -42,4 +42,26 @@ public final class PanelClient {
return message != null && message.isJsonPrimitive() && message.getAsJsonPrimitive().isString()
? message.getAsString() : "You do not have access to this server.";
}
public ChunkCheckResult checkChunk(String dimension, int chunkX, int chunkZ, java.util.UUID uuid) {
if (!settings.guildsEnabled() || !settings.landClaimingEnabled()) {
return new ChunkCheckResult(false, true, null, null);
}
JsonObject req = new JsonObject();
req.addProperty("dimension", dimension);
req.addProperty("chunk_x", chunkX);
req.addProperty("chunk_z", chunkZ);
req.addProperty("uuid", uuid.toString());
try {
JsonObject res = post("guilds/check-chunk", req);
boolean claimed = res.has("claimed") && res.get("claimed").getAsBoolean();
boolean allowed = !res.has("allowed") || res.get("allowed").getAsBoolean();
String guildName = res.has("guild_name") && !res.get("guild_name").isJsonNull() ? res.get("guild_name").getAsString() : null;
String guildTag = res.has("guild_tag") && !res.get("guild_tag").isJsonNull() ? res.get("guild_tag").getAsString() : null;
return new ChunkCheckResult(claimed, allowed, guildName, guildTag);
} catch (Exception e) {
return new ChunkCheckResult(false, true, null, null);
}
}
}
@@ -8,7 +8,19 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Properties;
public record Settings(URI panel, String token) {
public record Settings(
URI panel,
String token,
boolean levelingEnabled,
double globalXpMultiplier,
double serverXpMultiplier,
boolean questsEnabled,
boolean achievementsEnabled,
boolean guildsEnabled,
boolean landClaimingEnabled,
boolean socialEnabled,
boolean chatPrefixesEnabled
) {
public Settings {
if (panel == null || panel.getHost() == null || panel.getRawUserInfo() != null
|| panel.getRawQuery() != null || panel.getRawFragment() != null
@@ -21,19 +33,79 @@ public record Settings(URI panel, String token) {
}
}
public Settings(URI panel, String token) {
this(panel, token, true, 1.0, 1.5, true, true, true, true, true, true);
}
public static Settings of(String panel, String token) {
return new Settings(URI.create(panel.trim().replaceAll("/+$", "")), token);
return of(panel, token, true, 1.0, 1.5, true, true, true, true, true, true);
}
public static Settings of(
String panel,
String token,
boolean levelingEnabled,
double globalXpMultiplier,
double serverXpMultiplier,
boolean questsEnabled,
boolean achievementsEnabled,
boolean guildsEnabled,
boolean landClaimingEnabled,
boolean socialEnabled,
boolean chatPrefixesEnabled
) {
return new Settings(
URI.create(panel.trim().replaceAll("/+$", "")),
token,
levelingEnabled,
globalXpMultiplier,
serverXpMultiplier,
questsEnabled,
achievementsEnabled,
guildsEnabled,
landClaimingEnabled,
socialEnabled,
chatPrefixesEnabled
);
}
public static Settings load(Path path) throws IOException {
if (!Files.exists(path)) {
Files.createDirectories(path.toAbsolutePath().getParent());
Files.writeString(path, "# SCOPENET server integration\npanel-url=https://panel.example.com\ntoken=\n", StandardCharsets.UTF_8);
String defaultProps = """
# SCOPENET server integration
panel-url=https://panel.example.com
token=
# Feature Toggles (set false to disable systems on this server)
leveling.enabled=true
leveling.global_xp_multiplier=1.0
leveling.server_xp_multiplier=1.5
quests.enabled=true
achievements.enabled=true
guilds.enabled=true
guilds.land_claiming=true
social.enabled=true
social.chat_prefixes=true
""";
Files.writeString(path, defaultProps, StandardCharsets.UTF_8);
}
Properties properties = new Properties();
try (Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
properties.load(reader);
}
return of(properties.getProperty("panel-url", ""), properties.getProperty("token", ""));
return of(
properties.getProperty("panel-url", ""),
properties.getProperty("token", ""),
Boolean.parseBoolean(properties.getProperty("leveling.enabled", "true")),
Double.parseDouble(properties.getProperty("leveling.global_xp_multiplier", "1.0")),
Double.parseDouble(properties.getProperty("leveling.server_xp_multiplier", "1.5")),
Boolean.parseBoolean(properties.getProperty("quests.enabled", "true")),
Boolean.parseBoolean(properties.getProperty("achievements.enabled", "true")),
Boolean.parseBoolean(properties.getProperty("guilds.enabled", "true")),
Boolean.parseBoolean(properties.getProperty("guilds.land_claiming", "true")),
Boolean.parseBoolean(properties.getProperty("social.enabled", "true")),
Boolean.parseBoolean(properties.getProperty("social.chat_prefixes", "true"))
);
}
}