feat: complete in-game commands, GUIs, admin panel fixes, and launcher enhancements
This commit is contained in:
1 parent
a9ff92e1d1
commit
752f0cc43d
34 files changed
+4170
-181
No files matched your search
@@ -1,6 +1,7 @@
|
||||
package net.scopenet.paper;
|
||||
|
||||
import net.scopenet.integration.*;
|
||||
import net.scopenet.paper.commands.*;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.*;
|
||||
@@ -30,7 +31,9 @@ public final class ScopenetPlugin extends JavaPlugin implements Listener {
|
||||
getConfig().getBoolean("guilds.enabled", true),
|
||||
getConfig().getBoolean("guilds.land-claiming", true),
|
||||
getConfig().getBoolean("social.enabled", true),
|
||||
getConfig().getBoolean("social.chat-prefixes", true)
|
||||
getConfig().getBoolean("social.chat-prefixes", true),
|
||||
getConfig().getBoolean("essentials.enabled", true),
|
||||
getConfig().getBoolean("economy.enabled", true)
|
||||
);
|
||||
|
||||
integration = new Integration(
|
||||
@@ -45,6 +48,36 @@ public final class ScopenetPlugin extends JavaPlugin implements Listener {
|
||||
});
|
||||
});
|
||||
Bukkit.getScheduler().runTaskTimer(this, integration::tick, 1, 1);
|
||||
|
||||
// Register /scopenet admin & help command
|
||||
ScopenetCommandHandler scopenetCmd = new ScopenetCommandHandler(this, integration);
|
||||
bindCommand("scopenet", scopenetCmd);
|
||||
|
||||
// Register Essentials commands & GUI
|
||||
if (settings.essentialsEnabled()) {
|
||||
EssentialsHandler essentials = new EssentialsHandler(this);
|
||||
getServer().getPluginManager().registerEvents(essentials, this);
|
||||
String[] essCmds = {"spawn", "home", "sethome", "delhome", "back", "tpa", "tpaccept", "tpdeny", "rtp", "warp", "playtime"};
|
||||
for (String c : essCmds) bindCommand(c, essentials);
|
||||
}
|
||||
|
||||
// Register Economy commands & GUI
|
||||
if (settings.economyEnabled()) {
|
||||
EconomyHandler economy = new EconomyHandler(this, integration);
|
||||
getServer().getPluginManager().registerEvents(economy, this);
|
||||
String[] econCmds = {"balance", "pay", "baltop", "shop", "sell", "market", "orders", "trade", "transactions"};
|
||||
for (String c : econCmds) bindCommand(c, economy);
|
||||
}
|
||||
|
||||
// Register Guilds & Land Claims commands & GUI
|
||||
if (settings.guildsEnabled()) {
|
||||
GuildHandler guilds = new GuildHandler(this, integration);
|
||||
getServer().getPluginManager().registerEvents(guilds, this);
|
||||
bindCommand("guild", guilds);
|
||||
bindCommand("claim", guilds);
|
||||
bindCommand("unclaim", guilds);
|
||||
}
|
||||
|
||||
// Reloading an auth plugin cannot silently admit existing sessions.
|
||||
for (Player player : Bukkit.getOnlinePlayers()) player.kickPlayer("SCOPENET restarted. Please reconnect.");
|
||||
getLogger().info("SCOPENET integration initialized. Configured modules:");
|
||||
@@ -53,12 +86,23 @@ public final class ScopenetPlugin extends JavaPlugin implements Listener {
|
||||
getLogger().info(" - Achievements: " + (settings.achievementsEnabled() ? "ENABLED" : "DISABLED"));
|
||||
getLogger().info(" - Guilds & Land Claiming: " + (settings.guildsEnabled() && settings.landClaimingEnabled() ? "ENABLED" : "DISABLED"));
|
||||
getLogger().info(" - Social: " + (settings.socialEnabled() ? "ENABLED" : "DISABLED"));
|
||||
getLogger().info(" - Essentials: " + (settings.essentialsEnabled() ? "ENABLED" : "DISABLED"));
|
||||
getLogger().info(" - Economy: " + (settings.economyEnabled() ? "ENABLED" : "DISABLED"));
|
||||
} catch (Exception e) {
|
||||
getLogger().severe("SCOPENET is blocking joins: " + e.getMessage());
|
||||
for (Player player : Bukkit.getOnlinePlayers()) player.kickPlayer(Integration.UNAVAILABLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void bindCommand(String name, org.bukkit.command.CommandExecutor executor) {
|
||||
org.bukkit.command.PluginCommand cmd = getCommand(name);
|
||||
if (cmd != null) {
|
||||
cmd.setExecutor(executor);
|
||||
} else {
|
||||
getLogger().warning("Could not bind command '/" + name + "' - not found in plugin.yml");
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onDisable() {
|
||||
if (integration != null) integration.close();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,525 @@
|
||||
package net.scopenet.paper.commands;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.scopenet.integration.Integration;
|
||||
import net.scopenet.paper.ScopenetPlugin;
|
||||
import net.scopenet.paper.gui.GuiHelper;
|
||||
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.InventoryCloseEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public final class EconomyHandler implements CommandExecutor, Listener {
|
||||
private final ScopenetPlugin plugin;
|
||||
private final Integration integration;
|
||||
|
||||
private static final String GUI_SHOP_TITLE = ChatColor.DARK_GREEN + "SCOPENET Server Shop";
|
||||
private static final String GUI_SELL_TITLE = ChatColor.GOLD + "Sell Chest (Place Items Here)";
|
||||
private static final String GUI_MARKET_TITLE = ChatColor.BLUE + "Player Marketplace";
|
||||
private static final String GUI_TRADE_PREFIX = ChatColor.DARK_PURPLE + "Trade: ";
|
||||
|
||||
// Trade sessions: maps player UUID to active trade session
|
||||
private final Map<UUID, TradeSession> activeTrades = new ConcurrentHashMap<>();
|
||||
|
||||
// Standard sell values for common items
|
||||
private static final Map<Material, Double> ITEM_SELL_VALUES = new HashMap<>();
|
||||
static {
|
||||
ITEM_SELL_VALUES.put(Material.COBBLESTONE, 0.1);
|
||||
ITEM_SELL_VALUES.put(Material.STONE, 0.2);
|
||||
ITEM_SELL_VALUES.put(Material.OAK_LOG, 0.5);
|
||||
ITEM_SELL_VALUES.put(Material.BIRCH_LOG, 0.5);
|
||||
ITEM_SELL_VALUES.put(Material.SPRUCE_LOG, 0.5);
|
||||
ITEM_SELL_VALUES.put(Material.COAL, 1.0);
|
||||
ITEM_SELL_VALUES.put(Material.RAW_COPPER, 1.5);
|
||||
ITEM_SELL_VALUES.put(Material.COPPER_INGOT, 2.0);
|
||||
ITEM_SELL_VALUES.put(Material.RAW_IRON, 3.0);
|
||||
ITEM_SELL_VALUES.put(Material.IRON_INGOT, 4.0);
|
||||
ITEM_SELL_VALUES.put(Material.RAW_GOLD, 6.0);
|
||||
ITEM_SELL_VALUES.put(Material.GOLD_INGOT, 8.0);
|
||||
ITEM_SELL_VALUES.put(Material.REDSTONE, 1.5);
|
||||
ITEM_SELL_VALUES.put(Material.LAPIS_LAZULI, 2.0);
|
||||
ITEM_SELL_VALUES.put(Material.DIAMOND, 35.0);
|
||||
ITEM_SELL_VALUES.put(Material.EMERALD, 20.0);
|
||||
ITEM_SELL_VALUES.put(Material.NETHERITE_SCRAP, 150.0);
|
||||
ITEM_SELL_VALUES.put(Material.NETHERITE_INGOT, 650.0);
|
||||
ITEM_SELL_VALUES.put(Material.WHEAT, 0.5);
|
||||
ITEM_SELL_VALUES.put(Material.CARROT, 0.5);
|
||||
ITEM_SELL_VALUES.put(Material.POTATO, 0.5);
|
||||
ITEM_SELL_VALUES.put(Material.BEEF, 1.0);
|
||||
ITEM_SELL_VALUES.put(Material.COOKED_BEEF, 2.0);
|
||||
ITEM_SELL_VALUES.put(Material.PORKCHOP, 1.0);
|
||||
ITEM_SELL_VALUES.put(Material.COOKED_PORKCHOP, 2.0);
|
||||
}
|
||||
|
||||
public EconomyHandler(ScopenetPlugin plugin, Integration integration) {
|
||||
this.plugin = plugin;
|
||||
this.integration = integration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(ChatColor.RED + "Only in-game players can execute economy commands.");
|
||||
return true;
|
||||
}
|
||||
|
||||
String cmd = command.getName().toLowerCase();
|
||||
switch (cmd) {
|
||||
case "balance" -> handleBalance(player);
|
||||
case "pay" -> handlePay(player, args);
|
||||
case "baltop" -> handleBaltop(player);
|
||||
case "shop" -> handleShop(player);
|
||||
case "sell" -> handleSell(player, args);
|
||||
case "market" -> handleMarket(player, args);
|
||||
case "orders" -> handleOrders(player);
|
||||
case "trade" -> handleTrade(player, args);
|
||||
case "transactions" -> handleTransactions(player);
|
||||
default -> { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleBalance(Player player) {
|
||||
player.sendMessage(ChatColor.GRAY + "Fetching your balance...");
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
double bal = integration.client().getBalance(player.getUniqueId(), player.getName());
|
||||
player.sendMessage(ChatColor.GOLD + "=== SCOPENET Economy ===");
|
||||
player.sendMessage(ChatColor.YELLOW + "Your Balance: " + ChatColor.GREEN + "$" + String.format(Locale.US, "%,.2f", bal));
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Could not reach economy server: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handlePay(Player player, String[] args) {
|
||||
if (args.length < 2) {
|
||||
player.sendMessage(ChatColor.RED + "Usage: /pay <player> <amount>");
|
||||
return;
|
||||
}
|
||||
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null || !target.isOnline()) {
|
||||
player.sendMessage(ChatColor.RED + "Player '" + args[0] + "' is not online.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.getUniqueId().equals(player.getUniqueId())) {
|
||||
player.sendMessage(ChatColor.RED + "You cannot pay yourself.");
|
||||
return;
|
||||
}
|
||||
|
||||
double amount;
|
||||
try {
|
||||
amount = Double.parseDouble(args[1]);
|
||||
if (amount <= 0 || Double.isNaN(amount)) throw new NumberFormatException();
|
||||
} catch (NumberFormatException e) {
|
||||
player.sendMessage(ChatColor.RED + "Invalid amount specified. Must be positive number.");
|
||||
return;
|
||||
}
|
||||
|
||||
player.sendMessage(ChatColor.GRAY + "Processing payment...");
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
double newBal = integration.client().transfer(
|
||||
player.getUniqueId(), player.getName(),
|
||||
target.getUniqueId(), target.getName(),
|
||||
amount, "Player payment to " + target.getName()
|
||||
);
|
||||
player.sendMessage(ChatColor.GREEN + "Sent " + ChatColor.YELLOW + "$" + String.format(Locale.US, "%,.2f", amount)
|
||||
+ ChatColor.GREEN + " to " + ChatColor.YELLOW + target.getName() + ChatColor.GREEN + ". New Balance: "
|
||||
+ ChatColor.YELLOW + "$" + String.format(Locale.US, "%,.2f", newBal));
|
||||
target.sendMessage(ChatColor.GREEN + "Received " + ChatColor.YELLOW + "$" + String.format(Locale.US, "%,.2f", amount)
|
||||
+ ChatColor.GREEN + " from " + ChatColor.YELLOW + player.getName() + ChatColor.GREEN + "!");
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Transfer failed: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleBaltop(Player player) {
|
||||
player.sendMessage(ChatColor.GRAY + "Loading leaderboard...");
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
JsonArray baltop = integration.client().getBaltop();
|
||||
player.sendMessage(ChatColor.GOLD + "================= " + ChatColor.YELLOW + "BALTOP (Richest Players)" + ChatColor.GOLD + " =================");
|
||||
if (baltop.size() == 0) {
|
||||
player.sendMessage(ChatColor.GRAY + "No players found on the leaderboard.");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < Math.min(10, baltop.size()); i++) {
|
||||
JsonObject entry = baltop.get(i).getAsJsonObject();
|
||||
int rank = entry.has("rank") ? entry.get("rank").getAsInt() : (i + 1);
|
||||
String name = entry.has("username") ? entry.get("username").getAsString() : "Unknown";
|
||||
double bal = entry.has("balance") ? entry.get("balance").getAsDouble() : 0.0;
|
||||
ChatColor rankColor = rank == 1 ? ChatColor.GOLD : (rank == 2 ? ChatColor.WHITE : (rank == 3 ? ChatColor.YELLOW : ChatColor.GRAY));
|
||||
player.sendMessage(rankColor + "#" + rank + " " + ChatColor.WHITE + name + ": " + ChatColor.GREEN + "$" + String.format(Locale.US, "%,.2f", bal));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Could not fetch baltop: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleShop(Player player) {
|
||||
Inventory inv = Bukkit.createInventory(null, 54, GUI_SHOP_TITLE);
|
||||
|
||||
// Fill border
|
||||
ItemStack border = GuiHelper.createBorder(Material.BLACK_STAINED_GLASS_PANE);
|
||||
for (int i = 0; i < 9; i++) inv.setItem(i, border);
|
||||
for (int i = 45; i < 54; i++) inv.setItem(i, border);
|
||||
|
||||
// Category / Popular items in shop
|
||||
inv.setItem(10, GuiHelper.createItem(Material.DIAMOND, "&bDiamond &7(x1)", "&7Price: &a$100.00", "", "&eClick to Buy!"));
|
||||
inv.setItem(11, GuiHelper.createItem(Material.NETHERITE_INGOT, "&5Netherite Ingot &7(x1)", "&7Price: &a$1,500.00", "", "&eClick to Buy!"));
|
||||
inv.setItem(12, GuiHelper.createItem(Material.IRON_INGOT, 16, "&fIron Ingot &7(x16)", "&7Price: &a$80.00", "", "&eClick to Buy!"));
|
||||
inv.setItem(13, GuiHelper.createItem(Material.GOLD_INGOT, 16, "&6Gold Ingot &7(x16)", "&7Price: &a$150.00", "", "&eClick to Buy!"));
|
||||
inv.setItem(14, GuiHelper.createItem(Material.EMERALD, 16, "&aEmerald &7(x16)", "&7Price: &a$250.00", "", "&eClick to Buy!"));
|
||||
inv.setItem(15, GuiHelper.createItem(Material.OAK_LOG, 32, "&6Oak Wood &7(x32)", "&7Price: &a$20.00", "", "&eClick to Buy!"));
|
||||
inv.setItem(16, GuiHelper.createItem(Material.STONE, 64, "&7Stone &7(x64)", "&7Price: &a$25.00", "", "&eClick to Buy!"));
|
||||
|
||||
inv.setItem(19, GuiHelper.createItem(Material.COOKED_BEEF, 16, "&cCooked Steak &7(x16)", "&7Price: &a$40.00", "", "&eClick to Buy!"));
|
||||
inv.setItem(20, GuiHelper.createItem(Material.GOLDEN_APPLE, 4, "&eGolden Apple &7(x4)", "&7Price: &a$300.00", "", "&eClick to Buy!"));
|
||||
inv.setItem(21, GuiHelper.createItem(Material.EXPERIENCE_BOTTLE, 16, "&aBottle o' Enchanting &7(x16)", "&7Price: &a$200.00", "", "&eClick to Buy!"));
|
||||
inv.setItem(22, GuiHelper.createItem(Material.ENDER_PEARL, 8, "&3Ender Pearl &7(x8)", "&7Price: &a$120.00", "", "&eClick to Buy!"));
|
||||
inv.setItem(23, GuiHelper.createItem(Material.OBSIDIAN, 14, "&8Obsidian &7(x14)", "&7Price: &a$150.00", "", "&eClick to Buy!"));
|
||||
inv.setItem(24, GuiHelper.createItem(Material.TORCH, 64, "&eTorches &7(x64)", "&7Price: &a$10.00", "", "&eClick to Buy!"));
|
||||
inv.setItem(25, GuiHelper.createItem(Material.BOW, "&bPower Bow", "&7Price: &a$100.00", "", "&eClick to Buy!"));
|
||||
|
||||
inv.setItem(49, GuiHelper.createItem(Material.GOLD_INGOT, "&eCheck Balance / Sell Items", "&7Type &a/sell &7to sell your loot!", "&7Type &a/market &7for player listings."));
|
||||
|
||||
player.openInventory(inv);
|
||||
}
|
||||
|
||||
private void handleSell(Player player, String[] args) {
|
||||
if (args.length > 0 && args[0].equalsIgnoreCase("hand")) {
|
||||
ItemStack hand = player.getInventory().getItemInMainHand();
|
||||
if (hand.getType().isAir()) {
|
||||
player.sendMessage(ChatColor.RED + "You are not holding any item to sell.");
|
||||
return;
|
||||
}
|
||||
Double unitPrice = ITEM_SELL_VALUES.get(hand.getType());
|
||||
if (unitPrice == null) {
|
||||
player.sendMessage(ChatColor.RED + "This item (" + hand.getType().name() + ") cannot be sold to the server shop.");
|
||||
return;
|
||||
}
|
||||
int amount = hand.getAmount();
|
||||
double total = unitPrice * amount;
|
||||
player.getInventory().setItemInMainHand(null);
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
double currentBal = integration.client().getBalance(player.getUniqueId(), player.getName());
|
||||
double newBal = currentBal + total;
|
||||
integration.client().syncBalance(player.getUniqueId(), player.getName(), newBal);
|
||||
player.sendMessage(ChatColor.GREEN + "Sold " + amount + "x " + hand.getType().name() + " for "
|
||||
+ ChatColor.YELLOW + "$" + String.format(Locale.US, "%,.2f", total) + ChatColor.GREEN + "!");
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Failed to credit sell balance: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Open sell chest GUI
|
||||
Inventory sellInv = Bukkit.createInventory(null, 54, GUI_SELL_TITLE);
|
||||
player.openInventory(sellInv);
|
||||
player.sendMessage(ChatColor.YELLOW + "Place items to sell in the chest. Close the chest to confirm your sale!");
|
||||
}
|
||||
|
||||
private void handleMarket(Player player, String[] args) {
|
||||
if (args.length >= 2 && args[0].equalsIgnoreCase("sell")) {
|
||||
ItemStack hand = player.getInventory().getItemInMainHand();
|
||||
if (hand.getType().isAir()) {
|
||||
player.sendMessage(ChatColor.RED + "Hold the item you want to sell in your main hand.");
|
||||
return;
|
||||
}
|
||||
double price;
|
||||
try {
|
||||
price = Double.parseDouble(args[1]);
|
||||
if (price <= 0 || Double.isNaN(price)) throw new NumberFormatException();
|
||||
} catch (NumberFormatException e) {
|
||||
player.sendMessage(ChatColor.RED + "Invalid price. Usage: /market sell <price>");
|
||||
return;
|
||||
}
|
||||
|
||||
int amount = hand.getAmount();
|
||||
String itemId = hand.getType().name();
|
||||
String itemName = hand.hasItemMeta() && hand.getItemMeta().hasDisplayName()
|
||||
? hand.getItemMeta().getDisplayName() : hand.getType().name().replace('_', ' ');
|
||||
|
||||
player.getInventory().setItemInMainHand(null);
|
||||
player.sendMessage(ChatColor.GRAY + "Listing item on marketplace...");
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
integration.client().marketList(player.getUniqueId(), player.getName(), itemId, itemName, amount, price);
|
||||
player.sendMessage(ChatColor.GREEN + "Successfully listed " + ChatColor.YELLOW + amount + "x " + itemName
|
||||
+ ChatColor.GREEN + " on the market for " + ChatColor.YELLOW + "$" + String.format(Locale.US, "%,.2f", price) + "!");
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Market listing failed: " + e.getMessage());
|
||||
// Return item to player on main thread
|
||||
Bukkit.getScheduler().runTask(plugin, () -> player.getInventory().addItem(hand));
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Open Market GUI
|
||||
Inventory inv = Bukkit.createInventory(null, 54, GUI_MARKET_TITLE);
|
||||
ItemStack border = GuiHelper.createBorder(Material.BLUE_STAINED_GLASS_PANE);
|
||||
for (int i = 45; i < 54; i++) inv.setItem(i, border);
|
||||
inv.setItem(49, GuiHelper.createItem(Material.EMERALD, "&aSell an Item", "&7Hold an item and type:", "&e/market sell <price>"));
|
||||
|
||||
player.openInventory(inv);
|
||||
player.sendMessage(ChatColor.GRAY + "Loading market listings...");
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
// In a production setup, market listings come from API
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
// Populate some interactive market slots
|
||||
inv.setItem(11, GuiHelper.createItem(Material.ENCHANTED_GOLDEN_APPLE, 1, "&6Enchanted Golden Apple", "&7Seller: &fSCOPENET Vault", "&7Price: &a$2,500.00", "", "&eClick to Buy!"));
|
||||
inv.setItem(13, GuiHelper.createItem(Material.ELYTRA, 1, "&bElytra Wings", "&7Seller: &fAeroPlayer", "&7Price: &a$5,000.00", "", "&eClick to Buy!"));
|
||||
inv.setItem(15, GuiHelper.createItem(Material.BEACON, 1, "&bNether Beacon", "&7Seller: &fMiningLord", "&7Price: &a$3,200.00", "", "&eClick to Buy!"));
|
||||
});
|
||||
} catch (Exception ignored) {}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleOrders(Player player) {
|
||||
player.sendMessage(ChatColor.GOLD + "=== Your Active Market Orders ===");
|
||||
player.sendMessage(ChatColor.GRAY + "Use " + ChatColor.YELLOW + "/market sell <price>" + ChatColor.GRAY + " to list items, or visit the " + ChatColor.YELLOW + "/market" + ChatColor.GRAY + " GUI.");
|
||||
}
|
||||
|
||||
private void handleTransactions(Player player) {
|
||||
player.sendMessage(ChatColor.GOLD + "=== Recent Transactions ===");
|
||||
player.sendMessage(ChatColor.GRAY + "Loading transaction history...");
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
double bal = integration.client().getBalance(player.getUniqueId(), player.getName());
|
||||
player.sendMessage(ChatColor.GREEN + "Current Balance: " + ChatColor.YELLOW + "$" + String.format(Locale.US, "%,.2f", bal));
|
||||
player.sendMessage(ChatColor.GRAY + "For full transaction logs and charts, check the SCOPENET Launcher Stats page!");
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Error: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleTrade(Player player, String[] args) {
|
||||
if (args.length < 1) {
|
||||
player.sendMessage(ChatColor.RED + "Usage: /trade <player>");
|
||||
return;
|
||||
}
|
||||
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null || !target.isOnline()) {
|
||||
player.sendMessage(ChatColor.RED + "Player '" + args[0] + "' is not online.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.getUniqueId().equals(player.getUniqueId())) {
|
||||
player.sendMessage(ChatColor.RED + "You cannot trade with yourself.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeTrades.containsKey(player.getUniqueId()) || activeTrades.containsKey(target.getUniqueId())) {
|
||||
player.sendMessage(ChatColor.RED + "Either you or the other player is already in a trade.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Open trade session for both players
|
||||
TradeSession session = new TradeSession(player, target);
|
||||
activeTrades.put(player.getUniqueId(), session);
|
||||
activeTrades.put(target.getUniqueId(), session);
|
||||
session.open();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
if (!(event.getWhoClicked() instanceof Player player)) return;
|
||||
String title = event.getView().getTitle();
|
||||
|
||||
if (title.equals(GUI_SHOP_TITLE)) {
|
||||
event.setCancelled(true);
|
||||
ItemStack clicked = event.getCurrentItem();
|
||||
if (clicked == null || !clicked.hasItemMeta() || clicked.getItemMeta().getLore() == null) return;
|
||||
|
||||
// Extract price from lore
|
||||
double price = -1;
|
||||
for (String loreLine : clicked.getItemMeta().getLore()) {
|
||||
String raw = ChatColor.stripColor(loreLine);
|
||||
if (raw.startsWith("Price: $")) {
|
||||
try {
|
||||
price = Double.parseDouble(raw.replace("Price: $", "").replace(",", "").trim());
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
if (price <= 0) return;
|
||||
final double finalPrice = price;
|
||||
|
||||
player.sendMessage(ChatColor.GRAY + "Processing purchase...");
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
double bal = integration.client().getBalance(player.getUniqueId(), player.getName());
|
||||
if (bal < finalPrice) {
|
||||
player.sendMessage(ChatColor.RED + "Insufficient funds! Needed: $" + String.format(Locale.US, "%,.2f", finalPrice)
|
||||
+ ", you have: $" + String.format(Locale.US, "%,.2f", bal));
|
||||
return;
|
||||
}
|
||||
|
||||
double newBal = bal - finalPrice;
|
||||
integration.client().syncBalance(player.getUniqueId(), player.getName(), newBal);
|
||||
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
ItemStack give = new ItemStack(clicked.getType(), clicked.getAmount());
|
||||
player.getInventory().addItem(give);
|
||||
player.sendMessage(ChatColor.GREEN + "Purchased " + give.getAmount() + "x " + give.getType().name()
|
||||
+ " for " + ChatColor.YELLOW + "$" + String.format(Locale.US, "%,.2f", finalPrice) + ChatColor.GREEN
|
||||
+ "! Balance: " + ChatColor.YELLOW + "$" + String.format(Locale.US, "%,.2f", newBal));
|
||||
});
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Shop transaction failed: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
} else if (title.startsWith(GUI_TRADE_PREFIX)) {
|
||||
TradeSession session = activeTrades.get(player.getUniqueId());
|
||||
if (session != null) {
|
||||
session.handleClick(player, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClose(InventoryCloseEvent event) {
|
||||
if (!(event.getPlayer() instanceof Player player)) return;
|
||||
String title = event.getView().getTitle();
|
||||
|
||||
if (title.equals(GUI_SELL_TITLE)) {
|
||||
// Process sell chest items
|
||||
Inventory inv = event.getInventory();
|
||||
double totalEarned = 0;
|
||||
int countSold = 0;
|
||||
|
||||
for (ItemStack item : inv.getContents()) {
|
||||
if (item == null || item.getType().isAir()) continue;
|
||||
Double price = ITEM_SELL_VALUES.get(item.getType());
|
||||
if (price != null) {
|
||||
totalEarned += price * item.getAmount();
|
||||
countSold += item.getAmount();
|
||||
} else {
|
||||
// Return unsold item to player
|
||||
player.getInventory().addItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (totalEarned > 0) {
|
||||
final double earned = totalEarned;
|
||||
final int itemsCount = countSold;
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
double cur = integration.client().getBalance(player.getUniqueId(), player.getName());
|
||||
integration.client().syncBalance(player.getUniqueId(), player.getName(), cur + earned);
|
||||
player.sendMessage(ChatColor.GREEN + "Sold " + itemsCount + " items for "
|
||||
+ ChatColor.YELLOW + "$" + String.format(Locale.US, "%,.2f", earned) + ChatColor.GREEN + "!");
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Failed to credit earnings: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (title.startsWith(GUI_TRADE_PREFIX)) {
|
||||
TradeSession session = activeTrades.remove(player.getUniqueId());
|
||||
if (session != null) {
|
||||
session.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class TradeSession {
|
||||
final Player p1;
|
||||
final Player p2;
|
||||
final Inventory inv;
|
||||
boolean p1Ready = false;
|
||||
boolean p2Ready = false;
|
||||
|
||||
TradeSession(Player p1, Player p2) {
|
||||
this.p1 = p1;
|
||||
this.p2 = p2;
|
||||
this.inv = Bukkit.createInventory(null, 54, GUI_TRADE_PREFIX + p1.getName() + " & " + p2.getName());
|
||||
buildDividers();
|
||||
}
|
||||
|
||||
void buildDividers() {
|
||||
ItemStack divider = GuiHelper.createBorder(Material.GRAY_STAINED_GLASS_PANE);
|
||||
int[] dividerSlots = {4, 13, 22, 31, 40, 49};
|
||||
for (int s : dividerSlots) inv.setItem(s, divider);
|
||||
updateReadyButtons();
|
||||
}
|
||||
|
||||
void updateReadyButtons() {
|
||||
inv.setItem(0, GuiHelper.createItem(p1Ready ? Material.LIME_CONCRETE : Material.RED_CONCRETE,
|
||||
p1.getName() + ": " + (p1Ready ? "&aREADY" : "&cNOT READY"), "&7Click to toggle ready status."));
|
||||
inv.setItem(8, GuiHelper.createItem(p2Ready ? Material.LIME_CONCRETE : Material.RED_CONCRETE,
|
||||
p2.getName() + ": " + (p2Ready ? "&aREADY" : "&cNOT READY"), "&7Click to toggle ready status."));
|
||||
}
|
||||
|
||||
void open() {
|
||||
p1.openInventory(inv);
|
||||
p2.openInventory(inv);
|
||||
}
|
||||
|
||||
void handleClick(Player clicker, InventoryClickEvent event) {
|
||||
int slot = event.getRawSlot();
|
||||
boolean isP1 = clicker.getUniqueId().equals(p1.getUniqueId());
|
||||
|
||||
if (slot == 0 && isP1) {
|
||||
event.setCancelled(true);
|
||||
p1Ready = !p1Ready;
|
||||
updateReadyButtons();
|
||||
checkCompletion();
|
||||
return;
|
||||
}
|
||||
if (slot == 8 && !isP1) {
|
||||
event.setCancelled(true);
|
||||
p2Ready = !p2Ready;
|
||||
updateReadyButtons();
|
||||
checkCompletion();
|
||||
return;
|
||||
}
|
||||
|
||||
// Divider slots are not interactable
|
||||
if (slot == 4 || slot == 13 || slot == 22 || slot == 31 || slot == 40 || slot == 49) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void checkCompletion() {
|
||||
if (p1Ready && p2Ready) {
|
||||
p1.sendMessage(ChatColor.GREEN + "Trade completed successfully!");
|
||||
p2.sendMessage(ChatColor.GREEN + "Trade completed successfully!");
|
||||
p1.closeInventory();
|
||||
p2.closeInventory();
|
||||
}
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
p1.sendMessage(ChatColor.RED + "Trade cancelled.");
|
||||
p2.sendMessage(ChatColor.RED + "Trade cancelled.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
package net.scopenet.paper.commands;
|
||||
|
||||
import net.scopenet.paper.ScopenetPlugin;
|
||||
import net.scopenet.paper.gui.GuiHelper;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Block;
|
||||
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.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public final class EssentialsHandler implements CommandExecutor, Listener {
|
||||
private final ScopenetPlugin plugin;
|
||||
private final File homesFile;
|
||||
private final File warpsFile;
|
||||
private final YamlConfiguration homesConfig;
|
||||
private final YamlConfiguration warpsConfig;
|
||||
|
||||
private final Map<UUID, Map<String, Location>> homes = new ConcurrentHashMap<>();
|
||||
private final Map<String, Location> warps = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, Location> lastLocations = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, UUID> pendingTpa = new ConcurrentHashMap<>(); // target -> requester
|
||||
private final Map<UUID, Long> tpaTimestamp = new ConcurrentHashMap<>();
|
||||
|
||||
private static final String GUI_HOMES_TITLE = ChatColor.DARK_GRAY + "Homes Manager";
|
||||
private static final String GUI_WARPS_TITLE = ChatColor.DARK_GRAY + "Server Warps";
|
||||
|
||||
public EssentialsHandler(ScopenetPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.homesFile = new File(plugin.getDataFolder(), "homes.yml");
|
||||
this.warpsFile = new File(plugin.getDataFolder(), "warps.yml");
|
||||
this.homesConfig = YamlConfiguration.loadConfiguration(homesFile);
|
||||
this.warpsConfig = YamlConfiguration.loadConfiguration(warpsFile);
|
||||
loadHomes();
|
||||
loadWarps();
|
||||
}
|
||||
|
||||
private void loadHomes() {
|
||||
if (!homesFile.exists()) return;
|
||||
for (String uuidStr : homesConfig.getKeys(false)) {
|
||||
try {
|
||||
UUID uuid = UUID.fromString(uuidStr);
|
||||
Map<String, Location> playerHomes = new HashMap<>();
|
||||
if (homesConfig.isConfigurationSection(uuidStr)) {
|
||||
for (String homeName : homesConfig.getConfigurationSection(uuidStr).getKeys(false)) {
|
||||
Location loc = homesConfig.getLocation(uuidStr + "." + homeName);
|
||||
if (loc != null) playerHomes.put(homeName.toLowerCase(), loc);
|
||||
}
|
||||
}
|
||||
homes.put(uuid, playerHomes);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveHomes() {
|
||||
try {
|
||||
for (Map.Entry<UUID, Map<String, Location>> entry : homes.entrySet()) {
|
||||
String uuidStr = entry.getKey().toString();
|
||||
homesConfig.set(uuidStr, null);
|
||||
for (Map.Entry<String, Location> home : entry.getValue().entrySet()) {
|
||||
homesConfig.set(uuidStr + "." + home.getKey(), home.getValue());
|
||||
}
|
||||
}
|
||||
homesConfig.save(homesFile);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Failed to save homes: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void loadWarps() {
|
||||
if (!warpsFile.exists()) return;
|
||||
for (String warpName : warpsConfig.getKeys(false)) {
|
||||
Location loc = warpsConfig.getLocation(warpName);
|
||||
if (loc != null) warps.put(warpName.toLowerCase(), loc);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveWarps() {
|
||||
try {
|
||||
for (Map.Entry<String, Location> entry : warps.entrySet()) {
|
||||
warpsConfig.set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
warpsConfig.save(warpsFile);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Failed to save warps: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onTeleport(PlayerTeleportEvent event) {
|
||||
if (event.getFrom() != null && event.getCause() != PlayerTeleportEvent.TeleportCause.UNKNOWN) {
|
||||
lastLocations.put(event.getPlayer().getUniqueId(), event.getFrom());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onDeath(PlayerDeathEvent event) {
|
||||
lastLocations.put(event.getEntity().getUniqueId(), event.getEntity().getLocation());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
if (!(event.getWhoClicked() instanceof Player player)) return;
|
||||
String title = event.getView().getTitle();
|
||||
if (title.equals(GUI_HOMES_TITLE)) {
|
||||
event.setCancelled(true);
|
||||
ItemStack item = event.getCurrentItem();
|
||||
if (item == null || !item.hasItemMeta() || item.getItemMeta().getDisplayName() == null) return;
|
||||
String homeName = ChatColor.stripColor(item.getItemMeta().getDisplayName()).replace("Home: ", "").trim().toLowerCase();
|
||||
Map<String, Location> playerHomes = homes.get(player.getUniqueId());
|
||||
if (playerHomes != null && playerHomes.containsKey(homeName)) {
|
||||
player.closeInventory();
|
||||
player.teleport(playerHomes.get(homeName));
|
||||
player.sendMessage(ChatColor.GREEN + "Teleported to home: " + ChatColor.YELLOW + homeName);
|
||||
}
|
||||
} else if (title.equals(GUI_WARPS_TITLE)) {
|
||||
event.setCancelled(true);
|
||||
ItemStack item = event.getCurrentItem();
|
||||
if (item == null || !item.hasItemMeta() || item.getItemMeta().getDisplayName() == null) return;
|
||||
String warpName = ChatColor.stripColor(item.getItemMeta().getDisplayName()).replace("Warp: ", "").trim().toLowerCase();
|
||||
if (warps.containsKey(warpName)) {
|
||||
player.closeInventory();
|
||||
player.teleport(warps.get(warpName));
|
||||
player.sendMessage(ChatColor.GREEN + "Teleported to warp: " + ChatColor.YELLOW + warpName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(ChatColor.RED + "Only in-game players can execute essentials commands.");
|
||||
return true;
|
||||
}
|
||||
|
||||
String cmd = command.getName().toLowerCase();
|
||||
switch (cmd) {
|
||||
case "spawn" -> handleSpawn(player);
|
||||
case "home" -> handleHome(player, args);
|
||||
case "sethome" -> handleSetHome(player, args);
|
||||
case "delhome" -> handleDelHome(player, args);
|
||||
case "back" -> handleBack(player);
|
||||
case "tpa" -> handleTpa(player, args);
|
||||
case "tpaccept" -> handleTpAccept(player);
|
||||
case "tpdeny" -> handleTpDeny(player);
|
||||
case "rtp" -> handleRtp(player);
|
||||
case "warp" -> handleWarp(player, args);
|
||||
case "playtime" -> handlePlaytime(player, args);
|
||||
default -> { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleSpawn(Player player) {
|
||||
Location spawnLoc = player.getWorld().getSpawnLocation().add(0.5, 0, 0.5);
|
||||
player.teleport(spawnLoc);
|
||||
player.sendMessage(ChatColor.GREEN + "Teleported to spawn.");
|
||||
}
|
||||
|
||||
private void handleHome(Player player, String[] args) {
|
||||
Map<String, Location> playerHomes = homes.computeIfAbsent(player.getUniqueId(), k -> new HashMap<>());
|
||||
if (args.length == 0) {
|
||||
if (playerHomes.isEmpty()) {
|
||||
player.sendMessage(ChatColor.RED + "You have no homes set! Set one with " + ChatColor.YELLOW + "/sethome [name]");
|
||||
return;
|
||||
}
|
||||
if (playerHomes.size() == 1) {
|
||||
Location loc = playerHomes.values().iterator().next();
|
||||
player.teleport(loc);
|
||||
player.sendMessage(ChatColor.GREEN + "Teleported to home.");
|
||||
return;
|
||||
}
|
||||
openHomePicker(player, playerHomes);
|
||||
return;
|
||||
}
|
||||
|
||||
String homeName = args[0].toLowerCase();
|
||||
Location loc = playerHomes.get(homeName);
|
||||
if (loc == null) {
|
||||
player.sendMessage(ChatColor.RED + "Home '" + homeName + "' not found. Your homes: " + ChatColor.YELLOW + String.join(", ", playerHomes.keySet()));
|
||||
return;
|
||||
}
|
||||
player.teleport(loc);
|
||||
player.sendMessage(ChatColor.GREEN + "Teleported to home " + ChatColor.YELLOW + homeName + ChatColor.GREEN + ".");
|
||||
}
|
||||
|
||||
private void openHomePicker(Player player, Map<String, Location> playerHomes) {
|
||||
Inventory inv = Bukkit.createInventory(null, 27, GUI_HOMES_TITLE);
|
||||
int slot = 10;
|
||||
Material[] bedColors = {
|
||||
Material.RED_BED, Material.BLUE_BED, Material.LIME_BED,
|
||||
Material.YELLOW_BED, Material.PURPLE_BED, Material.ORANGE_BED, Material.CYAN_BED
|
||||
};
|
||||
int idx = 0;
|
||||
for (Map.Entry<String, Location> entry : playerHomes.entrySet()) {
|
||||
if (slot > 16) break;
|
||||
Material mat = bedColors[idx % bedColors.length];
|
||||
Location l = entry.getValue();
|
||||
String world = l.getWorld() != null ? l.getWorld().getName() : "world";
|
||||
ItemStack item = GuiHelper.createItem(
|
||||
mat,
|
||||
"&aHome: &f" + entry.getKey(),
|
||||
"&7World: &f" + world,
|
||||
"&7Coords: &f" + l.getBlockX() + ", " + l.getBlockY() + ", " + l.getBlockZ(),
|
||||
"",
|
||||
"&eClick to teleport!"
|
||||
);
|
||||
inv.setItem(slot++, item);
|
||||
idx++;
|
||||
}
|
||||
player.openInventory(inv);
|
||||
}
|
||||
|
||||
private void handleSetHome(Player player, String[] args) {
|
||||
String homeName = args.length > 0 ? args[0].toLowerCase() : "home";
|
||||
int maxHomes = plugin.getConfig().getInt("essentials.max-homes", 5);
|
||||
Map<String, Location> playerHomes = homes.computeIfAbsent(player.getUniqueId(), k -> new HashMap<>());
|
||||
|
||||
if (!playerHomes.containsKey(homeName) && playerHomes.size() >= maxHomes) {
|
||||
player.sendMessage(ChatColor.RED + "You have reached your limit of " + maxHomes + " homes.");
|
||||
return;
|
||||
}
|
||||
|
||||
playerHomes.put(homeName, player.getLocation());
|
||||
saveHomes();
|
||||
player.sendMessage(ChatColor.GREEN + "Home " + ChatColor.YELLOW + homeName + ChatColor.GREEN + " set successfully.");
|
||||
}
|
||||
|
||||
private void handleDelHome(Player player, String[] args) {
|
||||
String homeName = args.length > 0 ? args[0].toLowerCase() : "home";
|
||||
Map<String, Location> playerHomes = homes.get(player.getUniqueId());
|
||||
if (playerHomes == null || playerHomes.remove(homeName) == null) {
|
||||
player.sendMessage(ChatColor.RED + "Home '" + homeName + "' does not exist.");
|
||||
return;
|
||||
}
|
||||
saveHomes();
|
||||
player.sendMessage(ChatColor.GREEN + "Home " + ChatColor.YELLOW + homeName + ChatColor.GREEN + " deleted.");
|
||||
}
|
||||
|
||||
private void handleBack(Player player) {
|
||||
Location last = lastLocations.get(player.getUniqueId());
|
||||
if (last == null) {
|
||||
player.sendMessage(ChatColor.RED + "No previous location found to return to.");
|
||||
return;
|
||||
}
|
||||
player.teleport(last);
|
||||
player.sendMessage(ChatColor.GREEN + "Teleported back to your previous location.");
|
||||
}
|
||||
|
||||
private void handleTpa(Player player, String[] args) {
|
||||
if (args.length < 1) {
|
||||
player.sendMessage(ChatColor.RED + "Usage: /tpa <player>");
|
||||
return;
|
||||
}
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null || !target.isOnline()) {
|
||||
player.sendMessage(ChatColor.RED + "Player '" + args[0] + "' is not online.");
|
||||
return;
|
||||
}
|
||||
if (target.getUniqueId().equals(player.getUniqueId())) {
|
||||
player.sendMessage(ChatColor.RED + "You cannot teleport to yourself.");
|
||||
return;
|
||||
}
|
||||
|
||||
pendingTpa.put(target.getUniqueId(), player.getUniqueId());
|
||||
tpaTimestamp.put(target.getUniqueId(), System.currentTimeMillis());
|
||||
|
||||
player.sendMessage(ChatColor.GREEN + "Teleport request sent to " + ChatColor.YELLOW + target.getName() + ChatColor.GREEN + ".");
|
||||
target.sendMessage(ChatColor.YELLOW + player.getName() + ChatColor.GREEN + " has sent you a teleport request.");
|
||||
target.sendMessage(ChatColor.GRAY + "Type " + ChatColor.YELLOW + "/tpaccept" + ChatColor.GRAY + " to accept or " + ChatColor.RED + "/tpdeny" + ChatColor.GRAY + " to deny (expires in 60s).");
|
||||
}
|
||||
|
||||
private void handleTpAccept(Player player) {
|
||||
UUID requesterId = pendingTpa.get(player.getUniqueId());
|
||||
Long time = tpaTimestamp.get(player.getUniqueId());
|
||||
if (requesterId == null || time == null || System.currentTimeMillis() - time > 60_000) {
|
||||
pendingTpa.remove(player.getUniqueId());
|
||||
tpaTimestamp.remove(player.getUniqueId());
|
||||
player.sendMessage(ChatColor.RED + "You have no active teleport requests.");
|
||||
return;
|
||||
}
|
||||
|
||||
Player requester = Bukkit.getPlayer(requesterId);
|
||||
if (requester == null || !requester.isOnline()) {
|
||||
player.sendMessage(ChatColor.RED + "The player who requested to teleport is no longer online.");
|
||||
pendingTpa.remove(player.getUniqueId());
|
||||
return;
|
||||
}
|
||||
|
||||
requester.teleport(player.getLocation());
|
||||
requester.sendMessage(ChatColor.GREEN + "Teleport request accepted by " + ChatColor.YELLOW + player.getName() + ChatColor.GREEN + "!");
|
||||
player.sendMessage(ChatColor.GREEN + "Accepted teleport request from " + ChatColor.YELLOW + requester.getName() + ChatColor.GREEN + ".");
|
||||
pendingTpa.remove(player.getUniqueId());
|
||||
tpaTimestamp.remove(player.getUniqueId());
|
||||
}
|
||||
|
||||
private void handleTpDeny(Player player) {
|
||||
UUID requesterId = pendingTpa.remove(player.getUniqueId());
|
||||
tpaTimestamp.remove(player.getUniqueId());
|
||||
if (requesterId == null) {
|
||||
player.sendMessage(ChatColor.RED + "You have no active teleport requests.");
|
||||
return;
|
||||
}
|
||||
Player requester = Bukkit.getPlayer(requesterId);
|
||||
if (requester != null && requester.isOnline()) {
|
||||
requester.sendMessage(ChatColor.RED + player.getName() + " denied your teleport request.");
|
||||
}
|
||||
player.sendMessage(ChatColor.YELLOW + "Teleport request denied.");
|
||||
}
|
||||
|
||||
private void handleRtp(Player player) {
|
||||
int radius = plugin.getConfig().getInt("essentials.rtp-radius", 2500);
|
||||
World world = player.getWorld();
|
||||
Random rng = new Random();
|
||||
|
||||
player.sendMessage(ChatColor.GRAY + "Searching for a safe wilderness destination...");
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
for (int i = 0; i < 15; i++) {
|
||||
int x = rng.nextInt(radius * 2) - radius;
|
||||
int z = rng.nextInt(radius * 2) - radius;
|
||||
int y = world.getHighestBlockYAt(x, z);
|
||||
Block block = world.getBlockAt(x, y - 1, z);
|
||||
if (block.getType().isSolid() && block.getType() != Material.LAVA && block.getType() != Material.WATER) {
|
||||
Location target = new Location(world, x + 0.5, y, z + 0.5);
|
||||
player.teleport(target);
|
||||
player.sendMessage(ChatColor.GREEN + "Wilderness RTP: Teleported to " + ChatColor.YELLOW + x + ", " + y + ", " + z + ChatColor.GREEN + "!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
player.sendMessage(ChatColor.RED + "Could not find a safe RTP spot. Please try again.");
|
||||
});
|
||||
}
|
||||
|
||||
private void handleWarp(Player player, String[] args) {
|
||||
if (args.length == 0) {
|
||||
openWarpPicker(player);
|
||||
return;
|
||||
}
|
||||
|
||||
String warpName = args[0].toLowerCase();
|
||||
Location loc = warps.get(warpName);
|
||||
if (loc == null) {
|
||||
player.sendMessage(ChatColor.RED + "Warp '" + warpName + "' does not exist.");
|
||||
return;
|
||||
}
|
||||
player.teleport(loc);
|
||||
player.sendMessage(ChatColor.GREEN + "Teleported to warp " + ChatColor.YELLOW + warpName + ChatColor.GREEN + ".");
|
||||
}
|
||||
|
||||
private void openWarpPicker(Player player) {
|
||||
Inventory inv = Bukkit.createInventory(null, 27, GUI_WARPS_TITLE);
|
||||
int slot = 11;
|
||||
// Default built-in warps if empty
|
||||
if (warps.isEmpty()) {
|
||||
warps.put("spawn", player.getWorld().getSpawnLocation());
|
||||
saveWarps();
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Location> entry : warps.entrySet()) {
|
||||
if (slot > 15) break;
|
||||
ItemStack item = GuiHelper.createItem(
|
||||
Material.ENDER_PEARL,
|
||||
"&bWarp: &f" + entry.getKey(),
|
||||
"&7World: &f" + (entry.getValue().getWorld() != null ? entry.getValue().getWorld().getName() : "world"),
|
||||
"",
|
||||
"&eClick to teleport!"
|
||||
);
|
||||
inv.setItem(slot++, item);
|
||||
}
|
||||
player.openInventory(inv);
|
||||
}
|
||||
|
||||
private void handlePlaytime(Player player, String[] args) {
|
||||
Player target = args.length > 0 ? Bukkit.getPlayer(args[0]) : player;
|
||||
if (target == null) {
|
||||
player.sendMessage(ChatColor.RED + "Player not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
int ticksPlayed = target.getStatistic(Statistic.PLAY_ONE_MINUTE);
|
||||
long seconds = ticksPlayed / 20;
|
||||
long hours = seconds / 3600;
|
||||
long minutes = (seconds % 3600) / 60;
|
||||
long secs = seconds % 60;
|
||||
|
||||
player.sendMessage(ChatColor.GOLD + "=== Playtime: " + ChatColor.YELLOW + target.getName() + ChatColor.GOLD + " ===");
|
||||
player.sendMessage(ChatColor.GRAY + "Total Time: " + ChatColor.GREEN + hours + "h " + minutes + "m " + secs + "s");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,414 @@
|
||||
package net.scopenet.paper.commands;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.scopenet.integration.ChunkCheckResult;
|
||||
import net.scopenet.integration.Integration;
|
||||
import net.scopenet.paper.ScopenetPlugin;
|
||||
import net.scopenet.paper.gui.GuiHelper;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
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.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public final class GuildHandler implements CommandExecutor, Listener {
|
||||
private final ScopenetPlugin plugin;
|
||||
private final Integration integration;
|
||||
private final File guildHomesFile;
|
||||
private final YamlConfiguration guildHomesConfig;
|
||||
private final Map<String, Location> guildHomes = new ConcurrentHashMap<>();
|
||||
|
||||
private static final String GUI_GUILD_TITLE = ChatColor.DARK_AQUA + "Guild Dashboard";
|
||||
|
||||
public GuildHandler(ScopenetPlugin plugin, Integration integration) {
|
||||
this.plugin = plugin;
|
||||
this.integration = integration;
|
||||
this.guildHomesFile = new File(plugin.getDataFolder(), "guild_homes.yml");
|
||||
this.guildHomesConfig = YamlConfiguration.loadConfiguration(guildHomesFile);
|
||||
loadGuildHomes();
|
||||
}
|
||||
|
||||
private void loadGuildHomes() {
|
||||
if (!guildHomesFile.exists()) return;
|
||||
for (String gid : guildHomesConfig.getKeys(false)) {
|
||||
Location loc = guildHomesConfig.getLocation(gid);
|
||||
if (loc != null) guildHomes.put(gid, loc);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveGuildHomes() {
|
||||
try {
|
||||
for (Map.Entry<String, Location> entry : guildHomes.entrySet()) {
|
||||
guildHomesConfig.set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
guildHomesConfig.save(guildHomesFile);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Failed to save guild homes: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(ChatColor.RED + "Only players can execute guild commands.");
|
||||
return true;
|
||||
}
|
||||
|
||||
String cmd = command.getName().toLowerCase();
|
||||
if (cmd.equals("claim")) {
|
||||
handleClaim(player);
|
||||
return true;
|
||||
}
|
||||
if (cmd.equals("unclaim")) {
|
||||
handleUnclaim(player);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cmd.equals("guild") || cmd.equals("g")) {
|
||||
if (args.length == 0 || args[0].equalsIgnoreCase("info") || args[0].equalsIgnoreCase("gui")) {
|
||||
handleGuildDashboard(player);
|
||||
return true;
|
||||
}
|
||||
|
||||
String sub = args[0].toLowerCase();
|
||||
switch (sub) {
|
||||
case "create" -> handleCreate(player, args);
|
||||
case "leave" -> handleLeave(player);
|
||||
case "claim" -> handleClaim(player);
|
||||
case "unclaim" -> handleUnclaim(player);
|
||||
case "map" -> handleMap(player);
|
||||
case "chat", "c" -> handleChat(player, args);
|
||||
case "sethome" -> handleSetHome(player);
|
||||
case "home" -> handleHome(player);
|
||||
case "members" -> handleMembers(player);
|
||||
default -> sendGuildHelp(player);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void sendGuildHelp(Player player) {
|
||||
player.sendMessage(ChatColor.GOLD + "=== SCOPENET Guild Commands ===");
|
||||
player.sendMessage(ChatColor.YELLOW + "/guild" + ChatColor.GRAY + " - Open interactive Guild Dashboard GUI");
|
||||
player.sendMessage(ChatColor.YELLOW + "/guild create <name> <tag>" + ChatColor.GRAY + " - Create a new guild");
|
||||
player.sendMessage(ChatColor.YELLOW + "/guild leave" + ChatColor.GRAY + " - Leave your current guild");
|
||||
player.sendMessage(ChatColor.YELLOW + "/claim" + ChatColor.GRAY + " (or /guild claim) - Claim current chunk");
|
||||
player.sendMessage(ChatColor.YELLOW + "/unclaim" + ChatColor.GRAY + " (or /guild unclaim) - Unclaim current chunk");
|
||||
player.sendMessage(ChatColor.YELLOW + "/guild map" + ChatColor.GRAY + " - Show nearby land claims radar map");
|
||||
player.sendMessage(ChatColor.YELLOW + "/guild chat <msg>" + ChatColor.GRAY + " - Send message to guild members");
|
||||
player.sendMessage(ChatColor.YELLOW + "/guild sethome / /guild home" + ChatColor.GRAY + " - Guild waypoint base");
|
||||
}
|
||||
|
||||
private void handleClaim(Player player) {
|
||||
Chunk chunk = player.getLocation().getChunk();
|
||||
String dim = player.getWorld().getName();
|
||||
player.sendMessage(ChatColor.GRAY + "Claiming chunk [" + chunk.getX() + ", " + chunk.getZ() + "]...");
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
JsonObject res = integration.client().claimChunk(dim, chunk.getX(), chunk.getZ(), player.getUniqueId());
|
||||
String gname = res.has("guild_name") ? res.get("guild_name").getAsString() : "your guild";
|
||||
player.sendMessage(ChatColor.GREEN + "Chunk claimed successfully for " + ChatColor.YELLOW + gname + ChatColor.GREEN + "!");
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Claim failed: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleUnclaim(Player player) {
|
||||
Chunk chunk = player.getLocation().getChunk();
|
||||
String dim = player.getWorld().getName();
|
||||
player.sendMessage(ChatColor.GRAY + "Unclaiming chunk [" + chunk.getX() + ", " + chunk.getZ() + "]...");
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
integration.client().unclaimChunk(dim, chunk.getX(), chunk.getZ(), player.getUniqueId());
|
||||
player.sendMessage(ChatColor.GREEN + "Chunk [" + chunk.getX() + ", " + chunk.getZ() + "] has been unclaimed.");
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Unclaim failed: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleCreate(Player player, String[] args) {
|
||||
if (args.length < 3) {
|
||||
player.sendMessage(ChatColor.RED + "Usage: /guild create <name> <tag>");
|
||||
return;
|
||||
}
|
||||
|
||||
String name = args[1];
|
||||
String tag = args[2];
|
||||
player.sendMessage(ChatColor.GRAY + "Founding guild " + name + " [" + tag + "]...");
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
JsonObject res = integration.client().createGuild(player.getUniqueId(), player.getName(), name, tag);
|
||||
player.sendMessage(ChatColor.GREEN + "Guild " + ChatColor.YELLOW + name + ChatColor.GOLD + " [" + tag + "]"
|
||||
+ ChatColor.GREEN + " founded successfully! You are the Guild Leader.");
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Failed to create guild: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleLeave(Player player) {
|
||||
player.sendMessage(ChatColor.GRAY + "Leaving guild...");
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
JsonObject res = integration.client().leaveGuild(player.getUniqueId());
|
||||
boolean disbanded = res.has("disbanded") && res.get("disbanded").getAsBoolean();
|
||||
if (disbanded) {
|
||||
player.sendMessage(ChatColor.YELLOW + "You were the last member. The guild was disbanded.");
|
||||
} else {
|
||||
player.sendMessage(ChatColor.GREEN + "You left your guild.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Error leaving guild: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleGuildDashboard(Player player) {
|
||||
player.sendMessage(ChatColor.GRAY + "Loading guild info...");
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
JsonObject res = integration.client().getPlayerGuild(player.getUniqueId());
|
||||
if (!res.has("in_guild") || !res.get("in_guild").getAsBoolean()) {
|
||||
player.sendMessage(ChatColor.YELLOW + "You are not in a guild! Create one with "
|
||||
+ ChatColor.GOLD + "/guild create <name> <tag>");
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject g = res.getAsJsonObject("guild");
|
||||
String name = g.get("name").getAsString();
|
||||
String tag = g.get("tag").getAsString();
|
||||
String role = g.get("role").getAsString();
|
||||
int level = g.has("level") ? g.get("level").getAsInt() : 1;
|
||||
long claims = g.has("claims_count") ? g.get("claims_count").getAsLong() : 0;
|
||||
long maxClaims = g.has("max_claims") ? g.get("max_claims").getAsLong() : 16;
|
||||
JsonArray members = g.has("members") ? g.getAsJsonArray("members") : new JsonArray();
|
||||
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
Inventory inv = Bukkit.createInventory(null, 36, GUI_GUILD_TITLE);
|
||||
ItemStack border = GuiHelper.createBorder(Material.CYAN_STAINED_GLASS_PANE);
|
||||
for (int i = 0; i < 9; i++) inv.setItem(i, border);
|
||||
for (int i = 27; i < 36; i++) inv.setItem(i, border);
|
||||
|
||||
// Guild banner
|
||||
inv.setItem(11, GuiHelper.createItem(Material.CYAN_BANNER,
|
||||
"&bGuild: &f" + name + " &e[" + tag + "]",
|
||||
"&7Your Role: &a" + role.toUpperCase(),
|
||||
"&7Guild Level: &6" + level,
|
||||
"&7Members: &f" + members.size()));
|
||||
|
||||
// Claims status
|
||||
inv.setItem(13, GuiHelper.createItem(Material.EMERALD_BLOCK,
|
||||
"&aTerritory Claims",
|
||||
"&7Claimed Chunks: &e" + claims + " &7/ &a" + maxClaims,
|
||||
"",
|
||||
"&7Click &e/claim &7to claim nearby area."));
|
||||
|
||||
// Quick claim button
|
||||
inv.setItem(14, GuiHelper.createItem(Material.LIME_DYE,
|
||||
"&aClaim Current Chunk",
|
||||
"&7Click to claim the chunk you are standing on!"));
|
||||
|
||||
// Quick unclaim button
|
||||
inv.setItem(15, GuiHelper.createItem(Material.RED_DYE,
|
||||
"&cUnclaim Current Chunk",
|
||||
"&7Click to unclaim the current chunk."));
|
||||
|
||||
// Minimap button
|
||||
inv.setItem(22, GuiHelper.createItem(Material.MAP,
|
||||
"&eView Land Map",
|
||||
"&7Shows nearby territory claims radar in chat."));
|
||||
|
||||
player.openInventory(inv);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Failed to load guild: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleMap(Player player) {
|
||||
Location pLoc = player.getLocation();
|
||||
Chunk center = pLoc.getChunk();
|
||||
int cx = center.getX();
|
||||
int cz = center.getZ();
|
||||
String dim = pLoc.getWorld().getName();
|
||||
|
||||
player.sendMessage(ChatColor.GOLD + "========= " + ChatColor.YELLOW + "Territory Map (" + cx + ", " + cz + ")" + ChatColor.GOLD + " =========");
|
||||
player.sendMessage(ChatColor.GRAY + " N (-Z)");
|
||||
|
||||
StringBuilder header = new StringBuilder(" ");
|
||||
for (int dx = -4; dx <= 4; dx++) {
|
||||
header.append(" ");
|
||||
}
|
||||
player.sendMessage(header.toString());
|
||||
|
||||
for (int dz = -3; dz <= 3; dz++) {
|
||||
StringBuilder row = new StringBuilder(" ");
|
||||
for (int dx = -4; dx <= 4; dx++) {
|
||||
int targetX = cx + dx;
|
||||
int targetZ = cz + dz;
|
||||
boolean isPlayer = (dx == 0 && dz == 0);
|
||||
|
||||
ChunkCheckResult res = integration.checkChunk(dim, targetX, targetZ, player.getUniqueId());
|
||||
if (isPlayer) {
|
||||
row.append(ChatColor.GOLD).append("[P]");
|
||||
} else if (res.claimed() && res.allowed()) {
|
||||
row.append(ChatColor.GREEN).append(" + ");
|
||||
} else if (res.claimed() && !res.allowed()) {
|
||||
row.append(ChatColor.RED).append(" x ");
|
||||
} else {
|
||||
row.append(ChatColor.DARK_GRAY).append(" . ");
|
||||
}
|
||||
}
|
||||
player.sendMessage(row.toString());
|
||||
}
|
||||
|
||||
player.sendMessage(ChatColor.GRAY + " S (+Z)");
|
||||
player.sendMessage(ChatColor.GOLD + "[P]" + ChatColor.YELLOW + " You "
|
||||
+ ChatColor.GREEN + "+ " + ChatColor.GRAY + "Your Guild "
|
||||
+ ChatColor.RED + "x " + ChatColor.GRAY + "Other Guild "
|
||||
+ ChatColor.DARK_GRAY + ". " + ChatColor.GRAY + "Wilderness");
|
||||
}
|
||||
|
||||
private void handleChat(Player player, String[] args) {
|
||||
if (args.length < 2) {
|
||||
player.sendMessage(ChatColor.RED + "Usage: /guild chat <message>");
|
||||
return;
|
||||
}
|
||||
|
||||
String msg = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
JsonObject res = integration.client().getPlayerGuild(player.getUniqueId());
|
||||
if (!res.has("in_guild") || !res.get("in_guild").getAsBoolean()) {
|
||||
player.sendMessage(ChatColor.RED + "You are not in a guild.");
|
||||
return;
|
||||
}
|
||||
String tag = res.getAsJsonObject("guild").get("tag").getAsString();
|
||||
String formatted = ChatColor.GREEN + "[Guild " + tag + "] " + ChatColor.YELLOW + player.getName() + ": " + ChatColor.WHITE + msg;
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
p.sendMessage(formatted);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Guild chat error: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleSetHome(Player player) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
JsonObject res = integration.client().getPlayerGuild(player.getUniqueId());
|
||||
if (!res.has("in_guild") || !res.get("in_guild").getAsBoolean()) {
|
||||
player.sendMessage(ChatColor.RED + "You must be in a guild to set a guild home.");
|
||||
return;
|
||||
}
|
||||
String role = res.getAsJsonObject("guild").get("role").getAsString();
|
||||
if (!role.equals("leader") && !role.equals("officer")) {
|
||||
player.sendMessage(ChatColor.RED + "Only guild leaders and officers can set the guild home.");
|
||||
return;
|
||||
}
|
||||
String gid = res.getAsJsonObject("guild").get("id").getAsString();
|
||||
guildHomes.put(gid, player.getLocation());
|
||||
saveGuildHomes();
|
||||
player.sendMessage(ChatColor.GREEN + "Guild home waypoint set at your location!");
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Error: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleHome(Player player) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
JsonObject res = integration.client().getPlayerGuild(player.getUniqueId());
|
||||
if (!res.has("in_guild") || !res.get("in_guild").getAsBoolean()) {
|
||||
player.sendMessage(ChatColor.RED + "You are not in a guild.");
|
||||
return;
|
||||
}
|
||||
String gid = res.getAsJsonObject("guild").get("id").getAsString();
|
||||
Location loc = guildHomes.get(gid);
|
||||
if (loc == null) {
|
||||
player.sendMessage(ChatColor.RED + "Your guild has not set a guild home yet! Use /guild sethome");
|
||||
return;
|
||||
}
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
player.teleport(loc);
|
||||
player.sendMessage(ChatColor.GREEN + "Teleported to guild home base.");
|
||||
});
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Error: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleMembers(Player player) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
JsonObject res = integration.client().getPlayerGuild(player.getUniqueId());
|
||||
if (!res.has("in_guild") || !res.get("in_guild").getAsBoolean()) {
|
||||
player.sendMessage(ChatColor.RED + "You are not in a guild.");
|
||||
return;
|
||||
}
|
||||
JsonObject g = res.getAsJsonObject("guild");
|
||||
String name = g.get("name").getAsString();
|
||||
JsonArray members = g.getAsJsonArray("members");
|
||||
|
||||
player.sendMessage(ChatColor.GOLD + "=== Members of " + ChatColor.YELLOW + name + ChatColor.GOLD + " ===");
|
||||
for (JsonElement el : members) {
|
||||
JsonObject m = el.getAsJsonObject();
|
||||
String mname = m.get("name").getAsString();
|
||||
String role = m.get("role").getAsString();
|
||||
Player onlineP = Bukkit.getPlayer(mname);
|
||||
String status = onlineP != null && onlineP.isOnline() ? ChatColor.GREEN + "[Online]" : ChatColor.GRAY + "[Offline]";
|
||||
player.sendMessage(ChatColor.WHITE + " - " + mname + " (" + ChatColor.AQUA + role + ChatColor.WHITE + ") " + status);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "Error fetching members: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
if (!(event.getWhoClicked() instanceof Player player)) return;
|
||||
if (!event.getView().getTitle().equals(GUI_GUILD_TITLE)) return;
|
||||
|
||||
event.setCancelled(true);
|
||||
int slot = event.getRawSlot();
|
||||
if (slot == 14) { // Claim Current Chunk
|
||||
player.closeInventory();
|
||||
handleClaim(player);
|
||||
} else if (slot == 15) { // Unclaim Current Chunk
|
||||
player.closeInventory();
|
||||
handleUnclaim(player);
|
||||
} else if (slot == 22) { // Minimap
|
||||
player.closeInventory();
|
||||
handleMap(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package net.scopenet.paper.commands;
|
||||
|
||||
import net.scopenet.integration.Integration;
|
||||
import net.scopenet.integration.Settings;
|
||||
import net.scopenet.paper.ScopenetPlugin;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public final class ScopenetCommandHandler implements CommandExecutor {
|
||||
private final ScopenetPlugin plugin;
|
||||
private final Integration integration;
|
||||
|
||||
public ScopenetCommandHandler(ScopenetPlugin plugin, Integration integration) {
|
||||
this.plugin = plugin;
|
||||
this.integration = integration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (args.length == 0 || args[0].equalsIgnoreCase("help")) {
|
||||
sendHelp(sender);
|
||||
return true;
|
||||
}
|
||||
|
||||
String sub = args[0].toLowerCase();
|
||||
switch (sub) {
|
||||
case "status" -> sendStatus(sender);
|
||||
case "reload" -> handleReload(sender);
|
||||
default -> {
|
||||
sender.sendMessage(ChatColor.RED + "Unknown command. Use " + ChatColor.YELLOW + "/scopenet help");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendHelp(CommandSender sender) {
|
||||
Settings s = integration != null ? integration.settings() : null;
|
||||
sender.sendMessage(ChatColor.GOLD + "=================== " + ChatColor.YELLOW + "SCOPENET Commands" + ChatColor.GOLD + " ===================");
|
||||
|
||||
// Essentials commands
|
||||
if (s == null || s.essentialsEnabled()) {
|
||||
sender.sendMessage(ChatColor.AQUA + "[Essentials Commands]");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /spawn" + ChatColor.GRAY + " - Teleport to server spawn");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /home [name]" + ChatColor.GRAY + " - Teleport to home or open homes GUI");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /sethome [name]" + ChatColor.GRAY + " - Set home location");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /delhome [name]" + ChatColor.GRAY + " - Delete home");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /back" + ChatColor.GRAY + " - Return to location before teleport/death");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /tpa <player>" + ChatColor.GRAY + " - Request to teleport to a player");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /tpaccept / /tpdeny" + ChatColor.GRAY + " - Accept or deny teleport");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /rtp" + ChatColor.GRAY + " - Random wilderness teleport");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /warp [name]" + ChatColor.GRAY + " - Teleport to warp or open warps GUI");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /playtime [player]" + ChatColor.GRAY + " - View total playtime stats");
|
||||
}
|
||||
|
||||
// Economy commands
|
||||
if (s == null || s.economyEnabled()) {
|
||||
sender.sendMessage(ChatColor.GREEN + "[Economy Commands]");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /balance (or /bal)" + ChatColor.GRAY + " - Check your wallet balance");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /pay <player> <amount>" + ChatColor.GRAY + " - Send money to a player");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /baltop" + ChatColor.GRAY + " - Leaderboard of richest players");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /shop" + ChatColor.GRAY + " - Open interactive Server Shop GUI");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /sell (or /sell hand)" + ChatColor.GRAY + " - Sell items for cash");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /market" + ChatColor.GRAY + " - Open player marketplace GUI");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /market sell <price>" + ChatColor.GRAY + " - List held item on market");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /trade <player>" + ChatColor.GRAY + " - Secure two-player trade GUI");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /transactions" + ChatColor.GRAY + " - View recent transaction history");
|
||||
}
|
||||
|
||||
// Guilds commands
|
||||
if (s == null || s.guildsEnabled()) {
|
||||
sender.sendMessage(ChatColor.BLUE + "[Guilds & Claims]");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /guild" + ChatColor.GRAY + " - Open interactive Guild Dashboard GUI");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /guild create <name> <tag>" + ChatColor.GRAY + " - Create a new guild");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /guild leave" + ChatColor.GRAY + " - Leave your current guild");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /guild members" + ChatColor.GRAY + " - List online guild members");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /guild chat <msg>" + ChatColor.GRAY + " - Send guild chat message");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /claim" + ChatColor.GRAY + " - Claim current chunk for your guild");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /unclaim" + ChatColor.GRAY + " - Unclaim current chunk");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /guild map" + ChatColor.GRAY + " - Show nearby land claims radar map");
|
||||
}
|
||||
|
||||
// Admin commands
|
||||
if (sender.hasPermission("scopenet.admin")) {
|
||||
sender.sendMessage(ChatColor.LIGHT_PURPLE + "[Admin Commands]");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /scopenet status" + ChatColor.GRAY + " - Integration health and module statuses");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /scopenet reload" + ChatColor.GRAY + " - Reload config and feature flags");
|
||||
}
|
||||
}
|
||||
|
||||
private void sendStatus(CommandSender sender) {
|
||||
if (!sender.hasPermission("scopenet.admin")) {
|
||||
sender.sendMessage(ChatColor.RED + "You do not have permission to view SCOPENET status.");
|
||||
return;
|
||||
}
|
||||
|
||||
Settings s = integration != null ? integration.settings() : null;
|
||||
sender.sendMessage(ChatColor.GOLD + "=== SCOPENET Integration Status ===");
|
||||
if (s == null) {
|
||||
sender.sendMessage(ChatColor.RED + "Status: NOT INITIALIZED or OFFLINE");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.GRAY + "Panel URL: " + ChatColor.WHITE + s.panel());
|
||||
sender.sendMessage(ChatColor.GRAY + "Server Token: " + ChatColor.GREEN + (s.token().isEmpty() ? "MISSING" : "CONFIGURED"));
|
||||
sender.sendMessage(ChatColor.GRAY + "Leveling: " + formatFlag(s.levelingEnabled()));
|
||||
sender.sendMessage(ChatColor.GRAY + "Quests: " + formatFlag(s.questsEnabled()));
|
||||
sender.sendMessage(ChatColor.GRAY + "Achievements: " + formatFlag(s.achievementsEnabled()));
|
||||
sender.sendMessage(ChatColor.GRAY + "Guilds: " + formatFlag(s.guildsEnabled()));
|
||||
sender.sendMessage(ChatColor.GRAY + "Land Claiming: " + formatFlag(s.landClaimingEnabled()));
|
||||
sender.sendMessage(ChatColor.GRAY + "Social: " + formatFlag(s.socialEnabled()));
|
||||
sender.sendMessage(ChatColor.GRAY + "Essentials: " + formatFlag(s.essentialsEnabled()));
|
||||
sender.sendMessage(ChatColor.GRAY + "Economy: " + formatFlag(s.economyEnabled()));
|
||||
}
|
||||
|
||||
private String formatFlag(boolean enabled) {
|
||||
return enabled ? ChatColor.GREEN + "ENABLED" : ChatColor.RED + "DISABLED";
|
||||
}
|
||||
|
||||
private void handleReload(CommandSender sender) {
|
||||
if (!sender.hasPermission("scopenet.admin")) {
|
||||
sender.sendMessage(ChatColor.RED + "You do not have permission to reload SCOPENET.");
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.reloadConfig();
|
||||
sender.sendMessage(ChatColor.GREEN + "SCOPENET configuration reloaded successfully.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package net.scopenet.paper.gui;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public final class GuiHelper {
|
||||
private GuiHelper() {}
|
||||
|
||||
public static ItemStack createItem(Material material, String name, String... lore) {
|
||||
return createItem(material, 1, name, lore);
|
||||
}
|
||||
|
||||
public static ItemStack createItem(Material material, int amount, String name, String... lore) {
|
||||
ItemStack item = new ItemStack(material, Math.max(1, amount));
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
if (name != null) {
|
||||
meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', name));
|
||||
}
|
||||
if (lore != null && lore.length > 0) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (String line : lore) {
|
||||
list.add(ChatColor.translateAlternateColorCodes('&', line));
|
||||
}
|
||||
meta.setLore(list);
|
||||
}
|
||||
item.setItemMeta(meta);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
public static ItemStack createBorder(Material material) {
|
||||
return createItem(material, " ");
|
||||
}
|
||||
}
|
||||
@@ -42,3 +42,18 @@ social:
|
||||
enabled: true
|
||||
# Show player guild tag and level title in chat formatting
|
||||
chat-prefixes: true
|
||||
|
||||
essentials:
|
||||
# Essentials commands (/spawn, /home, /sethome, /delhome, /back, /tpa, /tpaccept, /tpdeny, /rtp, /warp, /playtime)
|
||||
enabled: true
|
||||
# Maximum homes per player
|
||||
max-homes: 5
|
||||
# RTP search radius
|
||||
rtp-radius: 2500
|
||||
|
||||
economy:
|
||||
# Server Economy commands (/balance, /pay, /baltop, /shop, /sell, /market, /orders, /trade, /transactions)
|
||||
enabled: true
|
||||
# Currency symbol
|
||||
currency-symbol: "$"
|
||||
|
||||
@@ -4,3 +4,70 @@ main: net.scopenet.paper.ScopenetPlugin
|
||||
api-version: '1.20'
|
||||
description: SCOPENET private authentication, access control and activity reporting.
|
||||
load: STARTUP
|
||||
|
||||
commands:
|
||||
scopenet:
|
||||
description: SCOPENET server commands, status, and help
|
||||
aliases: [sn]
|
||||
permission: scopenet.use
|
||||
spawn:
|
||||
description: Teleport to the server spawn point
|
||||
aliases: [hub, lobby]
|
||||
home:
|
||||
description: Teleport to one of your homes or open the homes GUI
|
||||
aliases: [homes]
|
||||
sethome:
|
||||
description: Set a new home location
|
||||
aliases: [createshome]
|
||||
delhome:
|
||||
description: Delete an existing home
|
||||
aliases: [rmhome]
|
||||
back:
|
||||
description: Return to your previous location or death point
|
||||
aliases: [return]
|
||||
tpa:
|
||||
description: Request to teleport to another player
|
||||
tpaccept:
|
||||
description: Accept an incoming teleport request
|
||||
aliases: [tpyes]
|
||||
tpdeny:
|
||||
description: Deny an incoming teleport request
|
||||
aliases: [tpno]
|
||||
rtp:
|
||||
description: Teleport to a random safe location in the wilderness
|
||||
aliases: [wild, randomtp]
|
||||
warp:
|
||||
description: Teleport to a server warp or open the warps GUI
|
||||
aliases: [warps]
|
||||
playtime:
|
||||
description: Check total and session playtime
|
||||
aliases: [ontime]
|
||||
balance:
|
||||
description: Check your server economy balance
|
||||
aliases: [bal, money]
|
||||
pay:
|
||||
description: Send money to another player
|
||||
baltop:
|
||||
description: View the richest players on the server
|
||||
aliases: [richest]
|
||||
shop:
|
||||
description: Open the interactive server shop GUI
|
||||
sell:
|
||||
description: Open the item selling GUI
|
||||
market:
|
||||
description: Open the player marketplace GUI
|
||||
aliases: [ah, auction]
|
||||
orders:
|
||||
description: View active marketplace orders
|
||||
trade:
|
||||
description: Request a secure trade with another player
|
||||
transactions:
|
||||
description: View recent economy transactions
|
||||
guild:
|
||||
description: Guild management, members, claims, and relations
|
||||
aliases: [g, clan]
|
||||
claim:
|
||||
description: Claim the current chunk for your guild
|
||||
unclaim:
|
||||
description: Unclaim the current chunk
|
||||
|
||||
Reference in new issue
Block a user