diff --git a/build.gradle b/build.gradle index d458cf5a..fcff641d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,14 @@ plugins { id 'java' id 'com.github.johnrengelman.shadow' version '7.1.2' - id("io.papermc.paperweight.userdev") version "1.3.3" + id("io.papermc.paperweight.userdev") version "1.3.7" id("xyz.jpenilla.run-paper") version "1.0.6" // Adds runServer and runMojangMappedServer tasks for testing - id 'net.minecrell.plugin-yml.bukkit' version '0.5.1' + id 'net.minecrell.plugin-yml.bukkit' version '0.5.2' } group = 'org.parallelmc' -version = '2.4.0' +version = '2.5.0' description = 'A set of utilities and features for use on the Parallel Minecraft server' java { @@ -29,22 +29,23 @@ repositories { maven { url = 'https://repo.extendedclip.com/content/repositories/placeholderapi/' } maven { url = 'https://repo.dmulloy2.net/repository/public/' } maven { url = 'https://repo.codemc.org/repository/maven-public/' } + maven { url = 'https://repo.rosewooddev.io/repository/public/' } } dependencies { - paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.18.2-R0.1-SNAPSHOT") + paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.19-R0.1-SNAPSHOT") - compileOnly "io.papermc.paper:paper-api:1.18.2-R0.1-SNAPSHOT" - implementation 'org.jetbrains:annotations:16.0.1' - testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.7.0' + compileOnly "io.papermc.paper:paper-api:1.19-R0.1-SNAPSHOT" + implementation 'org.jetbrains:annotations:23.0.0' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.2' implementation 'net.dv8tion:JDA:4.4.0_350' - compileOnly 'me.clip:placeholderapi:2.10.10' + compileOnly 'me.clip:placeholderapi:2.11.2' compileOnly fileTree('libs') { include '*.jar' } - compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.6' - implementation 'mysql:mysql-connector-java:8.0.26' - compileOnly 'net.luckperms:api:5.3' - compileOnly 'com.comphenix.protocol:ProtocolLib:4.7.0' - compileOnly 'dev.esophose:playerparticles:7.25' + compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.7' + implementation 'mysql:mysql-connector-java:8.0.29' + compileOnly 'net.luckperms:api:5.4' + compileOnly 'com.comphenix.protocol:ProtocolLib:4.8.0' + compileOnly 'dev.esophose:playerparticles:8.2' } artifacts { @@ -81,7 +82,7 @@ bukkit { main = 'parallelmc.parallelutils.Parallelutils' - apiVersion = '1.18' + apiVersion = '1.19' depend = ['PlaceholderAPI', 'ProtocolLib'] softDepend = ['Multiverse-Core', 'FractalForest', 'WorldGuard', 'VoteParty', 'PlayerParticles', 'ProNouns'] @@ -93,6 +94,12 @@ bukkit { permissionMessage = 'You do not have permission' aliases = ['pu',] } + chatroom { + description = 'Base command for all ChatRoom commands' + usage = '/cr' + permissionMessage = 'You do not have permission' + aliases = ['cr',] + } depositexp { description = 'Deposit exp into the player\' s ender chest ' usage = '/depositexp ' @@ -176,6 +183,12 @@ bukkit { permissionMessage = 'You do not have permission' permission = 'parallelutils.commandspy' } + chatroomspy { + description = 'Toggles chatroom spy' + usage = '/chatroomspy' + permissionMessage = 'You do not have permission' + permission = 'parallelutils.chatrooms.chatroomspy' + } mutechat { description = 'Mute or unmute the chat' usage = '/mutechat' @@ -329,7 +342,7 @@ bukkit { } 'parallelutils.bypass' { description = 'Bypasses some ParallelUtils modules' - childrenMap = ['parallelutils.bypass.anticaps': true, 'parallelutils.bypass.antislur': true, 'parallelutils.bypass.clearchat': true, 'parallelutils.bypass.socialspy': true, 'parallelutils.bypass.commandspy': true, 'parallelutils.bypass.mutechat': true,] + childrenMap = ['parallelutils.bypass.anticaps': true, 'parallelutils.bypass.antislur': true, 'parallelutils.bypass.clearchat': true, 'parallelutils.bypass.socialspy': true, 'parallelutils.bypass.commandspy': true, 'parallelutils.bypass.mutechat': true,'parallelutils.bypass.chestshops': true, 'parallelutils.bypass.chatroomspy': true] } 'parallelutils.bypass.anticaps' { description = 'Bypasses the Anti-Caps module' @@ -346,9 +359,15 @@ bukkit { 'parallelutils.bypass.commandspy' { description = 'Bypasses the Command Spy module' } + 'parallelutils.bypass.chatroomspy' { + description = 'Bypasses the ChatRoom Spy module' + } 'parallelutils.bypass.mutechat' { description = 'Bypasses chat being muted' } + 'parallelutils.bypass.chestshops' { + description = 'Bypasses chest shop protection' + } 'parallelutils.notify' { description = 'Be notified by some ParallelUtils modules' childrenMap = ['parallelutils.notify.antislur': true,] @@ -356,5 +375,15 @@ bukkit { 'parallelutils.notify.antislur' { description = 'Be notified of swearing' } + 'parallelutils.chestshops.create' { + description = 'Allows creation of Chest Shops' + } + 'parallelutils.chatrooms' { + description = 'Chatroom admin permissions' + childrenMap = ['parallelutils.chatrooms.chatroomspy': true,] + } + 'parallelutils.chatrooms.chatroomspy' { + description = 'View messages from all chatrooms' + } } } \ No newline at end of file diff --git a/src/main/java/parallelmc/parallelutils/Constants.java b/src/main/java/parallelmc/parallelutils/Constants.java index 6fd801e4..45c9afc5 100644 --- a/src/main/java/parallelmc/parallelutils/Constants.java +++ b/src/main/java/parallelmc/parallelutils/Constants.java @@ -2,7 +2,7 @@ public class Constants { - public static final Version VERSION = new Version(2, 4, 0); + public static final Version VERSION = new Version(2, 5, 0); public static final String PLUGIN_NAME = "ParallelUtils"; public static final String DEFAULT_WORLD = "world2"; } diff --git a/src/main/java/parallelmc/parallelutils/Parallelutils.java b/src/main/java/parallelmc/parallelutils/Parallelutils.java index f9a9a455..15629f63 100644 --- a/src/main/java/parallelmc/parallelutils/Parallelutils.java +++ b/src/main/java/parallelmc/parallelutils/Parallelutils.java @@ -8,12 +8,14 @@ import parallelmc.parallelutils.commands.*; import parallelmc.parallelutils.modules.charms.ParallelCharms; import parallelmc.parallelutils.modules.bitsandbobs.BitsAndBobs; +import parallelmc.parallelutils.modules.chestshops.ChestShops; import parallelmc.parallelutils.modules.custommobs.CustomMobs; import parallelmc.parallelutils.modules.customtrees.ParallelTrees; import parallelmc.parallelutils.modules.discordintegration.DiscordIntegration; import parallelmc.parallelutils.modules.expstorage.ExpStorage; import parallelmc.parallelutils.modules.gamemode4.beehiveInspector.BeehiveInspector; import parallelmc.parallelutils.modules.parallelchat.ParallelChat; +import parallelmc.parallelutils.modules.parallelchat.commands.chatrooms.*; import parallelmc.parallelutils.modules.parallelflags.ParallelFlags; import parallelmc.parallelutils.modules.parallelitems.ParallelItems; import parallelmc.parallelutils.modules.effectextender.EffectExtender; @@ -47,6 +49,8 @@ public final class Parallelutils extends JavaPlugin { private HashMap registeredModules; private Commands commands; + private ChatroomCommands chatroomCommands; + @Override public void onLoad() { registeredModules = new HashMap<>(); @@ -151,17 +155,34 @@ public void onEnable() { } commands = new Commands(); + chatroomCommands = new ChatroomCommands(); addCommand("help", new ParallelHelpCommand()); addCommand("test", new ParallelTestCommand()); addCommand("wait", new ParallelWaitCommand(this)); + addChatRoomCommand("create", new ParallelCreateChatroom()); + addChatRoomCommand("leave", new ParallelLeaveChatroom()); + addChatRoomCommand("join", new ParallelJoinChatroom()); + addChatRoomCommand("promote", new ParallelPromoteMember()); + addChatRoomCommand("demote", new ParallelDemoteMember()); + addChatRoomCommand("members", new ParallelListMembers()); + addChatRoomCommand("kick", new ParallelKickMember()); + addChatRoomCommand("list", new ParallelListChatrooms()); + addChatRoomCommand("invite", new ParallelSendInvite()); + addChatRoomCommand("accept", new ParallelAcceptInvite()); + addChatRoomCommand("disband", new ParallelDisbandChatroom()); + addChatRoomCommand("help", new ParallelHelpChatrooms()); + addChatRoomCommand("msg", new ParallelMsgChatroom()); + getCommand("parallelutils").setExecutor(commands); getCommand("parallelutils").setTabCompleter(commands); getCommand("pu").setExecutor(commands); getCommand("pu").setTabCompleter(commands); - - + getCommand("chatroom").setExecutor(chatroomCommands); + getCommand("chatroom").setTabCompleter(chatroomCommands); + getCommand("cr").setExecutor(chatroomCommands); + getCommand("cr").setTabCompleter(chatroomCommands); // Setup modules @@ -270,6 +291,14 @@ public void onEnable() { e.printStackTrace(); } + try { + ChestShops chestShops = new ChestShops(); + chestShops.onEnable(); + } catch (Exception e) { + Parallelutils.log(Level.SEVERE, "Error while enabling module ChestShops!"); + e.printStackTrace(); + } + // TODO: Make this not horrible try { ParallelModule flags = getModule("ParallelFlags"); @@ -423,6 +452,15 @@ public boolean addCommand(String name, ParallelCommand command) { return commands.addCommand(name, command); } + /** + * Wrapper for {@code parallelmc.parallelutils.modules.parallelchat.commands.chatrooms.ChatroomCommand.addCommand} + * Adds a new command to the commandmap + * @param name The name of the command + * @param command The command to be run when the name is called + * @return Returns true when the command was added successfully, false if the command already exists. + */ + public boolean addChatRoomCommand(String name, ChatroomCommand command) { return chatroomCommands.addCommand(name, command); } + /** * Wrapper for {@code parallelmc.parallelutils.commands.Commands.getCommands} * @return A deep copy of the command map @@ -431,6 +469,14 @@ public Map getCommands() { return commands.getCommands(); } + /** + * Wrapper for {@code parallelmc.parallelutils.modules.parallelchat.commands.chatrooms.ChatroomCommands#getCommands()} + * @return A deep copy of the command map + */ + public Map getChatroomCommands() { + return chatroomCommands.getCommands(); + } + /** * A helper method to log a message at a specific log level with the prefix "[ParallelUtils] " * @param level The level to log the message at diff --git a/src/main/java/parallelmc/parallelutils/commands/Commands.java b/src/main/java/parallelmc/parallelutils/commands/Commands.java index 16304079..ba7989e6 100644 --- a/src/main/java/parallelmc/parallelutils/commands/Commands.java +++ b/src/main/java/parallelmc/parallelutils/commands/Commands.java @@ -5,7 +5,7 @@ import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.command.*; -import org.bukkit.craftbukkit.v1_18_R2.command.ServerCommandSender; +import org.bukkit.craftbukkit.v1_19_R1.command.ServerCommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -88,7 +88,7 @@ public List onTabComplete(@NotNull CommandSender sender, @NotNull Comman String lowerName = command.getName().toLowerCase().strip(); - if (lowerName.equals("parallelutils") || lowerName.equals("pu") && args.length == 1) { + if ((lowerName.equals("parallelutils") || lowerName.equals("pu")) && args.length == 1) { // List every sub-command list.addAll(commandMap.keySet()); } else { diff --git a/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/KeepSpecialItems.java b/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/KeepSpecialItems.java index 313ffee6..d644b5d8 100644 --- a/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/KeepSpecialItems.java +++ b/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/KeepSpecialItems.java @@ -2,7 +2,7 @@ import net.minecraft.nbt.CompoundTag; import org.bukkit.Material; -import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; diff --git a/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/SpecialItems.java b/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/SpecialItems.java index b0e6f88c..d7f11b37 100644 --- a/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/SpecialItems.java +++ b/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/SpecialItems.java @@ -4,7 +4,7 @@ import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; diff --git a/src/main/java/parallelmc/parallelutils/modules/charms/handlers/impl/CharmParticleHandler.java b/src/main/java/parallelmc/parallelutils/modules/charms/handlers/impl/CharmParticleHandler.java index 5ac7e8c5..566852b9 100644 --- a/src/main/java/parallelmc/parallelutils/modules/charms/handlers/impl/CharmParticleHandler.java +++ b/src/main/java/parallelmc/parallelutils/modules/charms/handlers/impl/CharmParticleHandler.java @@ -4,7 +4,7 @@ import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.block.data.BlockData; -import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_19_R1.block.data.CraftBlockData; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.scheduler.BukkitRunnable; diff --git a/src/main/java/parallelmc/parallelutils/modules/charms/handlers/impl/CharmPlayerParticleHandler.java b/src/main/java/parallelmc/parallelutils/modules/charms/handlers/impl/CharmPlayerParticleHandler.java index 02cb915d..b2ea9b18 100644 --- a/src/main/java/parallelmc/parallelutils/modules/charms/handlers/impl/CharmPlayerParticleHandler.java +++ b/src/main/java/parallelmc/parallelutils/modules/charms/handlers/impl/CharmPlayerParticleHandler.java @@ -10,7 +10,7 @@ import dev.esophose.playerparticles.styles.ParticleStyle; import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryPlayer; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; diff --git a/src/main/java/parallelmc/parallelutils/modules/charms/listeners/PlayerJoinContainerListenerOverwrite.java b/src/main/java/parallelmc/parallelutils/modules/charms/listeners/PlayerJoinContainerListenerOverwrite.java index 0852aac8..a2047e99 100644 --- a/src/main/java/parallelmc/parallelutils/modules/charms/listeners/PlayerJoinContainerListenerOverwrite.java +++ b/src/main/java/parallelmc/parallelutils/modules/charms/listeners/PlayerJoinContainerListenerOverwrite.java @@ -5,7 +5,7 @@ import net.minecraft.world.inventory.*; import net.minecraft.world.item.ItemStack; import org.bukkit.Bukkit; -import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; diff --git a/src/main/java/parallelmc/parallelutils/modules/chestshops/ChestShops.java b/src/main/java/parallelmc/parallelutils/modules/chestshops/ChestShops.java new file mode 100644 index 00000000..f94dc33d --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/chestshops/ChestShops.java @@ -0,0 +1,327 @@ +package parallelmc.parallelutils.modules.chestshops; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Chest; +import org.bukkit.block.DoubleChest; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; +import parallelmc.parallelutils.Constants; +import parallelmc.parallelutils.ParallelModule; +import parallelmc.parallelutils.Parallelutils; +import parallelmc.parallelutils.modules.chestshops.events.*; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; + +import java.sql.*; +import java.util.*; +import java.util.logging.Level; + +public class ChestShops implements ParallelModule { + + private final HashMap> chestShops = new HashMap<>(); + private final HashMap shopPreviews = new HashMap<>(); + private final HashMap shoppingPlayers = new HashMap<>(); + + private Parallelutils puPlugin; + + private static ChestShops INSTANCE; + + // 26 stacks of 64 diamonds + private static final int MAX_DIAMONDS = 1664; + + @Override + public void onEnable() { + PluginManager manager = Bukkit.getPluginManager(); + Plugin plugin = manager.getPlugin(Constants.PLUGIN_NAME); + + if (plugin == null) { + Parallelutils.log(Level.SEVERE, "Unable to enable ChestShops. Plugin " + Constants.PLUGIN_NAME + + " does not exist!"); + return; + } + + this.puPlugin = (Parallelutils) plugin; + + if (!puPlugin.registerModule("ChestShops", this)) { + Parallelutils.log(Level.SEVERE, "Unable to register module ChestShops! " + + "Module may already be registered. Quitting..."); + return; + } + + try (Connection conn = puPlugin.getDbConn()) { + if (conn == null) throw new SQLException("Unable to establish connection!"); + Statement statement = conn.createStatement(); + statement.setQueryTimeout(15); + statement.execute(""" + create table if not exists ChestShops + ( + UUID varchar(36) not null, + World varchar(32) not null, + ChestX int not null, + ChestY int not null, + ChestZ int not null, + SignX int not null, + SignY int not null, + SignZ int not null, + Item varchar(50) not null, + SellAmt int not null, + BuyAmt int not null + );"""); + conn.commit(); + statement.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + + // load existing player config + try (Connection conn = puPlugin.getDbConn()) { + if (conn == null) throw new SQLException("Unable to establish connection!"); + Statement statement = conn.createStatement(); + statement.setQueryTimeout(60); + ResultSet results = statement.executeQuery("select * from ChestShops"); + while (results.next()) { + UUID uuid = UUID.fromString(results.getString("UUID")); + World world = puPlugin.getServer().getWorld(results.getString("World")); + Location chestLoc = new Location(world, results.getInt("ChestX"), results.getInt("ChestY"), results.getInt("ChestZ")); + Location signLoc = new Location(world, results.getInt("SignX"), results.getInt("SignY"), results.getInt("SignZ")); + Material item = Material.getMaterial(results.getString("Item")); + int sellAmt = results.getInt("SellAmt"); + int buyAmt = results.getInt("BuyAmt"); + addShop(uuid, chestLoc, signLoc, item, sellAmt, buyAmt); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + manager.registerEvents(new OnSignText(), puPlugin); + manager.registerEvents(new OnClickBlock(), puPlugin); + manager.registerEvents(new OnBreakShop(), puPlugin); + manager.registerEvents(new OnPreviewInteract(), puPlugin); + manager.registerEvents(new OnShopInteract(), puPlugin); + + INSTANCE = this; + } + + @Override + public void onDisable() { + try (Connection conn = puPlugin.getDbConn()) { + if (conn == null) throw new SQLException("Unable to establish connection!"); + // allow duplicate uuids since players can have multiple shops + PreparedStatement statement = conn.prepareStatement("INSERT INTO ChestShops (UUID, World, ChestX, ChestY, ChestZ, SignX, SignY, SignZ, Item, SellAmt, BuyAmt) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + statement.setQueryTimeout(30); + this.chestShops.forEach((u, o) -> { + o.forEach((s) -> { + try { + statement.setString(1, u.toString()); + statement.setString(2, s.chestPos().getWorld().getName()); + statement.setInt(3, s.chestPos().getBlockX()); + statement.setInt(4, s.chestPos().getBlockY()); + statement.setInt(5, s.chestPos().getBlockZ()); + statement.setInt(6, s.signPos().getBlockX()); + statement.setInt(7, s.signPos().getBlockY()); + statement.setInt(8, s.signPos().getBlockZ()); + statement.setString(9, s.item().toString()); + statement.setInt(10, s.sellAmt()); + statement.setInt(11, s.buyAmt()); + statement.addBatch(); + } catch (SQLException e) { + e.printStackTrace(); + } + }); + }); + statement.executeBatch(); + conn.commit(); + statement.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void addShop(UUID owner, Location chestPos, Location signPos, Material item, int sellAmt, int buyAmt) { + Shop shop = new Shop(owner, chestPos, signPos, item, sellAmt, buyAmt); + if (chestShops.containsKey(owner)) { + HashSet shops = chestShops.get(owner); + shops.add(shop); + } + else { + HashSet shops = new HashSet<>(); + shops.add(shop); + chestShops.put(owner, shops); + } + Parallelutils.log(Level.WARNING, "Added chest shop: \nChestPos: " + chestPos + "\nSignPos: " + signPos + "\nMaterial: " + item); + } + + public void removeShop(UUID owner, Location chestPos) { + HashSet shops = chestShops.get(owner); + shops.removeIf(x -> x.chestPos().equals(chestPos)); + if (shops.size() == 0) + chestShops.remove(owner); + } + + // ugly but no real better way to do it without storing multiple instances of shops which is a recipe for disaster + + public Shop getShopFromSignPos(Location signPos) { + Shop out = null; + for (HashSet s : chestShops.values()) { + Optional r = s.stream().filter(x -> x.signPos().equals(signPos)).findFirst(); + if (r.isPresent()) { + out = r.get(); + break; + } + } + return out; + } + + public Shop getShopFromChestPos(Location chestPos) { + Shop out = null; + for (HashSet s : chestShops.values()) { + Optional r = s.stream().filter(x -> x.chestPos().equals(chestPos)).findFirst(); + if (r.isPresent()) { + out = r.get(); + break; + } + } + return out; + } + + + public ShopResult attemptPurchase(Player player, Shop shop, Chest chest, ItemStack diamonds) { + if (player.getInventory().firstEmpty() == -1) { + return ShopResult.INVENTORY_FULL; + } + if (diamonds == null || diamonds.getType() != Material.DIAMOND) { + return ShopResult.NO_DIAMONDS; + } + if (diamonds.getAmount() < shop.buyAmt()) { + return ShopResult.INSUFFICIENT_FUNDS; + } + + Inventory inv = chest.getInventory(); + InventoryHolder holder = inv.getHolder(); + HashMap items; + if (holder instanceof DoubleChest dc) { + Chest left = (Chest)dc.getLeftSide(); + if (left == null) { + Parallelutils.log(Level.WARNING, "attemptPurchase: getLeftSide() returned null"); + return ShopResult.ERROR; + } + Chest right = (Chest)dc.getRightSide(); + if (right == null) { + Parallelutils.log(Level.WARNING, "attemptPurchase: getRightSide() returned null"); + return ShopResult.ERROR; + } + if (left.getInventory().containsAtLeast(new ItemStack(Material.DIAMOND), MAX_DIAMONDS - shop.buyAmt()) + && right.getInventory().containsAtLeast(new ItemStack(Material.DIAMOND), MAX_DIAMONDS - shop.buyAmt())) { + return ShopResult.SHOP_FULL; + } + items = left.getInventory().all(shop.item()); + Inventory shopping = Bukkit.createInventory(null, 54, Component.text("ChestShop (Click to Buy)")); + items.forEach(shopping::setItem); + int itemAmt = 0; + for (ItemStack i : items.values()) { + itemAmt += i.getAmount(); + } + // unfortunately have to do this in two iterations to avoid '? extends/captures' conflicts + items = right.getInventory().all(shop.item()); + items.forEach(shopping::setItem); + for (ItemStack i : items.values()) { + itemAmt += i.getAmount(); + } + if (inv.getContents().length == 0 || items.size() == 0 || itemAmt < shop.sellAmt()) { + return ShopResult.SHOP_EMPTY; + } + player.openInventory(shopping); + shoppingPlayers.put(player.getUniqueId(), new ShopperData(shopping, inv, shop, diamonds)); + } + else { + if (inv.containsAtLeast(new ItemStack(Material.DIAMOND), MAX_DIAMONDS - shop.buyAmt())) { + return ShopResult.SHOP_FULL; + } + items = inv.all(shop.item()); + Inventory shopping = Bukkit.createInventory(null, InventoryType.CHEST, Component.text("ChestShop (Click to Buy)")); + int itemAmt = 0; + // would rather use two for loops than AtomicInteger tee hee + for (ItemStack i : items.values()) { + itemAmt += i.getAmount(); + } + items.forEach(shopping::setItem); + if (inv.getContents().length == 0 || items.size() == 0 || itemAmt < shop.sellAmt()) { + return ShopResult.SHOP_EMPTY; + } + player.openInventory(shopping); + shoppingPlayers.put(player.getUniqueId(), new ShopperData(shopping, inv, shop, diamonds)); + } + return ShopResult.SUCCESS; + } + + public void openShopPreview(Player player, Shop shop, Inventory chest) { + if (chest.getHolder() instanceof DoubleChest dc) { + Chest left = (Chest)dc.getLeftSide(); + if (left == null) { + Parallelutils.log(Level.WARNING, "attemptPurchase: getLeftSide() returned null"); + return; + } + Chest right = (Chest)dc.getRightSide(); + if (right == null) { + Parallelutils.log(Level.WARNING, "attemptPurchase: getRightSide() returned null"); + return; + } + Inventory inv = Bukkit.createInventory(null, 54, Component.text("ChestShop Preview")); + HashMap items = left.getInventory().all(shop.item()); + // unfortunately have to do this in two iterations to avoid '? extends/captures' conflicts + items.forEach(inv::setItem); + items = right.getInventory().all(shop.item()); + items.forEach(inv::setItem); + player.openInventory(inv); + shopPreviews.put(player.getUniqueId(), inv); + } + else { + Inventory inv = Bukkit.createInventory(null, InventoryType.CHEST, Component.text("ChestShop Preview")); + HashMap items = chest.all(shop.item()); + items.forEach(inv::setItem); + player.openInventory(inv); + shopPreviews.put(player.getUniqueId(), inv); + } + } + + public void closeShopPreview(Player player) { + shopPreviews.remove(player.getUniqueId()); + } + + public void stopShopping(Player player) { shoppingPlayers.remove(player.getUniqueId()); } + + public Inventory getPreviewInventory(Player player) { + return shopPreviews.get(player.getUniqueId()); + } + + public ShopperData getShoppingData(Player player) { return shoppingPlayers.get(player.getUniqueId()); } + + public boolean previewInventoryExists(Inventory inv) { + return shopPreviews.containsValue(inv); + } + + public boolean shopInventoryExists(Inventory inv) { + return shoppingPlayers.values().stream().anyMatch(x -> x.fakeInv() == inv); + } + + public boolean isPlayerUsingShop(Shop shop) { + return shoppingPlayers.values().stream().anyMatch(x -> x.shop().equals(shop)); + } + + + public static ChestShops get() { + return INSTANCE; + } + + +} diff --git a/src/main/java/parallelmc/parallelutils/modules/chestshops/Shop.java b/src/main/java/parallelmc/parallelutils/modules/chestshops/Shop.java new file mode 100644 index 00000000..7a34b726 --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/chestshops/Shop.java @@ -0,0 +1,9 @@ +package parallelmc.parallelutils.modules.chestshops; + +import org.bukkit.Location; +import org.bukkit.Material; + +import java.util.UUID; + +// keep track of both chest and sign pos to make handling certain events easier +public record Shop(UUID owner, Location chestPos, Location signPos, Material item, int sellAmt, int buyAmt) { } diff --git a/src/main/java/parallelmc/parallelutils/modules/chestshops/ShopResult.java b/src/main/java/parallelmc/parallelutils/modules/chestshops/ShopResult.java new file mode 100644 index 00000000..decb02d9 --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/chestshops/ShopResult.java @@ -0,0 +1,11 @@ +package parallelmc.parallelutils.modules.chestshops; + +public enum ShopResult { + SHOP_EMPTY, + INVENTORY_FULL, + NO_DIAMONDS, + INSUFFICIENT_FUNDS, + SHOP_FULL, + SUCCESS, + ERROR +} diff --git a/src/main/java/parallelmc/parallelutils/modules/chestshops/ShopperData.java b/src/main/java/parallelmc/parallelutils/modules/chestshops/ShopperData.java new file mode 100644 index 00000000..1d06e857 --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/chestshops/ShopperData.java @@ -0,0 +1,7 @@ +package parallelmc.parallelutils.modules.chestshops; + +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +// stores various data for use in shop events +public record ShopperData(Inventory fakeInv, Inventory chestInv, Shop shop, ItemStack diamonds) { } diff --git a/src/main/java/parallelmc/parallelutils/modules/chestshops/events/OnBreakShop.java b/src/main/java/parallelmc/parallelutils/modules/chestshops/events/OnBreakShop.java new file mode 100644 index 00000000..5efc4b1f --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/chestshops/events/OnBreakShop.java @@ -0,0 +1,91 @@ +package parallelmc.parallelutils.modules.chestshops.events; + +import org.bukkit.block.Block; +import org.bukkit.block.Chest; +import org.bukkit.block.DoubleChest; +import org.bukkit.block.Sign; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.InventoryHolder; +import parallelmc.parallelutils.Parallelutils; +import parallelmc.parallelutils.modules.chestshops.ChestShops; +import parallelmc.parallelutils.modules.chestshops.Shop; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; + +import java.util.logging.Level; + +public class OnBreakShop implements Listener { + @EventHandler + public void onBreakShop(BlockBreakEvent event) { + Player player = event.getPlayer(); + Block block = event.getBlock(); + if (block.getState() instanceof Sign) { + Shop s = ChestShops.get().getShopFromSignPos(block.getLocation()); + if (s == null) return; + if (!player.hasPermission("parallelutils.bypass.chestshops") && s.owner() != player.getUniqueId()) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "You cannot break this chest shop!"); + return; + } + if (ChestShops.get().isPlayerUsingShop(s)) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "Please wait, a player is using this shop."); + return; + } + ChestShops.get().removeShop(s.owner(), s.chestPos()); + ParallelChat.sendParallelMessageTo(player, "Chest shop unregistered."); + } + else if (block.getState() instanceof Chest chest) { + Shop s; + InventoryHolder holder = chest.getInventory().getHolder(); + // check both sides of the double chest since each side is a separate block + if (holder instanceof DoubleChest dc) { + Chest temp = (Chest)dc.getLeftSide(); + if (temp == null) { + Parallelutils.log(Level.WARNING, "OnBreakShop: getLeftSide() returned null!"); + return; + } + s = ChestShops.get().getShopFromChestPos(temp.getLocation()); + if (s == null) { + temp = (Chest)dc.getRightSide(); + if (temp == null) { + Parallelutils.log(Level.WARNING, "OnBreakShop: getRightSide() returned null!"); + return; + } + s = ChestShops.get().getShopFromChestPos(temp.getLocation()); + if (s == null) + return; + } + if (!player.hasPermission("parallelutils.bypass.chestshops") && s.owner() != player.getUniqueId()) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "You cannot break this chest shop!"); + return; + } + if (ChestShops.get().isPlayerUsingShop(s)) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "Please wait, a player is using this shop."); + return; + } + ChestShops.get().removeShop(s.owner(), s.chestPos()); + ParallelChat.sendParallelMessageTo(player, "Chest shop unregistered."); + return; + } + s = ChestShops.get().getShopFromChestPos(chest.getLocation()); + if (s == null) return; + if (!player.hasPermission("parallelutils.bypass.chestshops") && s.owner() != player.getUniqueId()) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "You cannot break this chest shop!"); + return; + } + if (ChestShops.get().isPlayerUsingShop(s)) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "Please wait, a player is using this shop."); + return; + } + ChestShops.get().removeShop(s.owner(), s.chestPos()); + ParallelChat.sendParallelMessageTo(player, "Chest shop unregistered."); + } + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/chestshops/events/OnClickBlock.java b/src/main/java/parallelmc/parallelutils/modules/chestshops/events/OnClickBlock.java new file mode 100644 index 00000000..92986e66 --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/chestshops/events/OnClickBlock.java @@ -0,0 +1,127 @@ +package parallelmc.parallelutils.modules.chestshops.events; + +import org.bukkit.block.Block; +import org.bukkit.block.Chest; +import org.bukkit.block.DoubleChest; +import org.bukkit.block.Sign; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import parallelmc.parallelutils.Parallelutils; +import parallelmc.parallelutils.modules.chestshops.ChestShops; +import parallelmc.parallelutils.modules.chestshops.Shop; +import parallelmc.parallelutils.modules.chestshops.ShopResult; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; + +import java.util.logging.Level; + +public class OnClickBlock implements Listener { + @EventHandler(ignoreCancelled = true) + public void onClickBlock(PlayerInteractEvent event) { + Player player = event.getPlayer(); + Block block = event.getClickedBlock(); + if (block == null) return; + if (event.getAction() == Action.RIGHT_CLICK_BLOCK) { + if (block.getState() instanceof Sign) { + Shop shop = ChestShops.get().getShopFromSignPos(block.getLocation()); + if (shop == null) { + Parallelutils.log(Level.WARNING, "Returned shop was null."); + return; + } + Block c = player.getWorld().getBlockAt(shop.chestPos()); + if (!(c.getState() instanceof Chest chest)) { + Parallelutils.log(Level.WARNING, "Block at " + shop.chestPos() + " should be a chest but it is actually a " + c.getType() + "! Removing..."); + ChestShops.get().removeShop(shop.owner(), shop.chestPos()); + return; + } + if (ChestShops.get().isPlayerUsingShop(shop)) { + ParallelChat.sendParallelMessageTo(player, "Someone is already using this shop!"); + return; + } + ItemStack diamonds = event.getItem(); + ShopResult result = ChestShops.get().attemptPurchase(player, shop, chest, diamonds); + switch (result) { + case SHOP_EMPTY -> ParallelChat.sendParallelMessageTo(player, "This shop is out of stock!"); + case INVENTORY_FULL -> ParallelChat.sendParallelMessageTo(player, "Your inventory is full!"); + case NO_DIAMONDS -> ParallelChat.sendParallelMessageTo(player, "You must be holding diamonds in your hand to purchase an item!"); + case INSUFFICIENT_FUNDS -> ParallelChat.sendParallelMessageTo(player, "You do not have enough diamonds to purchase this item!"); + case SHOP_FULL -> ParallelChat.sendParallelMessageTo(player, "This chest shop cannot accept any more currency!"); + } + } + else if (block.getState() instanceof Chest chest) { + Shop shop; + InventoryHolder holder = chest.getInventory().getHolder(); + // check both sides of the double chest since each side is a separate block + if (holder instanceof DoubleChest dc) { + Chest temp = (Chest)dc.getLeftSide(); + if (temp == null) { + Parallelutils.log(Level.WARNING, "OnClickBlock: getLeftSide() returned null!"); + return; + } + shop = ChestShops.get().getShopFromChestPos(temp.getLocation()); + if (shop == null) { + temp = (Chest)dc.getRightSide(); + if (temp == null) { + Parallelutils.log(Level.WARNING, "OnClickBlock: getRightSide() returned null!"); + return; + } + shop = ChestShops.get().getShopFromChestPos(temp.getLocation()); + if (shop == null) + return; + } + if (!player.hasPermission("parallelutils.bypass.chestshops") && shop.owner() != player.getUniqueId()) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "You cannot open this chest shop!"); + } + if (ChestShops.get().isPlayerUsingShop(shop)) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "Please wait, a player is using this shop."); + } + return; + } + shop = ChestShops.get().getShopFromChestPos(block.getLocation()); + if (shop == null) + return; + if (!player.hasPermission("parallelutils.bypass.chestshops") && shop.owner() != player.getUniqueId()) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "You cannot open this chest shop!"); + } + if (ChestShops.get().isPlayerUsingShop(shop)) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "Please wait, a player is using this shop."); + } + } + } + else if (event.getAction() == Action.LEFT_CLICK_BLOCK) { + if (block.getState() instanceof Sign) { + Shop shop = ChestShops.get().getShopFromSignPos(block.getLocation()); + if (shop != null) { + if (ChestShops.get().getPreviewInventory(player) != null) { + Parallelutils.log(Level.WARNING, player.getName() + " tried to open a shop preview with one already open!"); + event.setCancelled(true); + return; + } + Block c = player.getWorld().getBlockAt(shop.chestPos()); + if (!(c.getState() instanceof Chest chest)) { + Parallelutils.log(Level.WARNING, "Block at " + shop.chestPos() + " should be a chest but it is actually a " + c.getType() + "! Removing..."); + ChestShops.get().removeShop(shop.owner(), shop.chestPos()); + return; + } + Inventory inv = chest.getInventory(); + int slot = inv.first(shop.item()); + if (slot == -1) { + ParallelChat.sendParallelMessageTo(player, "This shop is out of stock!"); + return; + } + ChestShops.get().openShopPreview(player, shop, inv); + ParallelChat.sendParallelMessageTo(player, "Opening preview..."); + } + } + } + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/chestshops/events/OnPreviewInteract.java b/src/main/java/parallelmc/parallelutils/modules/chestshops/events/OnPreviewInteract.java new file mode 100644 index 00000000..ab0885af --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/chestshops/events/OnPreviewInteract.java @@ -0,0 +1,56 @@ +package parallelmc.parallelutils.modules.chestshops.events; + +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.*; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.Inventory; +import parallelmc.parallelutils.modules.chestshops.ChestShops; + +public class OnPreviewInteract implements Listener { + @EventHandler + public void onPreviewClick(InventoryClickEvent event) { + Player player = (Player)event.getWhoClicked(); + Inventory inv = ChestShops.get().getPreviewInventory(player); + if (inv != null && (inv.equals(event.getClickedInventory()) || inv.equals(event.getInventory()))) { + event.setCancelled(true); + } + } + + @EventHandler + public void onPreviewDrag(InventoryDragEvent event) { + Player player = (Player)event.getWhoClicked(); + Inventory inv = ChestShops.get().getPreviewInventory(player); + if (inv != null && inv.equals(event.getInventory())) { + event.setCancelled(true); + } + } + + @EventHandler + public void onPreviewMoveItem(InventoryMoveItemEvent event) { + if (ChestShops.get().previewInventoryExists(event.getDestination())) { + event.setCancelled(true); + } + } + + @EventHandler + public void onClosePreview(InventoryCloseEvent event) { + Player player = (Player)event.getPlayer(); + Inventory inv = ChestShops.get().getPreviewInventory(player); + if (inv != null) { + if (event.getInventory().equals(inv)) { + ChestShops.get().closeShopPreview(player); + } + } + } + + @EventHandler + public void onLeaveWhilePreviewing(PlayerQuitEvent event) { + Player player = event.getPlayer(); + Inventory inv = ChestShops.get().getPreviewInventory(player); + if (inv != null) { + ChestShops.get().closeShopPreview(player); + } + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/chestshops/events/OnShopInteract.java b/src/main/java/parallelmc/parallelutils/modules/chestshops/events/OnShopInteract.java new file mode 100644 index 00000000..5a8458cf --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/chestshops/events/OnShopInteract.java @@ -0,0 +1,107 @@ +package parallelmc.parallelutils.modules.chestshops.events; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.block.Chest; +import org.bukkit.block.DoubleChest; +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.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryMoveItemEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import parallelmc.parallelutils.Parallelutils; +import parallelmc.parallelutils.modules.chestshops.ChestShops; +import parallelmc.parallelutils.modules.chestshops.ShopperData; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; + +import java.util.logging.Level; + +public class OnShopInteract implements Listener { + @EventHandler + public void onShopClick(InventoryClickEvent event) { + Player player = (Player)event.getWhoClicked(); + ShopperData data = ChestShops.get().getShoppingData(player); + Inventory inv = event.getClickedInventory(); + boolean isRightSide = false; + if (data != null && data.fakeInv().equals(inv) && event.getCurrentItem() != null) { + InventoryHolder holder = inv.getHolder(); + if (holder instanceof DoubleChest dc) { + Chest right = (Chest)dc.getRightSide(); + if (right == null) { + Parallelutils.log(Level.WARNING, "onShopClick: getRightSide was null!"); + return; + } + // only handle the right side since the left side is treated as a normal chest + // override the inv to the right side of the double chest + if (event.getRawSlot() > 26) { + inv = right.getInventory(); + isRightSide = true; + } + } + // make a copy of each item + ItemStack give = new ItemStack(event.getCurrentItem()); + give.setAmount(data.shop().sellAmt()); + ItemStack take = new ItemStack(data.diamonds()); + take.setAmount(data.shop().buyAmt()); + data.diamonds().subtract(data.shop().buyAmt()); + player.getInventory().addItem(give); + ItemStack update = event.getCurrentItem().subtract(data.shop().sellAmt()); + if (isRightSide) { + inv.setItem(event.getRawSlot(), update); + inv.addItem(take); + } + else { + data.chestInv().setItem(event.getRawSlot(), update); + data.chestInv().addItem(take); + } + Component name = give.displayName(); + if (give.hasItemMeta() && give.getItemMeta().hasDisplayName()) { + name = give.getItemMeta().displayName(); + } + ParallelChat.sendParallelMessageTo(player, Component.text("You bought " + data.shop().sellAmt() + "x ", NamedTextColor.GREEN).append(name)); + event.setCancelled(true); + } + } + + @EventHandler + public void onShopDrag(InventoryDragEvent event) { + Player player = (Player)event.getWhoClicked(); + ShopperData data = ChestShops.get().getShoppingData(player); + if (data != null && data.fakeInv().equals(event.getInventory())) { + event.setCancelled(true); + } + } + + @EventHandler + public void onShopMoveItem(InventoryMoveItemEvent event) { + if (ChestShops.get().shopInventoryExists(event.getDestination())) { + event.setCancelled(true); + } + } + + @EventHandler + public void onCloseShop(InventoryCloseEvent event) { + Player player = (Player)event.getPlayer(); + ShopperData data = ChestShops.get().getShoppingData(player); + if (data != null) { + if (event.getInventory().equals(data.fakeInv())) { + ChestShops.get().stopShopping(player); + } + } + } + + @EventHandler + public void onLeaveWhileShopping(PlayerQuitEvent event) { + Player player = event.getPlayer(); + ShopperData data = ChestShops.get().getShoppingData(player); + if (data != null) { + ChestShops.get().stopShopping(player); + } + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/chestshops/events/OnSignText.java b/src/main/java/parallelmc/parallelutils/modules/chestshops/events/OnSignText.java new file mode 100644 index 00000000..54c4c425 --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/chestshops/events/OnSignText.java @@ -0,0 +1,131 @@ +package parallelmc.parallelutils.modules.chestshops.events; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.text.serializer.legacy.LegacyFormat; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Chest; +import org.bukkit.block.DoubleChest; +import org.bukkit.block.data.Directional; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import parallelmc.parallelutils.modules.chestshops.ChestShops; +import parallelmc.parallelutils.modules.chestshops.Shop; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; + +import java.util.ArrayList; +import java.util.List; + +public class OnSignText implements Listener { + // ChestShop formatting: + // ChestShop + // X items + // X currency + + // Plugin will update it to: + // ChestShop + // Player Name + // X items + // X currency + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onSignTextSet(SignChangeEvent event) { + Player player = event.getPlayer(); + if (player.hasPermission("parallelutils.chestshops.create") || player.isOp()) { + List lines = new ArrayList<>(); + // thanks kyori + event.lines().forEach((l) -> lines.add(PlainTextComponentSerializer.plainText().serialize(l))); + if (lines.get(0).equals("ChestShop")) { + Directional d = (Directional)event.getBlock().getBlockData(); + Block attached = event.getBlock().getRelative(d.getFacing().getOppositeFace()); + if (attached.getState() instanceof Chest chest) { + Shop existing = ChestShops.get().getShopFromChestPos(chest.getLocation()); + if (existing != null) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "Error: A shop already exists at this location."); + return; + } + if (chest.getInventory().getHolder() instanceof DoubleChest dc) { + InventoryHolder left = dc.getLeftSide(); + InventoryHolder right = dc.getRightSide(); + if (left == null || right == null) { + return; + } + if (ChestShops.get().getShopFromChestPos(((Chest)left).getLocation()) != null || + ChestShops.get().getShopFromChestPos(((Chest)right).getLocation()) != null) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "Error: A shop already exists at this location."); + return; + } + } + Inventory inv = chest.getInventory(); + ItemStack sell = inv.getItem(0); + if (sell == null || sell.getType() == Material.AIR) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "Please place the item to sell in the first slot of the chest."); + return; + } + if (sell.getType() == Material.DIAMOND || sell.getType() == Material.DIAMOND_BLOCK) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "You cannot sell diamonds!"); + return; + } + int sellNum; + try { + sellNum = Integer.parseInt(lines.get(1)); + if (sellNum < 1 || sellNum > sell.getMaxStackSize()) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "Invalid sell amount! Must be between 1 and " + sell.getMaxStackSize() + "."); + return; + } + } catch (NumberFormatException e) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "Invalid chest shop format!"); + return; + } + int buyNum = Integer.parseInt(lines.get(2)); + if (buyNum < 1 || buyNum > 64) { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "Invalid buy amount! Must be between 1 and 64."); + return; + } + event.line(0, Component.text("ChestShop")); + event.line(1, Component.text(player.getName())); + Component name = sell.displayName(); + if (sell.hasItemMeta() && sell.getItemMeta().hasDisplayName()) { + name = sell.getItemMeta().displayName(); + } + // warning can be ignored, compiler doesn't recognize the hasDisplayName check + // we have to do this or else long names don't display at all + String trim = LegacyComponentSerializer.legacyAmpersand().serialize(name); + if (trim.length() > 15) { + trim = trim.substring(0, 12); + if (trim.endsWith("&")) { + trim = trim.substring(0, 11); + } + trim += "..."; + event.line(2, Component.text(sellNum + " ").append(LegacyComponentSerializer.legacyAmpersand().deserialize(trim))); + } + else { + // if shorter than 13 characters just use the existing component + event.line(2, Component.text(sellNum + " ").append(name)); + } + event.line(3, Component.text(buyNum + " diamonds")); + ChestShops.get().addShop(player.getUniqueId(), attached.getLocation(), event.getBlock().getLocation(), sell.getType(), sellNum, buyNum); + ParallelChat.sendParallelMessageTo(player, "Chest shop created!"); + } + else { + event.setCancelled(true); + ParallelChat.sendParallelMessageTo(player, "Chest shop sign must be placed on the side of a regular chest!"); + } + } + } + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/custommobs/CustomMobs.java b/src/main/java/parallelmc/parallelutils/modules/custommobs/CustomMobs.java index 06341a03..89a6919c 100644 --- a/src/main/java/parallelmc/parallelutils/modules/custommobs/CustomMobs.java +++ b/src/main/java/parallelmc/parallelutils/modules/custommobs/CustomMobs.java @@ -4,8 +4,8 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Particle; -import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity; -import org.bukkit.craftbukkit.v1_18_R2.entity.CraftZombie; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftZombie; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import org.bukkit.scheduler.BukkitTask; diff --git a/src/main/java/parallelmc/parallelutils/modules/custommobs/bukkitmobs/CraftFireWisp.java b/src/main/java/parallelmc/parallelutils/modules/custommobs/bukkitmobs/CraftFireWisp.java index 73cffe77..cbb04c22 100644 --- a/src/main/java/parallelmc/parallelutils/modules/custommobs/bukkitmobs/CraftFireWisp.java +++ b/src/main/java/parallelmc/parallelutils/modules/custommobs/bukkitmobs/CraftFireWisp.java @@ -9,7 +9,7 @@ import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.craftbukkit.v1_18_R2.entity.CraftZombie; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftZombie; import org.bukkit.enchantments.Enchantment; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.inventory.ItemFlag; diff --git a/src/main/java/parallelmc/parallelutils/modules/custommobs/bukkitmobs/CraftWisp.java b/src/main/java/parallelmc/parallelutils/modules/custommobs/bukkitmobs/CraftWisp.java index 4eed0850..1c542cdc 100644 --- a/src/main/java/parallelmc/parallelutils/modules/custommobs/bukkitmobs/CraftWisp.java +++ b/src/main/java/parallelmc/parallelutils/modules/custommobs/bukkitmobs/CraftWisp.java @@ -7,7 +7,7 @@ import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.craftbukkit.v1_18_R2.entity.CraftZombie; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftZombie; import org.bukkit.enchantments.Enchantment; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.inventory.ItemFlag; diff --git a/src/main/java/parallelmc/parallelutils/modules/custommobs/commands/ParallelSummonCommand.java b/src/main/java/parallelmc/parallelutils/modules/custommobs/commands/ParallelSummonCommand.java index e70e6a7f..1bdf7f9e 100644 --- a/src/main/java/parallelmc/parallelutils/modules/custommobs/commands/ParallelSummonCommand.java +++ b/src/main/java/parallelmc/parallelutils/modules/custommobs/commands/ParallelSummonCommand.java @@ -5,8 +5,8 @@ import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; -import org.bukkit.craftbukkit.v1_18_R2.CraftServer; -import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_19_R1.CraftServer; +import org.bukkit.craftbukkit.v1_19_R1.CraftWorld; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; diff --git a/src/main/java/parallelmc/parallelutils/modules/custommobs/events/CustomMobsGeneralEntityListener.java b/src/main/java/parallelmc/parallelutils/modules/custommobs/events/CustomMobsGeneralEntityListener.java index 9e0560ba..8c99dfb1 100644 --- a/src/main/java/parallelmc/parallelutils/modules/custommobs/events/CustomMobsGeneralEntityListener.java +++ b/src/main/java/parallelmc/parallelutils/modules/custommobs/events/CustomMobsGeneralEntityListener.java @@ -5,7 +5,7 @@ import net.minecraft.world.entity.Entity; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftEntity; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import parallelmc.parallelutils.Parallelutils; diff --git a/src/main/java/parallelmc/parallelutils/modules/custommobs/nmsmobs/EntityFireWisp.java b/src/main/java/parallelmc/parallelutils/modules/custommobs/nmsmobs/EntityFireWisp.java index ec051b24..38b3a09a 100644 --- a/src/main/java/parallelmc/parallelutils/modules/custommobs/nmsmobs/EntityFireWisp.java +++ b/src/main/java/parallelmc/parallelutils/modules/custommobs/nmsmobs/EntityFireWisp.java @@ -8,10 +8,10 @@ import net.minecraft.world.entity.monster.Zombie; import net.minecraft.world.level.Level; import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_18_R2.CraftServer; -import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; -import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity; -import org.bukkit.craftbukkit.v1_18_R2.entity.CraftZombie; +import org.bukkit.craftbukkit.v1_19_R1.CraftServer; +import org.bukkit.craftbukkit.v1_19_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftZombie; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitTask; diff --git a/src/main/java/parallelmc/parallelutils/modules/custommobs/nmsmobs/EntityWisp.java b/src/main/java/parallelmc/parallelutils/modules/custommobs/nmsmobs/EntityWisp.java index 3f0c8ae5..e7923a1e 100644 --- a/src/main/java/parallelmc/parallelutils/modules/custommobs/nmsmobs/EntityWisp.java +++ b/src/main/java/parallelmc/parallelutils/modules/custommobs/nmsmobs/EntityWisp.java @@ -7,10 +7,10 @@ import net.minecraft.world.entity.monster.Zombie; import net.minecraft.world.level.Level; import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_18_R2.CraftServer; -import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; -import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity; -import org.bukkit.craftbukkit.v1_18_R2.entity.CraftZombie; +import org.bukkit.craftbukkit.v1_19_R1.CraftServer; +import org.bukkit.craftbukkit.v1_19_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftZombie; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitTask; diff --git a/src/main/java/parallelmc/parallelutils/modules/custommobs/registry/EntityRegistry.java b/src/main/java/parallelmc/parallelutils/modules/custommobs/registry/EntityRegistry.java index 13086a77..65b5fb17 100644 --- a/src/main/java/parallelmc/parallelutils/modules/custommobs/registry/EntityRegistry.java +++ b/src/main/java/parallelmc/parallelutils/modules/custommobs/registry/EntityRegistry.java @@ -4,7 +4,7 @@ import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftEntity; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import parallelmc.parallelutils.Constants; diff --git a/src/main/java/parallelmc/parallelutils/modules/custommobs/spawners/LeashTask.java b/src/main/java/parallelmc/parallelutils/modules/custommobs/spawners/LeashTask.java index 0dc30b01..d74b1528 100644 --- a/src/main/java/parallelmc/parallelutils/modules/custommobs/spawners/LeashTask.java +++ b/src/main/java/parallelmc/parallelutils/modules/custommobs/spawners/LeashTask.java @@ -2,8 +2,8 @@ import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_18_R2.entity.CraftLivingEntity; -import org.bukkit.craftbukkit.v1_18_R2.entity.CraftMob; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftLivingEntity; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftMob; import org.bukkit.plugin.IllegalPluginAccessException; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; diff --git a/src/main/java/parallelmc/parallelutils/modules/custommobs/spawners/SpawnTask.java b/src/main/java/parallelmc/parallelutils/modules/custommobs/spawners/SpawnTask.java index 0b273928..024bdb36 100644 --- a/src/main/java/parallelmc/parallelutils/modules/custommobs/spawners/SpawnTask.java +++ b/src/main/java/parallelmc/parallelutils/modules/custommobs/spawners/SpawnTask.java @@ -4,8 +4,8 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.block.Block; -import org.bukkit.craftbukkit.v1_18_R2.CraftServer; -import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_19_R1.CraftServer; +import org.bukkit.craftbukkit.v1_19_R1.CraftWorld; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/ParallelChat.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/ParallelChat.java index e8e58847..02f634d4 100644 --- a/src/main/java/parallelmc/parallelutils/modules/parallelchat/ParallelChat.java +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/ParallelChat.java @@ -22,6 +22,7 @@ import parallelmc.parallelutils.Constants; import parallelmc.parallelutils.ParallelModule; import parallelmc.parallelutils.Parallelutils; +import parallelmc.parallelutils.modules.parallelchat.chatrooms.ChatRoomManager; import parallelmc.parallelutils.modules.parallelchat.commands.*; import parallelmc.parallelutils.modules.parallelchat.events.*; import parallelmc.parallelutils.modules.parallelchat.events.OnChatMessage; @@ -80,6 +81,8 @@ public class ParallelChat implements ParallelModule { public BufferedWriter chatLogWriter; public BufferedWriter cmdLogWriter; + public ChatRoomManager chatRoomManager; + private final Random rand = new Random(); private Parallelutils puPlugin; @@ -114,7 +117,8 @@ public void onEnable() { ( UUID varchar(36) not null, SocSpy tinyint not null, - CmdSpy tinyint not null + CmdSpy tinyint not null, + ChatRoomSpy tinyint not null );"""); conn.commit(); statement.close(); @@ -132,7 +136,8 @@ UUID varchar(36) not null, UUID uuid = UUID.fromString(results.getString("UUID")); boolean socialSpy = results.getBoolean("SocSpy"); boolean cmdSpy = results.getBoolean("CmdSpy"); - socialSpyUsers.put(uuid, new SocialSpyOptions(socialSpy, cmdSpy)); + boolean chatRoomSpy = results.getBoolean("ChatRoomSpy"); + socialSpyUsers.put(uuid, new SocialSpyOptions(socialSpy, cmdSpy, chatRoomSpy)); } } catch (SQLException e) { e.printStackTrace(); @@ -203,6 +208,9 @@ UUID varchar(36) not null, catch (IOException e) { Parallelutils.log(Level.SEVERE, "Failed to open writer to loggers!"); } + + this.chatRoomManager = new ChatRoomManager(Path.of(puPlugin.getDataFolder().getAbsolutePath() + "/chatrooms.json")); + manager.registerEvents(new OnChatMessage(), puPlugin); manager.registerEvents(new OnJoinLeave(puPlugin), puPlugin); manager.registerEvents(new OnSignTextSet(), puPlugin); @@ -219,6 +227,7 @@ UUID varchar(36) not null, puPlugin.getCommand("clearchat").setExecutor(new ParallelClearChat()); puPlugin.getCommand("socialspy").setExecutor(new ParallelSocialSpy()); puPlugin.getCommand("commandspy").setExecutor(new ParallelCommandSpy()); + puPlugin.getCommand("chatroomspy").setExecutor(new ParallelChatRoomSpy()); puPlugin.getCommand("mutechat").setExecutor(new ParallelMuteChat()); puPlugin.getCommand("colors").setExecutor(new ParallelColors()); puPlugin.getCommand("formats").setExecutor(new ParallelFormats()); @@ -246,7 +255,7 @@ public void onDisable() { Parallelutils.log(Level.SEVERE, "Failed to close chat log writer!"); } - // save socialspy and cmdspy data across shutdowns + // save spy data across shutdowns try (Connection conn = puPlugin.getDbConn()) { if (conn == null) throw new SQLException("Unable to establish connection!"); PreparedStatement statement = conn.prepareStatement("INSERT INTO SocialSpy (UUID, SocSpy, CmdSpy) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE SocSpy = ?, CmdSpy = ?"); @@ -269,6 +278,9 @@ public void onDisable() { } catch (SQLException e) { e.printStackTrace(); } + + // save chatrooms + chatRoomManager.saveChatroomsToFile(); } /** @@ -280,6 +292,17 @@ public static void sendParallelMessageTo(Player player, String message) { Component text = MiniMessage.miniMessage().deserialize("[P] " + message); player.sendMessage(text); } + + /** + * Sends a chat component to a player with the ParallelUtils prefix. + * Note that with this function the chat color is dependent on the message parameter + * @param player The player to send the message to + * @param message The component to send + */ + public static void sendParallelMessageTo(Player player, Component message) { + Component text = MiniMessage.miniMessage().deserialize("[P] ").append(message); + player.sendMessage(text); + } /** * Sends a chat message to a player @@ -467,4 +490,6 @@ public HashSet getTeamChat() { public HashSet getLoreChat() { return playersInLoreChat; } + public Parallelutils getPlugin() { return puPlugin; } + } diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/SocialSpyOptions.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/SocialSpyOptions.java index bd17f41c..6aa941e0 100644 --- a/src/main/java/parallelmc/parallelutils/modules/parallelchat/SocialSpyOptions.java +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/SocialSpyOptions.java @@ -4,10 +4,12 @@ public class SocialSpyOptions { private boolean socialSpy; private boolean cmdSpy; + private boolean chatRoomSpy; - public SocialSpyOptions(boolean socialSpy, boolean cmdSpy) { + public SocialSpyOptions(boolean socialSpy, boolean cmdSpy, boolean chatRoomSpy) { this.socialSpy = socialSpy; this.cmdSpy = cmdSpy; + this.chatRoomSpy = chatRoomSpy; } public boolean isSocialSpy() { @@ -25,4 +27,12 @@ public boolean isCmdSpy() { public void setCmdSpy(boolean cmdSpy) { this.cmdSpy = cmdSpy; } + + public boolean isChatRoomSpy() { + return chatRoomSpy; + } + + public void setChatRoomSpy(boolean chatRoomSpy) { + this.chatRoomSpy = chatRoomSpy; + } } diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/chatrooms/ChatRoom.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/chatrooms/ChatRoom.java new file mode 100644 index 00000000..6158e06c --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/chatrooms/ChatRoom.java @@ -0,0 +1,146 @@ +package parallelmc.parallelutils.modules.parallelchat.chatrooms; + +import net.kyori.adventure.bossbar.BossBar; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.entity.Player; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; + +import java.util.HashMap; +import java.util.UUID; + +public class ChatRoom { + private final UUID owner; + private final String name; + private final NamedTextColor chatColor; + private final HashMap members; + private final boolean isPrivate; + private final BossBar activeBossbar; + + public static final int OWNER = 2; + public static final int MODERATOR = 1; + public static final int MEMBER = 0; + + public ChatRoom(UUID owner, String name, String color, boolean isPrivate) { + this.owner = owner; + this.name = name; + this.chatColor = NamedTextColor.NAMES.value(color); + this.members = new HashMap<>(); + this.members.put(owner, OWNER); + this.isPrivate = isPrivate; + this.activeBossbar = BossBar.bossBar(Component.text("ChatRoom: " + name, chatColor), 1, BossBar.Color.PURPLE, BossBar.Overlay.PROGRESS); + } + + public ChatRoom(UUID owner, String name, String color, boolean isPrivate, HashMap members) { + this.owner = owner; + this.name = name; + this.chatColor = NamedTextColor.NAMES.value(color); + this.members = members; + this.isPrivate = isPrivate; + this.activeBossbar = BossBar.bossBar(Component.text("ChatRoom: " + name, chatColor), 1, BossBar.Color.PURPLE, BossBar.Overlay.PROGRESS); + } + + public void addMember(Player player) { + this.members.put(player.getUniqueId(), MEMBER); + announceMessage(player.getName() + " joined the chatroom.", NamedTextColor.GREEN); + } + + public void removeMember(Player player) { + announceMessage(player.getName() + " left the chatroom.", NamedTextColor.RED); + this.members.remove(player.getUniqueId()); + } + + public void kickMember(Player player, Player moderator) { + announceMessage(player.getName() + " was kicked by " + moderator.getName(), NamedTextColor.RED); + this.members.remove(player.getUniqueId()); + } + + public void promoteMember(Player player) { + this.members.put(player.getUniqueId(), MODERATOR); + announceMessage(player.getName() + " has been promoted to moderator.", NamedTextColor.GREEN); + } + + public void demoteMember(Player player) { + this.members.put(player.getUniqueId(), MEMBER); + announceMessage(player.getName() + " has been demoted to member.", NamedTextColor.RED); + } + + public boolean isPlayerModerator(Player player) { + if (members.get(player.getUniqueId()) == null) return false; + return members.get(player.getUniqueId()) == MODERATOR || isPlayerOwner(player); + } + + public boolean isPlayerOwner(Player player) { + return members.get(player.getUniqueId()) == OWNER; + } + + public boolean hasMember(Player player) { return members.containsKey(player.getUniqueId()); } + + public void sendMessage(Player sender, Component message) { + Component text = MiniMessage.miniMessage().deserialize("[<" + chatColor + ">" + name + "] <" + chatColor + ">" + getPrefix(sender) + " > ").append(message.color(chatColor)); + members.forEach((u, b) -> { + Player p = ParallelChat.get().getPlugin().getServer().getPlayer(u); + if (p != null) { + p.sendMessage(text); + } + }); + if (!sender.hasPermission("parallelutils.bypass.chatroomspy")) { + Component chatroomSpy = MiniMessage.miniMessage().deserialize("[ChatRoom-Spy] ").append(text); + // ChatRoom Spy + ParallelChat.get().socialSpyUsers.forEach((u, o) -> { + if (u.equals(sender.getUniqueId())) return; + if (o.isChatRoomSpy()) { + // this kinda sucks but not much can be done + Player spyUser = sender.getServer().getPlayer(u); + if (spyUser != null) { + spyUser.sendMessage(chatroomSpy); + } + } + }); + } + } + + public void announceMessage(String message, NamedTextColor color) { + Component text = MiniMessage.miniMessage().deserialize("[<" + chatColor + ">" + name + "] > ").append(Component.text(message, color)); + members.forEach((u, b) -> { + Player p = ParallelChat.get().getPlugin().getServer().getPlayer(u); + if (p != null) { + p.sendMessage(text); + } + }); + Component chatroomSpy = MiniMessage.miniMessage().deserialize("[ChatRoom-Spy] ").append(text); + // ChatRoom Spy + ParallelChat.get().socialSpyUsers.forEach((u, o) -> { + if (o.isChatRoomSpy()) { + // this kinda sucks but not much can be done + Player spyUser = ParallelChat.get().getPlugin().getServer().getPlayer(u); + if (spyUser != null) { + spyUser.sendMessage(chatroomSpy); + } + } + }); + } + + private String getPrefix(Player player) { + if (isPlayerOwner(player)) + return "O " + player.getName(); + if (isPlayerModerator(player)) + return "M " + player.getName(); + return player.getName(); + } + + public UUID getOwner() { return this.owner; } + + public String getName() { return this.name; } + + public String getColor() { return this.chatColor.toString(); } + + public boolean isPrivate() { return this.isPrivate; } + + public BossBar getBossBar() { return this.activeBossbar; } + + public HashMap getMembers() { return this.members; } + + +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/chatrooms/ChatRoomManager.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/chatrooms/ChatRoomManager.java new file mode 100644 index 00000000..e425ecfa --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/chatrooms/ChatRoomManager.java @@ -0,0 +1,200 @@ +package parallelmc.parallelutils.modules.parallelchat.chatrooms; + +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.entity.Player; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import parallelmc.parallelutils.Parallelutils; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.logging.Level; + +public class ChatRoomManager { + private final HashMap chatRooms = new HashMap<>(); + // list of players who are in a chatroom + private final HashMap playersInChatrooms = new HashMap<>(); + // list of players who have a chatroom passively active (i.e. by running /cr with no arguments) + private final HashMap hasChatroomActive = new HashMap<>(); + + // list of players waiting to accept an invite + private final HashMap pendingInvites = new HashMap<>(); + + private final Path jsonPath; + + public ChatRoomManager(Path jsonPath) { + this.jsonPath = jsonPath; + if (!jsonPath.toFile().exists()) { + Parallelutils.log(Level.WARNING, "ChatRooms JSON file does not exist, skipping loading."); + return; + } + String data; + try { + data = Files.readString(jsonPath); + JSONParser parser = new JSONParser(); + JSONArray arr = (JSONArray)parser.parse(data); + for (Object o : arr) { + JSONObject json = (JSONObject)o; + HashMap members = new HashMap<>(); + String name = (String)json.get("name"); + for (Object m : (JSONArray)json.get("members")) { + JSONObject member = (JSONObject)m; + UUID uuid = UUID.fromString((String)member.get("uuid")); + // the json parser reads it as a long so change it to an int + members.put(uuid, Math.toIntExact((Long)member.get("rank"))); + playersInChatrooms.put(uuid, name); + } + chatRooms.put(name, new ChatRoom( + UUID.fromString((String)json.get("owner")), + name, + (String)json.get("chatColor"), + (Boolean)json.get("isPrivate"), + members)); + } + Parallelutils.log(Level.INFO, "Loaded " + chatRooms.size() + " existing chatrooms."); + } catch (IOException e) { + Parallelutils.log(Level.SEVERE, "Failed to load chat rooms!\n" + e.getMessage()); + } catch (ParseException e) { + Parallelutils.log(Level.SEVERE, "Failed to parse chat room data!\n" + e.getMessage()); + } + + } + + @SuppressWarnings("unchecked") + public void saveChatroomsToFile() { + JSONArray json = new JSONArray(); + for (Map.Entry e : chatRooms.entrySet()) { + ChatRoom c = e.getValue(); + JSONObject entry = new JSONObject(); + entry.put("owner", c.getOwner().toString()); + entry.put("name", c.getName()); + entry.put("chatColor", c.getColor()); + entry.put("isPrivate", c.isPrivate()); + JSONArray members = new JSONArray(); + c.getMembers().forEach((u, i) -> { + JSONObject member = new JSONObject(); + member.put("uuid", u.toString()); + member.put("rank", i); + members.add(member); + }); + entry.put("members", members); + json.add(entry); + } + try { + Files.writeString(jsonPath, json.toJSONString()); + Parallelutils.log(Level.INFO, "Saved " + chatRooms.size() + " chatrooms."); + } catch (IOException e) { + Parallelutils.log(Level.SEVERE, "Failed to save chat rooms!\n" + e.getMessage()); + } + } + + + public void addChatRoom(Player owner, String name, String color, boolean isPrivate) { + this.chatRooms.put(name, new ChatRoom(owner.getUniqueId(), name, color, isPrivate)); + this.playersInChatrooms.put(owner.getUniqueId(), name); + } + + public void removeChatRoom(String name) { + this.chatRooms.remove(name); + } + + public ChatRoom getChatRoom(String name) { + return chatRooms.get(name); + } + + public ChatRoom getPlayerChatRoom(Player player) { + return chatRooms.get(playersInChatrooms.get(player.getUniqueId())); + } + + public boolean isPlayerInChatroom(Player player) { + return this.playersInChatrooms.get(player.getUniqueId()) != null; + } + + public void addPlayerToChatroom(Player player, String name) { + this.playersInChatrooms.put(player.getUniqueId(), name); + chatRooms.get(name).addMember(player); + } + + public void removePlayerFromChatroom(Player player) { + ChatRoom c = getPlayerChatRoom(player); + c.removeMember(player); + this.playersInChatrooms.remove(player.getUniqueId()); + this.hasChatroomActive.remove(player.getUniqueId()); + player.hideBossBar(c.getBossBar()); + } + + public void kickPlayerFromChatroom(Player player, Player moderator) { + ChatRoom c = getPlayerChatRoom(moderator); + c.kickMember(player, moderator); + this.playersInChatrooms.remove(player.getUniqueId()); + this.hasChatroomActive.remove(player.getUniqueId()); + player.hideBossBar(c.getBossBar()); + } + + public void invitePlayerToChatroom(Player player, Player moderator) { + ChatRoom c = getPlayerChatRoom(moderator); + this.pendingInvites.put(player.getUniqueId(), c.getName()); + ParallelChat.sendParallelMessageTo(player, "You have been invited to the chatroom " + c.getName() + " by " + moderator.getName() + ". Type /cr accept to join!"); + player.getServer().getScheduler().runTaskLater(ParallelChat.get().getPlugin(), () -> { + if (hasPendingInvite(player)) { + this.pendingInvites.remove(player.getUniqueId()); + ParallelChat.sendParallelMessageTo(player, "Chatroom invite expired."); + ParallelChat.sendParallelMessageTo(moderator, "Chatroom invite expired."); + } + }, 600L); + } + + public void acceptChatroomInvite(Player player) { + addPlayerToChatroom(player, this.pendingInvites.get(player.getUniqueId())); + this.pendingInvites.remove(player.getUniqueId()); + } + + public void disbandChatroom(ChatRoom c) { + c.announceMessage("The chatroom has been disbanded.", NamedTextColor.RED); + c.getMembers().forEach((u, b) -> { + this.playersInChatrooms.remove(u); + this.hasChatroomActive.remove(u); + // sucks but we have to do it + Player p = ParallelChat.get().getPlugin().getServer().getPlayer(u); + if (p != null) + p.hideBossBar(c.getBossBar()); + }); + removeChatRoom(c.getName()); + } + + public boolean hasPendingInvite(Player player) { + return this.pendingInvites.containsKey(player.getUniqueId()); + } + + public void toggleActiveChatroom(Player player) { + ChatRoom c = getPlayerChatRoom(player); + if (hasChatroomActive.get(player.getUniqueId()) != null) { + player.hideBossBar(c.getBossBar()); + hasChatroomActive.remove(player.getUniqueId()); + } + else { + player.showBossBar(c.getBossBar()); + hasChatroomActive.put(player.getUniqueId(), c.getName()); + } + } + + public void removeActiveChatroom(Player player) { + ChatRoom c = getPlayerChatRoom(player); + if (c == null) return; + player.hideBossBar(c.getBossBar()); + hasChatroomActive.remove(player.getUniqueId()); + } + + public boolean hasChatroomActive(Player player) { + return hasChatroomActive.containsKey(player.getUniqueId()); + } + + public HashMap getChatRooms() { return chatRooms; } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/ParallelChatRoomSpy.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/ParallelChatRoomSpy.java new file mode 100644 index 00000000..597d04e0 --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/ParallelChatRoomSpy.java @@ -0,0 +1,41 @@ +package parallelmc.parallelutils.modules.parallelchat.commands; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; +import parallelmc.parallelutils.modules.parallelchat.SocialSpyOptions; + +import java.util.UUID; + +public class ParallelChatRoomSpy implements CommandExecutor { + + @Override + public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String label, String[] args) { + if (commandSender instanceof Player sender) { + if (sender.hasPermission("parallelutils.chatrooms.chatroomspy")) { + UUID uuid = sender.getUniqueId(); + if (ParallelChat.get().socialSpyUsers.containsKey(uuid)) { + SocialSpyOptions options = ParallelChat.get().socialSpyUsers.get(uuid); + if (options.isChatRoomSpy()) { + options.setChatRoomSpy(false); + ParallelChat.get().socialSpyUsers.put(uuid, options); + ParallelChat.sendParallelMessageTo(sender, "Disabled ChatRoom Spy"); + } + else { + options.setChatRoomSpy(true); + ParallelChat.get().socialSpyUsers.put(uuid, options); + ParallelChat.sendParallelMessageTo(sender, "Enabled ChatRoom Spy"); + } + } + else { + ParallelChat.get().socialSpyUsers.put(uuid, new SocialSpyOptions(false,false, true)); + ParallelChat.sendParallelMessageTo(sender, "Enabled ChatRoom Spy"); + } + } + } + return true; + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/ParallelCommandSpy.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/ParallelCommandSpy.java index 49d47389..f742a71a 100644 --- a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/ParallelCommandSpy.java +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/ParallelCommandSpy.java @@ -30,7 +30,7 @@ public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command } } else { - ParallelChat.get().socialSpyUsers.put(uuid, new SocialSpyOptions(false, true)); + ParallelChat.get().socialSpyUsers.put(uuid, new SocialSpyOptions(false, true, false)); ParallelChat.sendParallelMessageTo(sender, "Enabled Command Spy"); } } diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/ParallelSocialSpy.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/ParallelSocialSpy.java index 767cccac..4732e12a 100644 --- a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/ParallelSocialSpy.java +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/ParallelSocialSpy.java @@ -32,7 +32,7 @@ public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command } } else { - ParallelChat.get().socialSpyUsers.put(uuid, new SocialSpyOptions(true, false)); + ParallelChat.get().socialSpyUsers.put(uuid, new SocialSpyOptions(true, false, false)); ParallelChat.sendParallelMessageTo(sender, "Enabled Social Spy"); } } diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ChatroomCommand.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ChatroomCommand.java new file mode 100644 index 00000000..4b3b3028 --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ChatroomCommand.java @@ -0,0 +1,43 @@ +package parallelmc.parallelutils.modules.parallelchat.commands.chatrooms; + +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public abstract class ChatroomCommand { + + public String name; + public String helpText; + + /** + * Creates a new ChatroomCommand with the specified name and help text + * + * @param name The name of the command + * @param helpText The helpText of the command + */ + public ChatroomCommand(String name, String helpText) { + this.name = name; + this.helpText = helpText; + } + + /** + * Execute the command given the params from the Bukkit {@code onCommand} method + * + * @param player The Player that is executing this Command + * @param command The Bukkit {@code Command} object + * @param args The arguments for this command + * @return Returns true if the command executed successfully + */ + public abstract boolean execute(@NotNull Player player, @NotNull Command command, @NotNull String[] args); + + /** + * Retrieve the tab complete array associated with the given command and arguments + * + * @param player The sender of this command + * @param args The arguments associated with the command + * @return The List associated with the given command, sender, and arguments + */ + public abstract List getTabComplete(@NotNull Player player, @NotNull String[] args); +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ChatroomCommands.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ChatroomCommands.java new file mode 100644 index 00000000..6403cf76 --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ChatroomCommands.java @@ -0,0 +1,107 @@ +package parallelmc.parallelutils.modules.parallelchat.commands.chatrooms; + +import net.kyori.adventure.text.Component; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; +import parallelmc.parallelutils.modules.parallelchat.chatrooms.ChatRoom; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Pretty much a copy of the ParallelUtils command handler but with a different prefix + */ +public class ChatroomCommands implements CommandExecutor, TabCompleter { + + private final HashMap commandMap; + + public ChatroomCommands() { + commandMap = new HashMap<>(); + } + + /** + * Adds a new command to the commandmap + * + * @param name The name of the command + * @param command The command to be run when the name is called + * @return Returns true when the command was added successfully, false if the command already exists. + */ + public boolean addCommand(String name, ChatroomCommand command) { + if (commandMap.containsKey(name.toLowerCase().strip())) { + return false; + } + + commandMap.put(name.toLowerCase().strip(), command); + + return true; + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + // only players can run chatroom commands + if (sender instanceof Player player) { + // Both `chatroom` and `cr` are valid command prefixes + if (command.getName().equalsIgnoreCase("chatroom") || command.getName().equalsIgnoreCase("cr")) { + // If no command was specified, toggle the player's active chatroom + if (args.length == 0) { + if (ParallelChat.get().chatRoomManager.isPlayerInChatroom(player)) { + ParallelChat.get().chatRoomManager.toggleActiveChatroom(player); + } else { + ParallelChat.sendParallelMessageTo(player, "You are not in a chatroom! Type /cr help for all commands!"); + return true; + } + } else { + ChatroomCommand executingCommand = commandMap.get(args[0]); + + if (executingCommand != null) { + executingCommand.execute(player, command, args); + } else { + ParallelChat.sendParallelMessageTo(player, "Unknown chatroom subcommand. Type /cr help for all commands!"); + } + } + } + } + return true; + } + + @Override + @Nullable + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { + ArrayList list = new ArrayList<>(); + + if (sender instanceof Player player) { + // Show ChatRoom commands + + String lowerName = command.getName().toLowerCase().strip(); + + if ((lowerName.equals("chatroom") || lowerName.equals("cr")) && args.length == 1) { + // List every sub-command + list.addAll(commandMap.keySet()); + } else { + if (commandMap.containsKey(args[0].toLowerCase().strip())) { + return commandMap.get(args[0].toLowerCase().strip()).getTabComplete(player, args); + } + } + } + return list; + } + + /** + * Return a deep copy of the command map. Modifying the returned map will not modify the commands + * + * @return A deep copy of the command map + */ + public Map getCommands() { + return commandMap.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelAcceptInvite.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelAcceptInvite.java new file mode 100644 index 00000000..d7b73c3b --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelAcceptInvite.java @@ -0,0 +1,37 @@ +package parallelmc.parallelutils.modules.parallelchat.commands.chatrooms; + +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; + +import java.util.ArrayList; +import java.util.List; + +public class ParallelAcceptInvite extends ChatroomCommand { + + private final String USAGE = "/cr accept"; + + public ParallelAcceptInvite() { + super("accept", "Accept a chatroom invite."); + } + + @Override + public boolean execute(@NotNull Player player, @NotNull Command command, String[] args) { + if (args.length != 1) { + player.sendMessage(USAGE); + return false; + } + if (ParallelChat.get().chatRoomManager.isPlayerInChatroom(player)) { + ParallelChat.sendParallelMessageTo(player, "You are already in a chatroom!"); + return true; + } + ParallelChat.get().chatRoomManager.acceptChatroomInvite(player); + return true; + } + + @Override + public List getTabComplete(@NotNull Player player, @NotNull String[] args) { + return new ArrayList<>(); + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelCreateChatroom.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelCreateChatroom.java new file mode 100644 index 00000000..e1c513fb --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelCreateChatroom.java @@ -0,0 +1,61 @@ +package parallelmc.parallelutils.modules.parallelchat.commands.chatrooms; + +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; + +import java.util.ArrayList; +import java.util.List; + +public class ParallelCreateChatroom extends ChatroomCommand { + + private final String USAGE = "/cr create "; + + public ParallelCreateChatroom() { + super("create", "Creates a new chatroom."); + } + + @Override + public boolean execute(@NotNull Player player, @NotNull Command command, String[] args) { + if (args.length != 4) { + player.sendMessage(USAGE); + return false; + } + if (args[1].length() > 16) { + ParallelChat.sendParallelMessageTo(player, "Chat room name must be 16 characters or less!"); + return true; + } + if (ParallelChat.get().bannedWords.contains(args[1])) { + ParallelChat.sendParallelMessageTo(player, "Invalid chatroom name."); + return true; + } + if (ParallelChat.get().chatRoomManager.getChatRoom(args[1]) != null) { + ParallelChat.sendParallelMessageTo(player, "Chat room with name " + args[1] + " already exists!"); + return true; + } + if (ParallelChat.get().chatRoomManager.getPlayerChatRoom(player) != null) { + ParallelChat.sendParallelMessageTo(player, "You are already in a chatroom!"); + return true; + } + String color = args[2].toLowerCase(); + if (NamedTextColor.NAMES.value(color) == null) { + ParallelChat.sendParallelMessageTo(player, "Unknown color " + color + "! Colors are Minecraft namespaced! (red, dark_green, aqua, etc.)"); + return true; + } + boolean isPrivate = Boolean.parseBoolean(args[3]); + ParallelChat.get().chatRoomManager.addChatRoom(player, args[1], color, isPrivate); + ParallelChat.sendParallelMessageTo(player, "Created new " + (isPrivate ? "private " : "") + "chatroom " + args[1] + "!"); + return true; + } + + @Override + public List getTabComplete(@NotNull Player player, @NotNull String[] args) { + if (args.length == 3) + return NamedTextColor.NAMES.keys().stream().toList(); + if (args.length == 4) + return List.of("true", "false"); + else return new ArrayList<>(); + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelDemoteMember.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelDemoteMember.java new file mode 100644 index 00000000..f9a4fba4 --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelDemoteMember.java @@ -0,0 +1,71 @@ +package parallelmc.parallelutils.modules.parallelchat.commands.chatrooms; + +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; +import parallelmc.parallelutils.modules.parallelchat.chatrooms.ChatRoom; + +import java.util.ArrayList; +import java.util.List; + +public class ParallelDemoteMember extends ChatroomCommand { + + private final String USAGE = "/cr demote "; + + public ParallelDemoteMember() { + super("demote", "Demotes a player to member status in a chatroom."); + } + + @Override + public boolean execute(@NotNull Player player, @NotNull Command command, String[] args) { + if (args.length != 2) { + player.sendMessage(USAGE); + return false; + } + ChatRoom c = ParallelChat.get().chatRoomManager.getPlayerChatRoom(player); + if (c == null) { + ParallelChat.sendParallelMessageTo(player, "You are not in a chatroom!"); + return true; + } + if (!c.isPlayerOwner(player)) { + ParallelChat.sendParallelMessageTo(player, "Only the owner can demote players!"); + return true; + } + if (args[0].equals(player.getName())) { + ParallelChat.sendParallelMessageTo(player, "You cannot demote yourself!"); + return true; + } + Player demote = player.getServer().getPlayer(args[1]); + if (demote == null) { + ParallelChat.sendParallelMessageTo(player, "Could not find player " + args[1]); + return true; + } + if (!c.hasMember(demote)) { + ParallelChat.sendParallelMessageTo(player, args[1] + " is not in this chatroom!"); + return true; + } + if (!c.isPlayerModerator(demote)) { + ParallelChat.sendParallelMessageTo(player, args[1] + " is already a member!"); + return true; + } + c.demoteMember(demote); + return true; + } + + @Override + public List getTabComplete(@NotNull Player player, @NotNull String[] args) { + List list = new ArrayList<>(); + if(args.length == 2){ + ChatRoom c = ParallelChat.get().chatRoomManager.getPlayerChatRoom(player); + c.getMembers().forEach((u, b) -> { + OfflinePlayer p = player.getServer().getOfflinePlayer(u); + if (p.isOnline()) { + list.add(p.getName()); + } + }); + } + return list; + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelDisbandChatroom.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelDisbandChatroom.java new file mode 100644 index 00000000..f2917b16 --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelDisbandChatroom.java @@ -0,0 +1,44 @@ +package parallelmc.parallelutils.modules.parallelchat.commands.chatrooms; + +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; +import parallelmc.parallelutils.modules.parallelchat.chatrooms.ChatRoom; + +import java.util.ArrayList; +import java.util.List; + +public class ParallelDisbandChatroom extends ChatroomCommand { + + // require name to prevent accidentally running the command + private final String USAGE = "/cr disband "; + + public ParallelDisbandChatroom() { + super("disband", "Disbands a chatroom."); + } + + @Override + public boolean execute(@NotNull Player player, @NotNull Command command, String[] args) { + if (args.length != 2) { + player.sendMessage(USAGE); + return false; + } + ChatRoom c = ParallelChat.get().chatRoomManager.getPlayerChatRoom(player); + if (c == null) { + ParallelChat.sendParallelMessageTo(player, "You are not in a chatroom!"); + return true; + } + if (!c.isPlayerOwner(player)) { + ParallelChat.sendParallelMessageTo(player, "Only the owner can disband the chatroom!"); + return true; + } + ParallelChat.get().chatRoomManager.disbandChatroom(c); + return true; + } + + @Override + public List getTabComplete(@NotNull Player player, @NotNull String[] args) { + return new ArrayList<>(); + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelHelpChatrooms.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelHelpChatrooms.java new file mode 100644 index 00000000..fb1c4fc4 --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelHelpChatrooms.java @@ -0,0 +1,117 @@ +package parallelmc.parallelutils.modules.parallelchat.commands.chatrooms; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.Constants; +import parallelmc.parallelutils.Parallelutils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; + +public class ParallelHelpChatrooms extends ChatroomCommand { + + private static final int PAGE_SIZE = 8; + + private final Parallelutils puPlugin; + + public ParallelHelpChatrooms() { + super("help", "Show a list of Chatroom commands"); + + PluginManager manager = Bukkit.getPluginManager(); + Plugin plugin = manager.getPlugin(Constants.PLUGIN_NAME); + + if (plugin == null) { + Parallelutils.log(Level.SEVERE, "Unable to initialize ParallelHelpChatrooms. Plugin " + Constants.PLUGIN_NAME + + " does not exist!"); + puPlugin = null; + return; + } + + puPlugin = (Parallelutils) plugin; + } + + @Override + public boolean execute(@NotNull Player sender, @NotNull Command command, @NotNull String[] args) { + + Map commands = puPlugin.getChatroomCommands(); + + List sortedNames = new ArrayList<>(commands.keySet()); + Collections.sort(sortedNames); + + int numPages = (int)Math.ceil((double)commands.size() / (double)PAGE_SIZE); + + int page = 1; + + if (args.length > 1) { + page = Integer.parseInt(args[1]); + } + + if (page > numPages || page <= 0) { + sender.sendMessage("Invalid page number!"); + return true; + } + + int start = (page-1)*PAGE_SIZE; + int end = start+PAGE_SIZE; + + if (end > commands.size()) { + end = commands.size(); + } + + TextComponent.Builder builder = Component.text() + .append(Component.text("--------- ", NamedTextColor.YELLOW)) + .append(Component.text("Help: Index (")) + .append(Component.text(page)) + .append(Component.text("/")) + .append(Component.text(numPages)) + .append(Component.text(")")) + .append(Component.text(" --------------------\n", NamedTextColor.YELLOW)); + + // add this so people are aware of this functionality + builder.append(Component.text("/cr ", NamedTextColor.GREEN)).append(Component.text(": Toggles speaking in the chatroom")).append(Component.newline()); + + for (int i=start; i getTabComplete(@NotNull Player sender, @NotNull String[] args) { + ArrayList list = new ArrayList<>(); + if (args.length == 2) { + list.add("page"); + } + + return list; + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelJoinChatroom.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelJoinChatroom.java new file mode 100644 index 00000000..07bb008e --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelJoinChatroom.java @@ -0,0 +1,47 @@ +package parallelmc.parallelutils.modules.parallelchat.commands.chatrooms; + +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; +import parallelmc.parallelutils.modules.parallelchat.chatrooms.ChatRoom; + +import java.util.ArrayList; +import java.util.List; + +public class ParallelJoinChatroom extends ChatroomCommand { + + private final String USAGE = "/cr join "; + + public ParallelJoinChatroom() { + super("join", "Join a public chatroom."); + } + + @Override + public boolean execute(@NotNull Player player, @NotNull Command command, String[] args) { + if (args.length != 2) { + player.sendMessage(USAGE); + return false; + } + if (ParallelChat.get().chatRoomManager.isPlayerInChatroom(player)) { + ParallelChat.sendParallelMessageTo(player, "You are already in a chatroom!"); + return true; + } + ChatRoom c = ParallelChat.get().chatRoomManager.getChatRoom(args[1]); + if (c == null) { + ParallelChat.sendParallelMessageTo(player, "Unknown chatroom name " + args[1]); + return true; + } + if (c.isPrivate()) { + ParallelChat.sendParallelMessageTo(player, args[1] + " is a private chatroom. You must be invited in order to join."); + return true; + } + ParallelChat.get().chatRoomManager.addPlayerToChatroom(player, c.getName()); + return true; + } + + @Override + public List getTabComplete(@NotNull Player player, @NotNull String[] args) { + return new ArrayList<>(); + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelKickMember.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelKickMember.java new file mode 100644 index 00000000..075e5eff --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelKickMember.java @@ -0,0 +1,72 @@ +package parallelmc.parallelutils.modules.parallelchat.commands.chatrooms; + +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; +import parallelmc.parallelutils.modules.parallelchat.chatrooms.ChatRoom; + +import java.util.ArrayList; +import java.util.List; + +public class ParallelKickMember extends ChatroomCommand { + + private final String USAGE = "/cr kick "; + + public ParallelKickMember() { + super("kick", "Kicks a player from the chatroom."); + } + + @Override + public boolean execute(@NotNull Player player, @NotNull Command command, String[] args) { + if (args.length != 2) { + player.sendMessage(USAGE); + return false; + } + if (!ParallelChat.get().chatRoomManager.isPlayerInChatroom(player)) { + ParallelChat.sendParallelMessageTo(player, "You are not in a chatroom!"); + return true; + } + if (args[1].equals(player.getName())) { + ParallelChat.sendParallelMessageTo(player, "You cannot kick yourself!"); + return true; + } + Player kick = player.getServer().getPlayer(args[1]); + if (kick == null) { + ParallelChat.sendParallelMessageTo(player, "Could not find player " + args[1]); + return true; + } + ChatRoom c = ParallelChat.get().chatRoomManager.getPlayerChatRoom(player); + if (!c.isPlayerModerator(player)) { + ParallelChat.sendParallelMessageTo(player, "Only moderators can kick players!"); + return true; + } + if (!c.hasMember(kick)) { + ParallelChat.sendParallelMessageTo(player, args[1] + " is not in this chatroom!"); + return true; + } + if (!c.isPlayerOwner(player) && (c.isPlayerModerator(player) && c.isPlayerModerator(kick))) { + ParallelChat.sendParallelMessageTo(player, "You cannot kick another moderator!"); + return true; + } + ParallelChat.get().chatRoomManager.kickPlayerFromChatroom(kick, player); + return true; + } + + @Override + public List getTabComplete(@NotNull Player player, @NotNull String[] args) { + List list = new ArrayList<>(); + if(args.length == 2){ + ChatRoom c = ParallelChat.get().chatRoomManager.getPlayerChatRoom(player); + c.getMembers().forEach((u, b) -> { + OfflinePlayer p = player.getServer().getOfflinePlayer(u); + if (p.isOnline()) { + list.add(p.getName()); + } + }); + } + return list; + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelLeaveChatroom.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelLeaveChatroom.java new file mode 100644 index 00000000..9c03d708 --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelLeaveChatroom.java @@ -0,0 +1,44 @@ +package parallelmc.parallelutils.modules.parallelchat.commands.chatrooms; + +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; +import parallelmc.parallelutils.modules.parallelchat.chatrooms.ChatRoom; + +import java.util.ArrayList; +import java.util.List; + +public class ParallelLeaveChatroom extends ChatroomCommand { + + private final String USAGE = "/cr leave"; + + public ParallelLeaveChatroom() { + super("leave", "Leave the chatroom you are in."); + } + + @Override + public boolean execute(@NotNull Player player, @NotNull Command command, String[] args) { + if (args.length != 1) { + player.sendMessage(USAGE); + return false; + } + if (!ParallelChat.get().chatRoomManager.isPlayerInChatroom(player)) { + ParallelChat.sendParallelMessageTo(player, "You are not in a chatroom!"); + return true; + } + ChatRoom c = ParallelChat.get().chatRoomManager.getPlayerChatRoom(player); + if (c.isPlayerOwner(player)) { + ParallelChat.sendParallelMessageTo(player, "You cannot leave the chatroom since you are the owner. You can use /cr disband if you want to remove the chatroom entirely."); + return true; + } + ParallelChat.get().chatRoomManager.removePlayerFromChatroom(player); + return true; + } + + @Override + public List getTabComplete(@NotNull Player player, @NotNull String[] args) { + return new ArrayList<>(); + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelListChatrooms.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelListChatrooms.java new file mode 100644 index 00000000..e687dcfe --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelListChatrooms.java @@ -0,0 +1,46 @@ +package parallelmc.parallelutils.modules.parallelchat.commands.chatrooms; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; +import parallelmc.parallelutils.modules.parallelchat.chatrooms.ChatRoom; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class ParallelListChatrooms extends ChatroomCommand { + + private final String USAGE = "/cr list"; + + public ParallelListChatrooms() { + super("list", "Lists all public chatrooms"); + } + + @Override + public boolean execute(@NotNull Player player, @NotNull Command command, String[] args) { + if (args.length != 1) { + player.sendMessage(USAGE); + return false; + } + Component text = Component.text("Public Chatrooms: ", NamedTextColor.GOLD); + for (Map.Entry e : ParallelChat.get().chatRoomManager.getChatRooms().entrySet()) { + if (e.getValue().isPrivate()) continue; + text = text.append(Component.text(e.getKey() + " ", NamedTextColor.NAMES.value(e.getValue().getColor()))); + } + if (ParallelChat.get().chatRoomManager.isPlayerInChatroom(player)) { + ChatRoom c = ParallelChat.get().chatRoomManager.getPlayerChatRoom(player); + text = text.append(Component.newline()).append(Component.text("Your Chatroom: ", NamedTextColor.GOLD)).append(Component.text(c.getName(), NamedTextColor.NAMES.value(c.getColor()))); + } + player.sendMessage(text); + return true; + } + + @Override + public List getTabComplete(@NotNull Player player, @NotNull String[] args) { + return new ArrayList<>(); + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelListMembers.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelListMembers.java new file mode 100644 index 00000000..c8f96138 --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelListMembers.java @@ -0,0 +1,57 @@ +package parallelmc.parallelutils.modules.parallelchat.commands.chatrooms; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; +import parallelmc.parallelutils.modules.parallelchat.chatrooms.ChatRoom; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public class ParallelListMembers extends ChatroomCommand { + + private final String USAGE = "/cr members"; + + public ParallelListMembers() { + super("members", "Lists all chatroom members."); + } + + @Override + public boolean execute(@NotNull Player player, @NotNull Command command, String[] args) { + if (args.length != 1) { + player.sendMessage(USAGE); + return false; + } + if (!ParallelChat.get().chatRoomManager.isPlayerInChatroom(player)) { + ParallelChat.sendParallelMessageTo(player, "You are not in a chatroom!"); + return true; + } + ChatRoom c = ParallelChat.get().chatRoomManager.getPlayerChatRoom(player); + OfflinePlayer owner = player.getServer().getOfflinePlayer(c.getOwner()); + Component moderators = Component.text("Moderators: ", NamedTextColor.GOLD); + Component members = Component.text("Members: ", NamedTextColor.GOLD); + for (Map.Entry e : c.getMembers().entrySet()) { + OfflinePlayer p = player.getServer().getOfflinePlayer(e.getKey()); + if (p == owner) continue; + if (e.getValue() == ChatRoom.MODERATOR) moderators = moderators.append(Component.text(p.getName() + " ", p.isOnline() ? NamedTextColor.GREEN : NamedTextColor.RED)); + else members = members.append(Component.text(p.getName() + " ", p.isOnline() ? NamedTextColor.GREEN : NamedTextColor.RED)); + } + Component text = MiniMessage.miniMessage().deserialize("" + c.getName() + " Members:Owner: " + (owner.isOnline() ? "" : "") + owner.getName()) + .append(Component.newline()).append(moderators.append(Component.newline())).append(members.append(Component.newline())); + player.sendMessage(text); + return true; + } + + + @Override + public List getTabComplete(@NotNull Player player, @NotNull String[] args) { + return new ArrayList<>(); + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelMsgChatroom.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelMsgChatroom.java new file mode 100644 index 00000000..087fd35b --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelMsgChatroom.java @@ -0,0 +1,43 @@ +package parallelmc.parallelutils.modules.parallelchat.commands.chatrooms; + +import net.kyori.adventure.text.Component; +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; +import parallelmc.parallelutils.modules.parallelchat.chatrooms.ChatRoom; + +import java.util.ArrayList; +import java.util.List; + +public class ParallelMsgChatroom extends ChatroomCommand { + + private final String USAGE = "/cr msg "; + + public ParallelMsgChatroom() { + super("msg", "Sends a message to the chatroom."); + } + + @Override + public boolean execute(@NotNull Player player, @NotNull Command command, String[] args) { + if (args.length < 2) { + player.sendMessage(USAGE); + return false; + } + if (!ParallelChat.get().chatRoomManager.isPlayerInChatroom(player)) { + ParallelChat.sendParallelMessageTo(player, "You are not in a chatroom!"); + return true; + } + ChatRoom c = ParallelChat.get().chatRoomManager.getPlayerChatRoom(player); + String msg = ParallelChat.getStringArg(args); + // remove 'msg ' from the start of the returned getStringArg String + c.sendMessage(player, Component.text(msg.substring(4))); + return true; + } + + + @Override + public List getTabComplete(@NotNull Player player, @NotNull String[] args) { + return new ArrayList<>(); + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelPromoteMember.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelPromoteMember.java new file mode 100644 index 00000000..67c2494c --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelPromoteMember.java @@ -0,0 +1,71 @@ +package parallelmc.parallelutils.modules.parallelchat.commands.chatrooms; + +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; +import parallelmc.parallelutils.modules.parallelchat.chatrooms.ChatRoom; + +import java.util.ArrayList; +import java.util.List; + +public class ParallelPromoteMember extends ChatroomCommand { + + private final String USAGE = "/cr promote "; + + public ParallelPromoteMember() { + super("promote", "Promotes a player to moderator status in a chatroom."); + } + + @Override + public boolean execute(@NotNull Player player, @NotNull Command command, String[] args) { + if (args.length != 2) { + player.sendMessage(USAGE); + return false; + } + ChatRoom c = ParallelChat.get().chatRoomManager.getPlayerChatRoom(player); + if (c == null) { + ParallelChat.sendParallelMessageTo(player, "You are not in a chatroom!"); + return true; + } + if (!c.isPlayerOwner(player)) { + ParallelChat.sendParallelMessageTo(player, "Only the owner can promote players!"); + return true; + } + if (args[1].equals(player.getName())) { + ParallelChat.sendParallelMessageTo(player, "You cannot promote yourself!"); + return true; + } + Player promote = player.getServer().getPlayer(args[1]); + if (promote == null) { + ParallelChat.sendParallelMessageTo(player, "Could not find player " + args[1]); + return true; + } + if (!c.hasMember(promote)) { + ParallelChat.sendParallelMessageTo(player, args[1] + " is not in this chatroom!"); + return true; + } + if (c.isPlayerModerator(promote)) { + ParallelChat.sendParallelMessageTo(player, args[1] + " is already a moderator!"); + return true; + } + c.promoteMember(promote); + return true; + } + + @Override + public List getTabComplete(@NotNull Player player, @NotNull String[] args) { + List list = new ArrayList<>(); + if(args.length == 2){ + ChatRoom c = ParallelChat.get().chatRoomManager.getPlayerChatRoom(player); + c.getMembers().forEach((u, b) -> { + OfflinePlayer p = player.getServer().getOfflinePlayer(u); + if (p.isOnline()) { + list.add(p.getName()); + } + }); + } + return list; + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelSendInvite.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelSendInvite.java new file mode 100644 index 00000000..d23a944d --- /dev/null +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/commands/chatrooms/ParallelSendInvite.java @@ -0,0 +1,67 @@ +package parallelmc.parallelutils.modules.parallelchat.commands.chatrooms; + +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; +import parallelmc.parallelutils.modules.parallelchat.chatrooms.ChatRoom; + +import java.util.ArrayList; +import java.util.List; + +public class ParallelSendInvite extends ChatroomCommand { + + private final String USAGE = "/cr invite "; + + public ParallelSendInvite() { + super("invite", "Invite a player to a private chatroom."); + } + + @Override + public boolean execute(@NotNull Player player, @NotNull Command command, String[] args) { + if (args.length != 2) { + player.sendMessage(USAGE); + return false; + } + if (!ParallelChat.get().chatRoomManager.isPlayerInChatroom(player)) { + ParallelChat.sendParallelMessageTo(player, "You are not in a chatroom!"); + return true; + } + ChatRoom c = ParallelChat.get().chatRoomManager.getPlayerChatRoom(player); + if (!c.isPlayerModerator(player)) { + ParallelChat.sendParallelMessageTo(player, "Only moderators may invite players."); + return true; + } + if (args[1].equals(player.getName())) { + ParallelChat.sendParallelMessageTo(player, "You cannot invite yourself!"); + return true; + } + if (!c.isPrivate()) { + ParallelChat.sendParallelMessageTo(player, "This chatroom is public, anyone can join without an invite."); + return true; + } + Player invite = player.getServer().getPlayer(args[1]); + if (invite == null) { + ParallelChat.sendParallelMessageTo(player, "Could not find player " + args[1]); + return true; + } + if (ParallelChat.get().chatRoomManager.isPlayerInChatroom(invite)) { + ParallelChat.sendParallelMessageTo(player, args[1] + " is already in a chatroom!"); + return true; + } + ParallelChat.sendParallelMessageTo(player, "Sent an invite to " + args[1] + ". They have 30 seconds to accept."); + ParallelChat.get().chatRoomManager.invitePlayerToChatroom(invite, player); + return true; + } + + @Override + public List getTabComplete(@NotNull Player player, @NotNull String[] args) { + List list = new ArrayList<>(); + if(args.length == 2){ + list.addAll(player.getServer().getOnlinePlayers().stream().map(HumanEntity::getName).toList()); + } + return list; + } +} diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/events/OnChatMessage.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/events/OnChatMessage.java index 9d75522f..752c9e89 100644 --- a/src/main/java/parallelmc/parallelutils/modules/parallelchat/events/OnChatMessage.java +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/events/OnChatMessage.java @@ -94,6 +94,13 @@ public void onChatMessage(AsyncChatEvent event) { return; } + // ChatRooms + if (ParallelChat.get().chatRoomManager.hasChatroomActive(player)) { + event.setCancelled(true); + ParallelChat.get().chatRoomManager.getPlayerChatRoom(player).sendMessage(player, event.message()); + return; + } + // Mute Chat if (ParallelChat.get().isChatDisabled) { if (!player.hasPermission("parallelutils.bypass.mutechat")) { diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelchat/events/OnJoinLeave.java b/src/main/java/parallelmc/parallelutils/modules/parallelchat/events/OnJoinLeave.java index 1afb51b3..6cd4a81f 100644 --- a/src/main/java/parallelmc/parallelutils/modules/parallelchat/events/OnJoinLeave.java +++ b/src/main/java/parallelmc/parallelutils/modules/parallelchat/events/OnJoinLeave.java @@ -32,6 +32,7 @@ public void onPlayerLeave(PlayerQuitEvent event) { ParallelChat.get().removeFromTeamChat(player); ParallelChat.get().removeFromStaffChat(player); ParallelChat.get().removeFromLoreChat(player); + ParallelChat.get().chatRoomManager.removeActiveChatroom(player); event.quitMessage(null); Component leave = MiniMessage.miniMessage().deserialize(" left the game", TagResolver.resolver(Placeholder.parsed("player", player.getName()))); diff --git a/src/main/java/parallelmc/parallelutils/modules/parallelitems/ParallelItems.java b/src/main/java/parallelmc/parallelutils/modules/parallelitems/ParallelItems.java index 79b90b19..100f8fcc 100644 --- a/src/main/java/parallelmc/parallelutils/modules/parallelitems/ParallelItems.java +++ b/src/main/java/parallelmc/parallelutils/modules/parallelitems/ParallelItems.java @@ -12,7 +12,7 @@ import org.bukkit.Color; import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack;