diff --git a/src/main/java/xyz/soukup/ecoCraftCore/EcoCraftCore.java b/src/main/java/xyz/soukup/ecoCraftCore/EcoCraftCore.java index 2a1749e..907b7f6 100644 --- a/src/main/java/xyz/soukup/ecoCraftCore/EcoCraftCore.java +++ b/src/main/java/xyz/soukup/ecoCraftCore/EcoCraftCore.java @@ -6,7 +6,6 @@ import com.infernalsuite.asp.api.AdvancedSlimePaperAPI; import com.infernalsuite.asp.api.world.SlimeWorldInstance; import com.j256.ormlite.dao.DaoManager; import com.j256.ormlite.jdbc.DataSourceConnectionSource; -import com.j256.ormlite.jdbc.JdbcConnectionSource; import com.j256.ormlite.stmt.UpdateBuilder; import com.j256.ormlite.support.ConnectionSource; import com.j256.ormlite.table.TableUtils; @@ -25,6 +24,7 @@ import org.jetbrains.annotations.NotNull; import xyz.soukup.ecoCraftCore.database.objects.*; import xyz.soukup.ecoCraftCore.islands.IslandAdminCommand; import xyz.soukup.ecoCraftCore.islands.IslandSelectorCommand; +import xyz.soukup.ecoCraftCore.messages.JoinLeaveMessageSupress; import xyz.soukup.ecoCraftCore.mines.MineCommand; import xyz.soukup.ecoCraftCore.mines.MineWorldManager; import xyz.soukup.ecoCraftCore.money.MoneyCommand; @@ -40,11 +40,12 @@ import xyz.soukup.ecoCraftCore.database.objects.Island; import xyz.soukup.ecoCraftCore.database.objects.Shop; import xyz.soukup.ecoCraftCore.database.objects.Transaction; import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; -import xyz.soukup.ecoCraftCore.inventory.VirtualChest; +import xyz.soukup.ecoCraftCore.database.objects.VirtualChest; import xyz.soukup.ecoCraftCore.positionMarker.MarkerEvent; import xyz.soukup.ecoCraftCore.database.DaoRegistry; import xyz.soukup.ecoCraftCore.messages.Messages; import xyz.soukup.ecoCraftCore.shop.ShopLogic; +import xyz.soukup.ecoCraftCore.sign.SignEditCommand; import xyz.soukup.ecoCraftCore.virtualChest.VirtualChestLogic; import java.io.IOException; @@ -86,6 +87,7 @@ public final class EcoCraftCore extends JavaPlugin { registerCommands(); registerEvents(); prepareSlimeWorldsSaver(); + prepareWorldInactivityUnloader(); }catch (IOException e) { e.printStackTrace(); @@ -117,6 +119,7 @@ public final class EcoCraftCore extends JavaPlugin { AdvancedSlimePaperAPI asp = AdvancedSlimePaperAPI.instance(); for (SlimeWorldInstance slimeWorldInstance : asp.getLoadedWorlds()){ + getLogger().info("Saved database world " + slimeWorldInstance.getName()); try { asp.saveWorld(slimeWorldInstance); } catch (IOException e) { @@ -129,7 +132,7 @@ public final class EcoCraftCore extends JavaPlugin { private void prepareWorldInactivityUnloader(){ Bukkit.getScheduler().runTaskTimer(plugin, () -> { - long timeoutMillis = 30 * 60 * 1000; + long timeoutMillis = 6 * 60 * 1000; long now = System.currentTimeMillis(); AdvancedSlimePaperAPI asp = AdvancedSlimePaperAPI.instance(); @@ -152,6 +155,16 @@ public final class EcoCraftCore extends JavaPlugin { if ((now - emptySince) >= timeoutMillis) { Bukkit.unloadWorld(world, true); + getLogger().info("Unloading world " + world.getName() + " due to inactivity"); + try { + UpdateBuilder updateBuilder = DaoRegistry.getIslandDao().updateBuilder(); + updateBuilder.where().eq("uuid", world.getName()); + updateBuilder.updateColumnValue("active_on", ""); + updateBuilder.update(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } @@ -275,6 +288,7 @@ public final class EcoCraftCore extends JavaPlugin { lm.registerEventHandler(LifecycleEvents.COMMANDS, event -> event.registrar().register(MarkerCommand.getCommand().build())); lm.registerEventHandler(LifecycleEvents.COMMANDS, event -> event.registrar().register(MoneyCommand.getCommand().build())); lm.registerEventHandler(LifecycleEvents.COMMANDS, event -> event.registrar().register(MineCommand.createCommand().build())); + lm.registerEventHandler(LifecycleEvents.COMMANDS, event -> event.registrar().register(SignEditCommand.getCommand().build())); } private void registerEvents(){ @@ -288,6 +302,7 @@ public final class EcoCraftCore extends JavaPlugin { pm.registerEvents(new PreparePlayer(), this); pm.registerEvents(new RegionEvents(), this); pm.registerEvents(new MineWorldManager(), this); + pm.registerEvents(new JoinLeaveMessageSupress(), this); EventManager events = PacketEvents.getAPI().getEventManager(); events.registerListener(new ChunkModifier(this), PacketListenerPriority.NORMAL); diff --git a/src/main/java/xyz/soukup/ecoCraftCore/database/DaoRegistry.java b/src/main/java/xyz/soukup/ecoCraftCore/database/DaoRegistry.java index 9b62b01..eaf7907 100644 --- a/src/main/java/xyz/soukup/ecoCraftCore/database/DaoRegistry.java +++ b/src/main/java/xyz/soukup/ecoCraftCore/database/DaoRegistry.java @@ -2,7 +2,7 @@ package xyz.soukup.ecoCraftCore.database; import com.j256.ormlite.dao.Dao; import xyz.soukup.ecoCraftCore.database.objects.*; -import xyz.soukup.ecoCraftCore.inventory.VirtualChest; +import xyz.soukup.ecoCraftCore.database.objects.VirtualChest; public class DaoRegistry { private static Dao shopDao; diff --git a/src/main/java/xyz/soukup/ecoCraftCore/database/objects/Shop.java b/src/main/java/xyz/soukup/ecoCraftCore/database/objects/Shop.java index 6317a3c..86c53a5 100644 --- a/src/main/java/xyz/soukup/ecoCraftCore/database/objects/Shop.java +++ b/src/main/java/xyz/soukup/ecoCraftCore/database/objects/Shop.java @@ -7,6 +7,7 @@ import org.bukkit.block.sign.Side; import org.bukkit.block.sign.SignSide; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; import xyz.soukup.ecoCraftCore.database.DaoRegistry; import xyz.soukup.ecoCraftCore.utilities.Converter; @@ -102,6 +103,7 @@ public class Shop { this.stock = stock; } + public static Shop findById(int id) { Shop shop = cache.get(id); @@ -116,6 +118,10 @@ public class Shop { } } + public static HashMap getCache(){ + return cache; + } + public void save(){ try { DaoRegistry.getShopDao().createOrUpdate(this); @@ -126,13 +132,29 @@ public class Shop { cache.put(this.id, this); } + public void delete(){ + try { + cache.remove(this.id); + DaoRegistry.getShopDao().delete(this); + } catch (SQLException e) { + throw new RuntimeException(e); + } + + } + public ItemStack getItemStack() { return Converter.itemstackFromString(this.itemStackString); } + public void writeIntoSign(Sign sign){ PDC.set(sign, "shop", this.id); + if (PDC.getUniversal(sign, "line1", PersistentDataType.STRING) != null){ + sign.update(); + return; + } + Component prices; Component shopType; @@ -159,7 +181,7 @@ public class Shop { } - public long getId() { + public int getId() { return id; } diff --git a/src/main/java/xyz/soukup/ecoCraftCore/inventory/VirtualChest.java b/src/main/java/xyz/soukup/ecoCraftCore/database/objects/VirtualChest.java similarity index 70% rename from src/main/java/xyz/soukup/ecoCraftCore/inventory/VirtualChest.java rename to src/main/java/xyz/soukup/ecoCraftCore/database/objects/VirtualChest.java index 5a09a87..4b7ba97 100644 --- a/src/main/java/xyz/soukup/ecoCraftCore/inventory/VirtualChest.java +++ b/src/main/java/xyz/soukup/ecoCraftCore/database/objects/VirtualChest.java @@ -1,18 +1,22 @@ -package xyz.soukup.ecoCraftCore.inventory; +package xyz.soukup.ecoCraftCore.database.objects; import com.j256.ormlite.field.DataType; import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.stmt.DeleteBuilder; +import com.j256.ormlite.stmt.QueryBuilder; import com.j256.ormlite.table.DatabaseTable; import org.bukkit.block.Chest; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import xyz.soukup.ecoCraftCore.database.DaoRegistry; +import xyz.soukup.ecoCraftCore.inventory.InventoryUtils; import xyz.soukup.ecoCraftCore.utilities.Converter; import xyz.soukup.ecoCraftCore.utilities.PDC; import java.sql.SQLException; import java.util.HashMap; import java.util.HashSet; +import java.util.List; @DatabaseTable(tableName = "virtual_chests") public class VirtualChest { @@ -29,7 +33,7 @@ public class VirtualChest { private Inventory inventory; private static final HashMap cache = new HashMap<>(); - private static final HashSet cacheChanges = new HashSet<>(); + private static final HashSet changedCached = new HashSet<>(); public VirtualChest(){ @@ -77,7 +81,7 @@ public class VirtualChest { public static void saveCache(){ cache.forEach((key, value) -> { - if (!cacheChanges.contains(key)) return; + if (!changedCached.contains(key)) return; value.databaseSave(); }); @@ -87,7 +91,7 @@ public class VirtualChest { public void save() { cache.put(this.id, this); - cacheChanges.add(this.id); + changedCached.add(this.id); } public void databaseSave(){ @@ -101,7 +105,40 @@ public class VirtualChest { } cache.put(this.id, this); - cacheChanges.remove(this.id); + changedCached.remove(this.id); + } + + public void delete(){ + try { + DaoRegistry.getVirtualChestDao().delete(this); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void deleteSelfAndShops(){ + QueryBuilder queryBuilder = DaoRegistry.getShopDao().queryBuilder(); + DeleteBuilder deleteBuilder = DaoRegistry.getShopDao().deleteBuilder(); + try { + queryBuilder + .selectColumns("id") + .where() + .eq("virtualChestID", this.id); + + List shops = queryBuilder.query(); + HashMap cache = Shop.getCache(); + + for(Shop shop : shops){ + cache.remove(shop.getId()); + } + + deleteBuilder.where().eq("virtualChestID", this.id); + deleteBuilder.delete(); + + DaoRegistry.getVirtualChestDao().delete(this); + } catch (SQLException e) { + throw new RuntimeException(e); + } } public void setInventory(Inventory inventory) { diff --git a/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandAdminCommand.java b/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandAdminCommand.java index 5d7cd81..8af3232 100644 --- a/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandAdminCommand.java +++ b/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandAdminCommand.java @@ -9,8 +9,11 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.command.brigadier.Commands; +import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.block.Biome; import org.bukkit.entity.Player; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; @@ -101,6 +104,15 @@ public class IslandAdminCommand { })) .executes(IslandAdminCommand::loadTemplate)); + LiteralArgumentBuilder setBiome = Commands.literal("biome") + .then(Commands.argument("biome", StringArgumentType.string()) + .suggests(((context, builder) -> { + Registry biomeRegistry = Registry.BIOME; + biomeRegistry.stream().forEach(biome -> builder.suggest(biome.getKey().asString())); + return builder.buildFuture(); + })) + .executes(IslandAdminCommand::setBiome)); + LiteralArgumentBuilder template = Commands.literal("template") .then(Commands.argument("uuid", StringArgumentType.word()) .suggests((context, builder) -> { @@ -165,7 +177,8 @@ public class IslandAdminCommand { .then(spawn) .then(enviroment) .then(expand) - .then(setDefaultIsland); + .then(setDefaultIsland) + .then(setBiome); @@ -202,6 +215,16 @@ public class IslandAdminCommand { return 0; } + private static int setBiome(CommandContext context) { + if (!(context.getSource().getSender() instanceof Player player)) return 0; + IslandManager islandManager = new IslandManager(); + String uuid = player.getWorld().getName(); + islandManager.changeBiome(context.getArgument("biome", String.class), uuid); + Messages.send(player, "island.set-biome.success"); + + return 0; + } + private static int teleport(CommandContext context) { IslandManager islandManager = new IslandManager(); diff --git a/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandManager.java b/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandManager.java index 301594f..34e6594 100644 --- a/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandManager.java +++ b/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandManager.java @@ -270,6 +270,18 @@ public class IslandManager { } } + public void changeBiome(String biome, String uuid){ + SlimeWorld slimeWorld = asp.getLoadedWorld(uuid); + SlimePropertyMap slimePropertyMap = slimeWorld.getPropertyMap(); + slimePropertyMap.setValue(SlimeProperties.DEFAULT_BIOME, biome); + + try { + asp.saveWorld(slimeWorld); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + public void changeEnviroment( String environment, String uuid) { SlimeWorld slimeWorld = asp.getLoadedWorld(uuid); SlimePropertyMap slimePropertyMap = slimeWorld.getPropertyMap(); diff --git a/src/main/java/xyz/soukup/ecoCraftCore/messages/JoinLeaveMessageSupress.java b/src/main/java/xyz/soukup/ecoCraftCore/messages/JoinLeaveMessageSupress.java new file mode 100644 index 0000000..4b3e318 --- /dev/null +++ b/src/main/java/xyz/soukup/ecoCraftCore/messages/JoinLeaveMessageSupress.java @@ -0,0 +1,18 @@ +package xyz.soukup.ecoCraftCore.messages; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class JoinLeaveMessageSupress implements Listener { + @EventHandler + public void onJoin(PlayerJoinEvent event) { + event.joinMessage(null); + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + event.quitMessage(null); + } +} diff --git a/src/main/java/xyz/soukup/ecoCraftCore/messages/Messages.java b/src/main/java/xyz/soukup/ecoCraftCore/messages/Messages.java index 2371064..cfc7595 100644 --- a/src/main/java/xyz/soukup/ecoCraftCore/messages/Messages.java +++ b/src/main/java/xyz/soukup/ecoCraftCore/messages/Messages.java @@ -4,6 +4,7 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.YamlConfiguration; @@ -39,7 +40,7 @@ public class Messages { } public static String getAsString(String key){ - return MiniMessage.miniMessage().serialize(Messages.get(key)); + return LegacyComponentSerializer.legacySection().serialize(Messages.get(key)); } public static Component get(String key, HashMap placeholders){ diff --git a/src/main/java/xyz/soukup/ecoCraftCore/regions/RegionEvents.java b/src/main/java/xyz/soukup/ecoCraftCore/regions/RegionEvents.java index 151a597..7033ea4 100644 --- a/src/main/java/xyz/soukup/ecoCraftCore/regions/RegionEvents.java +++ b/src/main/java/xyz/soukup/ecoCraftCore/regions/RegionEvents.java @@ -1,16 +1,29 @@ package xyz.soukup.ecoCraftCore.regions; +import org.bukkit.Location; +import org.bukkit.NamespacedKey; import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Item; +import org.bukkit.entity.Minecart; 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.block.BlockBreakEvent; import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.inventory.InventoryPickupItemEvent; import org.bukkit.event.player.*; +import org.bukkit.event.vehicle.VehicleDestroyEvent; +import org.bukkit.event.vehicle.VehicleEntityCollisionEvent; import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataType; import xyz.soukup.ecoCraftCore.database.objects.Region; import xyz.soukup.ecoCraftCore.database.objects.RegionMember; +import static xyz.soukup.ecoCraftCore.EcoCraftCore.plugin; + public class RegionEvents implements Listener { @EventHandler @@ -40,7 +53,36 @@ public class RegionEvents implements Listener { block.getZ() ); - event.setCancelled(!allowed); + if (allowed){ + return; + } + + event.setCancelled(true); + } + + @EventHandler + public void onPlayerInteractPhysical(PlayerInteractEvent event){ + if (!event.getAction().equals(Action.PHYSICAL)){ + return; + } + + Player player = event.getPlayer(); + Location location = player.getLocation(); + + boolean allowed = isAllowedToInteract( + location.getWorld().getName(), + player, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ() + ); + + if (allowed){ + return; + } + + event.setCancelled(true); + } @EventHandler @@ -55,7 +97,11 @@ public class RegionEvents implements Listener { block.getZ() ); - event.setCancelled(!allowed); + if (allowed){ + return; + } + + event.setCancelled(true); } @EventHandler @@ -71,7 +117,11 @@ public class RegionEvents implements Listener { block.getZ() ); - event.setCancelled(!allowed); + if (allowed){ + return; + } + + event.setCancelled(true); } @EventHandler @@ -86,7 +136,11 @@ public class RegionEvents implements Listener { clickedBlock.getZ() ); - event.setCancelled(!allowed); + if (allowed){ + return; + } + + event.setCancelled(true); } @EventHandler @@ -101,10 +155,142 @@ public class RegionEvents implements Listener { clickedBlock.getZ() ); - event.setCancelled(!allowed); + if (allowed){ + return; + } + + event.setCancelled(true); } + @EventHandler + public void onEntityInteract(PlayerInteractEntityEvent event){ + Entity entity = event.getRightClicked(); + Location location = entity.getLocation(); + + + boolean allowed = isAllowedToInteract( + location.getWorld().getName(), + event.getPlayer(), + location.getBlockX(), + location.getBlockY(), + location.getBlockZ()); + + if (allowed){ + return; + } + + event.setCancelled(true); + } + + @EventHandler + public void onEntityHit(EntityDamageByEntityEvent event){ + + if (!(event.getDamager() instanceof Player player)){ + return; + } + + Entity entity = event.getEntity(); + Location location = entity.getLocation(); + + boolean allowed = isAllowedToInteract( + location.getWorld().getName(), + player, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ()); + + if (allowed){ + return; + } + + event.setCancelled(true); + } + + @EventHandler + public void onMinecartPush(VehicleEntityCollisionEvent event) { + + + if (!(event.getEntity() instanceof Player player)){ + return; + } + + Location location = event.getVehicle().getLocation(); + + boolean allowed = isAllowedToInteract( + location.getWorld().getName(), + player, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ()); + + if (allowed){ + return; + } + + event.setCancelled(true); + } + + //Preventing hopper interaction :P + @EventHandler + public void onDrop(PlayerDropItemEvent event) { + Player player = event.getPlayer(); + Location location = player.getLocation(); + + boolean allowed = isAllowedToInteract( + location.getWorld().getName(), + player, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ()); + + if (allowed){ + return; + } + + Item item = event.getItemDrop(); + item.getPersistentDataContainer().set(new NamespacedKey(plugin, "hopper_stopper"), PersistentDataType.BYTE, (byte) 1); + + } + + @EventHandler + public void onHopperPickup(InventoryPickupItemEvent event) { + Item item = event.getItem(); + + if (!item.getPersistentDataContainer().has(new NamespacedKey(plugin, "hopper_stopper"), PersistentDataType.BYTE)) { + return; + } + + event.setCancelled(true); + } + + @EventHandler + public void onVehicleDestroy(VehicleDestroyEvent event) { + if (!(event.getAttacker() instanceof Player player)){ + return; + } + + Location location = event.getVehicle().getLocation(); + + boolean allowed = isAllowedToInteract( + location.getWorld().getName(), + player, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ()); + + if (allowed){ + return; + } + + event.setCancelled(true); + } + + + + private boolean isAllowedToInteract(String island, Player player, Location location){ + return isAllowedToInteract(island, player, location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } private boolean isAllowedToInteract(String island, Player player, int x, int y, int z){ if (player.isOp()){ diff --git a/src/main/java/xyz/soukup/ecoCraftCore/regions/RegionManager.java b/src/main/java/xyz/soukup/ecoCraftCore/regions/RegionManager.java index 14c480d..63c365c 100644 --- a/src/main/java/xyz/soukup/ecoCraftCore/regions/RegionManager.java +++ b/src/main/java/xyz/soukup/ecoCraftCore/regions/RegionManager.java @@ -3,6 +3,7 @@ package xyz.soukup.ecoCraftCore.regions; import org.bukkit.Location; import org.bukkit.entity.Player; import xyz.soukup.ecoCraftCore.database.objects.Region; +import xyz.soukup.ecoCraftCore.database.objects.RegionMember; import xyz.soukup.ecoCraftCore.positionMarker.MarkerEvent; public class RegionManager { @@ -45,4 +46,32 @@ public class RegionManager { return 0; } + + public static boolean isAllowedToInteract(Player player, Location location){ + if (player.isOp()){ + return true; + } + + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + String island = location.getWorld().getName(); + + Region region = Region.findRegion(x, y, z, island); + + if (region == null){ + return false; + } + + String name = player.getName(); + + for (RegionMember regionMember : region.getRegionMembers()){ + + if (regionMember.getMembertype().equals("player") && regionMember.getName().equals(name)){ + return true; + } + } + + return false; + } } diff --git a/src/main/java/xyz/soukup/ecoCraftCore/shop/ShopCommand.java b/src/main/java/xyz/soukup/ecoCraftCore/shop/ShopCommand.java index c53fbe0..a7c98d2 100644 --- a/src/main/java/xyz/soukup/ecoCraftCore/shop/ShopCommand.java +++ b/src/main/java/xyz/soukup/ecoCraftCore/shop/ShopCommand.java @@ -11,7 +11,7 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import xyz.soukup.ecoCraftCore.positionMarker.MarkerEvent; import xyz.soukup.ecoCraftCore.database.objects.Shop; -import xyz.soukup.ecoCraftCore.inventory.VirtualChest; +import xyz.soukup.ecoCraftCore.database.objects.VirtualChest; import xyz.soukup.ecoCraftCore.inventory.InventoryUtils; import xyz.soukup.ecoCraftCore.messages.Messages; import xyz.soukup.ecoCraftCore.utilities.PDC; diff --git a/src/main/java/xyz/soukup/ecoCraftCore/shop/ShopLogic.java b/src/main/java/xyz/soukup/ecoCraftCore/shop/ShopLogic.java index e9905a6..78a3c0c 100644 --- a/src/main/java/xyz/soukup/ecoCraftCore/shop/ShopLogic.java +++ b/src/main/java/xyz/soukup/ecoCraftCore/shop/ShopLogic.java @@ -1,6 +1,8 @@ package xyz.soukup.ecoCraftCore.shop; import com.github.stefvanschie.inventoryframework.gui.GuiItem; import com.github.stefvanschie.inventoryframework.gui.type.ChestGui; +import com.github.stefvanschie.inventoryframework.gui.type.HopperGui; +import com.github.stefvanschie.inventoryframework.pane.OutlinePane; import com.github.stefvanschie.inventoryframework.pane.StaticPane; import io.papermc.paper.dialog.Dialog; import io.papermc.paper.dialog.DialogResponseView; @@ -11,13 +13,16 @@ import io.papermc.paper.registry.data.dialog.input.DialogInput; import io.papermc.paper.registry.data.dialog.type.DialogType; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.text.event.ClickCallback; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.Sign; 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.Action; +import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; @@ -29,7 +34,7 @@ import xyz.soukup.ecoCraftCore.messages.Messages; import xyz.soukup.ecoCraftCore.database.objects.Account; import xyz.soukup.ecoCraftCore.database.objects.Shop; import xyz.soukup.ecoCraftCore.database.objects.Transaction; -import xyz.soukup.ecoCraftCore.inventory.VirtualChest; +import xyz.soukup.ecoCraftCore.database.objects.VirtualChest; import xyz.soukup.ecoCraftCore.utilities.*; import java.util.HashMap; @@ -87,6 +92,39 @@ public class ShopLogic implements Listener { gui.show(player); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onDestroy(BlockBreakEvent event){ + + if (event.isCancelled()){ + return; + } + + Block block = event.getBlock(); + + if (!(block.getState() instanceof Sign sign)){ + return; + } + + Integer id = PDC.getInteger(sign, "shop"); + + if (id == null){ + return; + } + + Shop shop = Shop.findById(id); + + if (shop == null){ + return; + } + + event.setCancelled(true); + Player player = event.getPlayer(); + HopperGui gui = confirmBreakGui(block, shop); + gui.show(player); + + } public static ChestGui buildShopGui(Player player, Shop shop){ @@ -260,7 +298,7 @@ public class ShopLogic implements Listener { HashMap hashMap = new HashMap<>(); hashMap.put("count", Integer.toString(amount)); hashMap.put("price", String.format("%.2f", price)); - hashMap.put("item", itemStack.displayName().toString()); + hashMap.put("item", PlainTextComponentSerializer.plainText().serialize(itemStack.displayName())); Messages.send(player, "shop.buy", hashMap); @@ -348,9 +386,48 @@ public class ShopLogic implements Listener { HashMap hashMap = new HashMap<>(); hashMap.put("count", Integer.toString(amount)); hashMap.put("price", String.format("%.2f", price)); - hashMap.put("item", itemStack.displayName().toString()); + hashMap.put("item", PlainTextComponentSerializer.plainText().serialize(itemStack.displayName())); Messages.send(player, "shop.sell", hashMap); } + + public static HopperGui confirmBreakGui(Block block, Shop shop){ + HopperGui gui = new HopperGui(Messages.getAsString("menu.destroy-confirmation.title")); + OutlinePane pane = new OutlinePane(0,0, 5, 1); + + gui.setOnGlobalClick(inventoryClickEvent -> { + inventoryClickEvent.setCancelled(true); + }); + + GuiItem confirmButton = new GuiItemBuilder(Material.LIME_WOOL) + .setName(Messages.get("menu.destroy-confirmation.confirm")) + .build(); + + GuiItem cancelButton = new GuiItemBuilder(Material.RED_WOOL) + .setName(Messages.get("menu.destroy-confirmation.cancel")) + .build(); + + cancelButton.setAction(event -> { + event.getClickedInventory().close(); + }); + + confirmButton.setAction(event -> { + destroyShopAndBlock(block, shop); + event.getClickedInventory().close(); + }); + + pane.addItem(confirmButton); + pane.addItem(cancelButton); + + gui.getSlotsComponent().addPane(pane); + + + return gui; + } + + private static void destroyShopAndBlock(Block block, Shop shop){ + block.breakNaturally(); + shop.delete(); + } } diff --git a/src/main/java/xyz/soukup/ecoCraftCore/sign/SignEditCommand.java b/src/main/java/xyz/soukup/ecoCraftCore/sign/SignEditCommand.java new file mode 100644 index 0000000..3d6b9d3 --- /dev/null +++ b/src/main/java/xyz/soukup/ecoCraftCore/sign/SignEditCommand.java @@ -0,0 +1,187 @@ +package xyz.soukup.ecoCraftCore.sign; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.Commands; +import io.papermc.paper.dialog.Dialog; +import io.papermc.paper.dialog.DialogResponseView; +import io.papermc.paper.registry.data.dialog.ActionButton; +import io.papermc.paper.registry.data.dialog.DialogBase; +import io.papermc.paper.registry.data.dialog.action.DialogAction; +import io.papermc.paper.registry.data.dialog.input.DialogInput; +import io.papermc.paper.registry.data.dialog.type.DialogType; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickCallback; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.Sign; +import org.bukkit.block.sign.Side; +import org.bukkit.block.sign.SignSide; +import org.bukkit.entity.Player; +import org.bukkit.persistence.PersistentDataType; +import xyz.soukup.ecoCraftCore.database.objects.Shop; +import xyz.soukup.ecoCraftCore.messages.Messages; +import xyz.soukup.ecoCraftCore.regions.RegionManager; +import xyz.soukup.ecoCraftCore.utilities.PDC; + +import javax.sound.sampled.Line; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + + +public class SignEditCommand { + + private static final HashMap> clipBoard = new HashMap<>(); + + public static LiteralArgumentBuilder getCommand(){ + return Commands.literal("edit-sign") + .requires(source -> source.getSender() instanceof Player) + .executes(SignEditCommand::openEditMenu); + } + + private static int openEditMenu(CommandContext context){ + Player player = (Player) context.getSource().getSender(); + Block block = player.getTargetBlock(null, 10); + BlockState blockState = block.getState(); + + if (!(blockState instanceof Sign sign)){ + Messages.send(player, "sign-edit.error.not-sign"); + return 1; + } + + Location location = sign.getLocation(); + + if (!RegionManager.isAllowedToInteract(player, location)){ + Messages.send(player, "region.error.not-allowed-to-interact"); + return 1; + } + + Dialog dialog = buildEditDialog(sign, player, null); + player.showDialog(dialog); + + + + return 0; + } + + + public static Dialog buildEditDialog(Sign sign, Player player, List lines){ + + if (lines == null){ + lines = getLines(sign); + } + + List actionButtons = new ArrayList<>(); + + + + actionButtons.add(ActionButton.builder(Messages.get("gui.sign-edit.buttons.copy")) + .action(DialogAction.customClick((view, audience) -> copySignText(view, player, sign), ClickCallback.Options.builder().build())) + .build()); + actionButtons.add(ActionButton.builder(Messages.get("gui.sign-edit.buttons.paste")) + .action(DialogAction.customClick((view, audience) -> pasteSignText(sign, player), ClickCallback.Options.builder().build())) + .build()); + actionButtons.add(ActionButton.builder(Messages.get("gui.sign-edit.buttons.confirm")) + .action(DialogAction.customClick((view, audience) -> editSign(view, sign, player), ClickCallback.Options.builder().build())) + .build()); + actionButtons.add(ActionButton.builder(Messages.get("gui.sign-edit.buttons.cancel")) + .action(DialogAction.customClick((view, audience) -> audience.closeDialog(), ClickCallback.Options.builder().build())) + .build()); + + + List finalLines = lines; + + return Dialog.create(builder -> builder.empty() + .base(DialogBase.builder(Messages.get("gui.sign-edit.title")) + .inputs(List.of( + DialogInput.text("line1", Messages.get("gui.sign-edit.inputs.line1")) + .initial(finalLines.getFirst()) + .maxLength(256) + .build(), + DialogInput.text("line2", Messages.get("gui.sign-edit.inputs.line2")) + .initial(finalLines.get(1)) + .maxLength(256) + .build(), + DialogInput.text("line3", Messages.get("gui.sign-edit.inputs.line3")) + .initial(finalLines.get(2)) + .maxLength(256) + .build(), + DialogInput.text("line4", Messages.get("gui.sign-edit.inputs.line4")) + .initial(finalLines.get(3)) + .maxLength(256) + .build() + )) + .build()) + .type(DialogType.multiAction(actionButtons).build()) + ); + } + + private static List getLines(Sign sign){ + List lines = new ArrayList<>(); + + if (PDC.getUniversal(sign, "line1", PersistentDataType.STRING) != null){ + for (int i = 1; i<5; i++){ + lines.add((String) PDC.getUniversal(sign, "line" + i,PersistentDataType.STRING)); + } + return lines; + } + + SignSide signSide = sign.getSide(Side.FRONT); + List componentLines = signSide.lines(); + + for (Component componentLine : componentLines){ + lines.add(MiniMessage.miniMessage().serialize(componentLine)); + } + + return lines; + } + + private static void copySignText(DialogResponseView view, Player player, Sign sign){ + List lines = new ArrayList<>(); + + lines.add(view.getText("line1")); + lines.add(view.getText("line2")); + lines.add(view.getText("line3")); + lines.add(view.getText("line4")); + + clipBoard.put(player, lines); + Messages.send(player, "gui.sign-edit.success.copied"); + pasteSignText(sign, player); + } + + + private static void pasteSignText(Sign sign, Player player){ + Dialog dialog = buildEditDialog(sign, player, clipBoard.get(player)); + player.showDialog(dialog); + } + + private static void editSign(DialogResponseView view, Sign sign, Audience audience){ + MiniMessage mm = MiniMessage.miniMessage(); + SignSide signSide = sign.getSide(Side.FRONT); + + List lines = new ArrayList<>(); + + + for (int i = 1; i < 5; i++) { + String key = "line" + i; + String line = view.getText(key); + + if (line == null){ + line = ""; + } + + PDC.setUniversal(sign, key, line, PersistentDataType.STRING); + signSide.line(i-1, mm.deserialize(line)); + + } + sign.update(); + + } + + +} diff --git a/src/main/java/xyz/soukup/ecoCraftCore/virtualChest/VirtualChestLogic.java b/src/main/java/xyz/soukup/ecoCraftCore/virtualChest/VirtualChestLogic.java index f84bf9f..14a6627 100644 --- a/src/main/java/xyz/soukup/ecoCraftCore/virtualChest/VirtualChestLogic.java +++ b/src/main/java/xyz/soukup/ecoCraftCore/virtualChest/VirtualChestLogic.java @@ -1,17 +1,24 @@ package xyz.soukup.ecoCraftCore.virtualChest; -import org.bukkit.block.Chest; -import org.bukkit.block.DoubleChest; -import org.bukkit.block.TileState; +import com.github.stefvanschie.inventoryframework.gui.GuiItem; +import com.github.stefvanschie.inventoryframework.gui.type.HopperGui; +import com.github.stefvanschie.inventoryframework.pane.OutlinePane; +import org.bukkit.Material; +import org.bukkit.block.*; 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.BlockBreakEvent; import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryMoveItemEvent; import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; -import xyz.soukup.ecoCraftCore.inventory.VirtualChest; +import xyz.soukup.ecoCraftCore.database.objects.Shop; +import xyz.soukup.ecoCraftCore.gui.GuiItemBuilder; +import xyz.soukup.ecoCraftCore.database.objects.VirtualChest; +import xyz.soukup.ecoCraftCore.messages.Messages; import xyz.soukup.ecoCraftCore.utilities.PDC; import java.util.HashMap; @@ -20,6 +27,39 @@ public class VirtualChestLogic implements Listener { public static HashMap openedChests = new HashMap<>(); + @EventHandler(priority = EventPriority.HIGHEST) + public void blockBreak(BlockBreakEvent event){ + Block block = event.getBlock(); + BlockState blockState = block.getState(); + Player player = event.getPlayer(); + + Integer id; + + if (blockState instanceof Chest chest){ + id = PDC.getInteger(chest, "virtual"); + } else if (blockState instanceof DoubleChest doubleChest) { + id = PDC.getInteger((TileState) doubleChest.getRightSide(), "virtual"); + }else { + return; + } + + if (id == null){ + return; + } + + VirtualChest virtualChest = VirtualChest.findById(id); + + if (virtualChest == null){ + return; + } + + event.setCancelled(true); + + HopperGui gui = confirmBreakGui(block, virtualChest); + gui.show(player); + + } + @EventHandler public void chestOpen(InventoryOpenEvent event){ InventoryHolder inventoryHolder = event.getInventory().getHolder(); @@ -143,8 +183,43 @@ public class VirtualChestLogic implements Listener { virtualChest.save(); + } + public static HopperGui confirmBreakGui(Block block, VirtualChest virtualChest){ + HopperGui gui = new HopperGui(Messages.getAsString("menu.destroy-confirmation.title")); + OutlinePane pane = new OutlinePane(0,0, 5, 1); + + gui.setOnGlobalClick(inventoryClickEvent -> { + inventoryClickEvent.setCancelled(true); + }); + + GuiItem confirmButton = new GuiItemBuilder(Material.LIME_WOOL) + .setName(Messages.get("menu.destroy-confirmation.confirm")) + .build(); + + GuiItem cancelButton = new GuiItemBuilder(Material.RED_WOOL) + .setName(Messages.get("menu.destroy-confirmation.cancel")) + .build(); + + cancelButton.setAction(event -> { + event.getClickedInventory().close(); + }); + confirmButton.setAction(event -> { + destroyShopAndBlock(block, virtualChest); + event.getClickedInventory().close(); + }); + pane.addItem(confirmButton); + pane.addItem(cancelButton); + + gui.getSlotsComponent().addPane(pane); + + + return gui; + } + private static void destroyShopAndBlock(Block block, VirtualChest virtualChest){ + block.breakNaturally(); + virtualChest.deleteSelfAndShops(); } } \ No newline at end of file diff --git a/src/main/resources/messages.yml b/src/main/resources/messages.yml index a711665..1c9a4fc 100644 --- a/src/main/resources/messages.yml +++ b/src/main/resources/messages.yml @@ -16,6 +16,10 @@ region: error: not-marked: "Musíš nejprve označit pozice" not-exist: "Region neexistuje" + not-allowed-to-interact: "Nemáš povolení dělat změny na tomto území" +sign-edit: + error: + not-sign: "Musíš se dívat na cedulku." shop: error: already-shop: "Tato cedule již je obchod" @@ -50,6 +54,10 @@ marker: primary: "První pozice označena (,,)" secondary: "Druhá pozice označena (,,)" menu: + destroy-confirmation: + title: "Zníčit obchod?" + confirm: "Ano." + cancel: "Ne." island-selector: title: "Výběr ostrovu" my-islands: "Moje ostrovy" @@ -63,6 +71,22 @@ gui: error: invalid-input: "Vámi zadané hodnoty nejsou platné." all: "Seznam guis: <1>, <2>" + sign-edit: + title: "Editace Cedulky" + inputs: + line1: "1. Linka" + line2: "2. Linka" + line3: "3. Linka" + line4: "4. Linka" + glow: "Zvíraznit text" + buttons: + confirm: "Potvrdit" + cancel: "Zrušit" + copy: "Kopírovat" + paste: "Vložit" + success: + copied: "Obsah cedulky zkopírován!" + pasted: "Obsah cedulky vložen!" shop-edit: title: "Editace Obchodu" inputs: