first commit

This commit is contained in:
2026-06-08 19:17:10 -04:00
commit 8e18e96acf
45 changed files with 2939 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bitnix</groupId>
<artifactId>DirtGuilds</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>DirtGuilds</name>
<properties>
<java.version>21</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<repository>
<id>placeholderapi</id>
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.8-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.11.6</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.46.0.0</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>9.0.0</version>
</dependency>
</dependencies>
<build>
<finalName>DirtGuilds</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<release>21</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<minimizeJar>false</minimizeJar>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,83 @@
package com.bitnix.dirtguilds;
import com.bitnix.dirtguilds.command.DirtGuildsCommand;
import com.bitnix.dirtguilds.data.DatabaseManager;
import com.bitnix.dirtguilds.guild.GuildManager;
import com.bitnix.dirtguilds.hook.EconomyHook;
import com.bitnix.dirtguilds.listener.ChatListener;
import com.bitnix.dirtguilds.listener.GuildFriendlyFireListener;
import com.bitnix.dirtguilds.listener.GuiListener;
import com.bitnix.dirtguilds.util.MessageUtil;
import org.bukkit.plugin.java.JavaPlugin;
public class DirtGuildsPlugin extends JavaPlugin {
private DatabaseManager databaseManager;
private GuildManager guildManager;
private MessageUtil messageUtil;
private EconomyHook economyHook;
@Override
public void onEnable() {
saveDefaultConfig();
this.messageUtil = new MessageUtil(this);
this.economyHook = new EconomyHook(this);
boolean economyEnabled = this.economyHook.setup();
if (economyEnabled) {
getLogger().info("Vault economy hooked successfully.");
} else {
getLogger().warning("Vault economy not found. Guild bank will run in internal-only mode.");
}
this.databaseManager = new DatabaseManager(this);
this.databaseManager.initialize();
this.guildManager = new GuildManager(this, this.databaseManager);
this.guildManager.loadAll();
DirtGuildsCommand command = new DirtGuildsCommand(this);
if (getCommand("dirtguilds") != null) {
getCommand("dirtguilds").setExecutor(command);
getCommand("dirtguilds").setTabCompleter(command);
}
getServer().getPluginManager().registerEvents(new GuiListener(this), this);
getServer().getPluginManager().registerEvents(new ChatListener(this), this);
getServer().getPluginManager().registerEvents(new GuildFriendlyFireListener(this), this);
getLogger().info("DirtGuilds enabled.");
}
@Override
public void onDisable() {
getServer().getScheduler().cancelTasks(this);
if (guildManager != null) {
guildManager.shutdown();
}
if (databaseManager != null) {
databaseManager.close();
}
getLogger().info("DirtGuilds disabled.");
}
public DatabaseManager getDatabaseManager() {
return databaseManager;
}
public GuildManager getGuildManager() {
return guildManager;
}
public MessageUtil getMessageUtil() {
return messageUtil;
}
public EconomyHook getEconomyHook() {
return economyHook;
}
}
@@ -0,0 +1,667 @@
package com.bitnix.dirtguilds.command;
import com.bitnix.dirtguilds.DirtGuildsPlugin;
import com.bitnix.dirtguilds.guild.Guild;
import com.bitnix.dirtguilds.guild.GuildInvite;
import com.bitnix.dirtguilds.gui.GuildLevelsMenu;
import com.bitnix.dirtguilds.gui.GuildMainMenu;
import com.bitnix.dirtguilds.gui.GuildMembersMenu;
import com.bitnix.dirtguilds.gui.GuildVaultMenu;
import com.bitnix.dirtguilds.util.MessageUtil;
import net.milkbowl.vault.economy.EconomyResponse;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
public class DirtGuildsCommand implements TabExecutor, TabCompleter {
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,##0.00");
private final DirtGuildsPlugin plugin;
public DirtGuildsCommand(DirtGuildsPlugin plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) {
plugin.getMessageUtil().send(sender, "messages.players-only");
return true;
}
if (!player.hasPermission("dirtguilds.use")) {
plugin.getMessageUtil().send(player, "messages.no-permission");
return true;
}
if (args.length == 0) {
new GuildMainMenu(plugin).open(player);
return true;
}
String sub = args[0].toLowerCase(Locale.ROOT);
Guild guild = plugin.getGuildManager().getGuildByPlayer(player.getUniqueId());
switch (sub) {
case "create" -> {
if (guild != null) {
plugin.getMessageUtil().send(player, "messages.already-in-guild");
return true;
}
if (args.length < 2) {
plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " create <name></red>");
return true;
}
String name = args[1];
int min = plugin.getConfig().getInt("settings.min-name-length", 3);
int max = plugin.getConfig().getInt("settings.max-name-length", 16);
if (name.length() < min || name.length() > max || !name.matches("[A-Za-z0-9_]+")) {
plugin.getMessageUtil().send(player, "messages.invalid-name");
return true;
}
if (plugin.getGuildManager().guildNameExists(name)) {
plugin.getMessageUtil().send(player, "messages.name-taken");
return true;
}
Guild created = plugin.getGuildManager().createGuild(player.getUniqueId(), name);
plugin.getMessageUtil().send(player, "messages.guild-created", "%guild%", created.getName());
return true;
}
case "invite" -> {
if (guild == null) {
plugin.getMessageUtil().send(player, "messages.not-in-guild");
return true;
}
if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
plugin.getMessageUtil().send(player, "messages.no-permission");
return true;
}
if (args.length < 2) {
plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " invite <player></red>");
return true;
}
Player target = Bukkit.getPlayerExact(args[1]);
if (target == null) {
plugin.getMessageUtil().send(player, "messages.player-not-found");
return true;
}
if (plugin.getGuildManager().isInGuild(target.getUniqueId())) {
plugin.getMessageUtil().send(player, "messages.target-already-in-guild");
return true;
}
plugin.getGuildManager().createInvite(target.getUniqueId(), player.getUniqueId(), guild);
plugin.getMessageUtil().send(player, "messages.invite-sent", "%player%", target.getName());
plugin.getMessageUtil().send(target, "messages.invite-received", "%guild%", guild.getName());
return true;
}
case "accept", "join" -> {
if (guild != null) {
plugin.getMessageUtil().send(player, "messages.already-in-guild");
return true;
}
if (args.length < 2) {
plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " accept <guild></red>");
return true;
}
Guild targetGuild = plugin.getGuildManager().getGuildByName(args[1]);
if (targetGuild == null) {
plugin.getMessageUtil().send(player, "messages.guild-not-found");
return true;
}
GuildInvite invite = plugin.getGuildManager().getInvite(player.getUniqueId(), targetGuild.getId());
if (invite == null) {
plugin.getMessageUtil().send(player, "messages.no-invite");
return true;
}
if (invite.isExpired()) {
plugin.getGuildManager().removeInvite(player.getUniqueId(), targetGuild.getId());
plugin.getMessageUtil().send(player, "messages.invite-expired");
return true;
}
plugin.getGuildManager().addMember(targetGuild, player.getUniqueId());
plugin.getGuildManager().removeInvite(player.getUniqueId(), targetGuild.getId());
plugin.getMessageUtil().send(player, "messages.joined-guild", "%guild%", targetGuild.getName());
for (Player online : Bukkit.getOnlinePlayers()) {
Guild onlineGuild = plugin.getGuildManager().getGuildByPlayer(online.getUniqueId());
if (onlineGuild != null && onlineGuild.getId().equals(targetGuild.getId())) {
plugin.getMessageUtil().send(online, "messages.member-joined", "%player%", player.getName());
}
}
return true;
}
case "leave" -> {
if (guild == null) {
plugin.getMessageUtil().send(player, "messages.not-in-guild");
return true;
}
if (guild.getOwner().equals(player.getUniqueId())) {
plugin.getGuildManager().deleteGuild(guild);
plugin.getMessageUtil().send(player, "messages.guild-deleted", "%guild%", guild.getName());
return true;
}
plugin.getGuildManager().removeMember(guild, player.getUniqueId());
plugin.getMessageUtil().send(player, "messages.left-guild");
for (Player online : Bukkit.getOnlinePlayers()) {
Guild onlineGuild = plugin.getGuildManager().getGuildByPlayer(online.getUniqueId());
if (onlineGuild != null && onlineGuild.getId().equals(guild.getId())) {
plugin.getMessageUtil().send(online, "messages.member-left", "%player%", player.getName());
}
}
return true;
}
case "rename" -> {
if (guild == null) {
plugin.getMessageUtil().send(player, "messages.not-in-guild");
return true;
}
if (!guild.getOwner().equals(player.getUniqueId())) {
plugin.getMessageUtil().send(player, "messages.must-be-owner");
return true;
}
if (args.length < 2) {
plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " rename <name></red>");
return true;
}
String newName = args[1];
int min = plugin.getConfig().getInt("settings.min-name-length", 3);
int max = plugin.getConfig().getInt("settings.max-name-length", 16);
if (newName.length() < min || newName.length() > max || !newName.matches("[A-Za-z0-9_]+")) {
plugin.getMessageUtil().send(player, "messages.invalid-name");
return true;
}
if (!guild.getName().equalsIgnoreCase(newName) && plugin.getGuildManager().guildNameExists(newName)) {
plugin.getMessageUtil().send(player, "messages.name-taken");
return true;
}
plugin.getGuildManager().renameGuild(guild, newName);
plugin.getMessageUtil().send(player, "messages.guild-renamed", "%guild%", newName);
return true;
}
case "prefix" -> {
if (guild == null) {
plugin.getMessageUtil().send(player, "messages.not-in-guild");
return true;
}
if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
plugin.getMessageUtil().send(player, "messages.no-permission");
return true;
}
if (args.length < 2) {
plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " prefix <text></red>");
return true;
}
String prefix = args[1];
plugin.getGuildManager().setPrefix(guild, prefix);
plugin.getMessageUtil().send(player, "messages.prefix-updated");
return true;
}
case "ff", "friendlyfire", "pvp" -> {
if (guild == null) {
plugin.getMessageUtil().send(player, "messages.not-in-guild");
return true;
}
if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
plugin.getMessageUtil().send(player, "messages.no-permission");
return true;
}
boolean enabled = !guild.isFriendlyFire();
plugin.getGuildManager().setFriendlyFire(guild, enabled);
if (enabled) {
plugin.getMessageUtil().send(player, "messages.friendly-fire-enabled");
} else {
plugin.getMessageUtil().send(player, "messages.friendly-fire-disabled");
}
return true;
}
case "chat" -> {
if (guild == null) {
plugin.getMessageUtil().send(player, "messages.not-in-guild");
return true;
}
boolean enabled = !plugin.getGuildManager().isGuildChatEnabled(player.getUniqueId());
plugin.getGuildManager().setGuildChatEnabled(player.getUniqueId(), enabled);
if (enabled) {
plugin.getMessageUtil().send(player, "messages.guild-chat-enabled");
} else {
plugin.getMessageUtil().send(player, "messages.guild-chat-disabled");
}
return true;
}
case "bank" -> {
if (guild == null) {
plugin.getMessageUtil().send(player, "messages.not-in-guild");
return true;
}
if (args.length == 1) {
player.sendMessage(MessageUtil.mm(plugin.getMessageUtil().format("messages.prefix") + "<yellow>Use /guild bank deposit <amount> or /guild bank withdraw <amount></yellow>"));
return true;
}
if (args.length < 3) {
plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " bank <deposit|withdraw> <amount></red>");
return true;
}
String action = args[1].toLowerCase(Locale.ROOT);
double amount;
try {
amount = Double.parseDouble(args[2]);
} catch (NumberFormatException exception) {
plugin.getMessageUtil().send(player, "messages.bank-invalid-amount");
return true;
}
if (amount <= 0) {
plugin.getMessageUtil().send(player, "messages.bank-invalid-amount");
return true;
}
boolean useVault = plugin.getConfig().getBoolean("settings.bank.use-vault", true);
if (action.equals("deposit")) {
if (useVault) {
if (!plugin.getEconomyHook().isAvailable()) {
plugin.getMessageUtil().send(player, "messages.bank-vault-unavailable");
return true;
}
if (plugin.getEconomyHook().getEconomy().getBalance(player) < amount) {
plugin.getMessageUtil().send(player, "messages.bank-player-insufficient-funds");
return true;
}
EconomyResponse response = plugin.getEconomyHook().getEconomy().withdrawPlayer(player, amount);
if (!response.transactionSuccess()) {
plugin.getMessageUtil().send(player, "messages.bank-player-insufficient-funds");
return true;
}
}
plugin.getGuildManager().deposit(guild, amount);
plugin.getMessageUtil().send(player, "messages.bank-deposit", "%amount%", DECIMAL_FORMAT.format(amount));
return true;
}
if (action.equals("withdraw")) {
if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
plugin.getMessageUtil().send(player, "messages.no-permission");
return true;
}
if (!plugin.getGuildManager().withdraw(guild, amount)) {
plugin.getMessageUtil().send(player, "messages.bank-insufficient-funds");
return true;
}
if (useVault) {
if (!plugin.getEconomyHook().isAvailable()) {
plugin.getGuildManager().deposit(guild, amount);
plugin.getMessageUtil().send(player, "messages.bank-vault-unavailable");
return true;
}
EconomyResponse response = plugin.getEconomyHook().getEconomy().depositPlayer(player, amount);
if (!response.transactionSuccess()) {
plugin.getGuildManager().deposit(guild, amount);
plugin.getMessageUtil().send(player, "messages.bank-vault-unavailable");
return true;
}
}
plugin.getMessageUtil().send(player, "messages.bank-withdraw", "%amount%", DECIMAL_FORMAT.format(amount));
return true;
}
plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " bank <deposit|withdraw> <amount></red>");
return true;
}
case "vault" -> {
if (guild == null) {
plugin.getMessageUtil().send(player, "messages.not-in-guild");
return true;
}
new GuildVaultMenu(plugin).open(player, guild);
return true;
}
case "levels", "level" -> {
if (guild == null) {
plugin.getMessageUtil().send(player, "messages.not-in-guild");
return true;
}
new GuildLevelsMenu(plugin).open(player, guild);
return true;
}
case "members" -> {
if (guild == null) {
plugin.getMessageUtil().send(player, "messages.not-in-guild");
return true;
}
new GuildMembersMenu(plugin).open(player, guild);
return true;
}
case "promote" -> {
if (guild == null) {
plugin.getMessageUtil().send(player, "messages.not-in-guild");
return true;
}
if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
plugin.getMessageUtil().send(player, "messages.no-permission");
return true;
}
if (args.length < 2) {
plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " promote <player></red>");
return true;
}
OfflinePlayer target = Bukkit.getOfflinePlayer(args[1]);
UUID targetUuid = target.getUniqueId();
if (!guild.isMember(targetUuid) || targetUuid.equals(guild.getOwner())) {
plugin.getMessageUtil().send(player, "messages.player-not-found");
return true;
}
String current = guild.getRank(targetUuid).toUpperCase();
String next = switch (current) {
case "MEMBER" -> "MOD";
case "MOD" -> "ADMIN";
default -> "ADMIN";
};
plugin.getGuildManager().setMemberRank(guild, targetUuid, next);
String name = target.getName() == null ? targetUuid.toString() : target.getName();
plugin.getMessageUtil().send(player, "messages.rank-updated", "%player%", name, "%rank%", next);
return true;
}
case "demote" -> {
if (guild == null) {
plugin.getMessageUtil().send(player, "messages.not-in-guild");
return true;
}
if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
plugin.getMessageUtil().send(player, "messages.no-permission");
return true;
}
if (args.length < 2) {
plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " demote <player></red>");
return true;
}
OfflinePlayer target = Bukkit.getOfflinePlayer(args[1]);
UUID targetUuid = target.getUniqueId();
if (!guild.isMember(targetUuid) || targetUuid.equals(guild.getOwner())) {
plugin.getMessageUtil().send(player, "messages.player-not-found");
return true;
}
String current = guild.getRank(targetUuid).toUpperCase();
String next = switch (current) {
case "ADMIN" -> "MOD";
case "MOD" -> "MEMBER";
default -> "MEMBER";
};
plugin.getGuildManager().setMemberRank(guild, targetUuid, next);
String name = target.getName() == null ? targetUuid.toString() : target.getName();
plugin.getMessageUtil().send(player, "messages.rank-updated", "%player%", name, "%rank%", next);
return true;
}
case "kick" -> {
if (guild == null) {
plugin.getMessageUtil().send(player, "messages.not-in-guild");
return true;
}
if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
plugin.getMessageUtil().send(player, "messages.no-permission");
return true;
}
if (args.length < 2) {
plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " kick <player></red>");
return true;
}
OfflinePlayer target = Bukkit.getOfflinePlayer(args[1]);
UUID targetUuid = target.getUniqueId();
if (!guild.isMember(targetUuid) || targetUuid.equals(guild.getOwner())) {
plugin.getMessageUtil().send(player, "messages.player-not-found");
return true;
}
plugin.getGuildManager().removeMember(guild, targetUuid);
String name = target.getName() == null ? targetUuid.toString() : target.getName();
plugin.getMessageUtil().sendRaw(player, plugin.getMessageUtil().format("messages.prefix") + "<red>Kicked <yellow>" + name + "</yellow> from the guild.</red>");
return true;
}
case "tp" -> {
if (guild == null) {
plugin.getMessageUtil().send(player, "messages.not-in-guild");
return true;
}
if (args.length < 2) {
plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " tp <player></red>");
return true;
}
Player target = Bukkit.getPlayerExact(args[1]);
if (target == null || !guild.isMember(target.getUniqueId())) {
plugin.getMessageUtil().send(player, "messages.teleport-offline");
return true;
}
player.teleport(target.getLocation());
plugin.getMessageUtil().send(player, "messages.teleport-success", "%player%", target.getName());
return true;
}
case "inv", "inventory" -> {
if (guild == null) {
plugin.getMessageUtil().send(player, "messages.not-in-guild");
return true;
}
if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
plugin.getMessageUtil().send(player, "messages.no-permission");
return true;
}
if (args.length < 2) {
plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " inventory <player></red>");
return true;
}
Player target = Bukkit.getPlayerExact(args[1]);
if (target == null || !guild.isMember(target.getUniqueId())) {
plugin.getMessageUtil().send(player, "messages.player-not-found");
return true;
}
Inventory copy = Bukkit.createInventory(null, 54, MessageUtil.mm("<gold>Inventory: " + target.getName() + "</gold>"));
copy.setContents(target.getInventory().getContents());
player.openInventory(copy);
plugin.getMessageUtil().send(player, "messages.inventory-opened", "%player%", target.getName());
return true;
}
case "reload" -> {
if (!player.hasPermission("dirtguilds.admin")) {
plugin.getMessageUtil().send(player, "messages.no-permission");
return true;
}
plugin.reloadConfig();
plugin.getMessageUtil().send(player, "messages.reload");
return true;
}
default -> {
plugin.getMessageUtil().send(player, "messages.unknown-command");
return true;
}
}
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
List<String> suggestions = new ArrayList<>();
if (args.length == 1) {
suggestions.add("create");
suggestions.add("invite");
suggestions.add("accept");
suggestions.add("join");
suggestions.add("leave");
suggestions.add("rename");
suggestions.add("prefix");
suggestions.add("chat");
suggestions.add("pvp");
suggestions.add("bank");
suggestions.add("vault");
suggestions.add("level");
suggestions.add("members");
suggestions.add("promote");
suggestions.add("demote");
suggestions.add("kick");
suggestions.add("tp");
suggestions.add("inventory");
suggestions.add("reload");
return filter(suggestions, args[0]);
}
if (args.length == 2 && args[0].equalsIgnoreCase("invite")) {
for (Player player : Bukkit.getOnlinePlayers()) {
suggestions.add(player.getName());
}
return filter(suggestions, args[1]);
}
if (args.length == 2 && (args[0].equalsIgnoreCase("accept") || args[0].equalsIgnoreCase("join"))) {
for (Guild guild : plugin.getGuildManager().getAllGuilds()) {
suggestions.add(guild.getName());
}
return filter(suggestions, args[1]);
}
if (args.length == 2 && args[0].equalsIgnoreCase("bank")) {
suggestions.add("deposit");
suggestions.add("withdraw");
return filter(suggestions, args[1]);
}
if (args.length == 3 && args[0].equalsIgnoreCase("bank")) {
suggestions.add("100");
suggestions.add("500");
suggestions.add("1000");
suggestions.add("5000");
return filter(suggestions, args[2]);
}
if (args.length == 2 && isMemberTargetSubcommand(args[0]) && sender instanceof Player player) {
Guild guild = plugin.getGuildManager().getGuildByPlayer(player.getUniqueId());
if (guild != null) {
for (UUID uuid : guild.getMemberRanks().keySet()) {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid);
if (offlinePlayer.getName() != null) {
suggestions.add(offlinePlayer.getName());
}
}
}
return filter(suggestions, args[1]);
}
return suggestions;
}
private boolean isMemberTargetSubcommand(String input) {
String lower = input.toLowerCase(Locale.ROOT);
return lower.equals("promote")
|| lower.equals("demote")
|| lower.equals("kick")
|| lower.equals("tp")
|| lower.equals("inventory")
|| lower.equals("inv");
}
private List<String> filter(List<String> input, String token) {
String lower = token.toLowerCase(Locale.ROOT);
return input.stream()
.filter(value -> value.toLowerCase(Locale.ROOT).startsWith(lower))
.distinct()
.sorted()
.toList();
}
}
@@ -0,0 +1,101 @@
package com.bitnix.dirtguilds.data;
import com.bitnix.dirtguilds.DirtGuildsPlugin;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.io.File;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class DatabaseManager {
private final DirtGuildsPlugin plugin;
private HikariDataSource dataSource;
public DatabaseManager(DirtGuildsPlugin plugin) {
this.plugin = plugin;
}
public void initialize() {
String type = plugin.getConfig().getString("database.type", "SQLITE").toUpperCase();
HikariConfig config = new HikariConfig();
if (type.equals("MYSQL")) {
String host = plugin.getConfig().getString("database.mysql.host");
int port = plugin.getConfig().getInt("database.mysql.port");
String database = plugin.getConfig().getString("database.mysql.database");
String username = plugin.getConfig().getString("database.mysql.username");
String password = plugin.getConfig().getString("database.mysql.password");
String properties = plugin.getConfig().getString("database.mysql.properties", "");
config.setJdbcUrl("jdbc:mysql://" + host + ":" + port + "/" + database + properties);
config.setUsername(username);
config.setPassword(password);
config.setMaximumPoolSize(10);
} else {
String fileName = plugin.getConfig().getString("database.sqlite.file", "guilds.db");
File dbFile = new File(plugin.getDataFolder(), fileName);
if (!plugin.getDataFolder().exists()) {
plugin.getDataFolder().mkdirs();
}
config.setJdbcUrl("jdbc:sqlite:" + dbFile.getAbsolutePath());
config.setMaximumPoolSize(1);
}
config.setPoolName("DirtGuildsPool");
this.dataSource = new HikariDataSource(config);
createTables();
}
private void createTables() {
try (Connection connection = getConnection(); Statement statement = connection.createStatement()) {
statement.executeUpdate("""
CREATE TABLE IF NOT EXISTS guilds (
id VARCHAR(36) PRIMARY KEY,
name VARCHAR(32) NOT NULL UNIQUE,
prefix VARCHAR(64) NOT NULL,
owner VARCHAR(36) NOT NULL,
level INT NOT NULL,
bank_balance DOUBLE NOT NULL,
friendly_fire BOOLEAN NOT NULL
)
""");
statement.executeUpdate("""
CREATE TABLE IF NOT EXISTS guild_members (
guild_id VARCHAR(36) NOT NULL,
player_uuid VARCHAR(36) NOT NULL,
rank_name VARCHAR(32) NOT NULL,
PRIMARY KEY (guild_id, player_uuid)
)
""");
statement.executeUpdate("""
CREATE TABLE IF NOT EXISTS guild_vault_items (
guild_id VARCHAR(36) NOT NULL,
slot_index INT NOT NULL,
item_data TEXT NOT NULL,
PRIMARY KEY (guild_id, slot_index)
)
""");
} catch (SQLException exception) {
throw new RuntimeException("Failed to create database tables", exception);
}
}
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public void close() {
if (dataSource != null && !dataSource.isClosed()) {
dataSource.close();
}
}
}
@@ -0,0 +1,71 @@
package com.bitnix.dirtguilds.gui;
import com.bitnix.dirtguilds.DirtGuildsPlugin;
import com.bitnix.dirtguilds.guild.Guild;
import com.bitnix.dirtguilds.util.MessageUtil;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
public class GuildBankMenu {
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,##0.00");
private final DirtGuildsPlugin plugin;
public GuildBankMenu(DirtGuildsPlugin plugin) {
this.plugin = plugin;
}
public void open(Player player, Guild guild) {
String title = plugin.getConfig().getString("gui.bank.title", "<gold>Guild Bank</gold>");
int size = plugin.getConfig().getInt("gui.bank.size", 27);
Inventory inventory = Bukkit.createInventory(null, size, MessageUtil.mm(title));
inventory.setItem(11, createItem(Material.LIME_DYE, "<green>Deposit 100</green>", List.of(
"<gray>Click to deposit <green>100</green> into the guild bank.</gray>"
)));
inventory.setItem(13, createItem(Material.GOLD_INGOT, "<gold>Bank Balance</gold>", List.of(
"<gray>Current Balance:</gray> <green>" + DECIMAL_FORMAT.format(guild.getBankBalance()) + "</green>"
)));
inventory.setItem(15, createItem(Material.RED_DYE, "<red>Withdraw 100</red>", List.of(
"<gray>Click to withdraw <red>100</red> from the guild bank.</gray>"
)));
inventory.setItem(22, createItem(Material.ARROW, "<yellow>Back</yellow>", List.of(
"<gray>Return to the main guild menu.</gray>"
)));
player.openInventory(inventory);
}
private ItemStack createItem(Material material, String name, List<String> loreLines) {
ItemStack item = new ItemStack(material);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.displayName(plugin.getMessageUtil().parse(name));
List<Component> lore = new ArrayList<>();
for (String line : loreLines) {
lore.add(plugin.getMessageUtil().parse(line));
}
meta.lore(lore);
item.setItemMeta(meta);
}
return item;
}
}
@@ -0,0 +1,82 @@
package com.bitnix.dirtguilds.gui;
import com.bitnix.dirtguilds.DirtGuildsPlugin;
import com.bitnix.dirtguilds.guild.Guild;
import com.bitnix.dirtguilds.util.MessageUtil;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
public class GuildLevelsMenu {
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,##0.00");
private final DirtGuildsPlugin plugin;
public GuildLevelsMenu(DirtGuildsPlugin plugin) {
this.plugin = plugin;
}
public void open(Player player, Guild guild) {
String title = plugin.getConfig().getString("gui.levels.title", "<gold>Guild Levels</gold>");
int size = plugin.getConfig().getInt("gui.levels.size", 27);
Inventory inventory = Bukkit.createInventory(null, size, MessageUtil.mm(title));
int currentLevel = guild.getLevel();
int maxLevel = plugin.getGuildManager().getMaxConfiguredLevel();
double nextCost = plugin.getGuildManager().getNextLevelCost(guild);
inventory.setItem(11, createItem(Material.EXPERIENCE_BOTTLE, "<gold>Current Level</gold>", List.of(
"<gray>Level:</gray> <yellow>" + currentLevel + "</yellow>",
"<gray>Vault Slots:</gray> <yellow>" + guild.getVaultContents().length + "</yellow>"
)));
if (currentLevel >= maxLevel || nextCost < 0) {
inventory.setItem(13, createItem(Material.BARRIER, "<red>Max Level</red>", List.of(
"<gray>Your guild is already max level.</gray>"
)));
} else {
int nextVaultSlots = plugin.getGuildManager().getVaultSizeForLevel(currentLevel + 1);
inventory.setItem(13, createItem(Material.EMERALD, "<green>Upgrade Guild</green>", List.of(
"<gray>Next Level:</gray> <yellow>" + (currentLevel + 1) + "</yellow>",
"<gray>Cost:</gray> <green>" + DECIMAL_FORMAT.format(nextCost) + "</green>",
"<gray>Vault Slots:</gray> <yellow>" + nextVaultSlots + "</yellow>",
"<gray>Click to upgrade</gray>"
)));
}
inventory.setItem(22, createItem(Material.ARROW, "<yellow>Back</yellow>", List.of(
"<gray>Return to the main menu.</gray>"
)));
player.openInventory(inventory);
}
private ItemStack createItem(Material material, String name, List<String> loreLines) {
ItemStack item = new ItemStack(material);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.displayName(plugin.getMessageUtil().parse(name));
List<Component> lore = new ArrayList<>();
for (String line : loreLines) {
lore.add(plugin.getMessageUtil().parse(line));
}
meta.lore(lore);
item.setItemMeta(meta);
}
return item;
}
}
@@ -0,0 +1,101 @@
package com.bitnix.dirtguilds.gui;
import com.bitnix.dirtguilds.DirtGuildsPlugin;
import com.bitnix.dirtguilds.guild.Guild;
import com.bitnix.dirtguilds.util.MessageUtil;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
public class GuildMainMenu {
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,##0.00");
private final DirtGuildsPlugin plugin;
public GuildMainMenu(DirtGuildsPlugin plugin) {
this.plugin = plugin;
}
public void open(Player player) {
String title = plugin.getConfig().getString("gui.main.title", "<gold>DirtGuilds</gold>");
int size = plugin.getConfig().getInt("gui.main.size", 27);
Inventory inventory = Bukkit.createInventory(null, size, MessageUtil.mm(title));
Guild guild = plugin.getGuildManager().getGuildByPlayer(player.getUniqueId());
if (guild == null) {
inventory.setItem(11, createItem(Material.EMERALD, "<green>Create Guild</green>", List.of(
"<gray>Use:</gray> <white>/guild create <name></white>"
)));
inventory.setItem(15, createItem(Material.WRITABLE_BOOK, "<yellow>Join Guild</yellow>", List.of(
"<gray>Use:</gray> <white>/guild accept <guild></white>"
)));
} else {
inventory.setItem(10, createItem(Material.NAME_TAG, "<gold>Guild Info</gold>", List.of(
"<gray>Name:</gray> <yellow>" + guild.getName() + "</yellow>",
"<gray>Prefix:</gray> <yellow>" + guild.getPrefix() + "</yellow>",
"<gray>Level:</gray> <yellow>" + guild.getLevel() + "</yellow>",
"<gray>Owner:</gray> <yellow>" + Bukkit.getOfflinePlayer(guild.getOwner()).getName() + "</yellow>"
)));
inventory.setItem(11, createItem(Material.CHEST, "<gold>Guild Bank</gold>", List.of(
"<gray>Balance:</gray> <green>" + DECIMAL_FORMAT.format(guild.getBankBalance()) + "</green>",
"<gray>Click to open bank menu.</gray>"
)));
inventory.setItem(12, createItem(Material.BARREL, "<gold>Guild Vault</gold>", List.of(
"<gray>Slots:</gray> <yellow>" + guild.getVaultContents().length + "</yellow>",
"<gray>Click to open guild vault.</gray>"
)));
inventory.setItem(13, createItem(Material.EXPERIENCE_BOTTLE, "<gold>Guild Levels</gold>", List.of(
"<gray>Current Level:</gray> <yellow>" + guild.getLevel() + "</yellow>",
"<gray>Click to view upgrades.</gray>"
)));
inventory.setItem(14, createItem(Material.IRON_SWORD, "<gold>Friendly Fire</gold>", List.of(
guild.isFriendlyFire() ? "<green>Enabled</green>" : "<red>Disabled</red>",
"<gray>Click to toggle</gray>"
)));
inventory.setItem(15, createItem(Material.PLAYER_HEAD, "<gold>Members</gold>", List.of(
"<gray>Members:</gray> <yellow>" + guild.getMemberRanks().size() + "</yellow>",
"<gray>Click to view members</gray>"
)));
inventory.setItem(16, createItem(Material.PAPER, "<gold>Guild Chat</gold>", List.of(
plugin.getGuildManager().isGuildChatEnabled(player.getUniqueId()) ? "<green>Enabled</green>" : "<red>Disabled</red>",
"<gray>Click to toggle guild chat</gray>"
)));
}
player.openInventory(inventory);
}
private ItemStack createItem(Material material, String name, List<String> loreLines) {
ItemStack item = new ItemStack(material);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.displayName(plugin.getMessageUtil().parse(name));
List<Component> lore = new ArrayList<>();
for (String line : loreLines) {
lore.add(plugin.getMessageUtil().parse(line));
}
meta.lore(lore);
item.setItemMeta(meta);
}
return item;
}
}
@@ -0,0 +1,79 @@
package com.bitnix.dirtguilds.gui;
import com.bitnix.dirtguilds.DirtGuildsPlugin;
import com.bitnix.dirtguilds.guild.Guild;
import com.bitnix.dirtguilds.util.MessageUtil;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class GuildMemberManageMenu {
private final DirtGuildsPlugin plugin;
public GuildMemberManageMenu(DirtGuildsPlugin plugin) {
this.plugin = plugin;
}
public void open(Player viewer, Guild guild, UUID targetUuid) {
String title = plugin.getConfig().getString("gui.member-manage.title", "<gold>Manage Member</gold>");
int size = plugin.getConfig().getInt("gui.member-manage.size", 27);
Inventory inventory = Bukkit.createInventory(null, size, MessageUtil.mm(title));
OfflinePlayer target = Bukkit.getOfflinePlayer(targetUuid);
String name = target.getName() == null ? targetUuid.toString() : target.getName();
String rank = guild.getRank(targetUuid);
inventory.setItem(10, createItem(Material.ENDER_PEARL, "<green>Teleport</green>", List.of(
"<gray>Teleport to:</gray> <yellow>" + name + "</yellow>"
)));
inventory.setItem(12, createItem(Material.CHEST, "<gold>View Inventory</gold>", List.of(
"<gray>Open inventory of:</gray> <yellow>" + name + "</yellow>"
)));
inventory.setItem(14, createItem(Material.NAME_TAG, "<yellow>Cycle Rank</yellow>", List.of(
"<gray>Current Rank:</gray> <yellow>" + rank + "</yellow>",
"<gray>Click to cycle rank.</gray>"
)));
inventory.setItem(16, createItem(Material.BARRIER, "<red>Kick Member</red>", List.of(
"<gray>Remove:</gray> <yellow>" + name + "</yellow>"
)));
inventory.setItem(22, createItem(Material.ARROW, "<yellow>Back</yellow>", List.of(
"<gray>Return to members menu.</gray>"
)));
viewer.openInventory(inventory);
}
private ItemStack createItem(Material material, String name, List<String> loreLines) {
ItemStack item = new ItemStack(material);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.displayName(plugin.getMessageUtil().parse(name));
List<Component> lore = new ArrayList<>();
for (String line : loreLines) {
lore.add(plugin.getMessageUtil().parse(line));
}
meta.lore(lore);
item.setItemMeta(meta);
}
return item;
}
}
@@ -0,0 +1,94 @@
package com.bitnix.dirtguilds.gui;
import com.bitnix.dirtguilds.DirtGuildsPlugin;
import com.bitnix.dirtguilds.guild.Guild;
import com.bitnix.dirtguilds.util.MessageUtil;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
public class GuildMembersMenu {
private final DirtGuildsPlugin plugin;
public GuildMembersMenu(DirtGuildsPlugin plugin) {
this.plugin = plugin;
}
public void open(Player viewer, Guild guild) {
String title = plugin.getConfig().getString("gui.members.title", "<gold>Guild Members</gold>");
int size = plugin.getConfig().getInt("gui.members.size", 54);
Inventory inventory = Bukkit.createInventory(null, size, MessageUtil.mm(title));
List<UUID> members = new ArrayList<>(guild.getMemberRanks().keySet());
members.sort(Comparator.comparing(uuid -> {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid);
String name = offlinePlayer.getName();
return name == null ? uuid.toString() : name.toLowerCase();
}));
int slot = 0;
for (UUID memberId : members) {
if (slot >= size - 9) {
break;
}
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(memberId);
inventory.setItem(slot, createMemberHead(guild, offlinePlayer));
slot++;
}
inventory.setItem(size - 5, createBackItem());
viewer.openInventory(inventory);
}
private ItemStack createMemberHead(Guild guild, OfflinePlayer offlinePlayer) {
ItemStack item = new ItemStack(Material.PLAYER_HEAD);
SkullMeta meta = (SkullMeta) item.getItemMeta();
if (meta != null) {
meta.setOwningPlayer(offlinePlayer);
String playerName = offlinePlayer.getName() == null ? offlinePlayer.getUniqueId().toString() : offlinePlayer.getName();
boolean owner = offlinePlayer.getUniqueId().equals(guild.getOwner());
boolean online = offlinePlayer.isOnline();
String rank = guild.getRank(offlinePlayer.getUniqueId());
meta.displayName(plugin.getMessageUtil().parse(
(owner ? "<gold>" : "<yellow>") + playerName + (owner ? " <gray>(Owner)</gray>" : "")
));
List<Component> lore = new ArrayList<>();
lore.add(plugin.getMessageUtil().parse("<gray>Status:</gray> " + (online ? "<green>Online</green>" : "<red>Offline</red>")));
lore.add(plugin.getMessageUtil().parse("<gray>Rank:</gray> <yellow>" + rank + "</yellow>"));
lore.add(plugin.getMessageUtil().parse("<dark_gray>UUID:" + offlinePlayer.getUniqueId() + "</dark_gray>"));
lore.add(plugin.getMessageUtil().parse("<gray>Click to manage member.</gray>"));
meta.lore(lore);
item.setItemMeta(meta);
}
return item;
}
private ItemStack createBackItem() {
ItemStack item = new ItemStack(Material.ARROW);
var meta = item.getItemMeta();
if (meta != null) {
meta.displayName(plugin.getMessageUtil().parse("<yellow>Back</yellow>"));
item.setItemMeta(meta);
}
return item;
}
}
@@ -0,0 +1,32 @@
package com.bitnix.dirtguilds.gui;
import com.bitnix.dirtguilds.DirtGuildsPlugin;
import com.bitnix.dirtguilds.guild.Guild;
import com.bitnix.dirtguilds.util.MessageUtil;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
public class GuildVaultMenu {
private final DirtGuildsPlugin plugin;
public GuildVaultMenu(DirtGuildsPlugin plugin) {
this.plugin = plugin;
}
public void open(Player player, Guild guild) {
String title = plugin.getConfig().getString("gui.vault.title", "<gold>Guild Vault</gold>");
int size = guild.getVaultContents().length;
Inventory inventory = Bukkit.createInventory(player, size, MessageUtil.mm(title));
ItemStack[] contents = guild.getVaultContents();
for (int i = 0; i < Math.min(size, contents.length); i++) {
inventory.setItem(i, contents[i]);
}
player.openInventory(inventory);
}
}
@@ -0,0 +1,123 @@
package com.bitnix.dirtguilds.guild;
import org.bukkit.inventory.ItemStack;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class Guild {
private final UUID id;
private String name;
private String prefix;
private UUID owner;
private int level;
private double bankBalance;
private boolean friendlyFire;
private final Map<UUID, String> memberRanks;
private ItemStack[] vaultContents;
public Guild(UUID id, String name, String prefix, UUID owner, int level, double bankBalance, boolean friendlyFire, Map<UUID, String> memberRanks, ItemStack[] vaultContents) {
this.id = id;
this.name = name;
this.prefix = prefix;
this.owner = owner;
this.level = level;
this.bankBalance = bankBalance;
this.friendlyFire = friendlyFire;
this.memberRanks = new HashMap<>(memberRanks);
this.memberRanks.put(owner, "OWNER");
this.vaultContents = vaultContents == null ? new ItemStack[9] : vaultContents;
}
public UUID getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public UUID getOwner() {
return owner;
}
public void setOwner(UUID owner) {
this.owner = owner;
this.memberRanks.put(owner, "OWNER");
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public double getBankBalance() {
return bankBalance;
}
public void setBankBalance(double bankBalance) {
this.bankBalance = bankBalance;
}
public boolean isFriendlyFire() {
return friendlyFire;
}
public void setFriendlyFire(boolean friendlyFire) {
this.friendlyFire = friendlyFire;
}
public Map<UUID, String> getMemberRanks() {
return memberRanks;
}
public boolean isMember(UUID uuid) {
return memberRanks.containsKey(uuid);
}
public void addMember(UUID uuid) {
memberRanks.putIfAbsent(uuid, "MEMBER");
}
public void removeMember(UUID uuid) {
memberRanks.remove(uuid);
}
public String getRank(UUID uuid) {
return memberRanks.getOrDefault(uuid, "MEMBER");
}
public void setRank(UUID uuid, String rank) {
memberRanks.put(uuid, rank);
}
public boolean canManage(UUID uuid) {
String rank = getRank(uuid).toUpperCase();
return uuid.equals(owner) || rank.equals("OWNER") || rank.equals("ADMIN") || rank.equals("MOD");
}
public ItemStack[] getVaultContents() {
return vaultContents;
}
public void setVaultContents(ItemStack[] vaultContents) {
this.vaultContents = vaultContents;
}
}
@@ -0,0 +1,38 @@
package com.bitnix.dirtguilds.guild;
import java.util.UUID;
public class GuildInvite {
private final UUID guildId;
private final UUID target;
private final UUID inviter;
private final long expiresAt;
public GuildInvite(UUID guildId, UUID target, UUID inviter, long expiresAt) {
this.guildId = guildId;
this.target = target;
this.inviter = inviter;
this.expiresAt = expiresAt;
}
public UUID getGuildId() {
return guildId;
}
public UUID getTarget() {
return target;
}
public UUID getInviter() {
return inviter;
}
public long getExpiresAt() {
return expiresAt;
}
public boolean isExpired() {
return System.currentTimeMillis() > expiresAt;
}
}
@@ -0,0 +1,479 @@
package com.bitnix.dirtguilds.guild;
import com.bitnix.dirtguilds.DirtGuildsPlugin;
import com.bitnix.dirtguilds.data.DatabaseManager;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.io.BukkitObjectInputStream;
import org.bukkit.util.io.BukkitObjectOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Base64;
public class GuildManager {
private final DirtGuildsPlugin plugin;
private final DatabaseManager databaseManager;
private final Map<UUID, Guild> guildsById;
private final Map<String, UUID> guildIdsByLowerName;
private final Map<UUID, UUID> playerGuildMap;
private final Map<UUID, List<GuildInvite>> invitesByTarget;
private final Set<UUID> guildChatEnabled;
public GuildManager(DirtGuildsPlugin plugin, DatabaseManager databaseManager) {
this.plugin = plugin;
this.databaseManager = databaseManager;
this.guildsById = new ConcurrentHashMap<>();
this.guildIdsByLowerName = new ConcurrentHashMap<>();
this.playerGuildMap = new ConcurrentHashMap<>();
this.invitesByTarget = new ConcurrentHashMap<>();
this.guildChatEnabled = ConcurrentHashMap.newKeySet();
}
public void loadAll() {
guildsById.clear();
guildIdsByLowerName.clear();
playerGuildMap.clear();
invitesByTarget.clear();
guildChatEnabled.clear();
try (Connection connection = databaseManager.getConnection()) {
Map<UUID, Map<UUID, String>> memberMap = new HashMap<>();
Map<UUID, ItemStack[]> vaultMap = new HashMap<>();
try (PreparedStatement statement = connection.prepareStatement("SELECT guild_id, player_uuid, rank_name FROM guild_members");
ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
UUID guildId = UUID.fromString(resultSet.getString("guild_id"));
UUID playerUuid = UUID.fromString(resultSet.getString("player_uuid"));
String rankName = resultSet.getString("rank_name");
memberMap.computeIfAbsent(guildId, ignored -> new HashMap<>()).put(playerUuid, rankName);
playerGuildMap.put(playerUuid, guildId);
}
}
try (PreparedStatement statement = connection.prepareStatement("SELECT guild_id, slot_index, item_data FROM guild_vault_items");
ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
UUID guildId = UUID.fromString(resultSet.getString("guild_id"));
int slot = resultSet.getInt("slot_index");
String itemData = resultSet.getString("item_data");
ItemStack[] contents = vaultMap.computeIfAbsent(guildId, ignored -> new ItemStack[54]);
contents[slot] = deserializeItem(itemData);
}
}
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM guilds");
ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
UUID id = UUID.fromString(resultSet.getString("id"));
String name = resultSet.getString("name");
String prefix = resultSet.getString("prefix");
UUID owner = UUID.fromString(resultSet.getString("owner"));
int level = resultSet.getInt("level");
double bankBalance = resultSet.getDouble("bank_balance");
boolean friendlyFire = resultSet.getBoolean("friendly_fire");
ItemStack[] vault = vaultMap.getOrDefault(id, new ItemStack[getVaultSizeForLevel(level)]);
Guild guild = new Guild(
id,
name,
prefix,
owner,
level,
bankBalance,
friendlyFire,
memberMap.getOrDefault(id, new HashMap<>()),
normalizeVault(vault, getVaultSizeForLevel(level))
);
guildsById.put(id, guild);
guildIdsByLowerName.put(name.toLowerCase(Locale.ROOT), id);
for (UUID member : guild.getMemberRanks().keySet()) {
playerGuildMap.put(member, id);
}
}
}
} catch (Exception exception) {
throw new RuntimeException("Failed to load guild data", exception);
}
}
public Guild getGuildByPlayer(UUID playerUuid) {
UUID guildId = playerGuildMap.get(playerUuid);
return guildId == null ? null : guildsById.get(guildId);
}
public Guild getGuildByName(String name) {
if (name == null) {
return null;
}
UUID guildId = guildIdsByLowerName.get(name.toLowerCase(Locale.ROOT));
return guildId == null ? null : guildsById.get(guildId);
}
public Guild getGuildById(UUID guildId) {
return guildsById.get(guildId);
}
public boolean isInGuild(UUID playerUuid) {
return playerGuildMap.containsKey(playerUuid);
}
public boolean guildNameExists(String name) {
return guildIdsByLowerName.containsKey(name.toLowerCase(Locale.ROOT));
}
public Guild createGuild(UUID owner, String name) {
UUID guildId = UUID.randomUUID();
int defaultLevel = plugin.getConfig().getInt("settings.levels.default-level", 1);
int vaultSize = getVaultSizeForLevel(defaultLevel);
Guild guild = new Guild(
guildId,
name,
name,
owner,
defaultLevel,
0.0D,
plugin.getConfig().getBoolean("settings.default-friendly-fire", false),
new HashMap<>(),
new ItemStack[vaultSize]
);
guildsById.put(guildId, guild);
guildIdsByLowerName.put(name.toLowerCase(Locale.ROOT), guildId);
playerGuildMap.put(owner, guildId);
saveGuild(guild);
saveMembers(guild);
saveVault(guild);
return guild;
}
public void deleteGuild(Guild guild) {
guildsById.remove(guild.getId());
guildIdsByLowerName.remove(guild.getName().toLowerCase(Locale.ROOT));
for (UUID member : new HashSet<>(guild.getMemberRanks().keySet())) {
playerGuildMap.remove(member);
guildChatEnabled.remove(member);
}
invitesByTarget.values().forEach(list -> list.removeIf(invite -> invite.getGuildId().equals(guild.getId())));
try (Connection connection = databaseManager.getConnection()) {
try (PreparedStatement statement = connection.prepareStatement("DELETE FROM guild_members WHERE guild_id = ?")) {
statement.setString(1, guild.getId().toString());
statement.executeUpdate();
}
try (PreparedStatement statement = connection.prepareStatement("DELETE FROM guild_vault_items WHERE guild_id = ?")) {
statement.setString(1, guild.getId().toString());
statement.executeUpdate();
}
try (PreparedStatement statement = connection.prepareStatement("DELETE FROM guilds WHERE id = ?")) {
statement.setString(1, guild.getId().toString());
statement.executeUpdate();
}
} catch (SQLException exception) {
throw new RuntimeException("Failed to delete guild", exception);
}
}
public void renameGuild(Guild guild, String newName) {
guildIdsByLowerName.remove(guild.getName().toLowerCase(Locale.ROOT));
guild.setName(newName);
guildIdsByLowerName.put(newName.toLowerCase(Locale.ROOT), guild.getId());
saveGuild(guild);
}
public void setPrefix(Guild guild, String prefix) {
guild.setPrefix(prefix);
saveGuild(guild);
}
public void setFriendlyFire(Guild guild, boolean enabled) {
guild.setFriendlyFire(enabled);
saveGuild(guild);
}
public void addMember(Guild guild, UUID playerUuid) {
guild.addMember(playerUuid);
playerGuildMap.put(playerUuid, guild.getId());
saveMembers(guild);
}
public void removeMember(Guild guild, UUID playerUuid) {
guild.removeMember(playerUuid);
playerGuildMap.remove(playerUuid);
guildChatEnabled.remove(playerUuid);
saveMembers(guild);
}
public void setMemberRank(Guild guild, UUID playerUuid, String rank) {
guild.setRank(playerUuid, rank);
saveMembers(guild);
}
public void transferOwnership(Guild guild, UUID newOwner) {
guild.setOwner(newOwner);
guild.setRank(newOwner, "OWNER");
playerGuildMap.put(newOwner, guild.getId());
saveGuild(guild);
saveMembers(guild);
}
public void deposit(Guild guild, double amount) {
guild.setBankBalance(guild.getBankBalance() + amount);
saveGuild(guild);
}
public boolean withdraw(Guild guild, double amount) {
if (guild.getBankBalance() < amount) {
return false;
}
guild.setBankBalance(guild.getBankBalance() - amount);
saveGuild(guild);
return true;
}
public boolean upgradeLevel(Guild guild) {
int nextLevel = guild.getLevel() + 1;
ConfigurationSection levelSection = plugin.getConfig().getConfigurationSection("settings.levels.levels." + nextLevel);
if (levelSection == null) {
return false;
}
double cost = levelSection.getDouble("cost", 0.0D);
if (guild.getBankBalance() < cost) {
return false;
}
guild.setBankBalance(guild.getBankBalance() - cost);
guild.setLevel(nextLevel);
guild.setVaultContents(normalizeVault(guild.getVaultContents(), getVaultSizeForLevel(nextLevel)));
saveGuild(guild);
saveVault(guild);
return true;
}
public int getMaxConfiguredLevel() {
ConfigurationSection section = plugin.getConfig().getConfigurationSection("settings.levels.levels");
if (section == null) {
return 1;
}
int max = 1;
for (String key : section.getKeys(false)) {
try {
max = Math.max(max, Integer.parseInt(key));
} catch (NumberFormatException ignored) {
}
}
return max;
}
public int getVaultSizeForLevel(int level) {
int configured = plugin.getConfig().getInt("settings.levels.levels." + level + ".vault-slots",
plugin.getConfig().getInt("settings.vault.default-size", 9));
configured = Math.max(9, configured);
configured = Math.min(plugin.getConfig().getInt("settings.vault.max-size", 54), configured);
int remainder = configured % 9;
if (remainder != 0) {
configured += (9 - remainder);
}
return Math.max(9, Math.min(54, configured));
}
public double getNextLevelCost(Guild guild) {
return plugin.getConfig().getDouble("settings.levels.levels." + (guild.getLevel() + 1) + ".cost", -1.0D);
}
public void createInvite(UUID target, UUID inviter, Guild guild) {
long expiresAt = System.currentTimeMillis() + (plugin.getConfig().getLong("settings.invite-expire-seconds", 120L) * 1000L);
GuildInvite invite = new GuildInvite(guild.getId(), target, inviter, expiresAt);
invitesByTarget.computeIfAbsent(target, ignored -> new ArrayList<>()).removeIf(existing -> existing.getGuildId().equals(guild.getId()));
invitesByTarget.computeIfAbsent(target, ignored -> new ArrayList<>()).add(invite);
}
public GuildInvite getInvite(UUID target, UUID guildId) {
List<GuildInvite> invites = invitesByTarget.get(target);
if (invites == null) {
return null;
}
invites.removeIf(GuildInvite::isExpired);
for (GuildInvite invite : invites) {
if (invite.getGuildId().equals(guildId)) {
return invite;
}
}
return null;
}
public void removeInvite(UUID target, UUID guildId) {
List<GuildInvite> invites = invitesByTarget.get(target);
if (invites == null) {
return;
}
invites.removeIf(invite -> invite.getGuildId().equals(guildId));
if (invites.isEmpty()) {
invitesByTarget.remove(target);
}
}
public boolean isGuildChatEnabled(UUID playerUuid) {
return guildChatEnabled.contains(playerUuid);
}
public void setGuildChatEnabled(UUID playerUuid, boolean enabled) {
if (enabled) {
guildChatEnabled.add(playerUuid);
} else {
guildChatEnabled.remove(playerUuid);
}
}
public void saveVaultFromInventory(Guild guild, Inventory inventory) {
ItemStack[] contents = new ItemStack[inventory.getSize()];
for (int i = 0; i < inventory.getSize(); i++) {
contents[i] = inventory.getItem(i);
}
guild.setVaultContents(contents);
saveVault(guild);
}
public Collection<Guild> getAllGuilds() {
return Collections.unmodifiableCollection(guildsById.values());
}
private void saveGuild(Guild guild) {
try (Connection connection = databaseManager.getConnection();
PreparedStatement statement = connection.prepareStatement("""
INSERT INTO guilds (id, name, prefix, owner, level, bank_balance, friendly_fire)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
name = excluded.name,
prefix = excluded.prefix,
owner = excluded.owner,
level = excluded.level,
bank_balance = excluded.bank_balance,
friendly_fire = excluded.friendly_fire
""")) {
statement.setString(1, guild.getId().toString());
statement.setString(2, guild.getName());
statement.setString(3, guild.getPrefix());
statement.setString(4, guild.getOwner().toString());
statement.setInt(5, guild.getLevel());
statement.setDouble(6, guild.getBankBalance());
statement.setBoolean(7, guild.isFriendlyFire());
statement.executeUpdate();
} catch (SQLException exception) {
throw new RuntimeException("Failed to save guild", exception);
}
}
private void saveMembers(Guild guild) {
try (Connection connection = databaseManager.getConnection()) {
try (PreparedStatement delete = connection.prepareStatement("DELETE FROM guild_members WHERE guild_id = ?")) {
delete.setString(1, guild.getId().toString());
delete.executeUpdate();
}
try (PreparedStatement insert = connection.prepareStatement("INSERT INTO guild_members (guild_id, player_uuid, rank_name) VALUES (?, ?, ?)")) {
for (Map.Entry<UUID, String> entry : guild.getMemberRanks().entrySet()) {
insert.setString(1, guild.getId().toString());
insert.setString(2, entry.getKey().toString());
insert.setString(3, entry.getValue());
insert.addBatch();
}
insert.executeBatch();
}
} catch (SQLException exception) {
throw new RuntimeException("Failed to save guild members", exception);
}
}
private void saveVault(Guild guild) {
try (Connection connection = databaseManager.getConnection()) {
try (PreparedStatement delete = connection.prepareStatement("DELETE FROM guild_vault_items WHERE guild_id = ?")) {
delete.setString(1, guild.getId().toString());
delete.executeUpdate();
}
try (PreparedStatement insert = connection.prepareStatement("INSERT INTO guild_vault_items (guild_id, slot_index, item_data) VALUES (?, ?, ?)")) {
ItemStack[] contents = guild.getVaultContents();
for (int i = 0; i < contents.length; i++) {
ItemStack item = contents[i];
if (item == null) {
continue;
}
insert.setString(1, guild.getId().toString());
insert.setInt(2, i);
insert.setString(3, serializeItem(item));
insert.addBatch();
}
insert.executeBatch();
}
} catch (Exception exception) {
throw new RuntimeException("Failed to save guild vault", exception);
}
}
private String serializeItem(ItemStack item) throws Exception {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream);
dataOutput.writeObject(item);
dataOutput.close();
return Base64.getEncoder().encodeToString(outputStream.toByteArray());
}
private ItemStack deserializeItem(String data) throws Exception {
byte[] bytes = Base64.getDecoder().decode(data);
BukkitObjectInputStream dataInput = new BukkitObjectInputStream(new ByteArrayInputStream(bytes));
ItemStack item = (ItemStack) dataInput.readObject();
dataInput.close();
return item;
}
private ItemStack[] normalizeVault(ItemStack[] original, int newSize) {
ItemStack[] normalized = new ItemStack[newSize];
if (original != null) {
System.arraycopy(original, 0, normalized, 0, Math.min(original.length, newSize));
}
return normalized;
}
public void shutdown() {
invitesByTarget.clear();
guildChatEnabled.clear();
guildsById.clear();
guildIdsByLowerName.clear();
playerGuildMap.clear();
}
}
@@ -0,0 +1,38 @@
package com.bitnix.dirtguilds.hook;
import com.bitnix.dirtguilds.DirtGuildsPlugin;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.Bukkit;
import org.bukkit.plugin.RegisteredServiceProvider;
public class EconomyHook {
private final DirtGuildsPlugin plugin;
private Economy economy;
public EconomyHook(DirtGuildsPlugin plugin) {
this.plugin = plugin;
}
public boolean setup() {
if (Bukkit.getPluginManager().getPlugin("Vault") == null) {
return false;
}
RegisteredServiceProvider<Economy> provider = Bukkit.getServicesManager().getRegistration(Economy.class);
if (provider == null) {
return false;
}
this.economy = provider.getProvider();
return this.economy != null;
}
public boolean isAvailable() {
return economy != null;
}
public Economy getEconomy() {
return economy;
}
}
@@ -0,0 +1,48 @@
package com.bitnix.dirtguilds.listener;
import com.bitnix.dirtguilds.DirtGuildsPlugin;
import com.bitnix.dirtguilds.guild.Guild;
import io.papermc.paper.event.player.AsyncChatEvent;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class ChatListener implements Listener {
private final DirtGuildsPlugin plugin;
public ChatListener(DirtGuildsPlugin plugin) {
this.plugin = plugin;
}
@EventHandler
public void onChat(AsyncChatEvent event) {
Player player = event.getPlayer();
if (!plugin.getGuildManager().isGuildChatEnabled(player.getUniqueId())) {
return;
}
Guild guild = plugin.getGuildManager().getGuildByPlayer(player.getUniqueId());
if (guild == null) {
plugin.getGuildManager().setGuildChatEnabled(player.getUniqueId(), false);
return;
}
event.setCancelled(true);
Component message = event.message();
Component format = plugin.getMessageUtil().parse(
"<gray>[<gold>Guild</gold>]</gray> <yellow>" + guild.getPrefix() + "</yellow> <white>" + player.getName() + "</white><gray>:</gray> "
);
for (Player online : Bukkit.getOnlinePlayers()) {
Guild targetGuild = plugin.getGuildManager().getGuildByPlayer(online.getUniqueId());
if (targetGuild != null && targetGuild.getId().equals(guild.getId())) {
online.sendMessage(format.append(message));
}
}
}
}
@@ -0,0 +1,369 @@
package com.bitnix.dirtguilds.listener;
import com.bitnix.dirtguilds.DirtGuildsPlugin;
import com.bitnix.dirtguilds.gui.GuildBankMenu;
import com.bitnix.dirtguilds.gui.GuildLevelsMenu;
import com.bitnix.dirtguilds.gui.GuildMainMenu;
import com.bitnix.dirtguilds.gui.GuildMemberManageMenu;
import com.bitnix.dirtguilds.gui.GuildMembersMenu;
import com.bitnix.dirtguilds.gui.GuildVaultMenu;
import com.bitnix.dirtguilds.guild.Guild;
import com.bitnix.dirtguilds.util.MessageUtil;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.HumanEntity;
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.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class GuiListener implements Listener {
private final DirtGuildsPlugin plugin;
private final PlainTextComponentSerializer plainText;
private final Map<UUID, UUID> selectedMemberTargets;
public GuiListener(DirtGuildsPlugin plugin) {
this.plugin = plugin;
this.plainText = PlainTextComponentSerializer.plainText();
this.selectedMemberTargets = new HashMap<>();
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
if (!(event.getWhoClicked() instanceof Player player)) {
return;
}
String openedTitle = plainText.serialize(event.getView().title());
String mainTitle = title("gui.main.title", "<gold>DirtGuilds</gold>");
String membersTitle = title("gui.members.title", "<gold>Guild Members</gold>");
String memberManageTitle = title("gui.member-manage.title", "<gold>Manage Member</gold>");
String bankTitle = title("gui.bank.title", "<gold>Guild Bank</gold>");
String vaultTitle = title("gui.vault.title", "<gold>Guild Vault</gold>");
String levelsTitle = title("gui.levels.title", "<gold>Guild Levels</gold>");
if (openedTitle.equals(mainTitle)) {
handleMainMenuClick(event, player);
return;
}
if (openedTitle.equals(membersTitle)) {
handleMembersMenuClick(event, player);
return;
}
if (openedTitle.equals(memberManageTitle)) {
handleMemberManageMenuClick(event, player);
return;
}
if (openedTitle.equals(bankTitle)) {
handleBankMenuClick(event, player);
return;
}
if (openedTitle.equals(vaultTitle)) {
handleVaultMenuClick(event);
return;
}
if (openedTitle.equals(levelsTitle)) {
handleLevelsMenuClick(event, player);
}
}
@EventHandler
public void onInventoryClose(InventoryCloseEvent event) {
HumanEntity human = event.getPlayer();
if (!(human instanceof Player player)) {
return;
}
String openedTitle = plainText.serialize(event.getView().title());
String vaultTitle = title("gui.vault.title", "<gold>Guild Vault</gold>");
if (!openedTitle.equals(vaultTitle)) {
return;
}
Guild guild = plugin.getGuildManager().getGuildByPlayer(player.getUniqueId());
if (guild == null) {
return;
}
plugin.getGuildManager().saveVaultFromInventory(guild, event.getInventory());
}
private void handleMainMenuClick(InventoryClickEvent event, Player player) {
event.setCancelled(true);
if (event.getCurrentItem() == null || event.getCurrentItem().getType().isAir()) {
return;
}
Guild guild = plugin.getGuildManager().getGuildByPlayer(player.getUniqueId());
switch (event.getCurrentItem().getType()) {
case EMERALD -> plugin.getMessageUtil().sendRaw(player, "<yellow>Use /guild create <name></yellow>");
case WRITABLE_BOOK -> plugin.getMessageUtil().sendRaw(player, "<yellow>Use /guild accept <guild></yellow>");
case CHEST -> {
if (guild != null) {
new GuildBankMenu(plugin).open(player, guild);
}
}
case BARREL -> {
if (guild != null) {
new GuildVaultMenu(plugin).open(player, guild);
}
}
case EXPERIENCE_BOTTLE -> {
if (guild != null) {
new GuildLevelsMenu(plugin).open(player, guild);
}
}
case IRON_SWORD -> {
if (guild != null) {
player.closeInventory();
player.performCommand("guild pvp");
}
}
case PLAYER_HEAD -> {
if (guild != null) {
new GuildMembersMenu(plugin).open(player, guild);
}
}
case PAPER -> {
if (guild != null) {
player.closeInventory();
player.performCommand("guild chat");
}
}
default -> {
}
}
}
private void handleMembersMenuClick(InventoryClickEvent event, Player player) {
event.setCancelled(true);
if (event.getCurrentItem() == null || event.getCurrentItem().getType().isAir()) {
return;
}
Guild guild = plugin.getGuildManager().getGuildByPlayer(player.getUniqueId());
if (guild == null) {
player.closeInventory();
return;
}
if (event.getCurrentItem().getType() == Material.ARROW) {
new GuildMainMenu(plugin).open(player);
return;
}
if (event.getCurrentItem().getType() != Material.PLAYER_HEAD) {
return;
}
UUID targetUuid = extractUuidFromItem(event.getCurrentItem().getItemMeta());
if (targetUuid == null || !guild.isMember(targetUuid)) {
return;
}
selectedMemberTargets.put(player.getUniqueId(), targetUuid);
new GuildMemberManageMenu(plugin).open(player, guild, targetUuid);
}
private void handleMemberManageMenuClick(InventoryClickEvent event, Player player) {
event.setCancelled(true);
if (event.getCurrentItem() == null || event.getCurrentItem().getType().isAir()) {
return;
}
Guild guild = plugin.getGuildManager().getGuildByPlayer(player.getUniqueId());
if (guild == null) {
player.closeInventory();
return;
}
UUID targetUuid = selectedMemberTargets.get(player.getUniqueId());
if (targetUuid == null || !guild.isMember(targetUuid)) {
new GuildMembersMenu(plugin).open(player, guild);
return;
}
if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
plugin.getMessageUtil().send(player, "messages.no-permission");
return;
}
switch (event.getCurrentItem().getType()) {
case ENDER_PEARL -> {
Player target = Bukkit.getPlayer(targetUuid);
if (target == null) {
plugin.getMessageUtil().send(player, "messages.teleport-offline");
return;
}
player.teleport(target.getLocation());
plugin.getMessageUtil().send(player, "messages.teleport-success", "%player%", target.getName());
}
case CHEST -> {
Player target = Bukkit.getPlayer(targetUuid);
if (target == null) {
plugin.getMessageUtil().send(player, "messages.teleport-offline");
return;
}
Inventory copy = Bukkit.createInventory(null, 54, MessageUtil.mm("<gold>Inventory: " + target.getName() + "</gold>"));
copy.setContents(target.getInventory().getContents());
player.openInventory(copy);
plugin.getMessageUtil().send(player, "messages.inventory-opened", "%player%", target.getName());
}
case NAME_TAG -> {
if (targetUuid.equals(guild.getOwner())) {
return;
}
String current = guild.getRank(targetUuid).toUpperCase();
String next = switch (current) {
case "MEMBER" -> "MOD";
case "MOD" -> "ADMIN";
case "ADMIN" -> "MEMBER";
default -> "MEMBER";
};
plugin.getGuildManager().setMemberRank(guild, targetUuid, next);
OfflinePlayer target = Bukkit.getOfflinePlayer(targetUuid);
String name = target.getName() == null ? targetUuid.toString() : target.getName();
plugin.getMessageUtil().send(player, "messages.rank-updated", "%player%", name, "%rank%", next);
new GuildMemberManageMenu(plugin).open(player, guild, targetUuid);
}
case BARRIER -> {
if (targetUuid.equals(guild.getOwner())) {
return;
}
plugin.getGuildManager().removeMember(guild, targetUuid);
selectedMemberTargets.remove(player.getUniqueId());
new GuildMembersMenu(plugin).open(player, guild);
}
case ARROW -> new GuildMembersMenu(plugin).open(player, guild);
default -> {
}
}
}
private void handleBankMenuClick(InventoryClickEvent event, Player player) {
event.setCancelled(true);
if (event.getCurrentItem() == null || event.getCurrentItem().getType().isAir()) {
return;
}
Guild guild = plugin.getGuildManager().getGuildByPlayer(player.getUniqueId());
if (guild == null) {
player.closeInventory();
return;
}
switch (event.getCurrentItem().getType()) {
case LIME_DYE -> {
player.closeInventory();
player.performCommand("guild bank deposit 100");
}
case RED_DYE -> {
player.closeInventory();
player.performCommand("guild bank withdraw 100");
}
case ARROW -> new GuildMainMenu(plugin).open(player);
default -> {
}
}
}
private void handleVaultMenuClick(InventoryClickEvent event) {
// Guild vault should be editable, so do not cancel.
}
private void handleLevelsMenuClick(InventoryClickEvent event, Player player) {
event.setCancelled(true);
if (event.getCurrentItem() == null || event.getCurrentItem().getType().isAir()) {
return;
}
Guild guild = plugin.getGuildManager().getGuildByPlayer(player.getUniqueId());
if (guild == null) {
player.closeInventory();
return;
}
switch (event.getCurrentItem().getType()) {
case EMERALD -> {
if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
plugin.getMessageUtil().send(player, "messages.no-permission");
return;
}
int oldLevel = guild.getLevel();
boolean success = plugin.getGuildManager().upgradeLevel(guild);
if (!success) {
if (oldLevel >= plugin.getGuildManager().getMaxConfiguredLevel()) {
plugin.getMessageUtil().send(player, "messages.level-maxed");
} else {
plugin.getMessageUtil().send(player, "messages.level-not-enough-money");
}
return;
}
plugin.getMessageUtil().send(player, "messages.level-upgraded", "%level%", String.valueOf(guild.getLevel()));
new GuildLevelsMenu(plugin).open(player, guild);
}
case ARROW -> new GuildMainMenu(plugin).open(player);
default -> {
}
}
}
private UUID extractUuidFromItem(ItemMeta meta) {
if (meta == null || meta.lore() == null) {
return null;
}
List<String> loreLines = meta.lore().stream().map(plainText::serialize).toList();
for (String line : loreLines) {
if (line.startsWith("UUID:")) {
try {
return UUID.fromString(line.substring("UUID:".length()).trim());
} catch (IllegalArgumentException ignored) {
return null;
}
}
}
return null;
}
private String title(String path, String fallback) {
return plainText.serialize(MessageUtil.mm(plugin.getConfig().getString(path, fallback)));
}
}
@@ -0,0 +1,50 @@
package com.bitnix.dirtguilds.listener;
import com.bitnix.dirtguilds.DirtGuildsPlugin;
import com.bitnix.dirtguilds.guild.Guild;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
public class GuildFriendlyFireListener implements Listener {
private final DirtGuildsPlugin plugin;
public GuildFriendlyFireListener(DirtGuildsPlugin plugin) {
this.plugin = plugin;
}
@EventHandler
public void onDamage(EntityDamageByEntityEvent event) {
Player attacker = getPlayer(event.getDamager());
Player victim = getPlayer(event.getEntity());
if (attacker == null || victim == null) {
return;
}
Guild attackerGuild = plugin.getGuildManager().getGuildByPlayer(attacker.getUniqueId());
Guild victimGuild = plugin.getGuildManager().getGuildByPlayer(victim.getUniqueId());
if (attackerGuild == null || victimGuild == null) {
return;
}
if (!attackerGuild.getId().equals(victimGuild.getId())) {
return;
}
if (!attackerGuild.isFriendlyFire()) {
event.setCancelled(true);
}
}
private Player getPlayer(Entity entity) {
if (entity instanceof Player player) {
return player;
}
return null;
}
}
@@ -0,0 +1,50 @@
package com.bitnix.dirtguilds.util;
import com.bitnix.dirtguilds.DirtGuildsPlugin;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.command.CommandSender;
public class MessageUtil {
private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
private final DirtGuildsPlugin plugin;
public MessageUtil(DirtGuildsPlugin plugin) {
this.plugin = plugin;
}
public static Component mm(String text) {
if (text == null) {
text = "";
}
return MINI_MESSAGE.deserialize(text);
}
public Component parse(String text) {
return mm(text);
}
public String raw(String path) {
return plugin.getConfig().getString(path, "");
}
public String format(String path, String... replacements) {
String text = raw(path);
for (int i = 0; i + 1 < replacements.length; i += 2) {
text = text.replace(replacements[i], replacements[i + 1]);
}
return raw("messages.prefix") + text;
}
public void send(CommandSender sender, String path, String... replacements) {
sender.sendMessage(parse(format(path, replacements)));
}
public void sendRaw(CommandSender sender, String text) {
sender.sendMessage(parse(text));
}
}
+123
View File
@@ -0,0 +1,123 @@
database:
type: SQLITE # SQLITE or MYSQL
sqlite:
file: guilds.db
mysql:
host: localhost
port: 3306
database: dirtguilds
username: root
password: change-me
properties: "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"
settings:
invite-expire-seconds: 120
default-friendly-fire: false
max-name-length: 16
min-name-length: 3
bank:
enabled: true
use-vault: true
vault:
default-size: 9
max-size: 54
levels:
enabled: true
default-level: 1
levels:
1:
cost: 0
vault-slots: 9
2:
cost: 1000
vault-slots: 18
3:
cost: 5000
vault-slots: 27
4:
cost: 15000
vault-slots: 36
5:
cost: 50000
vault-slots: 54
messages:
prefix: "<gray>[<gold>DirtGuilds</gold>]</gray> "
no-permission: "<red>You do not have permission.</red>"
players-only: "<red>Only players may use this command.</red>"
unknown-command: "<red>Unknown subcommand.</red>"
reload: "<green>DirtGuilds reloaded.</green>"
prefix-updated: "<green>Guild prefix updated.</green>"
guild-created: "<green>Guild <yellow>%guild%</yellow> created.</green>"
guild-deleted: "<red>Guild <yellow>%guild%</yellow> deleted.</red>"
guild-renamed: "<green>Guild renamed to <yellow>%guild%</yellow>.</green>"
joined-guild: "<green>You joined <yellow>%guild%</yellow>.</green>"
left-guild: "<yellow>You left your guild.</yellow>"
already-in-guild: "<red>You are already in a guild.</red>"
not-in-guild: "<red>You are not in a guild.</red>"
guild-not-found: "<red>Guild not found.</red>"
player-not-found: "<red>Player not found.</red>"
invite-sent: "<green>Invite sent to <yellow>%player%</yellow>.</green>"
invite-received: "<green>You were invited to join <yellow>%guild%</yellow>. Use <white>/guild accept %guild%</white>.</green>"
no-invite: "<red>You do not have an invite for that guild.</red>"
member-joined: "<green><yellow>%player%</yellow> joined the guild.</green>"
member-left: "<yellow><yellow>%player%</yellow> left the guild.</yellow>"
invalid-name: "<red>That guild name is invalid.</red>"
name-taken: "<red>That guild name is already taken.</red>"
must-be-owner: "<red>You must be the guild owner.</red>"
must-be-in-guild: "<red>You must be in a guild.</red>"
target-already-in-guild: "<red>That player is already in a guild.</red>"
invite-expired: "<red>That invite has expired.</red>"
friendly-fire-enabled: "<green>Guild friendly fire enabled.</green>"
friendly-fire-disabled: "<yellow>Guild friendly fire disabled.</yellow>"
guild-chat-enabled: "<green>Guild chat enabled.</green>"
guild-chat-disabled: "<yellow>Guild chat disabled.</yellow>"
bank-deposit: "<green>Deposited <yellow>%amount%</yellow> to guild bank.</green>"
bank-withdraw: "<green>Withdrew <yellow>%amount%</yellow> from guild bank.</green>"
bank-invalid-amount: "<red>Invalid amount.</red>"
bank-insufficient-funds: "<red>Your guild bank does not have enough funds.</red>"
bank-player-insufficient-funds: "<red>You do not have enough money.</red>"
bank-vault-unavailable: "<red>Vault economy is not available.</red>"
level-upgraded: "<green>Your guild upgraded to level <yellow>%level%</yellow>.</green>"
level-maxed: "<red>Your guild is already at max level.</red>"
level-not-enough-money: "<red>Your guild bank does not have enough money to level up.</red>"
teleport-success: "<green>Teleported to <yellow>%player%</yellow>.</green>"
teleport-offline: "<red>That member is offline.</red>"
inventory-opened: "<green>Opened inventory of <yellow>%player%</yellow>.</green>"
rank-updated: "<green>Updated rank for <yellow>%player%</yellow> to <yellow>%rank%</yellow>.</green>"
gui:
main:
title: "<gold>DirtGuilds</gold>"
size: 27
members:
title: "<gold>Guild Members</gold>"
size: 54
member-manage:
title: "<gold>Manage Member</gold>"
size: 27
bank:
title: "<gold>Guild Bank</gold>"
size: 27
vault:
title: "<gold>Guild Vault</gold>"
size: 54
levels:
title: "<gold>Guild Levels</gold>"
size: 27
+22
View File
@@ -0,0 +1,22 @@
name: DirtGuilds
version: 1.0.0
main: com.bitnix.dirtguilds.DirtGuildsPlugin
api-version: '1.21'
authors: [bitnix]
description: GUI-driven guilds plugin for Paper
softdepend: [PlaceholderAPI, Vault]
commands:
dirtguilds:
description: Main DirtGuilds command
usage: /dirtguilds
aliases: [dg, guild, guilds]
permissions:
dirtguilds.use:
description: Allows use of DirtGuilds
default: true
dirtguilds.admin:
description: DirtGuilds admin permission
default: op
Binary file not shown.
+123
View File
@@ -0,0 +1,123 @@
database:
type: SQLITE # SQLITE or MYSQL
sqlite:
file: guilds.db
mysql:
host: localhost
port: 3306
database: dirtguilds
username: root
password: change-me
properties: "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"
settings:
invite-expire-seconds: 120
default-friendly-fire: false
max-name-length: 16
min-name-length: 3
bank:
enabled: true
use-vault: true
vault:
default-size: 9
max-size: 54
levels:
enabled: true
default-level: 1
levels:
1:
cost: 0
vault-slots: 9
2:
cost: 1000
vault-slots: 18
3:
cost: 5000
vault-slots: 27
4:
cost: 15000
vault-slots: 36
5:
cost: 50000
vault-slots: 54
messages:
prefix: "<gray>[<gold>DirtGuilds</gold>]</gray> "
no-permission: "<red>You do not have permission.</red>"
players-only: "<red>Only players may use this command.</red>"
unknown-command: "<red>Unknown subcommand.</red>"
reload: "<green>DirtGuilds reloaded.</green>"
prefix-updated: "<green>Guild prefix updated.</green>"
guild-created: "<green>Guild <yellow>%guild%</yellow> created.</green>"
guild-deleted: "<red>Guild <yellow>%guild%</yellow> deleted.</red>"
guild-renamed: "<green>Guild renamed to <yellow>%guild%</yellow>.</green>"
joined-guild: "<green>You joined <yellow>%guild%</yellow>.</green>"
left-guild: "<yellow>You left your guild.</yellow>"
already-in-guild: "<red>You are already in a guild.</red>"
not-in-guild: "<red>You are not in a guild.</red>"
guild-not-found: "<red>Guild not found.</red>"
player-not-found: "<red>Player not found.</red>"
invite-sent: "<green>Invite sent to <yellow>%player%</yellow>.</green>"
invite-received: "<green>You were invited to join <yellow>%guild%</yellow>. Use <white>/guild accept %guild%</white>.</green>"
no-invite: "<red>You do not have an invite for that guild.</red>"
member-joined: "<green><yellow>%player%</yellow> joined the guild.</green>"
member-left: "<yellow><yellow>%player%</yellow> left the guild.</yellow>"
invalid-name: "<red>That guild name is invalid.</red>"
name-taken: "<red>That guild name is already taken.</red>"
must-be-owner: "<red>You must be the guild owner.</red>"
must-be-in-guild: "<red>You must be in a guild.</red>"
target-already-in-guild: "<red>That player is already in a guild.</red>"
invite-expired: "<red>That invite has expired.</red>"
friendly-fire-enabled: "<green>Guild friendly fire enabled.</green>"
friendly-fire-disabled: "<yellow>Guild friendly fire disabled.</yellow>"
guild-chat-enabled: "<green>Guild chat enabled.</green>"
guild-chat-disabled: "<yellow>Guild chat disabled.</yellow>"
bank-deposit: "<green>Deposited <yellow>%amount%</yellow> to guild bank.</green>"
bank-withdraw: "<green>Withdrew <yellow>%amount%</yellow> from guild bank.</green>"
bank-invalid-amount: "<red>Invalid amount.</red>"
bank-insufficient-funds: "<red>Your guild bank does not have enough funds.</red>"
bank-player-insufficient-funds: "<red>You do not have enough money.</red>"
bank-vault-unavailable: "<red>Vault economy is not available.</red>"
level-upgraded: "<green>Your guild upgraded to level <yellow>%level%</yellow>.</green>"
level-maxed: "<red>Your guild is already at max level.</red>"
level-not-enough-money: "<red>Your guild bank does not have enough money to level up.</red>"
teleport-success: "<green>Teleported to <yellow>%player%</yellow>.</green>"
teleport-offline: "<red>That member is offline.</red>"
inventory-opened: "<green>Opened inventory of <yellow>%player%</yellow>.</green>"
rank-updated: "<green>Updated rank for <yellow>%player%</yellow> to <yellow>%rank%</yellow>.</green>"
gui:
main:
title: "<gold>DirtGuilds</gold>"
size: 27
members:
title: "<gold>Guild Members</gold>"
size: 54
member-manage:
title: "<gold>Manage Member</gold>"
size: 27
bank:
title: "<gold>Guild Bank</gold>"
size: 27
vault:
title: "<gold>Guild Vault</gold>"
size: 54
levels:
title: "<gold>Guild Levels</gold>"
size: 27
+22
View File
@@ -0,0 +1,22 @@
name: DirtGuilds
version: 1.0.0
main: com.bitnix.dirtguilds.DirtGuildsPlugin
api-version: '1.21'
authors: [bitnix]
description: GUI-driven guilds plugin for Paper
softdepend: [PlaceholderAPI, Vault]
commands:
dirtguilds:
description: Main DirtGuilds command
usage: /dirtguilds
aliases: [dg, guild, guilds]
permissions:
dirtguilds.use:
description: Allows use of DirtGuilds
default: true
dirtguilds.admin:
description: DirtGuilds admin permission
default: op
+5
View File
@@ -0,0 +1,5 @@
#Generated by Maven
#Mon Jun 08 18:24:37 EDT 2026
artifactId=DirtGuilds
groupId=com.bitnix
version=1.0.0
@@ -0,0 +1,18 @@
com/bitnix/dirtguilds/util/MessageUtil.class
com/bitnix/dirtguilds/DirtGuildsPlugin.class
com/bitnix/dirtguilds/command/DirtGuildsCommand.class
com/bitnix/dirtguilds/listener/GuiListener.class
com/bitnix/dirtguilds/listener/GuiListener$1.class
com/bitnix/dirtguilds/gui/GuildBankMenu.class
com/bitnix/dirtguilds/listener/GuildFriendlyFireListener.class
com/bitnix/dirtguilds/gui/GuildLevelsMenu.class
com/bitnix/dirtguilds/gui/GuildMembersMenu.class
com/bitnix/dirtguilds/gui/GuildMainMenu.class
com/bitnix/dirtguilds/guild/GuildInvite.class
com/bitnix/dirtguilds/data/DatabaseManager.class
com/bitnix/dirtguilds/gui/GuildVaultMenu.class
com/bitnix/dirtguilds/hook/EconomyHook.class
com/bitnix/dirtguilds/gui/GuildMemberManageMenu.class
com/bitnix/dirtguilds/guild/Guild.class
com/bitnix/dirtguilds/listener/ChatListener.class
com/bitnix/dirtguilds/guild/GuildManager.class
@@ -0,0 +1,17 @@
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/DirtGuildsPlugin.java
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/command/DirtGuildsCommand.java
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/data/DatabaseManager.java
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/gui/GuildBankMenu.java
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/gui/GuildLevelsMenu.java
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/gui/GuildMainMenu.java
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/gui/GuildMemberManageMenu.java
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/gui/GuildMembersMenu.java
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/gui/GuildVaultMenu.java
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/guild/Guild.java
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/guild/GuildInvite.java
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/guild/GuildManager.java
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/hook/EconomyHook.java
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/listener/ChatListener.java
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/listener/GuiListener.java
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/listener/GuildFriendlyFireListener.java
/home/bitnix/Desktop/DirtGuilds/src/main/java/com/bitnix/dirtguilds/util/MessageUtil.java
Binary file not shown.