Rework BlueMap claiming and expand ranks guilds chat and warps

This commit is contained in:
scoped committed 2026-09-30 18:09:18 -04:00
1 parent 434ed5338a
commit 6a2944a88e
68 files changed
+1453 -2500

No files matched your search

@@ -0,0 +1,57 @@
package net.scopenet.paper;
import net.scopenet.api.ScopenetPlayer;
import net.scopenet.paper.compat.ScopenetApiImpl;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** Formats player chat from the cached SCOPENET profile without a network call on the chat thread. */
public final class ChatFormatter {
private static final Pattern MARKDOWN = Pattern.compile("(\\*\\*(.+?)\\*\\*|__(.+?)__|~~(.+?)~~|`(.+?)`|\\*(.+?)\\*)");
private static final Pattern HEX = Pattern.compile("&#([0-9a-fA-F]{6})");
private final ScopenetApiImpl profiles;
public ChatFormatter(ScopenetApiImpl profiles) { this.profiles = profiles; }
private static String clean(String value) {
return value == null ? "" : ChatColor.stripColor(value).replace('%', ' ').replace('[', ' ').replace(']', ' ').trim();
}
public String format(Player player) {
ScopenetPlayer p = profiles.peek(player.getUniqueId()).orElse(null);
String guild = p == null || p.guild() == null ? "" : "§3[" + clean(p.guild().tag()) + "] ";
String title = p == null || p.rankTitle() == null || p.rankTitle().isBlank() ? "Novice" : clean(p.rankTitle());
String group = p == null || p.rankGroup() == null || p.rankGroup().isBlank() ? "Member"
: p.rankDisplay() == null || p.rankDisplay().isBlank() ? clean(p.rankGroup()) : clean(p.rankDisplay());
String prefix = p == null ? "" : p.rankPrefix();
String suffix = p == null ? "" : p.rankSuffix();
String decorated = prefix == null || prefix.isBlank() ? "" : ChatColor.translateAlternateColorCodes('&', prefix.replace("%", "%%")) + " ";
String name = player.getDisplayName().replace("%", "%%");
String tail = suffix == null || suffix.isBlank() ? "" : " " + ChatColor.translateAlternateColorCodes('&', suffix.replace("%", "%%"));
return guild + "§6[" + title + "] §b[" + group + "] §r" + decorated + name + tail + "§7: §f%2$s";
}
public String message(Player player, String raw) {
String input = player.hasPermission("scopenet.chat.color") ? ChatColor.translateAlternateColorCodes('&', raw) : ChatColor.stripColor(raw);
if (player.hasPermission("scopenet.chat.color")) {
Matcher hex = HEX.matcher(input);
StringBuffer colored = new StringBuffer();
while (hex.find()) hex.appendReplacement(colored, Matcher.quoteReplacement(net.md_5.bungee.api.ChatColor.of('#' + hex.group(1)).toString()));
hex.appendTail(colored);
input = colored.toString();
}
Matcher matcher = MARKDOWN.matcher(input);
StringBuffer out = new StringBuffer();
while (matcher.find()) {
String code = matcher.group(2) != null ? "§l" : matcher.group(3) != null ? "§n" : matcher.group(4) != null ? "§m" : matcher.group(5) != null ? "§7" : "§o";
String text = matcher.group(2) != null ? matcher.group(2) : matcher.group(3) != null ? matcher.group(3)
: matcher.group(4) != null ? matcher.group(4) : matcher.group(5) != null ? matcher.group(5) : matcher.group(6);
matcher.appendReplacement(out, Matcher.quoteReplacement(code + text + "§r§f"));
}
matcher.appendTail(out);
return out.toString();
}
}
@@ -27,7 +27,7 @@ public final class ScopenetPlugin extends JavaPlugin implements Listener {
private ClientLinkBridge clientLink;
private EssentialsHandler essentialsHandler;
private GuildHandler guildHandler;
private boolean liveMapStarted;
private ChatFormatter chatFormatter;
@Override public void onEnable() {
// Install the deny-by-default listener even if configuration is invalid.
@@ -51,17 +51,16 @@ public final class ScopenetPlugin extends JavaPlugin implements Listener {
// Claim protection runs from a local copy of the panel's claims, refreshed in the background.
integration.startClaimSync();
// Central Live Map: mirror chunks and player positions to the panel.
// Live positions center the in-app BlueMap claim view on the player.
if (getConfig().getBoolean("livemap.enabled", true)) {
// STARTUP plugins enable before Paper loads any worlds. WorldLoadEvent starts
// the map on a fresh boot; this also handles enabling after worlds exist.
for (World world : Bukkit.getWorlds()) startLiveMap(world);
integration.enableLiveMap();
Bukkit.getScheduler().runTaskTimer(this, this::snapshotPlayers, 40, 20);
}
// Developer API: other plugins use SCOPENET through ScopenetApiProvider / the services manager,
// and receive level-ups, achievements and guild changes as ordinary Bukkit events.
api = new ScopenetApiImpl(integration.client());
chatFormatter = new ChatFormatter(api);
ScopenetApiProvider.register(api);
getServer().getServicesManager().register(ScopenetApi.class, api, this, org.bukkit.plugin.ServicePriority.Normal);
// Optional client mod (HUD, claim overlay, shop and market windows).
@@ -85,9 +84,16 @@ public final class ScopenetPlugin extends JavaPlugin implements Listener {
EssentialsHandler essentials = new EssentialsHandler(this);
essentialsHandler = essentials;
getServer().getPluginManager().registerEvents(essentials, this);
String[] essCmds = {"spawn", "home", "sethome", "delhome", "back", "tpa", "tpaccept", "tpdeny", "rtp", "warp", "playtime"};
String[] essCmds = {"spawn", "home", "sethome", "delhome", "back", "tpa", "tpaccept", "tpdeny", "rtp", "warp", "reqwarp", "warprequests", "warpapprove", "warpdeny", "setwarp", "delwarp", "playtime"};
for (String c : essCmds) bindCommand(c, essentials);
}
HandHandler hand = new HandHandler(this);
getServer().getPluginManager().registerEvents(hand, this);
bindCommand("hand", hand);
bindCommand("handview", hand);
NicknameHandler nick = new NicknameHandler(this);
getServer().getPluginManager().registerEvents(nick, this);
bindCommand("nick", nick);
// Register Economy commands & GUI
{
@@ -105,6 +111,7 @@ public final class ScopenetPlugin extends JavaPlugin implements Listener {
bindCommand("guild", guilds);
bindCommand("claim", guilds);
bindCommand("unclaim", guilds);
bindCommand("autoclaim", guilds);
}
// Physical shops, actions from the map, and (when BlueMap is installed) claims, pins and the player panel on it.
@@ -126,18 +133,6 @@ public final class ScopenetPlugin extends JavaPlugin implements Listener {
}
}
@EventHandler
public void worldLoaded(org.bukkit.event.world.WorldLoadEvent event) {
startLiveMap(event.getWorld());
}
private void startLiveMap(World world) {
if (liveMapStarted || integration == null || world.getEnvironment() != World.Environment.NORMAL
|| !getConfig().getBoolean("livemap.enabled", true)) return;
integration.enableLiveMap(world.getWorldFolder().toPath());
liveMapStarted = true;
}
private Settings readSettings() {
return Settings.of(
getConfig().getString("panel-url", ""),
@@ -181,11 +176,6 @@ public final class ScopenetPlugin extends JavaPlugin implements Listener {
integration.liveMapPlayers(list);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void worldSaved(org.bukkit.event.world.WorldSaveEvent event) {
if (integration != null) integration.liveMapSaved();
}
public static String dimension(World world) {
return world.getKey().toString();
}
@@ -372,8 +362,12 @@ public final class ScopenetPlugin extends JavaPlugin implements Listener {
if (claimed(event.getBlock())) event.setCancelled(true);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void chat(AsyncPlayerChatEvent event) {
if (integration != null && integration.settings().socialEnabled() && integration.settings().chatPrefixesEnabled() && chatFormatter != null) {
event.setFormat(chatFormatter.format(event.getPlayer()));
event.setMessage(chatFormatter.message(event.getPlayer(), event.getMessage()));
}
add(event.getPlayer(), "messages");
}
@@ -27,8 +27,10 @@ public final class EssentialsHandler implements CommandExecutor, Listener {
private final ScopenetPlugin plugin;
private final File homesFile;
private final File warpsFile;
private final File warpRequestsFile;
private final YamlConfiguration homesConfig;
private final YamlConfiguration warpsConfig;
private final YamlConfiguration warpRequestsConfig;
private final Map<UUID, Map<String, Location>> homes = new ConcurrentHashMap<>();
private final Map<String, Location> warps = new ConcurrentHashMap<>();
@@ -43,8 +45,10 @@ public final class EssentialsHandler implements CommandExecutor, Listener {
this.plugin = plugin;
this.homesFile = new File(plugin.getDataFolder(), "homes.yml");
this.warpsFile = new File(plugin.getDataFolder(), "warps.yml");
this.warpRequestsFile = new File(plugin.getDataFolder(), "warp_requests.yml");
this.homesConfig = YamlConfiguration.loadConfiguration(homesFile);
this.warpsConfig = YamlConfiguration.loadConfiguration(warpsFile);
this.warpRequestsConfig = YamlConfiguration.loadConfiguration(warpRequestsFile);
loadHomes();
loadWarps();
}
@@ -100,6 +104,11 @@ public final class EssentialsHandler implements CommandExecutor, Listener {
}
}
private void saveWarpRequests() {
try { warpRequestsConfig.save(warpRequestsFile); }
catch (IOException e) { plugin.getLogger().warning("Failed to save warp requests: " + e.getMessage()); }
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onTeleport(PlayerTeleportEvent event) {
if (event.getFrom() != null && event.getCause() != PlayerTeleportEvent.TeleportCause.UNKNOWN) {
@@ -160,6 +169,12 @@ public final class EssentialsHandler implements CommandExecutor, Listener {
case "tpdeny" -> handleTpDeny(player);
case "rtp" -> handleRtp(player);
case "warp" -> handleWarp(player, args);
case "reqwarp" -> requestWarp(player, args);
case "warprequests" -> listWarpRequests(player);
case "warpapprove" -> reviewWarp(player, args, true);
case "warpdeny" -> reviewWarp(player, args, false);
case "setwarp" -> setWarp(player, args);
case "delwarp" -> deleteWarp(player, args);
case "playtime" -> handlePlaytime(player, args);
default -> { return false; }
}
@@ -389,6 +404,88 @@ public final class EssentialsHandler implements CommandExecutor, Listener {
player.sendMessage(ChatColor.GREEN + "Teleported to warp " + ChatColor.YELLOW + warpName + ChatColor.GREEN + ".");
}
private String warpName(Player player, String[] args, String usage) {
if (args.length != 1 || !args[0].matches("[A-Za-z0-9_-]{2,24}")) {
player.sendMessage(ChatColor.RED + "Usage: " + usage + " (2–24 letters, numbers, _ or -)");
return null;
}
return args[0].toLowerCase(Locale.ROOT);
}
private void requestWarp(Player player, String[] args) {
String name = warpName(player, args, "/reqwarp <name>");
if (name == null) return;
if (warps.containsKey(name)) { player.sendMessage(ChatColor.RED + "That warp already exists."); return; }
if (warpRequestsConfig.contains(name) && !player.getUniqueId().toString().equals(warpRequestsConfig.getString(name + ".uuid"))) {
player.sendMessage(ChatColor.RED + "That warp name has already been requested."); return;
}
warpRequestsConfig.set(name + ".location", player.getLocation());
warpRequestsConfig.set(name + ".uuid", player.getUniqueId().toString());
warpRequestsConfig.set(name + ".requester", player.getName());
warpRequestsConfig.set(name + ".created_at", System.currentTimeMillis());
saveWarpRequests();
player.sendMessage(ChatColor.GREEN + "Warp request sent. A server admin can approve it with /warpapprove " + name + ".");
Bukkit.getOnlinePlayers().stream().filter(p -> p.hasPermission("scopenet.command.warp.manage"))
.forEach(p -> p.sendMessage(ChatColor.YELLOW + player.getName() + " requested warp '" + name + "'. Use /warprequests to review."));
}
private boolean warpAdmin(Player player) {
if (player.hasPermission("scopenet.command.warp.manage")) return true;
player.sendMessage(ChatColor.RED + "Only server admins can manage warps.");
return false;
}
private void listWarpRequests(Player player) {
if (!warpAdmin(player)) return;
if (warpRequestsConfig.getKeys(false).isEmpty()) { player.sendMessage(ChatColor.YELLOW + "No pending warp requests."); return; }
player.sendMessage(ChatColor.GOLD + "Pending warp requests:");
for (String name : warpRequestsConfig.getKeys(false)) {
Location at = warpRequestsConfig.getLocation(name + ".location");
player.sendMessage(ChatColor.YELLOW + name + ChatColor.GRAY + " by " + warpRequestsConfig.getString(name + ".requester", "unknown")
+ (at == null || at.getWorld() == null ? "" : " at " + at.getWorld().getName() + " " + at.getBlockX() + ", " + at.getBlockY() + ", " + at.getBlockZ()));
}
}
private void reviewWarp(Player player, String[] args, boolean approve) {
if (!warpAdmin(player)) return;
String name = warpName(player, args, approve ? "/warpapprove <name>" : "/warpdeny <name>");
if (name == null) return;
Location at = warpRequestsConfig.getLocation(name + ".location");
if (at == null || at.getWorld() == null) { player.sendMessage(ChatColor.RED + "No pending request with a loaded world has that name."); return; }
if (approve) {
if (warps.containsKey(name)) { player.sendMessage(ChatColor.RED + "Warp already exists. Deny this request or choose another name."); return; }
warps.put(name, at);
saveWarps();
}
String uuid = warpRequestsConfig.getString(name + ".uuid", "");
warpRequestsConfig.set(name, null);
saveWarpRequests();
player.sendMessage(ChatColor.GREEN + "Warp request " + (approve ? "approved" : "denied") + ": " + name);
try {
Player requester = Bukkit.getPlayer(UUID.fromString(uuid));
if (requester != null) requester.sendMessage(ChatColor.YELLOW + "Your warp request '" + name + "' was " + (approve ? "approved" : "denied") + ".");
} catch (IllegalArgumentException ignored) {}
}
private void setWarp(Player player, String[] args) {
if (!warpAdmin(player)) return;
String name = warpName(player, args, "/setwarp <name>");
if (name == null) return;
warps.put(name, player.getLocation());
saveWarps();
player.sendMessage(ChatColor.GREEN + "Warp set: " + name);
}
private void deleteWarp(Player player, String[] args) {
if (!warpAdmin(player)) return;
String name = warpName(player, args, "/delwarp <name>");
if (name == null) return;
if (warps.remove(name) == null) { player.sendMessage(ChatColor.RED + "Warp not found."); return; }
warpsConfig.set(name, null);
saveWarps();
player.sendMessage(ChatColor.GREEN + "Warp deleted: " + name);
}
private void openWarpPicker(Player player) {
Inventory inv = Bukkit.createInventory(null, 27, GUI_WARPS_TITLE);
int slot = 11;
@@ -20,6 +20,8 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
@@ -34,6 +36,8 @@ public final class GuildHandler implements CommandExecutor, Listener {
private final File guildHomesFile;
private final YamlConfiguration guildHomesConfig;
private final Map<String, Location> guildHomes = new ConcurrentHashMap<>();
private final Set<UUID> autoClaim = ConcurrentHashMap.newKeySet();
private final Map<UUID, String> lastAutoClaim = new ConcurrentHashMap<>();
private static final String GUI_GUILD_TITLE = ChatColor.DARK_AQUA + "Guild Dashboard";
@@ -86,6 +90,23 @@ public final class GuildHandler implements CommandExecutor, Listener {
handleUnclaim(player);
return true;
}
if (cmd.equals("autoclaim")) {
if (!player.hasPermission("scopenet.command.claim")) { player.sendMessage(ChatColor.RED + "You cannot claim land."); return true; }
if (args.length != 1 || (!args[0].equalsIgnoreCase("on") && !args[0].equalsIgnoreCase("off"))) {
player.sendMessage(ChatColor.YELLOW + "Usage: /autoclaim on|off"); return true;
}
if (args[0].equalsIgnoreCase("on")) {
autoClaim.add(player.getUniqueId());
lastAutoClaim.remove(player.getUniqueId());
player.sendMessage(ChatColor.GREEN + "Auto-claim is on. New chunks you walk into will be claimed for your guild.");
claimOnMove(player, player.getLocation());
} else {
autoClaim.remove(player.getUniqueId());
lastAutoClaim.remove(player.getUniqueId());
player.sendMessage(ChatColor.YELLOW + "Auto-claim is off.");
}
return true;
}
if (cmd.equals("guild") || cmd.equals("g")) {
if (args.length == 0 || args[0].equalsIgnoreCase("info") || args[0].equalsIgnoreCase("gui")) {
@@ -211,6 +232,39 @@ public final class GuildHandler implements CommandExecutor, Listener {
});
}
@EventHandler
public void onMove(PlayerMoveEvent event) {
if (event.getTo() == null || !autoClaim.contains(event.getPlayer().getUniqueId())) return;
if (event.getFrom().getWorld() == event.getTo().getWorld()
&& event.getFrom().getChunk().getX() == event.getTo().getChunk().getX()
&& event.getFrom().getChunk().getZ() == event.getTo().getChunk().getZ()) return;
claimOnMove(event.getPlayer(), event.getTo());
}
@EventHandler public void onQuit(PlayerQuitEvent event) {
autoClaim.remove(event.getPlayer().getUniqueId());
lastAutoClaim.remove(event.getPlayer().getUniqueId());
}
private void claimOnMove(Player player, Location location) {
if (!integration.settings().landClaimingEnabled()) return;
int x = location.getBlockX() >> 4, z = location.getBlockZ() >> 4;
String dim = ScopenetPlugin.dimension(location.getWorld());
String key = dim + ':' + x + ':' + z;
if (key.equals(lastAutoClaim.put(player.getUniqueId(), key))) return;
if (integration.isClaimed(dim, x, z)) return;
UUID uuid = player.getUniqueId();
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
try {
integration.client().claimChunk(dim, x, z, uuid);
player.sendMessage(ChatColor.GREEN + "Auto-claimed chunk [" + x + ", " + z + "].");
} catch (Exception e) {
autoClaim.remove(uuid);
player.sendMessage(ChatColor.RED + "Auto-claim stopped: " + e.getMessage());
}
});
}
private void handleUnclaim(Player player) {
Chunk chunk = player.getLocation().getChunk();
String dim = ScopenetPlugin.dimension(player.getWorld());
@@ -0,0 +1,71 @@
package net.scopenet.paper.commands;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/** Share a read-only snapshot of the held item through a clickable chat message. */
public final class HandHandler implements CommandExecutor, Listener {
private record Shared(ItemStack item, long expires) {}
private final Map<String, Shared> shares = new ConcurrentHashMap<>();
private static final String TITLE = ChatColor.DARK_GRAY + "Shared item";
public HandHandler(Plugin plugin) {
Bukkit.getScheduler().runTaskTimer(plugin, () -> shares.entrySet().removeIf(e -> e.getValue().expires < System.currentTimeMillis()), 1200, 1200);
}
@Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) { sender.sendMessage("Only players can use this command."); return true; }
if (command.getName().equalsIgnoreCase("handview")) {
if (args.length != 1) return false;
Shared shared = shares.get(args[0]);
if (shared == null || shared.expires < System.currentTimeMillis()) { player.sendMessage(ChatColor.RED + "That item share has expired."); return true; }
Inventory view = Bukkit.createInventory(null, 9, TITLE);
view.setItem(4, shared.item.clone());
player.openInventory(view);
return true;
}
ItemStack item = player.getInventory().getItemInMainHand();
if (item.getType() == Material.AIR) { player.sendMessage(ChatColor.RED + "Hold an item first."); return true; }
String token = UUID.randomUUID().toString().substring(0, 8);
shares.put(token, new Shared(item.clone(), System.currentTimeMillis() + 300_000));
String name = item.hasItemMeta() && item.getItemMeta().hasDisplayName()
? item.getItemMeta().getDisplayName() : item.getType().name().toLowerCase().replace('_', ' ');
TextComponent link = new TextComponent("[" + name + " ×" + item.getAmount() + "]");
link.setColor(net.md_5.bungee.api.ChatColor.AQUA);
link.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/handview " + token));
link.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("Click to inspect " + name + " (5 minutes)").create()));
TextComponent line = new TextComponent(player.getDisplayName() + ChatColor.WHITE + " is holding ");
line.addExtra(link);
for (Player target : Bukkit.getOnlinePlayers()) {
target.spigot().sendMessage(line);
}
return true;
}
@EventHandler public void onInventoryClick(InventoryClickEvent event) {
if (TITLE.equals(event.getView().getTitle())) event.setCancelled(true);
}
@EventHandler public void onInventoryDrag(InventoryDragEvent event) {
if (TITLE.equals(event.getView().getTitle())) event.setCancelled(true);
}
}
@@ -0,0 +1,63 @@
package net.scopenet.paper.commands;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.Plugin;
import java.io.File;
import java.io.IOException;
/** Player-owned chat nicknames, keyed by UUID and restored on every join. */
public final class NicknameHandler implements CommandExecutor, Listener {
private final Plugin plugin;
private final File file;
private final YamlConfiguration config;
public NicknameHandler(Plugin plugin) {
this.plugin = plugin;
this.file = new File(plugin.getDataFolder(), "nicknames.yml");
this.config = YamlConfiguration.loadConfiguration(file);
}
@EventHandler public void join(PlayerJoinEvent event) {
Player player = event.getPlayer();
String saved = config.getString(player.getUniqueId().toString());
if (saved != null && !saved.isBlank()) player.setDisplayName(ChatColor.translateAlternateColorCodes('&', saved));
}
@Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) { sender.sendMessage("Only players can set a nickname."); return true; }
if (args.length != 1) { player.sendMessage(ChatColor.YELLOW + "Usage: /nick <name|off>"); return true; }
String value = args[0];
if (value.equalsIgnoreCase("off")) {
config.set(player.getUniqueId().toString(), null);
player.setDisplayName(player.getName());
save();
player.sendMessage(ChatColor.GREEN + "Nickname removed.");
return true;
}
String plain = ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', value));
if (plain.length() < 2 || plain.length() > 24 || !plain.matches("[A-Za-z0-9_ -]+")) {
player.sendMessage(ChatColor.RED + "Nicknames must be 2–24 letters, numbers, spaces, _ or -.");
return true;
}
if (!player.hasPermission("scopenet.chat.color")) value = plain;
config.set(player.getUniqueId().toString(), value);
player.setDisplayName(ChatColor.translateAlternateColorCodes('&', value));
save();
player.sendMessage(ChatColor.GREEN + "Nickname set to " + player.getDisplayName() + ChatColor.GREEN + ".");
return true;
}
private void save() {
try { config.save(file); }
catch (IOException e) { plugin.getLogger().warning("Failed to save nicknames: " + e.getMessage()); }
}
}
@@ -102,6 +102,7 @@ public final class LuckPermsModule implements IntegrationModule {
JsonObject config = answer.has("config") && answer.get("config").isJsonObject() ? answer.getAsJsonObject("config") : new JsonObject();
if (config.has("mode")) lastMode = config.get("mode").getAsString();
if (context.getBoolean("luckperms", "apply-panel-groups", false) && config.has("assign")) apply(config.getAsJsonArray("assign"));
if (config.has("level_assign")) apply(config.getAsJsonArray("level_assign"));
} catch (Exception e) {
context.log().fine("LuckPerms report failed: " + e.getMessage());
}
@@ -69,7 +69,7 @@ public final class ScopenetApiImpl implements ScopenetApi, AutoCloseable {
guild == null ? null : new GuildSummary(str(guild, "id"), str(guild, "name"), str(guild, "tag"), str(guild, "role"), (int) num(guild, "claims")),
dbl(j, "balance"), num(j, "playtime_secs"), num(j, "server_playtime_secs"), (int) num(j, "kills"), (int) num(j, "deaths"),
(int) num(j, "friends"), (int) num(j, "achievements"), quests(j, "daily"), quests(j, "weekly"),
str(j, "joined"), str(rank, "group"), str(rank, "prefix"));
str(j, "joined"), str(rank, "group"), str(rank, "display"), str(rank, "prefix"), str(rank, "suffix"));
}
static ScopenetGuild guild(JsonObject j) {
@@ -52,9 +52,8 @@ essentials:
rtp-radius: 2500
livemap:
# Send world chunks and live player positions to the panel's central Live Map.
# Players open it from the launcher; you don't run Vantage on this server.
# It also needs "Live Map" switched on for this server in the panel.
# Send live player positions to the panel for the launcher BlueMap claim view.
# Configure BlueMap's public address and enable live positions in the panel.
enabled: true
economy:
@@ -50,10 +50,37 @@ commands:
permission: scopenet.command.warp
description: Teleport to a server warp or open the warps GUI
aliases: [warps]
reqwarp:
permission: scopenet.command.reqwarp
description: Request a new public warp at your location
warprequests:
permission: scopenet.command.warp.manage
description: List pending warp requests
warpapprove:
permission: scopenet.command.warp.manage
description: Approve a pending warp request
warpdeny:
permission: scopenet.command.warp.manage
description: Deny a pending warp request
setwarp:
permission: scopenet.command.warp.manage
description: Set a public warp at your location
delwarp:
permission: scopenet.command.warp.manage
description: Delete a public warp
playtime:
permission: scopenet.command.playtime
description: Check total and session playtime
aliases: [ontime]
hand:
permission: scopenet.command.hand
description: Share your held item in chat
handview:
permission: scopenet.command.hand
description: Inspect a shared item
nick:
permission: scopenet.command.nick
description: Set or remove your chat nickname
balance:
permission: scopenet.command.balance
description: Check your server economy balance
@@ -94,6 +121,9 @@ commands:
unclaim:
permission: scopenet.command.unclaim
description: Unclaim the current chunk
autoclaim:
permission: scopenet.command.claim
description: Claim chunks as you walk into them
permissions:
@@ -137,9 +167,21 @@ permissions:
scopenet.command.warp:
description: Use /warp
default: true
scopenet.command.reqwarp:
description: Request a public warp
default: true
scopenet.command.warp.manage:
description: Review and manage public warps
default: op
scopenet.command.playtime:
description: Use /playtime
default: true
scopenet.command.hand:
description: Share and inspect held items in chat
default: true
scopenet.command.nick:
description: Set or remove your chat nickname
default: true
scopenet.command.balance:
description: Use /balance
default: true
@@ -176,6 +218,9 @@ permissions:
scopenet.command.unclaim:
description: Use /unclaim
default: true
scopenet.chat.color:
description: Use Minecraft color codes in chat
default: op
scopenet.command.scopenet.help:
description: Use /scopenet help
default: true