From c478a1c382006e78f4d1a6a11f4fc7916143c47f Mon Sep 17 00:00:00 2001 From: balugaq <129668183+balugaq@users.noreply.github.com> Date: Sun, 10 Nov 2024 22:55:31 +0800 Subject: [PATCH] Resolves #170 #171 --- pom.xml | 2 +- .../utils/ReflectionUtil.java | 16 ++ .../networksexpansion/utils/WorldUtils.java | 56 +++++++ .../networks/commands/NetworksMain.java | 137 ++++++++++++++++-- 4 files changed, 196 insertions(+), 15 deletions(-) create mode 100644 src/main/java/com/ytdd9527/networksexpansion/utils/WorldUtils.java diff --git a/pom.xml b/pom.xml index 63b977d0..7eb910fa 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 4.0.0 com.ytdd9527.networksexpansion NetworksExpansion - 2.1-Alpha-5 + 2.1-Alpha-6 diff --git a/src/main/java/com/ytdd9527/networksexpansion/utils/ReflectionUtil.java b/src/main/java/com/ytdd9527/networksexpansion/utils/ReflectionUtil.java index c6d09217..6657798f 100644 --- a/src/main/java/com/ytdd9527/networksexpansion/utils/ReflectionUtil.java +++ b/src/main/java/com/ytdd9527/networksexpansion/utils/ReflectionUtil.java @@ -1,5 +1,6 @@ package com.ytdd9527.networksexpansion.utils; +import io.github.thebusybiscuit.slimefun4.libraries.dough.collections.Pair; import lombok.experimental.UtilityClass; import java.lang.reflect.Field; @@ -68,4 +69,19 @@ public static T getProperty(Object o, Class clazz, String fieldName) t return null; } + + public static Pair> getDeclaredFieldsRecursively(Class clazz, String fieldName){ + try{ + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return new Pair<>(field, clazz); + }catch (Throwable e){ + clazz = clazz.getSuperclass(); + if(clazz == null){ + return null; + } else { + return getDeclaredFieldsRecursively(clazz, fieldName); + } + } + } } diff --git a/src/main/java/com/ytdd9527/networksexpansion/utils/WorldUtils.java b/src/main/java/com/ytdd9527/networksexpansion/utils/WorldUtils.java new file mode 100644 index 00000000..823eb558 --- /dev/null +++ b/src/main/java/com/ytdd9527/networksexpansion/utils/WorldUtils.java @@ -0,0 +1,56 @@ +package com.ytdd9527.networksexpansion.utils; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; + +import java.lang.reflect.Field; + +public class WorldUtils { + protected static Class CraftBlockStateClass; + protected static Field IBlockDataField; + protected static Field BlockPositionField; + protected static Field WorldField; + protected static Field WeakWorldField; + protected static boolean invokeBlockStateSuccess = false; + static { + try { + World sampleWorld= Bukkit.getWorlds().get(0); + BlockState blockstate=sampleWorld.getBlockAt(0, 0, 0).getState(); + var result= ReflectionUtil.getDeclaredFieldsRecursively(blockstate.getClass(),"data"); + IBlockDataField = result.getFirstValue(); + IBlockDataField.setAccessible(true); + CraftBlockStateClass = result.getSecondValue(); + BlockPositionField = ReflectionUtil.getDeclaredFieldsRecursively(CraftBlockStateClass,"position").getFirstValue(); + BlockPositionField.setAccessible(true); + WorldField = ReflectionUtil.getDeclaredFieldsRecursively(CraftBlockStateClass,"world").getFirstValue(); + WorldField.setAccessible(true); + WeakWorldField = ReflectionUtil.getDeclaredFieldsRecursively(CraftBlockStateClass,"weakWorld").getFirstValue(); + WeakWorldField.setAccessible(true); + invokeBlockStateSuccess = true; + } catch (Throwable ignored) { + + } + } + public static boolean copyBlockState(BlockState stateToSet, Block toBlock){ + if (!invokeBlockStateSuccess) { + return false; + } + + BlockState originalState = toBlock.getState(); + if (!CraftBlockStateClass.isInstance(originalState) ||!CraftBlockStateClass.isInstance(stateToSet)) { + return false; + } + + try { + BlockPositionField.set(stateToSet, BlockPositionField.get(originalState)); + WorldField.set(stateToSet, WorldField.get(originalState)); + WeakWorldField.set(stateToSet, WeakWorldField.get(originalState)); + stateToSet.update(true, false); + return true; + } catch (Throwable ignored) { + return false; + } + } +} \ No newline at end of file diff --git a/src/main/java/io/github/sefiraat/networks/commands/NetworksMain.java b/src/main/java/io/github/sefiraat/networks/commands/NetworksMain.java index 3abb660b..6a5a34de 100644 --- a/src/main/java/io/github/sefiraat/networks/commands/NetworksMain.java +++ b/src/main/java/io/github/sefiraat/networks/commands/NetworksMain.java @@ -8,6 +8,7 @@ import com.xzavier0722.mc.plugin.slimefun4.storage.util.StorageCacheUtils; import com.ytdd9527.networksexpansion.core.items.unusable.AbstractBlueprint; import com.ytdd9527.networksexpansion.implementation.machines.unit.NetworksDrawer; +import com.ytdd9527.networksexpansion.utils.WorldUtils; import io.github.bakedlibs.dough.collections.Pair; import io.github.bakedlibs.dough.skins.PlayerHead; import io.github.bakedlibs.dough.skins.PlayerSkin; @@ -30,12 +31,14 @@ import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; +import org.bukkit.Bukkit; import org.bukkit.FluidCollisionMode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; @@ -55,33 +58,33 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -@SuppressWarnings("deprecation") +@SuppressWarnings({"deprecation", "unused"}) public class NetworksMain implements TabExecutor { - private static final Map> SELECTED_POS = new HashMap<>(); - + private static final Map> SELECTED_POS = new HashMap<>(); public static Location getPos1(Player p) { - if (SELECTED_POS.get(p.getName()) == null) { + if (SELECTED_POS.get(p.getUniqueId()) == null) { return null; } - return SELECTED_POS.get(p.getName()).getFirstValue(); + return SELECTED_POS.get(p.getUniqueId()).getFirstValue(); } public static Location getPos2(Player p) { - if (SELECTED_POS.get(p.getName()) == null) { + if (SELECTED_POS.get(p.getUniqueId()) == null) { return null; } - return SELECTED_POS.get(p.getName()).getSecondValue(); + return SELECTED_POS.get(p.getUniqueId()).getSecondValue(); } public static void setPos1(Player p, Location pos) { - SELECTED_POS.put(p.getName(), new Pair<>(pos, getPos2(p))); + SELECTED_POS.put(p.getUniqueId(), new Pair<>(pos, getPos2(p))); } public static void setPos2(Player p, Location pos) { - SELECTED_POS.put(p.getName(), new Pair<>(getPos1(p), pos)); + SELECTED_POS.put(p.getUniqueId(), new Pair<>(getPos1(p), pos)); } public static String locationToString(Location l) { @@ -353,6 +356,104 @@ public static void worldeditPos2(Player player, Location location) { } } + public static void worldeditClone(Player player) { + worldeditClone(player, false); + } + public static void worldeditClone(Player player, boolean overrideData) { + if (getPos1(player) == null || getPos2(player) == null) { + player.sendMessage(Networks.getLocalizationService().getString("messages.commands.worldedit.must-select-area")); + return; + } + + if (!Objects.equals(getPos1(player).getWorld().getUID(), getPos2(player).getWorld().getUID())) { + player.sendMessage(Networks.getLocalizationService().getString("messages.commands.worldedit.must-select-same-world")); + return; + } + + player.sendMessage(String.format(Networks.getLocalizationService().getString("messages.commands.worldedit.pasting-block"), locationToString(getPos1(player)), locationToString(getPos2(player)))); + final long currentMillSeconds = System.currentTimeMillis(); + + final AtomicInteger count = new AtomicInteger(); + final Location playerLocation = player.getLocation(); + final ItemStack itemInHand = player.getItemInHand(); + + final Location pos1 = getPos1(player); + final int dx = playerLocation.getBlockX() - pos1.getBlockX(); + final int dy = playerLocation.getBlockY() - pos1.getBlockY(); + final int dz = playerLocation.getBlockZ() - pos1.getBlockZ(); + + Bukkit.getScheduler().runTask(Networks.getInstance(), () -> { + doWorldEdit(getPos1(player), getPos2(player), (fromLocation -> { + final Block fromBlock = fromLocation.getBlock(); + final Block toBlock = playerLocation.getWorld().getBlockAt(fromLocation.getBlockX() + dx, fromLocation.getBlockY() + dy, fromLocation.getBlockZ() + dz); + final SlimefunItem slimefunItem = StorageCacheUtils.getSfItem(fromLocation); + final Location toLocation = toBlock.getLocation(); + + // Block Data + WorldUtils.copyBlockState(fromBlock.getState(), toBlock); + + // Count means successful pasting block data. Not including Slimefun data. + count.addAndGet(1); + + // Slimefun Data + if (slimefunItem == null) { + return; + } + + // Call Handler + slimefunItem.callItemHandler(BlockPlaceHandler.class, handler -> handler.onPlayerPlace( + new BlockPlaceEvent( + toBlock, + toBlock.getState(), + toBlock.getRelative(BlockFace.SOUTH), + itemInHand, + player, + true + ) + )); + + SlimefunBlockData fromSlimefunBlockData = Slimefun.getDatabaseManager().getBlockDataController().getBlockData(fromLocation); + if (overrideData) { + Slimefun.getDatabaseManager().getBlockDataController().removeBlock(toLocation); + } + + if (StorageCacheUtils.hasBlock(toLocation)) { + return; + } + + // Slimefun Block + Slimefun.getDatabaseManager().getBlockDataController().createBlock(toLocation, slimefunItem.getId()); + SlimefunBlockData toSlimefunBlockData = Slimefun.getDatabaseManager().getBlockDataController().getBlockData(toLocation); + + // SlimefunBlockData + if (fromSlimefunBlockData == null || toSlimefunBlockData == null) { + return; + } + + Map data = fromSlimefunBlockData.getAllData(); + for (String key : data.keySet()) { + toSlimefunBlockData.setData(key, data.get(key)); + } + + // BlockMenu + final BlockMenu fromMenu = fromSlimefunBlockData.getBlockMenu(); + final BlockMenu toMenu = toSlimefunBlockData.getBlockMenu(); + + if (fromMenu == null || toMenu == null) { + return; + } + + ItemStack[] contents = fromMenu.getContents(); + for (int i = 0; i < contents.length; i++) { + if (contents[i] != null) { + toMenu.getInventory().setItem(i, contents[i].clone()); + } + } + })); + player.sendMessage(String.format(Networks.getLocalizationService().getString("messages.commands.worldedit.paste-done"), count, System.currentTimeMillis() - currentMillSeconds)); + }); + } + public static void worldeditPaste(Player player, String sfid) { worldeditPaste(player, sfid, false, false); } @@ -415,9 +516,9 @@ public static void worldeditPaste(Player player, String sfid, boolean overrideDa } skin = skin0; isHead = isHead0; - // java's operation ↑ + doWorldEdit(getPos1(player), getPos2(player), (location -> { - final Block targetBlock = getPos1(player).getWorld().getBlockAt(location); + final Block targetBlock = location.getBlock(); sfItem.callItemHandler(BlockPlaceHandler.class, h -> h.onPlayerPlace( new BlockPlaceEvent( targetBlock, @@ -923,6 +1024,14 @@ public boolean onCommand(@Nonnull CommandSender sender, @Nonnull Command command } } + case "clone" -> { + if (args.length <= 2) { + worldeditClone(player); + } else if (args.length <= 3) { + worldeditClone(player, "override".equalsIgnoreCase(args[2])); + } + } + case "paste" -> { if (args.length <= 2) { player.sendMessage(getErrorMessage(ErrorType.MISSING_REQUIRED_ARGUMENT, "sfId")); @@ -932,13 +1041,13 @@ public boolean onCommand(@Nonnull CommandSender sender, @Nonnull Command command boolean force = false; switch (args.length) { case 5 -> { - if (args[3].toLowerCase(Locale.ROOT).equals("override")) { + if ("override".equalsIgnoreCase(args[3])) { overrideData = true; } force = Boolean.parseBoolean(args[4]); } case 4 -> { - if (args[3].toLowerCase(Locale.ROOT).equals("override")) { + if ("override".equalsIgnoreCase(args[3])) { overrideData = true; } } @@ -1166,7 +1275,7 @@ public void fixBlueprint(Player player, String before) { case "fillquantum", "addstorageitem", "reducestorageitem", "setquantum" -> List.of(""); case "fixblueprint" -> List.of(""); case "setcontainerid" -> List.of(""); - case "worldedit" -> List.of("pos1", "pos2", "paste", "clear", "blockmenu", "blockinfo"); + case "worldedit" -> List.of("pos1", "pos2", "paste", "clear", "clone", "blockmenu", "blockinfo"); default -> List.of(); }; }